summaryrefslogtreecommitdiffstats
path: root/framework/src/onos/apps/segmentrouting
diff options
context:
space:
mode:
Diffstat (limited to 'framework/src/onos/apps/segmentrouting')
-rw-r--r--framework/src/onos/apps/segmentrouting/pom.xml137
-rw-r--r--framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/ArpHandler.java216
-rw-r--r--framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/DefaultRoutingHandler.java551
-rw-r--r--framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/DefaultTunnel.java111
-rw-r--r--framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/DeviceConfiguration.java404
-rw-r--r--framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/ECMPShortestPathGraph.java374
-rw-r--r--framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/IcmpHandler.java171
-rw-r--r--framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/IpHandler.java152
-rw-r--r--framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/NetworkConfigHandler.java157
-rw-r--r--framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/Policy.java96
-rw-r--r--framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/PolicyHandler.java215
-rw-r--r--framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/RoutingRulePopulator.java430
-rw-r--r--framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java517
-rw-r--r--framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/SegmentRoutingService.java79
-rw-r--r--framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/Tunnel.java67
-rw-r--r--framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/TunnelHandler.java199
-rw-r--r--framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/TunnelPolicy.java291
-rw-r--r--framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/cli/PolicyAddCommand.java132
-rw-r--r--framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/cli/PolicyListCommand.java51
-rw-r--r--framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/cli/PolicyRemoveCommand.java50
-rw-r--r--framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/cli/TunnelAddCommand.java80
-rw-r--r--framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/cli/TunnelListCommand.java45
-rw-r--r--framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/cli/TunnelRemoveCommand.java58
-rw-r--r--framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/cli/package-info.java20
-rw-r--r--framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/config/NetworkConfig.java336
-rw-r--r--framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/config/NetworkConfigException.java200
-rw-r--r--framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/config/NetworkConfigManager.java323
-rw-r--r--framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/config/NetworkConfigService.java256
-rw-r--r--framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/config/PktLinkConfig.java144
-rw-r--r--framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/config/SegmentRouterConfig.java430
-rw-r--r--framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/config/package-info.java20
-rw-r--r--framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/grouphandler/DefaultEdgeGroupHandler.java180
-rw-r--r--framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/grouphandler/DefaultGroupHandler.java539
-rw-r--r--framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/grouphandler/DefaultTransitGroupHandler.java185
-rw-r--r--framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/grouphandler/DeviceProperties.java57
-rw-r--r--framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/grouphandler/GroupBucketIdentifier.java69
-rw-r--r--framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/grouphandler/NeighborSet.java123
-rw-r--r--framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/grouphandler/NeighborSetNextObjectiveStoreKey.java72
-rw-r--r--framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/grouphandler/PolicyGroupHandler.java352
-rw-r--r--framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/grouphandler/PolicyGroupIdentifier.java90
-rw-r--r--framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/grouphandler/PolicyGroupParams.java92
-rw-r--r--framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/grouphandler/package-info.java20
-rw-r--r--framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/package-info.java20
-rw-r--r--framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/web/PolicyCodec.java122
-rw-r--r--framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/web/PolicyWebResource.java108
-rw-r--r--framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/web/TunnelCodec.java66
-rw-r--r--framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/web/TunnelWebResource.java102
-rw-r--r--framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/web/package-info.java20
-rw-r--r--framework/src/onos/apps/segmentrouting/src/main/resources/OSGI-INF/blueprint/shell-config.xml40
-rw-r--r--framework/src/onos/apps/segmentrouting/src/main/webapp/WEB-INF/web.xml44
50 files changed, 8613 insertions, 0 deletions
diff --git a/framework/src/onos/apps/segmentrouting/pom.xml b/framework/src/onos/apps/segmentrouting/pom.xml
new file mode 100644
index 00000000..3ed4c4fe
--- /dev/null
+++ b/framework/src/onos/apps/segmentrouting/pom.xml
@@ -0,0 +1,137 @@
+<?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.3.0-SNAPSHOT</version>
+ <relativePath>../pom.xml</relativePath>
+ </parent>
+
+ <artifactId>onos-app-segmentrouting</artifactId>
+ <packaging>bundle</packaging>
+
+ <description>Segment routing application</description>
+
+ <properties>
+ <onos.app.name>org.onosproject.segmentrouting</onos.app.name>
+ <web.context>/onos/segmentrouting</web.context>
+ <api.version>1.0.0</api.version>
+ <api.title>ONOS Segment Routing REST API</api.title>
+ <api.description>
+ APIs for interacting with the Segment Routing application.
+ </api.description>
+ <api.package>org.onosproject.segmentrouting.web</api.package>
+ </properties>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onos-cli</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.karaf.shell</groupId>
+ <artifactId>org.apache.karaf.shell.console</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onos-rest</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onlab-rest</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>javax.ws.rs</groupId>
+ <artifactId>jsr311-api</artifactId>
+ <version>1.1.1</version>
+ </dependency>
+ <dependency>
+ <groupId>com.sun.jersey</groupId>
+ <artifactId>jersey-servlet</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>com.fasterxml.jackson.core</groupId>
+ <artifactId>jackson-databind</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>com.fasterxml.jackson.core</groupId>
+ <artifactId>jackson-annotations</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.osgi</groupId>
+ <artifactId>org.osgi.compendium</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.osgi</groupId>
+ <artifactId>org.osgi.core</artifactId>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <extensions>true</extensions>
+ <configuration>
+ <instructions>
+ <_wab>src/main/webapp/</_wab>
+ <Include-Resource>
+ WEB-INF/classes/apidoc/swagger.json=target/swagger.json,
+ {maven-resources}
+ </Include-Resource>
+ <Bundle-SymbolicName>
+ ${project.groupId}.${project.artifactId}
+ </Bundle-SymbolicName>
+ <Import-Package>
+ org.slf4j,
+ org.osgi.framework,
+ javax.ws.rs,
+ javax.ws.rs.core,
+ com.sun.jersey.api.core,
+ com.sun.jersey.spi.container.servlet,
+ com.sun.jersey.server.impl.container.servlet,
+ com.fasterxml.jackson.databind,
+ com.fasterxml.jackson.databind.node,
+ com.fasterxml.jackson.core,
+ org.apache.karaf.shell.commands,
+ org.apache.commons.lang.math.*,
+ com.google.common.*,
+ org.onlab.packet.*,
+ org.onlab.rest.*,
+ org.onosproject.*,
+ org.onlab.util.*,
+ org.jboss.netty.util.*
+ </Import-Package>
+ <Web-ContextPath>${web.context}</Web-ContextPath>
+ </instructions>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+
+</project>
diff --git a/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/ArpHandler.java b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/ArpHandler.java
new file mode 100644
index 00000000..6ca6d193
--- /dev/null
+++ b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/ArpHandler.java
@@ -0,0 +1,216 @@
+/*
+ * 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.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 java.util.List;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+public class ArpHandler {
+
+ private static Logger log = LoggerFactory.getLogger(ArpHandler.class);
+
+ private SegmentRoutingManager srManager;
+ private DeviceConfiguration config;
+
+ /**
+ * Creates an ArpHandler object.
+ *
+ * @param srManager SegmentRoutingManager object
+ */
+ public ArpHandler(SegmentRoutingManager srManager) {
+ this.srManager = srManager;
+ this.config = checkNotNull(srManager.deviceConfiguration);
+ }
+
+ /**
+ * 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.getRouterMacForAGatewayIp(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) {
+ List<Ip4Address> gatewayIpAddresses = config.getSubnetGatewayIps(deviceId);
+ if (gatewayIpAddresses != null) {
+ Ip4Address targetProtocolAddress = Ip4Address.valueOf(arpRequest
+ .getTargetProtocolAddress());
+ if (gatewayIpAddresses.contains(targetProtocolAddress)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private boolean isArpReqForSubnet(DeviceId deviceId, ARP arpRequest) {
+ return config.getSubnets(deviceId).stream()
+ .anyMatch((prefix)->
+ prefix.contains(Ip4Address.
+ valueOf(arpRequest.
+ getTargetProtocolAddress())));
+ }
+
+ /**
+ * 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.getDeviceMac(deviceId).toBytes();
+ byte[] senderIpAddress = config.getRouterIp(deviceId).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/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/DefaultRoutingHandler.java b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/DefaultRoutingHandler.java
new file mode 100644
index 00000000..8b447af4
--- /dev/null
+++ b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/DefaultRoutingHandler.java
@@ -0,0 +1,551 @@
+/*
+ * 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.Maps;
+import com.google.common.collect.Sets;
+import org.onlab.packet.Ip4Address;
+import org.onlab.packet.Ip4Prefix;
+import org.onlab.packet.IpPrefix;
+import org.onosproject.net.Device;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.Link;
+import org.onosproject.net.MastershipRole;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
+
+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 HashMap<DeviceId, ECMPShortestPathGraph> currentEcmpSpgMap;
+ private HashMap<DeviceId, ECMPShortestPathGraph> updatedEcmpSpgMap;
+ private DeviceConfiguration config;
+ private final Lock statusLock = new ReentrantLock();
+ private volatile 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.deviceConfiguration);
+ this.populationStatus = Status.IDLE;
+ this.currentEcmpSpgMap = Maps.newHashMap();
+ }
+
+ /**
+ * 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() {
+
+ statusLock.lock();
+ try {
+ populationStatus = Status.STARTED;
+ rulePopulator.resetCounter();
+ log.info("Starts to populate routing rules");
+ log.debug("populateAllRoutingRules: populationStatus is STARTED");
+
+ for (Device sw : srManager.deviceService.getDevices()) {
+ if (srManager.mastershipService.getLocalRole(sw.id()) != MastershipRole.MASTER) {
+ log.debug("populateAllRoutingRules: skipping device {}...we are not master",
+ sw.id());
+ continue;
+ }
+
+ ECMPShortestPathGraph ecmpSpg = new ECMPShortestPathGraph(sw.id(), srManager);
+ if (!populateEcmpRoutingRules(sw.id(), ecmpSpg)) {
+ log.debug("populateAllRoutingRules: populationStatus is ABORTED");
+ populationStatus = Status.ABORTED;
+ log.debug("Abort routing rule population");
+ return false;
+ }
+ currentEcmpSpgMap.put(sw.id(), ecmpSpg);
+
+ // TODO: Set adjacency routing rule for all switches
+ }
+
+ log.debug("populateAllRoutingRules: populationStatus is SUCCEEDED");
+ populationStatus = Status.SUCCEEDED;
+ log.info("Completes routing rule population. Total # of rules pushed : {}",
+ rulePopulator.getCounter());
+ return true;
+ } finally {
+ statusLock.unlock();
+ }
+ }
+
+ /**
+ * Populates the routing rules according to the route changes due to the link
+ * failure or link add. It computes the routes changed due to the link changes and
+ * repopulates the rules only for the routes.
+ *
+ * @param linkFail link failed, null for link added
+ * @return true if it succeeds to populate all rules, false otherwise
+ */
+ public boolean populateRoutingRulesForLinkStatusChange(Link linkFail) {
+
+ statusLock.lock();
+ try {
+
+ if (populationStatus == Status.STARTED) {
+ log.warn("Previous rule population is not finished.");
+ return true;
+ }
+
+ // Take the snapshots of the links
+ updatedEcmpSpgMap = new HashMap<>();
+ for (Device sw : srManager.deviceService.getDevices()) {
+ if (srManager.mastershipService.
+ getLocalRole(sw.id()) != MastershipRole.MASTER) {
+ continue;
+ }
+ ECMPShortestPathGraph ecmpSpgUpdated =
+ new ECMPShortestPathGraph(sw.id(), srManager);
+ updatedEcmpSpgMap.put(sw.id(), ecmpSpgUpdated);
+ }
+
+ log.info("Starts rule population from link change");
+
+ Set<ArrayList<DeviceId>> routeChanges;
+ log.trace("populateRoutingRulesForLinkStatusChange: "
+ + "populationStatus is STARTED");
+ populationStatus = Status.STARTED;
+ if (linkFail == null) {
+ // Compare all routes of existing ECMP SPG with the new ones
+ routeChanges = computeRouteChange();
+ } else {
+ // Compare existing ECMP SPG only with the link removed
+ routeChanges = computeDamagedRoutes(linkFail);
+ }
+
+ if (routeChanges.isEmpty()) {
+ log.info("No route changes for the link status change");
+ log.debug("populateRoutingRulesForLinkStatusChange: populationStatus is SUCCEEDED");
+ populationStatus = Status.SUCCEEDED;
+ return true;
+ }
+
+ if (repopulateRoutingRulesForRoutes(routeChanges)) {
+ log.debug("populateRoutingRulesForLinkStatusChange: populationStatus is SUCCEEDED");
+ populationStatus = Status.SUCCEEDED;
+ log.info("Complete to repopulate the rules. # of rules populated : {}",
+ rulePopulator.getCounter());
+ return true;
+ } else {
+ log.debug("populateRoutingRulesForLinkStatusChange: populationStatus is ABORTED");
+ populationStatus = Status.ABORTED;
+ log.warn("Failed to repopulate the rules.");
+ return false;
+ }
+ } finally {
+ statusLock.unlock();
+ }
+ }
+
+ private boolean repopulateRoutingRulesForRoutes(Set<ArrayList<DeviceId>> routes) {
+ rulePopulator.resetCounter();
+ HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>> routesBydevice =
+ new HashMap<>();
+ for (ArrayList<DeviceId> link: routes) {
+ // When only the source device is defined, reinstall routes to all other devices
+ if (link.size() == 1) {
+ log.trace("repopulateRoutingRulesForRoutes: running ECMP graph for device {}", link.get(0));
+ ECMPShortestPathGraph ecmpSpg = new ECMPShortestPathGraph(link.get(0), srManager);
+ if (populateEcmpRoutingRules(link.get(0), ecmpSpg)) {
+ log.debug("Populating flow rules from {} to all is successful",
+ link.get(0));
+ currentEcmpSpgMap.put(link.get(0), ecmpSpg);
+ } else {
+ log.warn("Failed to populate the flow rules from {} to all", link.get(0));
+ return false;
+ }
+ } else {
+ ArrayList<ArrayList<DeviceId>> deviceRoutes =
+ routesBydevice.get(link.get(1));
+ if (deviceRoutes == null) {
+ deviceRoutes = new ArrayList<>();
+ routesBydevice.put(link.get(1), deviceRoutes);
+ }
+ deviceRoutes.add(link);
+ }
+ }
+
+ for (DeviceId impactedDevice : routesBydevice.keySet()) {
+ ArrayList<ArrayList<DeviceId>> deviceRoutes =
+ routesBydevice.get(impactedDevice);
+ for (ArrayList<DeviceId> link: deviceRoutes) {
+ log.debug("repopulate RoutingRules For Routes {} -> {}",
+ link.get(0), link.get(1));
+ DeviceId src = link.get(0);
+ DeviceId dst = link.get(1);
+ ECMPShortestPathGraph ecmpSpg = updatedEcmpSpgMap.get(dst);
+ 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()) {
+ if (!targetSw.equals(src)) {
+ continue;
+ }
+ Set<DeviceId> nextHops = new HashSet<>();
+ for (ArrayList<DeviceId> via : swViaMap.get(targetSw)) {
+ if (via.isEmpty()) {
+ nextHops.add(dst);
+ } else {
+ nextHops.add(via.get(0));
+ }
+ }
+ if (!populateEcmpRoutingRulePartial(targetSw, dst, nextHops)) {
+ return false;
+ }
+ log.debug("Populating flow rules from {} to {} is successful",
+ targetSw, dst);
+ }
+ }
+ //currentEcmpSpgMap.put(dst, ecmpSpg);
+ }
+ //Only if all the flows for all impacted routes to a
+ //specific target are pushed successfully, update the
+ //ECMP graph for that target. (Or else the next event
+ //would not see any changes in the ECMP graphs)
+ currentEcmpSpgMap.put(impactedDevice,
+ updatedEcmpSpgMap.get(impactedDevice));
+ }
+ return true;
+ }
+
+ private Set<ArrayList<DeviceId>> computeDamagedRoutes(Link linkFail) {
+
+ Set<ArrayList<DeviceId>> routes = new HashSet<>();
+
+ for (Device sw : srManager.deviceService.getDevices()) {
+ log.debug("Computing the impacted routes for device {} due to link fail",
+ sw.id());
+ if (srManager.mastershipService.
+ getLocalRole(sw.id()) != MastershipRole.MASTER) {
+ continue;
+ }
+ ECMPShortestPathGraph ecmpSpg = currentEcmpSpgMap.get(sw.id());
+ if (ecmpSpg == null) {
+ log.error("No existing ECMP graph for switch {}", sw.id());
+ continue;
+ }
+ 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<ArrayList<DeviceId>> subLinks =
+ computeLinks(targetSw, destSw, swViaMap);
+ for (ArrayList<DeviceId> alink: subLinks) {
+ if ((alink.get(0).equals(linkFail.src().deviceId()) &&
+ alink.get(1).equals(linkFail.dst().deviceId()))
+ ||
+ (alink.get(0).equals(linkFail.dst().deviceId()) &&
+ alink.get(1).equals(linkFail.src().deviceId()))) {
+ log.debug("Impacted route:{}->{}", targetSw, destSw);
+ ArrayList<DeviceId> aRoute = new ArrayList<>();
+ aRoute.add(targetSw);
+ aRoute.add(destSw);
+ routes.add(aRoute);
+ break;
+ }
+ }
+ }
+ }
+
+ }
+
+ return routes;
+ }
+
+ private Set<ArrayList<DeviceId>> computeRouteChange() {
+
+ Set<ArrayList<DeviceId>> routes = new HashSet<>();
+
+ for (Device sw : srManager.deviceService.getDevices()) {
+ log.debug("Computing the impacted routes for device {}",
+ sw.id());
+ if (srManager.mastershipService.
+ getLocalRole(sw.id()) != MastershipRole.MASTER) {
+ log.debug("No mastership for {} and skip route optimization",
+ sw.id());
+ continue;
+ }
+
+ log.trace("link of {} - ", sw.id());
+ for (Link link: srManager.linkService.getDeviceLinks(sw.id())) {
+ log.trace("{} -> {} ", link.src().deviceId(), link.dst().deviceId());
+ }
+
+ log.debug("Checking route change for switch {}", sw.id());
+ ECMPShortestPathGraph ecmpSpg = currentEcmpSpgMap.get(sw.id());
+ if (ecmpSpg == null) {
+ log.debug("No existing ECMP graph for device {}", sw.id());
+ ArrayList<DeviceId> route = new ArrayList<>();
+ route.add(sw.id());
+ routes.add(route);
+ continue;
+ }
+ ECMPShortestPathGraph newEcmpSpg = updatedEcmpSpgMap.get(sw.id());
+ //currentEcmpSpgMap.put(sw.id(), newEcmpSpg);
+ HashMap<Integer, HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>>> switchVia =
+ ecmpSpg.getAllLearnedSwitchesAndVia();
+ HashMap<Integer, HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>>> switchViaUpdated =
+ newEcmpSpg.getAllLearnedSwitchesAndVia();
+
+ for (Integer itrIdx : switchViaUpdated.keySet()) {
+ HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>> swViaMapUpdated =
+ switchViaUpdated.get(itrIdx);
+ for (DeviceId srcSw : swViaMapUpdated.keySet()) {
+ ArrayList<ArrayList<DeviceId>> viaUpdated = swViaMapUpdated.get(srcSw);
+ ArrayList<ArrayList<DeviceId>> via = getVia(switchVia, srcSw);
+ if ((via == null) || !viaUpdated.equals(via)) {
+ log.debug("Impacted route:{}->{}", srcSw, sw.id());
+ ArrayList<DeviceId> route = new ArrayList<>();
+ route.add(srcSw);
+ route.add(sw.id());
+ routes.add(route);
+ }
+ }
+ }
+ }
+
+ for (ArrayList<DeviceId> link: routes) {
+ log.trace("Route changes - ");
+ if (link.size() == 1) {
+ log.trace(" : {} - all", link.get(0));
+ } else {
+ log.trace(" : {} - {}", link.get(0), link.get(1));
+ }
+ }
+
+ return routes;
+ }
+
+ private ArrayList<ArrayList<DeviceId>> getVia(HashMap<Integer, HashMap<DeviceId,
+ ArrayList<ArrayList<DeviceId>>>> switchVia, DeviceId srcSw) {
+ for (Integer itrIdx : switchVia.keySet()) {
+ HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>> swViaMap =
+ switchVia.get(itrIdx);
+ if (swViaMap.get(srcSw) == null) {
+ continue;
+ } else {
+ return swViaMap.get(srcSw);
+ }
+ }
+
+ return null;
+ }
+
+ private Set<ArrayList<DeviceId>> computeLinks(DeviceId src,
+ DeviceId dst,
+ HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>> viaMap) {
+ Set<ArrayList<DeviceId>> subLinks = Sets.newHashSet();
+ for (ArrayList<DeviceId> via : viaMap.get(src)) {
+ DeviceId linkSrc = src;
+ DeviceId linkDst = dst;
+ for (DeviceId viaDevice: via) {
+ ArrayList<DeviceId> link = new ArrayList<>();
+ linkDst = viaDevice;
+ link.add(linkSrc);
+ link.add(linkDst);
+ subLinks.add(link);
+ linkSrc = viaDevice;
+ }
+ ArrayList<DeviceId> link = new ArrayList<>();
+ link.add(linkSrc);
+ link.add(dst);
+ subLinks.add(link);
+ }
+
+ return subLinks;
+ }
+
+ private boolean populateEcmpRoutingRules(DeviceId destSw,
+ 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()) {
+ 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.isEdgeDevice(targetSw) && config.isEdgeDevice(destSw)) {
+ List<Ip4Prefix> subnets = config.getSubnets(destSw);
+ log.debug("populateEcmpRoutingRulePartial in device {} towards {} for subnets {}",
+ targetSw, destSw, subnets);
+ result = rulePopulator.populateIpRuleForSubnet(targetSw,
+ subnets,
+ destSw,
+ nextHops);
+ if (!result) {
+ return false;
+ }
+
+ Ip4Address routerIp = config.getRouterIp(destSw);
+ IpPrefix routerIpPrefix = IpPrefix.valueOf(routerIp, IpPrefix.MAX_INET_MASK_LENGTH);
+ log.debug("populateEcmpRoutingRulePartial in device {} towards {} for router IP {}",
+ targetSw, destSw, routerIpPrefix);
+ result = rulePopulator.populateIpRuleForRouter(targetSw, routerIpPrefix, 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.isEdgeDevice(targetSw)) {
+ Ip4Address routerIp = config.getRouterIp(destSw);
+ IpPrefix routerIpPrefix = IpPrefix.valueOf(routerIp, IpPrefix.MAX_INET_MASK_LENGTH);
+ log.debug("populateEcmpRoutingRulePartial in device {} towards {} for router IP {}",
+ targetSw, destSw, routerIpPrefix);
+ result = rulePopulator.populateIpRuleForRouter(targetSw, routerIpPrefix, destSw, nextHops);
+ if (!result) {
+ return false;
+ }
+ }
+
+ // Populates MPLS rules to all routers
+ log.debug("populateEcmpRoutingRulePartial in device{} towards {} for all MPLS rules",
+ targetSw, destSw);
+ result = rulePopulator.populateMplsRule(targetSw, destSw, nextHops);
+ if (!result) {
+ 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.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() {
+ statusLock.lock();
+ try {
+ if (populationStatus == Status.IDLE
+ || populationStatus == Status.SUCCEEDED
+ || populationStatus == Status.ABORTED) {
+ populationStatus = Status.STARTED;
+ populateAllRoutingRules();
+ } else {
+ log.warn("Not initiating startPopulationProcess as populationStatus is {}",
+ populationStatus);
+ }
+ } finally {
+ statusLock.unlock();
+ }
+ }
+
+ /**
+ * 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() {
+ statusLock.lock();
+ try {
+ if (populationStatus == Status.ABORTED) {
+ populationStatus = Status.STARTED;
+ // TODO: we need to restart from the point aborted instead of
+ // restarting.
+ populateAllRoutingRules();
+ }
+ } finally {
+ statusLock.unlock();
+ }
+ }
+}
diff --git a/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/DefaultTunnel.java b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/DefaultTunnel.java
new file mode 100644
index 00000000..8c6fbe8d
--- /dev/null
+++ b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/DefaultTunnel.java
@@ -0,0 +1,111 @@
+/*
+ * 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.util.List;
+import java.util.Objects;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Tunnel class.
+ */
+public class DefaultTunnel implements Tunnel {
+
+ private final String id;
+ private final List<Integer> labelIds;
+
+ private int groupId;
+ private boolean allowedToRemoveGroup;
+
+ /**
+ * Creates a Tunnel reference.
+ *
+ * @param tid Tunnel ID
+ * @param labelIds Label stack of the tunnel
+ */
+ public DefaultTunnel(String tid, List<Integer> labelIds) {
+ this.id = checkNotNull(tid);
+ this.labelIds = labelIds;
+ //TODO: need to register the class in Kryo for this
+ //this.labelIds = Collections.unmodifiableList(labelIds);
+ this.groupId = -1;
+ }
+
+ /**
+ * Creates a new DefaultTunnel reference using the tunnel reference.
+ *
+ * @param tunnel DefaultTunnel reference
+ */
+ public DefaultTunnel(DefaultTunnel tunnel) {
+ this.id = tunnel.id;
+ this.labelIds = tunnel.labelIds;
+ this.groupId = tunnel.groupId;
+ }
+
+ @Override
+ public String id() {
+ return this.id;
+ }
+
+ @Override
+ public List<Integer> labelIds() {
+ return this.labelIds;
+ }
+
+ @Override
+ public int groupId() {
+ return this.groupId;
+ }
+
+ @Override
+ public void setGroupId(int id) {
+ this.groupId = id;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+
+ if (o instanceof DefaultTunnel) {
+ DefaultTunnel tunnel = (DefaultTunnel) o;
+ // We compare only the tunnel paths.
+ if (tunnel.labelIds.equals(this.labelIds)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(labelIds);
+ }
+
+ @Override
+ public boolean isAllowedToRemoveGroup() {
+ return this.allowedToRemoveGroup;
+ }
+
+ @Override
+ public void allowToRemoveGroup(boolean b) {
+ this.allowedToRemoveGroup = b;
+ }
+}
diff --git a/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/DeviceConfiguration.java b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/DeviceConfiguration.java
new file mode 100644
index 00000000..d82eb5ec
--- /dev/null
+++ b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/DeviceConfiguration.java
@@ -0,0 +1,404 @@
+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.segmentrouting.grouphandler.DeviceProperties;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.PortNumber;
+import org.onosproject.segmentrouting.config.NetworkConfig.SwitchConfig;
+import org.onosproject.segmentrouting.config.NetworkConfigManager;
+import org.onosproject.segmentrouting.config.SegmentRouterConfig;
+import org.onosproject.segmentrouting.config.SegmentRouterConfig.Subnet;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Segment Routing configuration component that reads the
+ * segment routing related configuration from Network Configuration Manager
+ * component and organizes in more accessible formats.
+ *
+ * TODO: Merge multiple Segment Routing configuration wrapper classes into one.
+ */
+public class DeviceConfiguration implements DeviceProperties {
+
+ private static final Logger log = LoggerFactory
+ .getLogger(DeviceConfiguration.class);
+ private final List<Integer> allSegmentIds = new ArrayList<Integer>();
+ private final HashMap<DeviceId, SegmentRouterInfo> deviceConfigMap = new HashMap<>();
+ private final NetworkConfigManager configService;
+
+ private class SegmentRouterInfo {
+ int nodeSid;
+ DeviceId deviceId;
+ Ip4Address ip;
+ MacAddress mac;
+ boolean isEdge;
+ HashMap<PortNumber, Ip4Address> gatewayIps;
+ HashMap<PortNumber, Ip4Prefix> subnets;
+ List<SegmentRouterConfig.AdjacencySid> adjacencySids;
+ }
+
+ /**
+ * Constructor. Reads all the configuration for all devices of type
+ * Segment Router and organizes into various maps for easier access.
+ *
+ * @param configService handle to network configuration manager
+ * component from where the relevant configuration is retrieved.
+ */
+ public DeviceConfiguration(NetworkConfigManager configService) {
+ this.configService = checkNotNull(configService);
+ List<SwitchConfig> allSwitchCfg =
+ this.configService.getConfiguredAllowedSwitches();
+ for (SwitchConfig cfg : allSwitchCfg) {
+ if (!(cfg instanceof SegmentRouterConfig)) {
+ continue;
+ }
+ SegmentRouterInfo info = new SegmentRouterInfo();
+ info.nodeSid = ((SegmentRouterConfig) cfg).getNodeSid();
+ info.deviceId = ((SegmentRouterConfig) cfg).getDpid();
+ info.mac = MacAddress.valueOf(((
+ SegmentRouterConfig) cfg).getRouterMac());
+ String routerIp = ((SegmentRouterConfig) cfg).getRouterIp();
+ Ip4Prefix prefix = checkNotNull(IpPrefix.valueOf(routerIp).getIp4Prefix());
+ info.ip = prefix.address();
+ info.isEdge = ((SegmentRouterConfig) cfg).isEdgeRouter();
+ info.subnets = new HashMap<>();
+ info.gatewayIps = new HashMap<PortNumber, Ip4Address>();
+ for (Subnet s: ((SegmentRouterConfig) cfg).getSubnets()) {
+ info.subnets.put(PortNumber.portNumber(s.getPortNo()),
+ Ip4Prefix.valueOf(s.getSubnetIp()));
+ String gatewayIp = s.getSubnetIp().
+ substring(0, s.getSubnetIp().indexOf('/'));
+ info.gatewayIps.put(PortNumber.portNumber(s.getPortNo()),
+ Ip4Address.valueOf(gatewayIp));
+ }
+ info.adjacencySids = ((SegmentRouterConfig) cfg).getAdjacencySids();
+ this.deviceConfigMap.put(info.deviceId, info);
+ this.allSegmentIds.add(info.nodeSid);
+
+ }
+ }
+
+ /**
+ * Returns the segment id of a segment router.
+ *
+ * @param deviceId device identifier
+ * @return segment id
+ */
+ @Override
+ public int getSegmentId(DeviceId deviceId) {
+ if (deviceConfigMap.get(deviceId) != null) {
+ log.debug("getSegmentId for device{} is {}",
+ deviceId,
+ deviceConfigMap.get(deviceId).nodeSid);
+ return deviceConfigMap.get(deviceId).nodeSid;
+ } else {
+ log.warn("getSegmentId for device {} "
+ + "throwing IllegalStateException "
+ + "because device does not exist in config", deviceId);
+ throw new IllegalStateException();
+ }
+ }
+
+ /**
+ * Returns the segment id of a segment router given its mac address.
+ *
+ * @param routerMac router mac address
+ * @return segment id
+ */
+ public int getSegmentId(MacAddress routerMac) {
+ for (Map.Entry<DeviceId, SegmentRouterInfo> entry:
+ deviceConfigMap.entrySet()) {
+ if (entry.getValue().mac.equals(routerMac)) {
+ return entry.getValue().nodeSid;
+ }
+ }
+
+ return -1;
+ }
+
+ /**
+ * Returns the segment id of a segment router given its router ip address.
+ *
+ * @param routerAddress router ip address
+ * @return segment id
+ */
+ public int getSegmentId(Ip4Address routerAddress) {
+ for (Map.Entry<DeviceId, SegmentRouterInfo> entry:
+ deviceConfigMap.entrySet()) {
+ if (entry.getValue().ip.equals(routerAddress)) {
+ return entry.getValue().nodeSid;
+ }
+ }
+
+ return -1;
+ }
+
+ /**
+ * Returns the router mac of a segment router.
+ *
+ * @param deviceId device identifier
+ * @return router mac address
+ */
+ @Override
+ public MacAddress getDeviceMac(DeviceId deviceId) {
+ if (deviceConfigMap.get(deviceId) != null) {
+ log.debug("getDeviceMac for device{} is {}",
+ deviceId,
+ deviceConfigMap.get(deviceId).mac);
+ return deviceConfigMap.get(deviceId).mac;
+ } else {
+ log.warn("getDeviceMac for device {} "
+ + "throwing IllegalStateException "
+ + "because device does not exist in config", deviceId);
+ throw new IllegalStateException();
+ }
+ }
+
+ /**
+ * Returns the router ip address of a segment router.
+ *
+ * @param deviceId device identifier
+ * @return router ip address
+ */
+ public Ip4Address getRouterIp(DeviceId deviceId) {
+ if (deviceConfigMap.get(deviceId) != null) {
+ log.debug("getDeviceIp for device{} is {}",
+ deviceId,
+ deviceConfigMap.get(deviceId).ip);
+ return deviceConfigMap.get(deviceId).ip;
+ } else {
+ log.warn("getRouterIp for device {} "
+ + "throwing IllegalStateException "
+ + "because device does not exist in config", deviceId);
+ throw new IllegalStateException();
+ }
+ }
+
+ /**
+ * Indicates if the segment router is a edge router or
+ * a transit/back bone router.
+ *
+ * @param deviceId device identifier
+ * @return boolean
+ */
+ @Override
+ public boolean isEdgeDevice(DeviceId deviceId) {
+ if (deviceConfigMap.get(deviceId) != null) {
+ log.debug("isEdgeDevice for device{} is {}",
+ deviceId,
+ deviceConfigMap.get(deviceId).isEdge);
+ return deviceConfigMap.get(deviceId).isEdge;
+ } else {
+ log.warn("isEdgeDevice for device {} "
+ + "throwing IllegalStateException "
+ + "because device does not exist in config", deviceId);
+ throw new IllegalStateException();
+ }
+ }
+
+ /**
+ * Returns the segment ids of all configured segment routers.
+ *
+ * @return list of segment ids
+ */
+ @Override
+ public List<Integer> getAllDeviceSegmentIds() {
+ return allSegmentIds;
+ }
+
+ /**
+ * Returns the device identifier or data plane identifier (dpid)
+ * of a segment router given its segment id.
+ *
+ * @param sid segment id
+ * @return deviceId device identifier
+ */
+ public DeviceId getDeviceId(int sid) {
+ for (Map.Entry<DeviceId, SegmentRouterInfo> entry:
+ deviceConfigMap.entrySet()) {
+ if (entry.getValue().nodeSid == sid) {
+ return entry.getValue().deviceId;
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Returns the device identifier or data plane identifier (dpid)
+ * of a segment router given its router ip address.
+ *
+ * @param ipAddress router ip address
+ * @return deviceId device identifier
+ */
+ public DeviceId getDeviceId(Ip4Address ipAddress) {
+ for (Map.Entry<DeviceId, SegmentRouterInfo> entry:
+ deviceConfigMap.entrySet()) {
+ if (entry.getValue().ip.equals(ipAddress)) {
+ return entry.getValue().deviceId;
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Returns the configured subnet gateway ip addresses for a segment router.
+ *
+ * @param deviceId device identifier
+ * @return list of ip addresses
+ */
+ public List<Ip4Address> getSubnetGatewayIps(DeviceId deviceId) {
+ if (deviceConfigMap.get(deviceId) != null) {
+ log.debug("getSubnetGatewayIps for device{} is {}",
+ deviceId,
+ deviceConfigMap.get(deviceId).gatewayIps.values());
+ return new ArrayList<Ip4Address>(deviceConfigMap.
+ get(deviceId).gatewayIps.values());
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Returns the configured subnet prefixes for a segment router.
+ *
+ * @param deviceId device identifier
+ * @return list of ip prefixes
+ */
+ public List<Ip4Prefix> getSubnets(DeviceId deviceId) {
+ if (deviceConfigMap.get(deviceId) != null) {
+ log.debug("getSubnets for device{} is {}",
+ deviceId,
+ deviceConfigMap.get(deviceId).subnets.values());
+ return new ArrayList<Ip4Prefix>(deviceConfigMap.
+ get(deviceId).subnets.values());
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Returns the router ip address of segment router that has the
+ * specified ip address in its subnets.
+ *
+ * @param destIpAddress target ip address
+ * @return router ip address
+ */
+ public Ip4Address getRouterIpAddressForASubnetHost(Ip4Address destIpAddress) {
+ for (Map.Entry<DeviceId, SegmentRouterInfo> entry:
+ deviceConfigMap.entrySet()) {
+ for (Ip4Prefix prefix:entry.getValue().subnets.values()) {
+ if (prefix.contains(destIpAddress)) {
+ return entry.getValue().ip;
+ }
+ }
+ }
+
+ log.debug("No router was found for {}", destIpAddress);
+ return null;
+ }
+
+ /**
+ * Returns the router mac address of segment router that has the
+ * specified ip address as one of its subnet gateway ip address.
+ *
+ * @param gatewayIpAddress router gateway ip address
+ * @return router mac address
+ */
+ public MacAddress getRouterMacForAGatewayIp(Ip4Address gatewayIpAddress) {
+ for (Map.Entry<DeviceId, SegmentRouterInfo> entry:
+ deviceConfigMap.entrySet()) {
+ if (entry.getValue().gatewayIps.
+ values().contains(gatewayIpAddress)) {
+ return entry.getValue().mac;
+ }
+ }
+
+ log.debug("Cannot find a router for {}", gatewayIpAddress);
+ return null;
+ }
+
+
+ /**
+ * Checks if the host is in the subnet defined in the router with the
+ * device ID given.
+ *
+ * @param deviceId device identification of the router
+ * @param hostIp host IP address to check
+ * @return true if the host is within the subnet of the router,
+ * false if no subnet is defined under the router or if the host is not
+ * within the subnet defined in the router
+ */
+ public boolean inSameSubnet(DeviceId deviceId, Ip4Address hostIp) {
+
+ List<Ip4Prefix> subnets = getSubnets(deviceId);
+ if (subnets == null) {
+ return false;
+ }
+
+ for (Ip4Prefix subnet: subnets) {
+ if (subnet.contains(hostIp)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Returns the ports corresponding to the adjacency Sid given.
+ *
+ * @param deviceId device identification of the router
+ * @param sid adjacency Sid
+ * @return list of port numbers
+ */
+ public List<Integer> getPortsForAdjacencySid(DeviceId deviceId, int sid) {
+ if (deviceConfigMap.get(deviceId) != null) {
+ for (SegmentRouterConfig.AdjacencySid asid : deviceConfigMap.get(deviceId).adjacencySids) {
+ if (asid.getAdjSid() == sid) {
+ return asid.getPorts();
+ }
+ }
+ }
+
+ return Lists.newArrayList();
+ }
+
+ /**
+ * Check if the Sid given is whether adjacency Sid of the router device or not.
+ *
+ * @param deviceId device identification of the router
+ * @param sid Sid to check
+ * @return true if the Sid given is the adjacency Sid of the device,
+ * otherwise false
+ */
+ public boolean isAdjacencySid(DeviceId deviceId, int sid) {
+ if (deviceConfigMap.get(deviceId) != null) {
+ if (deviceConfigMap.get(deviceId).adjacencySids.isEmpty()) {
+ return false;
+ } else {
+ for (SegmentRouterConfig.AdjacencySid asid:
+ deviceConfigMap.get(deviceId).adjacencySids) {
+ if (asid.getAdjSid() == sid) {
+ return true;
+ }
+ }
+ return false;
+ }
+ }
+
+ return false;
+ }
+}
diff --git a/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/ECMPShortestPathGraph.java b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/ECMPShortestPathGraph.java
new file mode 100644
index 00000000..cd28fcf9
--- /dev/null
+++ b/framework/src/onos/apps/segmentrouting/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/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/IcmpHandler.java b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/IcmpHandler.java
new file mode 100644
index 00000000..0f8fa59d
--- /dev/null
+++ b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/IcmpHandler.java
@@ -0,0 +1,171 @@
+/*
+ * 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.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 java.nio.ByteBuffer;
+import java.util.List;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+public class IcmpHandler {
+
+ private static Logger log = LoggerFactory.getLogger(IcmpHandler.class);
+ private SegmentRoutingManager srManager;
+ private DeviceConfiguration config;
+
+ /**
+ * Creates an IcmpHandler object.
+ *
+ * @param srManager SegmentRoutingManager object
+ */
+ public IcmpHandler(SegmentRoutingManager srManager) {
+ this.srManager = srManager;
+ this.config = checkNotNull(srManager.deviceConfiguration);
+ }
+
+ /**
+ * 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 inbound 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());
+ List<Ip4Address> gatewayIpAddresses = config.getSubnetGatewayIps(deviceId);
+ Ip4Address routerIp = config.getRouterIp(deviceId);
+ IpPrefix routerIpPrefix = IpPrefix.valueOf(routerIp, IpPrefix.MAX_INET_MASK_LENGTH);
+ 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) ||
+ gatewayIpAddresses.contains(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 = new ICMP();
+ 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.getRouterIpAddressForASubnetHost(destIpAddress);
+ int sid = config.getSegmentId(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.getSegmentId(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/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/IpHandler.java b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/IpHandler.java
new file mode 100644
index 00000000..81d00f50
--- /dev/null
+++ b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/IpHandler.java
@@ -0,0 +1,152 @@
+/*
+ * 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 DeviceConfiguration 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.deviceConfiguration);
+ 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) {
+ if (ipPacketQueue.get(destIpAddress) == null) {
+ return;
+ }
+
+ 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.getDeviceMac(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);
+ ipPacketQueue.get(destIpAddress).remove(ipPacket);
+ }
+ ipPacketQueue.get(destIpAddress).remove(ipPacket);
+ }
+ }
+ }
+
+}
diff --git a/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/NetworkConfigHandler.java b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/NetworkConfigHandler.java
new file mode 100644
index 00000000..d3468178
--- /dev/null
+++ b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/NetworkConfigHandler.java
@@ -0,0 +1,157 @@
+/*
+ * 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.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.
+ *
+ * TODO: Knock off this wrapper and directly use DeviceConfiguration class
+ */
+
+public class NetworkConfigHandler {
+
+ private static Logger log = LoggerFactory.getLogger(NetworkConfigHandler.class);
+ private SegmentRoutingManager srManager;
+ private DeviceConfiguration deviceConfig;
+
+ public NetworkConfigHandler(SegmentRoutingManager srManager,
+ DeviceConfiguration deviceConfig) {
+ this.srManager = srManager;
+ this.deviceConfig = deviceConfig;
+ }
+
+ public List<Ip4Address> getGatewayIpAddress(DeviceId deviceId) {
+ return this.deviceConfig.getSubnetGatewayIps(deviceId);
+ }
+
+ 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) {
+
+ List<Ip4Prefix> subnets = getSubnetInfo(deviceId);
+ if (subnets == null) {
+ return false;
+ }
+
+ return subnets.stream()
+ .anyMatch((subnet) -> subnet.contains(destIp));
+ }
+
+ 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;
+ }
+
+ return inSameSubnet(deviceId, address);
+ }
+
+ public List<Ip4Prefix> getSubnetInfo(DeviceId deviceId) {
+ return deviceConfig.getSubnets(deviceId);
+ }
+
+ public int getMplsId(DeviceId deviceId) {
+ return deviceConfig.getSegmentId(deviceId);
+ }
+
+ public int getMplsId(MacAddress routerMac) {
+ return deviceConfig.getSegmentId(routerMac);
+ }
+
+ public int getMplsId(Ip4Address routerIpAddress) {
+ return deviceConfig.getSegmentId(routerIpAddress);
+ }
+
+ public boolean isEcmpNotSupportedInTransit(DeviceId deviceId) {
+ //TODO: temporarily changing to true to test with Dell
+ return true;
+ }
+
+ public boolean isTransitRouter(DeviceId deviceId) {
+ return !(deviceConfig.isEdgeDevice(deviceId));
+ }
+
+
+ public boolean isEdgeRouter(DeviceId deviceId) {
+ return deviceConfig.isEdgeDevice(deviceId);
+ }
+
+ 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) {
+ return deviceConfig.getRouterIpAddressForASubnetHost(destIpAddress);
+ }
+
+ public DeviceId getDeviceId(Ip4Address ip4Address) {
+ return deviceConfig.getDeviceId(ip4Address);
+ }
+
+ public MacAddress getRouterMac(Ip4Address targetAddress) {
+ return deviceConfig.getRouterMacForAGatewayIp(targetAddress);
+ }
+}
diff --git a/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/Policy.java b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/Policy.java
new file mode 100644
index 00000000..2e417959
--- /dev/null
+++ b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/Policy.java
@@ -0,0 +1,96 @@
+/*
+ * 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;
+
+/**
+ * Interface for Segment Routing Policy.
+ */
+public interface Policy {
+ /**
+ * Enums for policy type.
+ */
+ enum Type {
+ // Tunnel flow policy type
+ TUNNEL_FLOW,
+
+ // Load balancing policy type
+ LOADBALANCE,
+
+ // policy to avoid specific routers or links
+ AVOID,
+
+ // Access Control policy type
+ DENY
+ }
+
+ /**
+ * Returns the policy ID.
+ *
+ * @return policy ID
+ */
+ String id();
+
+ /**
+ * Returns the priority of the policy.
+ *
+ * @return priority
+ */
+ int priority();
+
+ /**
+ * Returns the policy type.
+ *
+ * @return policy type
+ */
+ Type type();
+
+ /**
+ * Returns the source IP address of the policy.
+ *
+ * @return source IP address
+ */
+ String srcIp();
+
+ /**
+ * Returns the destination IP address of the policy.
+ *
+ * @return destination IP address
+ */
+ String dstIp();
+
+ /**
+ * Returns the IP protocol of the policy.
+ *
+ * @return IP protocol
+ */
+ String ipProto();
+
+ /**
+ * Returns the source port of the policy.
+ *
+ * @return source port
+ */
+ short srcPort();
+
+ /**
+ * Returns the destination of the policy.
+ *
+ * @return destination port
+ */
+ short dstPort();
+
+}
diff --git a/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/PolicyHandler.java b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/PolicyHandler.java
new file mode 100644
index 00000000..83cb7e86
--- /dev/null
+++ b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/PolicyHandler.java
@@ -0,0 +1,215 @@
+/*
+ * 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.IpPrefix;
+import org.onlab.packet.TpPort;
+import org.onosproject.cli.net.IpProtocol;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.flow.DefaultTrafficSelector;
+import org.onosproject.net.flow.TrafficSelector;
+import org.onosproject.net.flowobjective.DefaultForwardingObjective;
+import org.onosproject.net.flowobjective.FlowObjectiveService;
+import org.onosproject.net.flowobjective.ForwardingObjective;
+import org.onosproject.store.service.EventuallyConsistentMap;
+import org.slf4j.Logger;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Segment Routing Policy Handler.
+ */
+public class PolicyHandler {
+
+ protected final Logger log = getLogger(getClass());
+
+ private ApplicationId appId;
+ private DeviceConfiguration deviceConfiguration;
+ private FlowObjectiveService flowObjectiveService;
+ private TunnelHandler tunnelHandler;
+ private final EventuallyConsistentMap<String, Policy> policyStore;
+
+ public enum Result {
+ SUCCESS,
+ POLICY_EXISTS,
+ ID_EXISTS,
+ TUNNEL_NOT_FOUND,
+ POLICY_NOT_FOUND,
+ UNSUPPORTED_TYPE
+ }
+
+ /**
+ * Creates a reference.
+ *
+ * @param appId segment routing application ID
+ * @param deviceConfiguration DeviceConfiguration reference
+ * @param flowObjectiveService FlowObjectiveService reference
+ * @param tunnelHandler tunnel handler reference
+ * @param policyStore policy store
+ */
+ public PolicyHandler(ApplicationId appId,
+ DeviceConfiguration deviceConfiguration,
+ FlowObjectiveService flowObjectiveService,
+ TunnelHandler tunnelHandler,
+ EventuallyConsistentMap<String, Policy> policyStore) {
+ this.appId = appId;
+ this.deviceConfiguration = deviceConfiguration;
+ this.flowObjectiveService = flowObjectiveService;
+ this.tunnelHandler = tunnelHandler;
+ this.policyStore = policyStore;
+ }
+
+ /**
+ * Returns the policies.
+ *
+ * @return policy list
+ */
+ public List<Policy> getPolicies() {
+ return policyStore.values()
+ .stream()
+ .filter(policy -> policy instanceof TunnelPolicy)
+ .map(policy -> new TunnelPolicy((TunnelPolicy) policy))
+ .collect(Collectors.toList());
+ }
+
+ /**
+ * Creates a policy using the policy information given.
+ * @param policy policy reference to create
+ * @return ID_EXISTS if the same policy ID exists,
+ * POLICY_EXISTS if the same policy exists, TUNNEL_NOT_FOUND if the tunnel
+ * does not exists, UNSUPPORTED_TYPE if the policy type is not supported,
+ * SUCCESS if the policy is created successfully
+ */
+ public Result createPolicy(Policy policy) {
+
+ if (policyStore.containsKey(policy.id())) {
+ log.warn("The policy id {} exists already", policy.id());
+ return Result.ID_EXISTS;
+ }
+
+ if (policyStore.containsValue(policy)) {
+ log.warn("The same policy exists already");
+ return Result.POLICY_EXISTS;
+ }
+
+ if (policy.type() == Policy.Type.TUNNEL_FLOW) {
+
+ TunnelPolicy tunnelPolicy = (TunnelPolicy) policy;
+ Tunnel tunnel = tunnelHandler.getTunnel(tunnelPolicy.tunnelId());
+ if (tunnel == null) {
+ return Result.TUNNEL_NOT_FOUND;
+ }
+
+ ForwardingObjective.Builder fwdBuilder = DefaultForwardingObjective
+ .builder()
+ .fromApp(appId)
+ .makePermanent()
+ .nextStep(tunnel.groupId())
+ .withPriority(tunnelPolicy.priority())
+ .withSelector(buildSelector(policy))
+ .withFlag(ForwardingObjective.Flag.VERSATILE);
+
+ DeviceId source = deviceConfiguration.getDeviceId(tunnel.labelIds().get(0));
+ flowObjectiveService.forward(source, fwdBuilder.add());
+
+ } else {
+ log.warn("Policy type {} is not supported yet.", policy.type());
+ return Result.UNSUPPORTED_TYPE;
+ }
+
+ policyStore.put(policy.id(), policy);
+
+ return Result.SUCCESS;
+ }
+
+ /**
+ * Removes the policy given.
+ *
+ * @param policyInfo policy information to remove
+ * @return POLICY_NOT_FOUND if the policy to remove does not exists,
+ * SUCCESS if it is removed successfully
+ */
+ public Result removePolicy(Policy policyInfo) {
+
+ if (policyStore.get(policyInfo.id()) != null) {
+ Policy policy = policyStore.get(policyInfo.id());
+ if (policy.type() == Policy.Type.TUNNEL_FLOW) {
+ TunnelPolicy tunnelPolicy = (TunnelPolicy) policy;
+ Tunnel tunnel = tunnelHandler.getTunnel(tunnelPolicy.tunnelId());
+
+ ForwardingObjective.Builder fwdBuilder = DefaultForwardingObjective
+ .builder()
+ .fromApp(appId)
+ .makePermanent()
+ .withSelector(buildSelector(policy))
+ .withPriority(tunnelPolicy.priority())
+ .nextStep(tunnel.groupId())
+ .withFlag(ForwardingObjective.Flag.VERSATILE);
+
+ DeviceId source = deviceConfiguration.getDeviceId(tunnel.labelIds().get(0));
+ flowObjectiveService.forward(source, fwdBuilder.remove());
+
+ policyStore.remove(policyInfo.id());
+ }
+ } else {
+ log.warn("Policy {} was not found", policyInfo.id());
+ return Result.POLICY_NOT_FOUND;
+ }
+
+ return Result.SUCCESS;
+ }
+
+
+ private TrafficSelector buildSelector(Policy policy) {
+
+ TrafficSelector.Builder tsb = DefaultTrafficSelector.builder();
+ tsb.matchEthType(Ethernet.TYPE_IPV4);
+ if (policy.dstIp() != null && !policy.dstIp().isEmpty()) {
+ tsb.matchIPDst(IpPrefix.valueOf(policy.dstIp()));
+ }
+ if (policy.srcIp() != null && !policy.srcIp().isEmpty()) {
+ tsb.matchIPSrc(IpPrefix.valueOf(policy.srcIp()));
+ }
+ if (policy.ipProto() != null && !policy.ipProto().isEmpty()) {
+ Short ipProto = IpProtocol.valueOf(policy.ipProto()).value();
+ tsb.matchIPProtocol(ipProto.byteValue());
+ if (IpProtocol.valueOf(policy.ipProto()).equals(IpProtocol.TCP)) {
+ if (policy.srcPort() != 0) {
+ tsb.matchTcpSrc(TpPort.tpPort(policy.srcPort()));
+ }
+ if (policy.dstPort() != 0) {
+ tsb.matchTcpDst(TpPort.tpPort(policy.dstPort()));
+ }
+ } else if (IpProtocol.valueOf(policy.ipProto()).equals(IpProtocol.UDP)) {
+ if (policy.srcPort() != 0) {
+ tsb.matchUdpSrc(TpPort.tpPort(policy.srcPort()));
+ }
+ if (policy.dstPort() != 0) {
+ tsb.matchUdpDst(TpPort.tpPort(policy.dstPort()));
+ }
+ }
+ }
+
+ return tsb.build();
+ }
+
+}
diff --git a/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/RoutingRulePopulator.java b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/RoutingRulePopulator.java
new file mode 100644
index 00000000..59fc4ca7
--- /dev/null
+++ b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/RoutingRulePopulator.java
@@ -0,0 +1,430 @@
+/*
+ * 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.Ip4Prefix;
+import org.onlab.packet.IpPrefix;
+import org.onlab.packet.MacAddress;
+import org.onlab.packet.MplsLabel;
+import org.onlab.packet.VlanId;
+import org.onosproject.segmentrouting.grouphandler.NeighborSet;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.Link;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.flow.DefaultTrafficSelector;
+import org.onosproject.net.flow.DefaultTrafficTreatment;
+import org.onosproject.net.flow.TrafficSelector;
+import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.flow.criteria.Criteria;
+import org.onosproject.net.flowobjective.DefaultFilteringObjective;
+import org.onosproject.net.flowobjective.DefaultForwardingObjective;
+import org.onosproject.net.flowobjective.FilteringObjective;
+import org.onosproject.net.flowobjective.ForwardingObjective;
+import org.onosproject.net.flowobjective.Objective;
+import org.onosproject.net.flowobjective.ObjectiveError;
+import org.onosproject.net.flowobjective.ForwardingObjective.Builder;
+import org.onosproject.net.flowobjective.ObjectiveContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicLong;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+public class RoutingRulePopulator {
+
+ private static final Logger log = LoggerFactory
+ .getLogger(RoutingRulePopulator.class);
+
+ private AtomicLong rulePopulationCounter;
+ private SegmentRoutingManager srManager;
+ private DeviceConfiguration config;
+ /**
+ * Creates a RoutingRulePopulator object.
+ *
+ * @param srManager segment routing manager reference
+ */
+ public RoutingRulePopulator(SegmentRoutingManager srManager) {
+ this.srManager = srManager;
+ this.config = checkNotNull(srManager.deviceConfiguration);
+ this.rulePopulationCounter = new AtomicLong(0);
+ }
+
+ /**
+ * Resets the population counter.
+ */
+ public void resetCounter() {
+ rulePopulationCounter.set(0);
+ }
+
+ /**
+ * Returns the number of rules populated.
+ *
+ * @return number of rules
+ */
+ public long getCounter() {
+ return rulePopulationCounter.get();
+ }
+
+ /**
+ * 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.deferred()
+ .setEthDst(hostMac)
+ .setEthSrc(config.getDeviceMac(deviceId))
+ .setOutput(outPort);
+
+ TrafficTreatment treatment = tbuilder.build();
+ TrafficSelector selector = sbuilder.build();
+
+ ForwardingObjective.Builder fwdBuilder = DefaultForwardingObjective
+ .builder().fromApp(srManager.appId).makePermanent()
+ .withSelector(selector).withTreatment(treatment)
+ .withPriority(100).withFlag(ForwardingObjective.Flag.SPECIFIC);
+
+ log.debug("Installing IPv4 forwarding objective "
+ + "for host {} in switch {}", hostIp, deviceId);
+ srManager.flowObjectiveService.
+ forward(deviceId,
+ fwdBuilder.
+ add(new SRObjectiveContext(deviceId,
+ SRObjectiveContext.ObjectiveType.FORWARDING)));
+ rulePopulationCounter.incrementAndGet();
+ }
+
+ /**
+ * Populates IP flow rules for the subnets of the destination router.
+ *
+ * @param deviceId switch ID to set the rules
+ * @param subnets 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,
+ List<Ip4Prefix> subnets,
+ DeviceId destSw,
+ Set<DeviceId> nextHops) {
+
+ 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.deferred().decNwTtl();
+ ns = new NeighborSet(nextHops);
+ } else {
+ tbuilder.deferred().copyTtlOut();
+ ns = new NeighborSet(nextHops, config.getSegmentId(destSw));
+ }
+
+ TrafficTreatment treatment = tbuilder.build();
+ TrafficSelector selector = sbuilder.build();
+
+ if (srManager.getNextObjectiveId(deviceId, ns) <= 0) {
+ log.warn("No next objective in {} for ns: {}", deviceId, ns);
+ return false;
+ }
+
+ ForwardingObjective.Builder fwdBuilder = DefaultForwardingObjective
+ .builder()
+ .fromApp(srManager.appId)
+ .makePermanent()
+ .nextStep(srManager.getNextObjectiveId(deviceId, ns))
+ .withTreatment(treatment)
+ .withSelector(selector)
+ .withPriority(100)
+ .withFlag(ForwardingObjective.Flag.SPECIFIC);
+ log.debug("Installing IPv4 forwarding objective "
+ + "for router IP/subnet {} in switch {}",
+ ipPrefix,
+ deviceId);
+ srManager.flowObjectiveService.
+ forward(deviceId,
+ fwdBuilder.
+ add(new SRObjectiveContext(deviceId,
+ SRObjectiveContext.ObjectiveType.FORWARDING)));
+ rulePopulationCounter.incrementAndGet();
+
+ 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();
+ List<ForwardingObjective.Builder> fwdObjBuilders = new ArrayList<ForwardingObjective.Builder>();
+
+ // TODO Handle the case of Bos == false
+ sbuilder.matchMplsLabel(MplsLabel.mplsLabel(config.getSegmentId(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])) {
+ log.debug("populateMplsRule: Installing MPLS forwarding objective for "
+ + "label {} in switch {} with PHP",
+ config.getSegmentId(destSwId),
+ deviceId);
+
+ ForwardingObjective.Builder fwdObjBosBuilder =
+ getMplsForwardingObjective(deviceId,
+ destSwId,
+ nextHops,
+ true,
+ true);
+ // TODO: Check with Sangho on why we need this
+ ForwardingObjective.Builder fwdObjNoBosBuilder =
+ getMplsForwardingObjective(deviceId,
+ destSwId,
+ nextHops,
+ true,
+ false);
+ if (fwdObjBosBuilder != null) {
+ fwdObjBuilders.add(fwdObjBosBuilder);
+ } else {
+ log.warn("Failed to set MPLS rules.");
+ return false;
+ }
+ } else {
+ log.debug("Installing MPLS forwarding objective for "
+ + "label {} in switch {} without PHP",
+ config.getSegmentId(destSwId),
+ deviceId);
+
+ ForwardingObjective.Builder fwdObjBosBuilder =
+ getMplsForwardingObjective(deviceId,
+ destSwId,
+ nextHops,
+ false,
+ true);
+ // TODO: Check with Sangho on why we need this
+ ForwardingObjective.Builder fwdObjNoBosBuilder =
+ getMplsForwardingObjective(deviceId,
+ destSwId,
+ nextHops,
+ false,
+ false);
+ if (fwdObjBosBuilder != null) {
+ fwdObjBuilders.add(fwdObjBosBuilder);
+ } else {
+ log.warn("Failed to set MPLS rules.");
+ return false;
+ }
+ }
+
+ TrafficSelector selector = sbuilder.build();
+ for (ForwardingObjective.Builder fwdObjBuilder : fwdObjBuilders) {
+ ((Builder) ((Builder) fwdObjBuilder.fromApp(srManager.appId)
+ .makePermanent()).withSelector(selector)
+ .withPriority(100))
+ .withFlag(ForwardingObjective.Flag.SPECIFIC);
+ srManager.flowObjectiveService.
+ forward(deviceId,
+ fwdObjBuilder.
+ add(new SRObjectiveContext(deviceId,
+ SRObjectiveContext.ObjectiveType.FORWARDING)));
+ rulePopulationCounter.incrementAndGet();
+ }
+
+ return true;
+ }
+
+ private ForwardingObjective.Builder getMplsForwardingObjective(DeviceId deviceId,
+ DeviceId destSw,
+ Set<DeviceId> nextHops,
+ boolean phpRequired,
+ boolean isBos) {
+
+ ForwardingObjective.Builder fwdBuilder = DefaultForwardingObjective
+ .builder().withFlag(ForwardingObjective.Flag.SPECIFIC);
+
+ TrafficTreatment.Builder tbuilder = DefaultTrafficTreatment.builder();
+
+ if (phpRequired) {
+ log.debug("getMplsForwardingObjective: php required");
+ tbuilder.deferred().copyTtlIn();
+ if (isBos) {
+ tbuilder.deferred().popMpls(Ethernet.TYPE_IPV4).decNwTtl();
+ } else {
+ tbuilder.deferred().popMpls(Ethernet.MPLS_UNICAST).decMplsTtl();
+ }
+ } else {
+ log.debug("getMplsForwardingObjective: php not required");
+ tbuilder.deferred().decMplsTtl();
+ }
+
+ if (!isECMPSupportedInTransitRouter() && !config.isEdgeDevice(deviceId)) {
+ PortNumber port = selectOnePort(deviceId, nextHops);
+ DeviceId nextHop = (DeviceId) nextHops.toArray()[0];
+ if (port == null) {
+ log.warn("No link from {} to {}", deviceId, nextHops);
+ return null;
+ }
+ tbuilder.deferred()
+ .setEthSrc(config.getDeviceMac(deviceId))
+ .setEthDst(config.getDeviceMac(nextHop))
+ .setOutput(port);
+ fwdBuilder.withTreatment(tbuilder.build());
+ } else {
+ NeighborSet ns = new NeighborSet(nextHops);
+ fwdBuilder.withTreatment(tbuilder.build());
+ fwdBuilder.nextStep(srManager
+ .getNextObjectiveId(deviceId, ns));
+ }
+
+ return fwdBuilder;
+ }
+
+ private boolean isECMPSupportedInTransitRouter() {
+
+ // TODO: remove this function when objectives subsystem is supported.
+ return false;
+ }
+
+ /**
+ * Populates VLAN flows rules. All packets are forwarded to TMAC table.
+ *
+ * @param deviceId switch ID to set the rules
+ */
+ public void populateTableVlan(DeviceId deviceId) {
+ FilteringObjective.Builder fob = DefaultFilteringObjective.builder();
+ fob.withKey(Criteria.matchInPort(PortNumber.ALL))
+ .addCondition(Criteria.matchVlanId(VlanId.NONE));
+ fob.permit().fromApp(srManager.appId);
+ log.debug("populateTableVlan: Installing filtering objective for untagged packets");
+ srManager.flowObjectiveService.
+ filter(deviceId,
+ fob.add(new SRObjectiveContext(deviceId,
+ SRObjectiveContext.ObjectiveType.FILTER)));
+ }
+
+ /**
+ * 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) {
+
+ FilteringObjective.Builder fob = DefaultFilteringObjective.builder();
+ fob.withKey(Criteria.matchInPort(PortNumber.ALL))
+ .addCondition(Criteria.matchEthDst(config
+ .getDeviceMac(deviceId)));
+ fob.permit().fromApp(srManager.appId);
+ log.debug("populateTableVlan: Installing filtering objective for router mac");
+ srManager.flowObjectiveService.
+ filter(deviceId,
+ fob.add(new SRObjectiveContext(deviceId,
+ SRObjectiveContext.ObjectiveType.FILTER)));
+ }
+
+ private PortNumber selectOnePort(DeviceId srcId, Set<DeviceId> destIds) {
+
+ Set<Link> links = srManager.linkService.getDeviceLinks(srcId);
+ for (DeviceId destId: destIds) {
+ for (Link link : links) {
+ if (link.dst().deviceId().equals(destId)) {
+ return link.src().port();
+ } else if (link.src().deviceId().equals(destId)) {
+ return link.dst().port();
+ }
+ }
+ }
+
+ return null;
+ }
+
+ private static class SRObjectiveContext implements ObjectiveContext {
+ enum ObjectiveType {
+ FILTER,
+ FORWARDING
+ }
+ final DeviceId deviceId;
+ final ObjectiveType type;
+
+ SRObjectiveContext(DeviceId deviceId, ObjectiveType type) {
+ this.deviceId = deviceId;
+ this.type = type;
+ }
+ @Override
+ public void onSuccess(Objective objective) {
+ log.debug("{} objective operation successful in device {}",
+ type.name(), deviceId);
+ }
+
+ @Override
+ public void onError(Objective objective, ObjectiveError error) {
+ log.warn("{} objective {} operation failed with error: {} in device {}",
+ type.name(), objective, error, deviceId);
+ }
+ }
+
+}
diff --git a/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java
new file mode 100644
index 00000000..874faabf
--- /dev/null
+++ b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java
@@ -0,0 +1,517 @@
+/*
+ * 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.apache.felix.scr.annotations.Service;
+import org.onlab.packet.Ethernet;
+import org.onlab.packet.IPv4;
+import org.onlab.util.KryoNamespace;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreService;
+import org.onosproject.event.Event;
+import org.onosproject.segmentrouting.grouphandler.DefaultGroupHandler;
+import org.onosproject.segmentrouting.grouphandler.NeighborSet;
+import org.onosproject.segmentrouting.grouphandler.NeighborSetNextObjectiveStoreKey;
+import org.onosproject.mastership.MastershipService;
+import org.onosproject.net.Device;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.Link;
+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.flowobjective.FlowObjectiveService;
+import org.onosproject.net.group.GroupKey;
+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.onosproject.segmentrouting.config.NetworkConfigManager;
+import org.onosproject.store.service.EventuallyConsistentMap;
+import org.onosproject.store.service.EventuallyConsistentMapBuilder;
+import org.onosproject.store.service.StorageService;
+import org.onosproject.store.service.WallClockTimestamp;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.net.URI;
+import java.util.HashSet;
+import java.util.List;
+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")
+@Service
+@Component(immediate = true)
+public class SegmentRoutingManager implements SegmentRoutingService {
+
+ 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 FlowObjectiveService flowObjectiveService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected LinkService linkService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected MastershipService mastershipService;
+
+ protected ArpHandler arpHandler = null;
+ protected IcmpHandler icmpHandler = null;
+ protected IpHandler ipHandler = null;
+ protected RoutingRulePopulator routingRulePopulator = null;
+ protected ApplicationId appId;
+ protected DeviceConfiguration deviceConfiguration = null;
+
+
+ private DefaultRoutingHandler defaultRoutingHandler = null;
+ private TunnelHandler tunnelHandler = null;
+ private PolicyHandler policyHandler = null;
+ 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>();
+ // Per device next objective ID store with (device id + neighbor set) as key
+ private EventuallyConsistentMap<NeighborSetNextObjectiveStoreKey,
+ Integer> nsNextObjStore = null;
+ private EventuallyConsistentMap<String, Tunnel> tunnelStore = null;
+ private EventuallyConsistentMap<String, Policy> policyStore = null;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected StorageService storageService;
+
+ private NetworkConfigManager networkConfigService = new NetworkConfigManager();;
+
+ private Object threadSchedulerLock = new Object();
+ private static int numOfEventsQueued = 0;
+ private static int numOfEventsExecuted = 0;
+ private static int numOfHandlerExecution = 0;
+ private static int numOfHandlerScheduled = 0;
+
+ private KryoNamespace.Builder kryoBuilder = null;
+
+ @Activate
+ protected void activate() {
+ appId = coreService
+ .registerApplication("org.onosproject.segmentrouting");
+
+ kryoBuilder = new KryoNamespace.Builder()
+ .register(NeighborSetNextObjectiveStoreKey.class,
+ NeighborSet.class,
+ DeviceId.class,
+ URI.class,
+ WallClockTimestamp.class,
+ org.onosproject.cluster.NodeId.class,
+ HashSet.class,
+ Tunnel.class,
+ DefaultTunnel.class,
+ Policy.class,
+ TunnelPolicy.class,
+ Policy.Type.class
+ );
+
+ log.debug("Creating EC map nsnextobjectivestore");
+ EventuallyConsistentMapBuilder<NeighborSetNextObjectiveStoreKey, Integer>
+ nsNextObjMapBuilder = storageService.eventuallyConsistentMapBuilder();
+
+ nsNextObjStore = nsNextObjMapBuilder
+ .withName("nsnextobjectivestore")
+ .withSerializer(kryoBuilder)
+ .withTimestampProvider((k, v) -> new WallClockTimestamp())
+ .build();
+ log.trace("Current size {}", nsNextObjStore.size());
+
+ EventuallyConsistentMapBuilder<String, Tunnel> tunnelMapBuilder =
+ storageService.eventuallyConsistentMapBuilder();
+
+ tunnelStore = tunnelMapBuilder
+ .withName("tunnelstore")
+ .withSerializer(kryoBuilder)
+ .withTimestampProvider((k, v) -> new WallClockTimestamp())
+ .build();
+
+ EventuallyConsistentMapBuilder<String, Policy> policyMapBuilder =
+ storageService.eventuallyConsistentMapBuilder();
+
+ policyStore = policyMapBuilder
+ .withName("policystore")
+ .withSerializer(kryoBuilder)
+ .withTimestampProvider((k, v) -> new WallClockTimestamp())
+ .build();
+
+ networkConfigService.init();
+ deviceConfiguration = new DeviceConfiguration(networkConfigService);
+ arpHandler = new ArpHandler(this);
+ icmpHandler = new IcmpHandler(this);
+ ipHandler = new IpHandler(this);
+ routingRulePopulator = new RoutingRulePopulator(this);
+ defaultRoutingHandler = new DefaultRoutingHandler(this);
+ tunnelHandler = new TunnelHandler(linkService, deviceConfiguration,
+ groupHandlerMap, tunnelStore);
+ policyHandler = new PolicyHandler(appId, deviceConfiguration,
+ flowObjectiveService, tunnelHandler, policyStore);
+
+ packetService.addProcessor(processor, PacketProcessor.director(2));
+ linkService.addListener(new InternalLinkListener());
+ deviceService.addListener(new InternalDeviceListener());
+
+ for (Device device : deviceService.getDevices()) {
+ //Irrespective whether the local is a MASTER or not for this device,
+ //create group handler instance and push default TTP flow rules.
+ //Because in a multi-instance setup, instances can initiate
+ //groups for any devices. Also the default TTP rules are needed
+ //to be pushed before inserting any IP table entries for any device
+ DefaultGroupHandler groupHandler = DefaultGroupHandler
+ .createGroupHandler(device.id(), appId,
+ deviceConfiguration, linkService,
+ flowObjectiveService,
+ nsNextObjStore);
+ groupHandlerMap.put(device.id(), groupHandler);
+ defaultRoutingHandler.populateTtpRules(device.id());
+ }
+
+ defaultRoutingHandler.startPopulationProcess();
+ log.info("Started");
+
+ }
+
+ @Deactivate
+ protected void deactivate() {
+ packetService.removeProcessor(processor);
+ processor = null;
+ log.info("Stopped");
+ }
+
+
+ @Override
+ public List<Tunnel> getTunnels() {
+ return tunnelHandler.getTunnels();
+ }
+
+ @Override
+ public TunnelHandler.Result createTunnel(Tunnel tunnel) {
+ return tunnelHandler.createTunnel(tunnel);
+ }
+
+ @Override
+ public TunnelHandler.Result removeTunnel(Tunnel tunnel) {
+ for (Policy policy: policyHandler.getPolicies()) {
+ if (policy.type() == Policy.Type.TUNNEL_FLOW) {
+ TunnelPolicy tunnelPolicy = (TunnelPolicy) policy;
+ if (tunnelPolicy.tunnelId().equals(tunnel.id())) {
+ log.warn("Cannot remove the tunnel used by a policy");
+ return TunnelHandler.Result.TUNNEL_IN_USE;
+ }
+ }
+ }
+ return tunnelHandler.removeTunnel(tunnel);
+ }
+
+ @Override
+ public PolicyHandler.Result removePolicy(Policy policy) {
+ return policyHandler.removePolicy(policy);
+ }
+
+ @Override
+ public PolicyHandler.Result createPolicy(Policy policy) {
+ return policyHandler.createPolicy(policy);
+ }
+
+ @Override
+ public List<Policy> getPolicies() {
+ return policyHandler.getPolicies();
+ }
+
+ /**
+ * Returns the tunnel object with the tunnel ID.
+ *
+ * @param tunnelId Tunnel ID
+ * @return Tunnel reference
+ */
+ public Tunnel getTunnel(String tunnelId) {
+ return tunnelHandler.getTunnel(tunnelId);
+ }
+
+ /**
+ * 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;
+ }
+
+ /**
+ * Returns the next objective ID for the NeighborSet given. If the nextObjectiveID does not exist,
+ * a new one is created and returned.
+ *
+ * @param deviceId Device ID
+ * @param ns NegighborSet
+ * @return next objective ID
+ */
+ public int getNextObjectiveId(DeviceId deviceId, NeighborSet ns) {
+
+ if (groupHandlerMap.get(deviceId) != null) {
+ log.trace("getNextObjectiveId query in device {}", deviceId);
+ return groupHandlerMap
+ .get(deviceId).getNextObjectiveId(ns);
+ } else {
+ log.warn("getNextObjectiveId query in device {} not found", deviceId);
+ return -1;
+ }
+ }
+
+ 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) {
+ log.debug("Event {} received from Link Service", event.type());
+ 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:
+ case DEVICE_UPDATED:
+ case DEVICE_AVAILABILITY_CHANGED:
+ log.debug("Event {} received from Device Service", event.type());
+ scheduleEventHandlerIfNotScheduled(event);
+ break;
+ default:
+ }
+ }
+ }
+
+ private void scheduleEventHandlerIfNotScheduled(Event event) {
+
+ synchronized (threadSchedulerLock) {
+ eventQueue.add(event);
+ numOfEventsQueued++;
+
+ if ((numOfHandlerScheduled - numOfHandlerExecution) == 0) {
+ //No pending scheduled event handling threads. So start a new one.
+ eventHandlerFuture = executorService
+ .schedule(eventHandler, 100, TimeUnit.MILLISECONDS);
+ numOfHandlerScheduled++;
+ }
+ log.trace("numOfEventsQueued {}, numOfEventHanlderScheduled {}",
+ numOfEventsQueued,
+ numOfHandlerScheduled);
+ }
+ }
+
+ private class InternalEventHandler implements Runnable {
+
+ @Override
+ public void run() {
+ try {
+ while (true) {
+ Event event = null;
+ synchronized (threadSchedulerLock) {
+ if (!eventQueue.isEmpty()) {
+ event = eventQueue.poll();
+ numOfEventsExecuted++;
+ } else {
+ numOfHandlerExecution++;
+ log.debug("numOfHandlerExecution {} numOfEventsExecuted {}",
+ numOfHandlerExecution, numOfEventsExecuted);
+ break;
+ }
+ }
+ 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 ||
+ event.type() == DeviceEvent.Type.DEVICE_AVAILABILITY_CHANGED ||
+ event.type() == DeviceEvent.Type.DEVICE_UPDATED) {
+ if (deviceService.isAvailable(((Device) event.subject()).id())) {
+ 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());
+ }
+ }
+ } catch (Exception e) {
+ log.error("SegmentRouting event handler "
+ + "thread thrown an exception: {}", e);
+ }
+ }
+ }
+
+ private void processLinkAdded(Link link) {
+ log.debug("A new link {} was added", link.toString());
+
+ //Irrespective whether the local is a MASTER or not for this device,
+ //create group handler instance and push default TTP flow rules.
+ //Because in a multi-instance setup, instances can initiate
+ //groups for any devices. Also the default TTP rules are needed
+ //to be pushed before inserting any IP table entries for any device
+ DefaultGroupHandler groupHandler = groupHandlerMap.get(link.src()
+ .deviceId());
+ if (groupHandler != null) {
+ groupHandler.linkUp(link);
+ } else {
+ Device device = deviceService.getDevice(link.src().deviceId());
+ if (device != null) {
+ log.warn("processLinkAdded: Link Added "
+ + "Notification without Device Added "
+ + "event, still handling it");
+ processDeviceAdded(device);
+ groupHandler = groupHandlerMap.get(link.src()
+ .deviceId());
+ groupHandler.linkUp(link);
+ }
+ }
+
+ log.trace("Starting optimized route population process");
+ defaultRoutingHandler.populateRoutingRulesForLinkStatusChange(null);
+ //log.trace("processLinkAdded: re-starting route population process");
+ //defaultRoutingHandler.startPopulationProcess();
+ }
+
+ private void processLinkRemoved(Link link) {
+ log.debug("A link {} was removed", link.toString());
+ DefaultGroupHandler groupHandler = groupHandlerMap.get(link.src().deviceId());
+ if (groupHandler != null) {
+ groupHandler.portDown(link.src().port());
+ }
+ log.trace("Starting optimized route population process");
+ defaultRoutingHandler.populateRoutingRulesForLinkStatusChange(link);
+ //log.trace("processLinkRemoved: re-starting route population process");
+ //defaultRoutingHandler.startPopulationProcess();
+ }
+
+ private void processDeviceAdded(Device device) {
+ log.debug("A new device with ID {} was added", device.id());
+ //Irrespective whether the local is a MASTER or not for this device,
+ //create group handler instance and push default TTP flow rules.
+ //Because in a multi-instance setup, instances can initiate
+ //groups for any devices. Also the default TTP rules are needed
+ //to be pushed before inserting any IP table entries for any device
+ DefaultGroupHandler dgh = DefaultGroupHandler.
+ createGroupHandler(device.id(),
+ appId,
+ deviceConfiguration,
+ linkService,
+ flowObjectiveService,
+ nsNextObjStore);
+ groupHandlerMap.put(device.id(), dgh);
+ defaultRoutingHandler.populateTtpRules(device.id());
+ }
+
+ 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());
+ }
+ }
+
+
+
+}
diff --git a/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/SegmentRoutingService.java b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/SegmentRoutingService.java
new file mode 100644
index 00000000..44bd453c
--- /dev/null
+++ b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/SegmentRoutingService.java
@@ -0,0 +1,79 @@
+/*
+ * 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.util.List;
+
+/**
+ * Segment Routing Service for REST API.
+ */
+public interface SegmentRoutingService {
+
+ /**
+ * Returns all tunnels.
+ *
+ * @return list of tunnels
+ */
+ List<Tunnel> getTunnels();
+
+ /**
+ * Creates a tunnel.
+ *
+ * @param tunnel tunnel reference to create
+ * @return WRONG_PATH if the tunnel path is wrong, ID_EXISTS if the tunnel ID
+ * exists already, TUNNEL_EXISTS if the same tunnel exists, INTERNAL_ERROR
+ * if the tunnel creation failed internally, SUCCESS if the tunnel is created
+ * successfully
+ */
+ TunnelHandler.Result createTunnel(Tunnel tunnel);
+
+ /**
+ * Returns all policies.
+ *
+ * @return list of policy
+ */
+ List<Policy> getPolicies();
+
+ /**
+ * Creates a policy.
+ *
+ * @param policy policy reference to create
+ * @return ID_EXISTS if the same policy ID exists,
+ * POLICY_EXISTS if the same policy exists, TUNNEL_NOT_FOUND if the tunnel
+ * does not exists, UNSUPPORTED_TYPE if the policy type is not supported,
+ * SUCCESS if the policy is created successfully.
+ */
+ PolicyHandler.Result createPolicy(Policy policy);
+
+ /**
+ * Removes a tunnel.
+ *
+ * @param tunnel tunnel reference to remove
+ * @return TUNNEL_NOT_FOUND if the tunnel to remove does not exists,
+ * INTERNAL_ERROR if the tunnel creation failed internally, SUCCESS
+ * if the tunnel is created successfully.
+ */
+ TunnelHandler.Result removeTunnel(Tunnel tunnel);
+
+ /**
+ * Removes a policy.
+ *
+ * @param policy policy reference to remove
+ * @return POLICY_NOT_FOUND if the policy to remove does not exists,
+ * SUCCESS if it is removed successfully
+ */
+ PolicyHandler.Result removePolicy(Policy policy);
+}
diff --git a/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/Tunnel.java b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/Tunnel.java
new file mode 100644
index 00000000..783d253e
--- /dev/null
+++ b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/Tunnel.java
@@ -0,0 +1,67 @@
+/*
+ * 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.util.List;
+
+/**
+ * Tunnel interface.
+ */
+public interface Tunnel {
+
+ /**
+ * Returns the tunnel ID.
+ *
+ * @return tunnel ID
+ */
+ String id();
+
+ /**
+ * Returns Segment IDs for the tunnel including source and destination.
+ *
+ * @return List of Node ID
+ */
+ List<Integer> labelIds();
+
+ /**
+ * Returns the group ID for the tunnel.
+ *
+ * @return group ID
+ */
+ int groupId();
+
+ /**
+ * Sets group ID for the tunnel.
+ *
+ * @param groupId group identifier
+ */
+ void setGroupId(int groupId);
+
+ /**
+ * Sets the flag to allow to remove the group or not.
+ *
+ * @param ok the flag; true - allow to remove
+ */
+ void allowToRemoveGroup(boolean ok);
+
+ /**
+ * Checks if it is allowed to remove the group for the tunnel.
+ *
+ * @return true if allowed, false otherwise
+ */
+ boolean isAllowedToRemoveGroup();
+}
diff --git a/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/TunnelHandler.java b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/TunnelHandler.java
new file mode 100644
index 00000000..820bb40a
--- /dev/null
+++ b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/TunnelHandler.java
@@ -0,0 +1,199 @@
+/*
+ * 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.DeviceId;
+import org.onosproject.net.Link;
+import org.onosproject.net.link.LinkService;
+import org.onosproject.segmentrouting.grouphandler.DefaultGroupHandler;
+import org.onosproject.segmentrouting.grouphandler.NeighborSet;
+import org.onosproject.store.service.EventuallyConsistentMap;
+import org.slf4j.Logger;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Tunnel Handler.
+ */
+public class TunnelHandler {
+ protected final Logger log = getLogger(getClass());
+
+ private final DeviceConfiguration config;
+ private final EventuallyConsistentMap<String, Tunnel> tunnelStore;
+ private Map<DeviceId, DefaultGroupHandler> groupHandlerMap;
+ private LinkService linkService;
+
+ public enum Result {
+ SUCCESS,
+ WRONG_PATH,
+ TUNNEL_EXISTS,
+ ID_EXISTS,
+ TUNNEL_NOT_FOUND,
+ TUNNEL_IN_USE,
+ INTERNAL_ERROR
+ }
+
+ public TunnelHandler(LinkService linkService,
+ DeviceConfiguration deviceConfiguration,
+ Map<DeviceId, DefaultGroupHandler> groupHandlerMap,
+ EventuallyConsistentMap<String, Tunnel> tunnelStore) {
+ this.linkService = linkService;
+ this.config = deviceConfiguration;
+ this.groupHandlerMap = groupHandlerMap;
+ this.tunnelStore = tunnelStore;
+ }
+
+ /**
+ * Creates a tunnel.
+ *
+ * @param tunnel tunnel reference to create a tunnel
+ * @return WRONG_PATH if the tunnel path is wrong, ID_EXISTS if the tunnel ID
+ * exists already, TUNNEL_EXISTS if the same tunnel exists, INTERNAL_ERROR
+ * if the tunnel creation failed internally, SUCCESS if the tunnel is created
+ * successfully
+ */
+ public Result createTunnel(Tunnel tunnel) {
+
+ if (tunnel.labelIds().isEmpty() || tunnel.labelIds().size() < 3) {
+ log.error("More than one router needs to specified to created a tunnel");
+ return Result.WRONG_PATH;
+ }
+
+ if (tunnelStore.containsKey(tunnel.id())) {
+ log.warn("The same tunnel ID exists already");
+ return Result.ID_EXISTS;
+ }
+
+ if (tunnelStore.containsValue(tunnel)) {
+ log.warn("The same tunnel exists already");
+ return Result.TUNNEL_EXISTS;
+ }
+
+ int groupId = createGroupsForTunnel(tunnel);
+ if (groupId < 0) {
+ log.error("Failed to create groups for the tunnel");
+ return Result.INTERNAL_ERROR;
+ }
+
+ tunnel.setGroupId(groupId);
+ tunnelStore.put(tunnel.id(), tunnel);
+
+ return Result.SUCCESS;
+ }
+
+ /**
+ * Removes the tunnel with the tunnel ID given.
+ *
+ * @param tunnelInfo tunnel information to delete tunnels
+ * @return TUNNEL_NOT_FOUND if the tunnel to remove does not exists,
+ * INTERNAL_ERROR if the tunnel creation failed internally, SUCCESS
+ * if the tunnel is created successfully.
+ */
+ public Result removeTunnel(Tunnel tunnelInfo) {
+
+ Tunnel tunnel = tunnelStore.get(tunnelInfo.id());
+ if (tunnel != null) {
+ DeviceId deviceId = config.getDeviceId(tunnel.labelIds().get(0));
+ if (tunnel.isAllowedToRemoveGroup()) {
+ if (groupHandlerMap.get(deviceId).removeGroup(tunnel.groupId())) {
+ tunnelStore.remove(tunnel.id());
+ } else {
+ log.error("Failed to remove the tunnel {}", tunnelInfo.id());
+ return Result.INTERNAL_ERROR;
+ }
+ } else {
+ log.debug("The group is not removed because it is being used.");
+ tunnelStore.remove(tunnel.id());
+ }
+ } else {
+ log.error("No tunnel found for tunnel ID {}", tunnelInfo.id());
+ return Result.TUNNEL_NOT_FOUND;
+ }
+
+ return Result.SUCCESS;
+ }
+
+ /**
+ * Returns the tunnel with the tunnel ID given.
+ *
+ * @param tid Tunnel ID
+ * @return Tunnel reference
+ */
+ public Tunnel getTunnel(String tid) {
+ return tunnelStore.get(tid);
+ }
+
+ /**
+ * Returns all tunnels.
+ *
+ * @return list of Tunnels
+ */
+ public List<Tunnel> getTunnels() {
+ List<Tunnel> tunnels = new ArrayList<>();
+ tunnelStore.values().forEach(tunnel -> tunnels.add(
+ new DefaultTunnel((DefaultTunnel) tunnel)));
+
+ return tunnels;
+ }
+
+ private int createGroupsForTunnel(Tunnel tunnel) {
+
+ List<Integer> portNumbers;
+ final int groupError = -1;
+
+ DeviceId deviceId = config.getDeviceId(tunnel.labelIds().get(0));
+ if (deviceId == null) {
+ log.warn("No device found for SID {}", tunnel.labelIds().get(0));
+ return groupError;
+ } else if (groupHandlerMap.get(deviceId) == null) {
+ log.warn("group handler not found for {}", deviceId);
+ return groupError;
+ }
+ Set<DeviceId> deviceIds = new HashSet<>();
+ int sid = tunnel.labelIds().get(1);
+ if (config.isAdjacencySid(deviceId, sid)) {
+ portNumbers = config.getPortsForAdjacencySid(deviceId, sid);
+ for (Link link: linkService.getDeviceEgressLinks(deviceId)) {
+ for (Integer port: portNumbers) {
+ if (link.src().port().toLong() == port) {
+ deviceIds.add(link.dst().deviceId());
+ }
+ }
+ }
+ } else {
+ deviceIds.add(config.getDeviceId(sid));
+ }
+
+ NeighborSet ns = new NeighborSet(deviceIds, tunnel.labelIds().get(2));
+
+ // If the tunnel reuses any existing groups, then tunnel handler
+ // should not remove the group.
+ if (groupHandlerMap.get(deviceId).hasNextObjectiveId(ns)) {
+ tunnel.allowToRemoveGroup(false);
+ } else {
+ tunnel.allowToRemoveGroup(true);
+ }
+
+ return groupHandlerMap.get(deviceId).getNextObjectiveId(ns);
+ }
+
+}
diff --git a/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/TunnelPolicy.java b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/TunnelPolicy.java
new file mode 100644
index 00000000..06dbdb21
--- /dev/null
+++ b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/TunnelPolicy.java
@@ -0,0 +1,291 @@
+/*
+ * 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.util.Objects;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Tunnel Policy.
+ */
+public final class TunnelPolicy implements Policy {
+
+ private final Type type;
+ private final String id;
+ private final int priority;
+ private final String tunnelId;
+ private String dstIp;
+ private String srcIp;
+ private String ipProto;
+ private short srcPort;
+ private short dstPort;
+
+ private TunnelPolicy(String policyId, Type type, int priority, String tunnelId, String srcIp,
+ String dstIp, String ipProto, short srcPort, short dstPort) {
+ this.id = checkNotNull(policyId);
+ this.type = type;
+ this.tunnelId = tunnelId;
+ this.priority = priority;
+ this.dstIp = dstIp;
+ this.srcIp = srcIp;
+ this.ipProto = ipProto;
+ this.srcPort = srcPort;
+ this.dstPort = dstPort;
+
+ }
+
+ /**
+ * Creates a TunnelPolicy reference.
+ *
+ * @param p TunnelPolicy reference
+ */
+ public TunnelPolicy(TunnelPolicy p) {
+ this.id = p.id;
+ this.type = p.type;
+ this.tunnelId = p.tunnelId;
+ this.priority = p.priority;
+ this.srcIp = p.srcIp;
+ this.dstIp = p.dstIp;
+ this.ipProto = p.ipProto;
+ this.srcPort = p.srcPort;
+ this.dstPort = p.dstPort;
+ }
+
+ /**
+ * Returns the TunnelPolicy builder reference.
+ *
+ * @return TunnelPolicy builder
+ */
+ public static TunnelPolicy.Builder builder() {
+ return new Builder();
+ }
+
+ @Override
+ public String id() {
+ return this.id;
+ }
+
+ @Override
+ public int priority() {
+ return priority;
+ }
+
+ @Override
+ public Type type() {
+ return type;
+ }
+
+ @Override
+ public String srcIp() {
+ return srcIp;
+ }
+
+ @Override
+ public String dstIp() {
+ return dstIp;
+ }
+
+ @Override
+ public String ipProto() {
+ return ipProto;
+ }
+
+ @Override
+ public short srcPort() {
+ return srcPort;
+ }
+
+ @Override
+ public short dstPort() {
+ return dstPort;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+
+ if (o instanceof TunnelPolicy) {
+ TunnelPolicy that = (TunnelPolicy) o;
+ // We do not compare the policy ID
+ if (this.type.equals(that.type) &&
+ this.tunnelId.equals(that.tunnelId) &&
+ this.priority == that.priority &&
+ this.srcIp.equals(that.srcIp) &&
+ this.dstIp.equals(that.dstIp) &&
+ this.srcPort == that.srcPort &&
+ this.dstPort == that.dstPort &&
+ this.ipProto.equals(that.ipProto)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(type, tunnelId, srcIp, dstIp, ipProto,
+ srcPort, dstPort, priority);
+ }
+
+ /**
+ * Returns the tunnel ID of the policy.
+ *
+ * @return Tunnel ID
+ */
+ public String tunnelId() {
+ return this.tunnelId;
+ }
+
+
+ /**
+ * Tunnel Policy Builder.
+ */
+ public static final class Builder {
+
+ private String id;
+ private Type type;
+ private int priority;
+ private String tunnelId;
+ private String dstIp;
+ private String srcIp;
+ private String ipProto;
+ private short srcPort;
+ private short dstPort;
+
+ /**
+ * Sets the policy Id.
+ *
+ * @param id policy Id
+ * @return Builder object
+ */
+ public Builder setPolicyId(String id) {
+ this.id = id;
+
+ return this;
+ }
+
+ /**
+ * Sets the policy type.
+ *
+ * @param type policy type
+ * @return Builder object
+ */
+ public Builder setType(Type type) {
+ this.type = type;
+
+ return this;
+ }
+
+ /**
+ * Sets the source IP address.
+ *
+ * @param srcIp source IP address
+ * @return Builder object
+ */
+ public Builder setSrcIp(String srcIp) {
+ this.srcIp = srcIp;
+
+ return this;
+ }
+
+ /**
+ * Sets the destination IP address.
+ *
+ * @param dstIp destination IP address
+ * @return Builder object
+ */
+ public Builder setDstIp(String dstIp) {
+ this.dstIp = dstIp;
+
+ return this;
+ }
+
+ /**
+ * Sets the IP protocol.
+ *
+ * @param proto IP protocol
+ * @return Builder object
+ */
+ public Builder setIpProto(String proto) {
+ this.ipProto = proto;
+
+ return this;
+ }
+
+ /**
+ * Sets the source port.
+ *
+ * @param srcPort source port
+ * @return Builder object
+ */
+ public Builder setSrcPort(short srcPort) {
+ this.srcPort = srcPort;
+
+ return this;
+ }
+
+ /**
+ * Sets the destination port.
+ *
+ * @param dstPort destination port
+ * @return Builder object
+ */
+ public Builder setDstPort(short dstPort) {
+ this.dstPort = dstPort;
+
+ return this;
+ }
+
+ /**
+ * Sets the priority of the policy.
+ *
+ * @param p priority
+ * @return Builder object
+ */
+ public Builder setPriority(int p) {
+ this.priority = p;
+
+ return this;
+ }
+
+ /**
+ * Sets the tunnel Id.
+ *
+ * @param tunnelId tunnel Id
+ * @return Builder object
+ */
+ public Builder setTunnelId(String tunnelId) {
+ this.tunnelId = tunnelId;
+
+ return this;
+ }
+
+ /**
+ * Builds the policy.
+ *
+ * @return Tunnel Policy reference
+ */
+ public Policy build() {
+ return new TunnelPolicy(id, type, priority, tunnelId, srcIp, dstIp,
+ ipProto, srcPort, dstPort);
+ }
+ }
+}
diff --git a/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/cli/PolicyAddCommand.java b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/cli/PolicyAddCommand.java
new file mode 100644
index 00000000..b00633cd
--- /dev/null
+++ b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/cli/PolicyAddCommand.java
@@ -0,0 +1,132 @@
+/*
+ * 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.cli;
+
+import org.apache.karaf.shell.commands.Argument;
+import org.apache.karaf.shell.commands.Command;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.segmentrouting.Policy;
+import org.onosproject.segmentrouting.PolicyHandler;
+import org.onosproject.segmentrouting.SegmentRoutingService;
+import org.onosproject.segmentrouting.TunnelPolicy;
+
+/**
+ * Command to add a new policy.
+ */
+@Command(scope = "onos", name = "srpolicy-add",
+ description = "Create a new policy")
+public class PolicyAddCommand extends AbstractShellCommand {
+
+ // TODO: Need to support skipping some parameters
+
+ @Argument(index = 0, name = "ID",
+ description = "policy ID",
+ required = true, multiValued = false)
+ String policyId;
+
+ @Argument(index = 1, name = "priority",
+ description = "priority",
+ required = true, multiValued = false)
+ int priority;
+
+ @Argument(index = 2, name = "src_IP",
+ description = "src IP",
+ required = false, multiValued = false)
+ String srcIp;
+
+ @Argument(index = 3, name = "src_port",
+ description = "src port",
+ required = false, multiValued = false)
+ short srcPort;
+
+ @Argument(index = 4, name = "dst_IP",
+ description = "dst IP",
+ required = false, multiValued = false)
+ String dstIp;
+
+ @Argument(index = 5, name = "dst_port",
+ description = "dst port",
+ required = false, multiValued = false)
+ short dstPort;
+
+ @Argument(index = 6, name = "proto",
+ description = "IP protocol",
+ required = false, multiValued = false)
+ String proto;
+
+ @Argument(index = 7, name = "policy_type",
+ description = "policy type",
+ required = true, multiValued = false)
+ String policyType;
+
+ @Argument(index = 8, name = "tunnel_ID",
+ description = "tunnel ID",
+ required = false, multiValued = false)
+ String tunnelId;
+
+ @Override
+ protected void execute() {
+
+ SegmentRoutingService srService =
+ AbstractShellCommand.get(SegmentRoutingService.class);
+
+ TunnelPolicy.Builder tpb = TunnelPolicy.builder().setPolicyId(policyId);
+ tpb.setPriority(priority);
+ tpb.setType(Policy.Type.valueOf(policyType));
+
+ if (srcIp != null) {
+ tpb.setSrcIp(srcIp);
+ }
+ if (dstIp != null) {
+ tpb.setDstIp(dstIp);
+ }
+ if (srcPort != 0) {
+ tpb.setSrcPort(srcPort);
+ }
+ if (dstPort != 0) {
+ tpb.setDstPort(dstPort);
+ }
+ if (!proto.equals("ip")) {
+ tpb.setIpProto(proto);
+ }
+ if (Policy.Type.valueOf(policyType) == Policy.Type.TUNNEL_FLOW) {
+ if (tunnelId == null) {
+ error("tunnel ID must be specified for TUNNEL_FLOW policy");
+ return;
+ }
+ tpb.setTunnelId(tunnelId);
+ }
+ PolicyHandler.Result result = srService.createPolicy(tpb.build());
+
+ switch (result) {
+ case POLICY_EXISTS:
+ error("the same policy exists");
+ break;
+ case ID_EXISTS:
+ error("the same policy ID exists");
+ break;
+ case TUNNEL_NOT_FOUND:
+ error("the tunnel is not found");
+ break;
+ case UNSUPPORTED_TYPE:
+ error("the policy type specified is not supported");
+ break;
+ default:
+ break;
+ }
+
+ }
+}
diff --git a/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/cli/PolicyListCommand.java b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/cli/PolicyListCommand.java
new file mode 100644
index 00000000..929c98c5
--- /dev/null
+++ b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/cli/PolicyListCommand.java
@@ -0,0 +1,51 @@
+/*
+ * 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.cli;
+
+import org.apache.karaf.shell.commands.Command;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.segmentrouting.Policy;
+import org.onosproject.segmentrouting.SegmentRoutingService;
+import org.onosproject.segmentrouting.TunnelPolicy;
+
+/**
+ * Command to show the list of policies.
+ */
+@Command(scope = "onos", name = "srpolicy-list",
+ description = "Lists all policies")
+public class PolicyListCommand extends AbstractShellCommand {
+
+ private static final String FORMAT_MAPPING_TUNNEL =
+ " id=%s, type=%s, prio=%d, src=%s, port=%d, dst=%s, port=%d, proto=%s, tunnel=%s";
+
+ @Override
+ protected void execute() {
+
+ SegmentRoutingService srService =
+ AbstractShellCommand.get(SegmentRoutingService.class);
+
+ srService.getPolicies().forEach(policy -> printPolicy(policy));
+ }
+
+ private void printPolicy(Policy policy) {
+ if (policy.type() == Policy.Type.TUNNEL_FLOW) {
+ print(FORMAT_MAPPING_TUNNEL, policy.id(), policy.type(), policy.priority(),
+ policy.srcIp(), policy.srcPort(), policy.dstIp(), policy.dstPort(),
+ (policy.ipProto() == null) ? "" : policy.ipProto(),
+ ((TunnelPolicy) policy).tunnelId());
+ }
+ }
+} \ No newline at end of file
diff --git a/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/cli/PolicyRemoveCommand.java b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/cli/PolicyRemoveCommand.java
new file mode 100644
index 00000000..34fe40d8
--- /dev/null
+++ b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/cli/PolicyRemoveCommand.java
@@ -0,0 +1,50 @@
+/*
+ * 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.cli;
+
+
+import org.apache.karaf.shell.commands.Argument;
+import org.apache.karaf.shell.commands.Command;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.segmentrouting.PolicyHandler;
+import org.onosproject.segmentrouting.SegmentRoutingService;
+import org.onosproject.segmentrouting.TunnelPolicy;
+
+/**
+ * Command to remove a policy.
+ */
+@Command(scope = "onos", name = "srpolicy-remove",
+ description = "Remove a policy")
+public class PolicyRemoveCommand extends AbstractShellCommand {
+
+ @Argument(index = 0, name = "policy ID",
+ description = "policy ID",
+ required = true, multiValued = false)
+ String policyId;
+
+ @Override
+ protected void execute() {
+
+ SegmentRoutingService srService =
+ AbstractShellCommand.get(SegmentRoutingService.class);
+
+ TunnelPolicy.Builder tpb = TunnelPolicy.builder().setPolicyId(policyId);
+ PolicyHandler.Result result = srService.removePolicy(tpb.build());
+ if (result == PolicyHandler.Result.POLICY_NOT_FOUND) {
+ print("ERROR: the policy is not found");
+ }
+ }
+} \ No newline at end of file
diff --git a/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/cli/TunnelAddCommand.java b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/cli/TunnelAddCommand.java
new file mode 100644
index 00000000..bb0ae549
--- /dev/null
+++ b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/cli/TunnelAddCommand.java
@@ -0,0 +1,80 @@
+
+/*
+ * 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.cli;
+
+import org.apache.karaf.shell.commands.Argument;
+import org.apache.karaf.shell.commands.Command;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.segmentrouting.DefaultTunnel;
+import org.onosproject.segmentrouting.SegmentRoutingService;
+import org.onosproject.segmentrouting.Tunnel;
+import org.onosproject.segmentrouting.TunnelHandler;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.StringTokenizer;
+
+/**
+ * Command to add a new tunnel.
+ */
+@Command(scope = "onos", name = "srtunnel-add",
+ description = "Create a new tunnel")
+public class TunnelAddCommand extends AbstractShellCommand {
+
+ @Argument(index = 0, name = "tunnel ID",
+ description = "tunnel ID",
+ required = true, multiValued = false)
+ String tunnelId;
+
+ @Argument(index = 1, name = "label path",
+ description = "label path",
+ required = true, multiValued = false)
+ String labels;
+
+
+ @Override
+ protected void execute() {
+
+ SegmentRoutingService srService =
+ AbstractShellCommand.get(SegmentRoutingService.class);
+
+ List<Integer> labelIds = new ArrayList<>();
+ StringTokenizer strToken = new StringTokenizer(labels, ",");
+ while (strToken.hasMoreTokens()) {
+ labelIds.add(Integer.valueOf(strToken.nextToken()));
+ }
+ Tunnel tunnel = new DefaultTunnel(tunnelId, labelIds);
+
+ TunnelHandler.Result result = srService.createTunnel(tunnel);
+ switch (result) {
+ case ID_EXISTS:
+ print("ERROR: the same tunnel ID exists");
+ break;
+ case TUNNEL_EXISTS:
+ print("ERROR: the same tunnel exists");
+ break;
+ case INTERNAL_ERROR:
+ print("ERROR: internal tunnel creation error");
+ break;
+ case WRONG_PATH:
+ print("ERROR: the tunnel path is wrong");
+ break;
+ default:
+ break;
+ }
+ }
+} \ No newline at end of file
diff --git a/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/cli/TunnelListCommand.java b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/cli/TunnelListCommand.java
new file mode 100644
index 00000000..fe510783
--- /dev/null
+++ b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/cli/TunnelListCommand.java
@@ -0,0 +1,45 @@
+/*
+ * 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.cli;
+
+import org.apache.karaf.shell.commands.Command;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.segmentrouting.SegmentRoutingService;
+import org.onosproject.segmentrouting.Tunnel;
+
+/**
+ * Command to show the list of tunnels.
+ */
+@Command(scope = "onos", name = "srtunnel-list",
+ description = "Lists all tunnels")
+public class TunnelListCommand extends AbstractShellCommand {
+
+ private static final String FORMAT_MAPPING =
+ " id=%s, path=%s";
+
+ @Override
+ protected void execute() {
+
+ SegmentRoutingService srService =
+ AbstractShellCommand.get(SegmentRoutingService.class);
+
+ srService.getTunnels().forEach(tunnel -> printTunnel(tunnel));
+ }
+
+ private void printTunnel(Tunnel tunnel) {
+ print(FORMAT_MAPPING, tunnel.id(), tunnel.labelIds());
+ }
+}
diff --git a/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/cli/TunnelRemoveCommand.java b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/cli/TunnelRemoveCommand.java
new file mode 100644
index 00000000..cca22c30
--- /dev/null
+++ b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/cli/TunnelRemoveCommand.java
@@ -0,0 +1,58 @@
+/*
+ * 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.cli;
+
+
+import com.google.common.collect.Lists;
+import org.apache.karaf.shell.commands.Argument;
+import org.apache.karaf.shell.commands.Command;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.segmentrouting.DefaultTunnel;
+import org.onosproject.segmentrouting.SegmentRoutingService;
+import org.onosproject.segmentrouting.Tunnel;
+import org.onosproject.segmentrouting.TunnelHandler;
+
+/**
+ * Command to remove a tunnel.
+ */
+@Command(scope = "onos", name = "srtunnel-remove",
+ description = "Remove a tunnel")
+public class TunnelRemoveCommand extends AbstractShellCommand {
+
+ @Argument(index = 0, name = "tunnel ID",
+ description = "tunnel ID",
+ required = true, multiValued = false)
+ String tunnelId;
+
+ @Override
+ protected void execute() {
+ SegmentRoutingService srService =
+ AbstractShellCommand.get(SegmentRoutingService.class);
+
+ Tunnel tunnel = new DefaultTunnel(tunnelId, Lists.newArrayList());
+ TunnelHandler.Result result = srService.removeTunnel(tunnel);
+ switch (result) {
+ case TUNNEL_IN_USE:
+ print("ERROR: the tunnel is still in use");
+ break;
+ case TUNNEL_NOT_FOUND:
+ print("ERROR: the tunnel is not found");
+ break;
+ default:
+ break;
+ }
+ }
+} \ No newline at end of file
diff --git a/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/cli/package-info.java b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/cli/package-info.java
new file mode 100644
index 00000000..1a9d3c78
--- /dev/null
+++ b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/cli/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * 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.
+ */
+
+/**
+ * Segment routing application CLI handlers.
+ */
+package org.onosproject.segmentrouting.cli; \ No newline at end of file
diff --git a/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/config/NetworkConfig.java b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/config/NetworkConfig.java
new file mode 100644
index 00000000..6ae0779e
--- /dev/null
+++ b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/config/NetworkConfig.java
@@ -0,0 +1,336 @@
+package org.onosproject.segmentrouting.config;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import org.onosproject.net.DeviceId;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.databind.JsonNode;
+
+/**
+ * Public class corresponding to JSON described data model. Defines the network
+ * configuration at startup.
+ */
+public class NetworkConfig {
+ protected static final Logger log = LoggerFactory.getLogger(NetworkConfig.class);
+
+ @SuppressWarnings("unused")
+ private String comment;
+
+ private Boolean restrictSwitches;
+ private Boolean restrictLinks;
+ private List<SwitchConfig> switches;
+ private List<LinkConfig> links;
+
+ /**
+ * Default constructor.
+ */
+ public NetworkConfig() {
+ switches = new ArrayList<SwitchConfig>();
+ links = new ArrayList<LinkConfig>();
+ }
+
+ @JsonProperty("comment")
+ public void setComment(String c) {
+ log.trace("NetworkConfig: comment={}", c);
+ comment = c;
+ }
+
+ @JsonProperty("restrictSwitches")
+ public void setRestrictSwitches(boolean rs) {
+ log.trace("NetworkConfig: restrictSwitches={}", rs);
+ restrictSwitches = rs;
+ }
+
+ /**
+ * Returns default restrict configuration for switches.
+ *
+ * @return boolean
+ */
+ public Boolean getRestrictSwitches() {
+ return restrictSwitches;
+ }
+
+ @JsonProperty("restrictLinks")
+ public void setRestrictLinks(boolean rl) {
+ log.trace("NetworkConfig: restrictLinks={}", rl);
+ restrictLinks = rl;
+ }
+
+ /**
+ * Returns default restrict configuration for links.
+ *
+ * @return boolean
+ */
+ public Boolean getRestrictLinks() {
+ return restrictLinks;
+ }
+
+ /**
+ * Returns configuration for switches.
+ *
+ * @return list of switch configuration
+ */
+ public List<SwitchConfig> getSwitchConfig() {
+ return switches;
+ }
+
+ @JsonProperty("switchConfig")
+ public void setSwitchConfig(List<SwitchConfig> switches2) {
+ log.trace("NetworkConfig: switchConfig={}", switches2);
+ this.switches = switches2;
+ }
+
+ /**
+ * Java class corresponding to JSON described switch
+ * configuration data model.
+ */
+ public static class SwitchConfig {
+ protected String nodeDpid;
+ protected String name;
+ protected String type;
+ protected boolean allowed;
+ protected double latitude;
+ protected double longitude;
+ protected Map<String, JsonNode> params;
+ protected Map<String, String> publishAttributes;
+ protected DeviceId dpid;
+
+ /**
+ * Returns the configured "name" of a switch.
+ *
+ * @return string
+ */
+ public String getName() {
+ return name;
+ }
+
+ @JsonProperty("name")
+ public void setName(String name) {
+ log.trace("SwitchConfig: name={}", name);
+ this.name = name;
+ }
+
+ /**
+ * Returns the data plane identifier of a switch.
+ *
+ * @return ONOS device identifier
+ */
+ public DeviceId getDpid() {
+ return dpid;
+ }
+
+ public void setDpid(DeviceId dpid) {
+ this.dpid = dpid;
+ this.nodeDpid = dpid.toString();
+ }
+
+ /**
+ * Returns the data plane identifier of a switch.
+ *
+ * @return string
+ */
+ public String getNodeDpid() {
+ return nodeDpid;
+ }
+
+ // mapper sets both DeviceId and string fields for dpid
+ @JsonProperty("nodeDpid")
+ public void setNodeDpid(String nodeDpid) {
+ log.trace("SwitchConfig: nodeDpid={}", nodeDpid);
+ this.nodeDpid = nodeDpid;
+ this.dpid = DeviceId.deviceId(nodeDpid);
+ }
+
+ /**
+ * Returns the type of a switch.
+ *
+ * @return string
+ */
+ public String getType() {
+ return type;
+ }
+
+ @JsonProperty("type")
+ public void setType(String type) {
+ log.trace("SwitchConfig: type={}", type);
+ this.type = type;
+ }
+
+ /**
+ * Returns the latitude of a switch.
+ *
+ * @return double
+ */
+ public double getLatitude() {
+ return latitude;
+ }
+
+ @JsonProperty("latitude")
+ public void setLatitude(double latitude) {
+ log.trace("SwitchConfig: latitude={}", latitude);
+ this.latitude = latitude;
+ }
+
+ /**
+ * Returns the longitude of a switch.
+ *
+ * @return double
+ */
+ public double getLongitude() {
+ return longitude;
+ }
+
+ @JsonProperty("longitude")
+ public void setLongitude(double longitude) {
+ log.trace("SwitchConfig: longitude={}", longitude);
+ this.longitude = longitude;
+ }
+
+ /**
+ * Returns the allowed flag for a switch.
+ *
+ * @return boolean
+ */
+ public boolean isAllowed() {
+ return allowed;
+ }
+
+ @JsonProperty("allowed")
+ public void setAllowed(boolean allowed) {
+ this.allowed = allowed;
+ }
+
+ /**
+ * Returns the additional configured parameters of a switch.
+ *
+ * @return key value map
+ */
+ public Map<String, JsonNode> getParams() {
+ return params;
+ }
+
+ @JsonProperty("params")
+ public void setParams(Map<String, JsonNode> params) {
+ this.params = params;
+ }
+
+ /**
+ * Reserved for future use.
+ *
+ * @return key value map
+ */
+ public Map<String, String> getPublishAttributes() {
+ return publishAttributes;
+ }
+
+ @JsonProperty("publishAttributes")
+ public void setPublishAttributes(Map<String, String> publishAttributes) {
+ this.publishAttributes = publishAttributes;
+ }
+
+ }
+
+ @JsonProperty("linkConfig")
+ public void setLinkConfig(List<LinkConfig> links2) {
+ this.links = links2;
+ }
+
+ /**
+ * Reserved for future use.
+ *
+ * @return list of configured link configuration
+ */
+ public List<LinkConfig> getLinkConfig() {
+ return links;
+ }
+
+ /**
+ * Reserved for future use.
+ */
+ public static class LinkConfig {
+ protected String type;
+ protected Boolean allowed;
+ protected DeviceId dpid1;
+ protected DeviceId dpid2;
+ protected String nodeDpid1;
+ protected String nodeDpid2;
+ protected Map<String, JsonNode> params;
+ protected Map<String, String> publishAttributes;
+
+ public String getType() {
+ return type;
+ }
+
+ public void setType(String type) {
+ this.type = type;
+ }
+
+ public Boolean isAllowed() {
+ return allowed;
+ }
+
+ public void setAllowed(Boolean allowed) {
+ this.allowed = allowed;
+ }
+
+ public String getNodeDpid1() {
+ return nodeDpid1;
+ }
+
+ // mapper sets both long and string fields for dpid
+ public void setNodeDpid1(String nodeDpid1) {
+ this.nodeDpid1 = nodeDpid1;
+ this.dpid1 = DeviceId.deviceId(nodeDpid1);
+ }
+
+ public String getNodeDpid2() {
+ return nodeDpid2;
+ }
+
+ // mapper sets both long and string fields for dpid
+ public void setNodeDpid2(String nodeDpid2) {
+ this.nodeDpid2 = nodeDpid2;
+ this.dpid2 = DeviceId.deviceId(nodeDpid2);
+ }
+
+ public DeviceId getDpid1() {
+ return dpid1;
+ }
+
+ public void setDpid1(DeviceId dpid1) {
+ this.dpid1 = dpid1;
+ this.nodeDpid1 = dpid1.toString();
+ }
+
+ public DeviceId getDpid2() {
+ return dpid2;
+ }
+
+ public void setDpid2(DeviceId dpid2) {
+ this.dpid2 = dpid2;
+ this.nodeDpid2 = dpid2.toString();
+ }
+
+ public Map<String, JsonNode> getParams() {
+ return params;
+ }
+
+ public void setParams(Map<String, JsonNode> params) {
+ this.params = params;
+ }
+
+ public Map<String, String> getPublishAttributes() {
+ return publishAttributes;
+ }
+
+ public void setPublishAttributes(Map<String, String> publishAttributes) {
+ this.publishAttributes = publishAttributes;
+ }
+ }
+}
+
diff --git a/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/config/NetworkConfigException.java b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/config/NetworkConfigException.java
new file mode 100644
index 00000000..91942216
--- /dev/null
+++ b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/config/NetworkConfigException.java
@@ -0,0 +1,200 @@
+package org.onosproject.segmentrouting.config;
+
+import org.onosproject.net.DeviceId;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * NetworkConfigExceptions specifies a set of unchecked runtime exceptions that
+ * can be thrown by the {@link NetworkConfigManager}. It indicates errors that
+ * must be fixed in the config file before controller execution can proceed.
+ */
+public class NetworkConfigException extends RuntimeException {
+
+ private static final long serialVersionUID = 4959684709803000652L;
+ protected static final Logger log = LoggerFactory
+ .getLogger(NetworkConfigException.class);
+
+ /**
+ * Exception for duplicate device identifier configuration.
+ */
+ public static class DuplicateDpid extends RuntimeException {
+ private static final long serialVersionUID = 5491113234592145335L;
+
+ public DuplicateDpid(DeviceId dpid) {
+ super();
+ log.error("Duplicate dpid found in switch-config Dpid:{}",
+ dpid);
+ }
+ }
+
+ /**
+ * Exception for duplicate device name configuration.
+ */
+ public static class DuplicateName extends RuntimeException {
+ private static final long serialVersionUID = -4090171438031376129L;
+
+ public DuplicateName(String name) {
+ super();
+ log.error("Duplicate name found in switch-config name:{}", name);
+ }
+ }
+
+ /**
+ * Exception for unspecified device identifier for a switch.
+ */
+ public static class DpidNotSpecified extends RuntimeException {
+ private static final long serialVersionUID = -8494418855597117254L;
+
+ public DpidNotSpecified(String name) {
+ super();
+ log.error("Dpid not specified for switch-config name:{}", name);
+ }
+ }
+
+ /**
+ * Exception for unspecified device name for a switch.
+ */
+ public static class NameNotSpecified extends RuntimeException {
+ private static final long serialVersionUID = -3518881744110422891L;
+
+ public NameNotSpecified(DeviceId dpid) {
+ super();
+ log.error("Name not specified for switch-config dpid:{}",
+ dpid);
+ }
+ }
+
+ /**
+ * Exception for unspecified device type for a switch.
+ */
+ public static class SwitchTypeNotSpecified extends RuntimeException {
+ private static final long serialVersionUID = 2527453336226053753L;
+
+ public SwitchTypeNotSpecified(DeviceId dpid) {
+ super();
+ log.error("Switch type not specified for switch-config dpid:{}",
+ dpid);
+ }
+ }
+
+ /**
+ * Exception for unknown device type configured for a switch.
+ */
+ public static class UnknownSwitchType extends RuntimeException {
+ private static final long serialVersionUID = 7758418165512249170L;
+
+ public UnknownSwitchType(String type, String name) {
+ super();
+ log.error("Unknown switch type {} for switch name:{}", type, name);
+ }
+ }
+
+ /**
+ * Exception for missing required parameter configuration for a switch.
+ */
+ public static class ParamsNotSpecified extends RuntimeException {
+ private static final long serialVersionUID = 6247582323691265513L;
+
+ public ParamsNotSpecified(String name) {
+ super();
+ log.error("Params required - not specified for switch:{}", name);
+ }
+ }
+
+ /**
+ * Reserved for future use.
+ */
+ public static class LinkTypeNotSpecified extends RuntimeException {
+ private static final long serialVersionUID = -2089470389588542215L;
+
+ public LinkTypeNotSpecified(String dpid1, String dpid2) {
+ super();
+ log.error("Link type not specified for link-config between "
+ + "dpid1:{} and dpid2:{}", dpid1, dpid2);
+ }
+ }
+
+ /**
+ * Reserved for future use.
+ */
+ public static class LinkDpidNotSpecified extends RuntimeException {
+ private static final long serialVersionUID = -5701825916378616004L;
+
+ public LinkDpidNotSpecified(String dpid1, String dpid2) {
+ super();
+ if (dpid1 == null) {
+ log.error("nodeDpid1 not specified for link-config ");
+ }
+ if (dpid2 == null) {
+ log.error("nodeDpid2 not specified for link-config ");
+ }
+ }
+ }
+
+ /**
+ * Reserved for future use.
+ */
+ public static class LinkForUnknownSwitchConfig extends RuntimeException {
+ private static final long serialVersionUID = -2910458439881964094L;
+
+ public LinkForUnknownSwitchConfig(String dpid) {
+ super();
+ log.error("Link configuration was specified for a switch-dpid {} "
+ + "that has not been configured", dpid);
+ }
+ }
+
+ /**
+ * Reserved for future use.
+ */
+ public static class UnknownLinkType extends RuntimeException {
+ private static final long serialVersionUID = -5505376193106542305L;
+
+ public UnknownLinkType(String linktype, String dpid1, String dpid2) {
+ super();
+ log.error("unknown link type {} for links between dpid1:{} "
+ + "and dpid2:{}", linktype, dpid1, dpid2);
+ }
+ }
+
+ /**
+ * Exception for generic configuration errors.
+ */
+ public static class ErrorConfig extends RuntimeException {
+ private static final long serialVersionUID = -2827406314700193147L;
+
+ public ErrorConfig(String errorMsg) {
+ super();
+ log.error(errorMsg);
+ }
+
+ }
+
+ /**
+ * Reserved for future use.
+ */
+ public static class SwitchDpidNotConverted extends RuntimeException {
+ private static final long serialVersionUID = 5640347104590170426L;
+
+ public SwitchDpidNotConverted(String name) {
+ super();
+ log.error("Switch dpid specified as a HexString {} does not match "
+ + "with long value", name);
+ }
+ }
+
+ /**
+ * Reserved for future use.
+ */
+ public static class LinkDpidNotConverted extends RuntimeException {
+ private static final long serialVersionUID = 2397245646094080774L;
+
+ public LinkDpidNotConverted(String dpid1, String dpid2) {
+ log.error("Dpids expressed as HexStrings for links between dpid1:{} "
+ + "and dpid2:{} do not match with long values", dpid1, dpid2);
+ }
+ }
+
+}
+
diff --git a/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/config/NetworkConfigManager.java b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/config/NetworkConfigManager.java
new file mode 100644
index 00000000..44e4f1c6
--- /dev/null
+++ b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/config/NetworkConfigManager.java
@@ -0,0 +1,323 @@
+package org.onosproject.segmentrouting.config;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.Link;
+import org.onosproject.segmentrouting.config.NetworkConfig.LinkConfig;
+import org.onosproject.segmentrouting.config.NetworkConfig.SwitchConfig;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.fasterxml.jackson.core.JsonParseException;
+import com.fasterxml.jackson.databind.JsonMappingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+/**
+ * NetworkConfigManager manages all network configuration for switches, links
+ * and any other state that needs to be configured for correct network
+ * operation.
+ *
+ */
+public class NetworkConfigManager implements NetworkConfigService {
+ protected static final Logger log = LoggerFactory
+ .getLogger(NetworkConfigManager.class);
+ private static final String CONFIG_DIR = "../config";
+ private static final String DEFAULT_CONFIG_FILE = "segmentrouting.conf";
+ private final String configFileName = DEFAULT_CONFIG_FILE;
+ /**
+ * JSON Config file needs to use one of the following types for defining the
+ * kind of switch or link it wishes to configure.
+ */
+ public static final String SEGMENT_ROUTER = "Router_SR";
+
+ public static final String PKT_LINK = "pktLink";
+
+ NetworkConfig networkConfig;
+ private ConcurrentMap<DeviceId, SwitchConfig> configuredSwitches;
+ private ConcurrentMap<Link, LinkConfig> configuredLinks;
+ private Map<String, DeviceId> nameToDpid;
+
+ @Override
+ public SwitchConfigStatus checkSwitchConfig(DeviceId dpid) {
+ SwitchConfig swc = configuredSwitches.get(dpid);
+ if (networkConfig.getRestrictSwitches()) {
+ // default deny behavior
+ if (swc == null) {
+ // switch is not configured - we deny this switch
+ return new SwitchConfigStatus(NetworkConfigState.DENY, null,
+ "Switch not configured, in network denying switches by default.");
+ }
+ if (swc.isAllowed()) {
+ // switch is allowed in config, return configured attributes
+ return new SwitchConfigStatus(NetworkConfigState.ACCEPT_ADD, swc);
+ } else {
+ // switch has been configured off (administratively down)
+ return new SwitchConfigStatus(NetworkConfigState.DENY, null,
+ "Switch configured down (allowed=false).");
+ }
+ } else {
+ // default allow behavior
+ if (swc == null) {
+ // no config to add
+ return new SwitchConfigStatus(NetworkConfigState.ACCEPT, null);
+ }
+ if (swc.isAllowed()) {
+ // switch is allowed in config, return configured attributes
+ return new SwitchConfigStatus(NetworkConfigState.ACCEPT_ADD, swc);
+ } else {
+ // switch has been configured off (administratively down)
+ return new SwitchConfigStatus(NetworkConfigState.DENY, null,
+ "Switch configured down (allowed=false).");
+ }
+ }
+
+ }
+
+ @Override
+ public LinkConfigStatus checkLinkConfig(Link linkTuple) {
+ LinkConfig lkc = getConfiguredLink(linkTuple);
+ // links are always disallowed if any one of the nodes that make up the
+ // link are disallowed
+ DeviceId linkNode1 = linkTuple.src().deviceId();
+ SwitchConfigStatus scs1 = checkSwitchConfig(linkNode1);
+ if (scs1.getConfigState() == NetworkConfigState.DENY) {
+ return new LinkConfigStatus(NetworkConfigState.DENY, null,
+ "Link-node: " + linkNode1 + " denied by config: " + scs1.getMsg());
+ }
+ DeviceId linkNode2 = linkTuple.dst().deviceId();
+ SwitchConfigStatus scs2 = checkSwitchConfig(linkNode2);
+ if (scs2.getConfigState() == NetworkConfigState.DENY) {
+ return new LinkConfigStatus(NetworkConfigState.DENY, null,
+ "Link-node: " + linkNode2 + " denied by config: " + scs2.getMsg());
+ }
+ if (networkConfig.getRestrictLinks()) {
+ // default deny behavior
+ if (lkc == null) {
+ // link is not configured - we deny this link
+ return new LinkConfigStatus(NetworkConfigState.DENY, null,
+ "Link not configured, in network denying links by default.");
+ }
+ if (lkc.isAllowed()) {
+ // link is allowed in config, return configured attributes
+ return new LinkConfigStatus(NetworkConfigState.ACCEPT_ADD, lkc);
+ } else {
+ // link has been configured off (administratively down)
+ return new LinkConfigStatus(NetworkConfigState.DENY, null,
+ "Link configured down (allowed=false).");
+ }
+ } else {
+ // default allow behavior
+ if (lkc == null) {
+ // no config to add
+ return new LinkConfigStatus(NetworkConfigState.ACCEPT, null);
+ }
+ if (lkc.isAllowed()) {
+ // link is allowed in config, return configured attributes
+ return new LinkConfigStatus(NetworkConfigState.ACCEPT_ADD, lkc);
+ } else {
+ // link has been configured off (administratively down)
+ return new LinkConfigStatus(NetworkConfigState.DENY, null,
+ "Link configured down (allowed=false).");
+ }
+ }
+
+ }
+
+ @Override
+ public List<SwitchConfig> getConfiguredAllowedSwitches() {
+ List<SwitchConfig> allowed = new ArrayList<SwitchConfig>();
+ for (SwitchConfig swc : configuredSwitches.values()) {
+ if (swc.isAllowed()) {
+ allowed.add(swc);
+ }
+ }
+ return allowed;
+ }
+
+ @Override
+ public List<LinkConfig> getConfiguredAllowedLinks() {
+ List<LinkConfig> allowed = new ArrayList<LinkConfig>();
+ for (LinkConfig lkc : configuredLinks.values()) {
+ if (lkc.isAllowed()) {
+ allowed.add(lkc);
+ }
+ }
+ return allowed;
+ }
+
+ @Override
+ public DeviceId getDpidForName(String name) {
+ if (nameToDpid.get(name) != null) {
+ return nameToDpid.get(name);
+ }
+ return null;
+ }
+
+ // **************
+ // Private methods
+ // **************
+
+ private void loadNetworkConfig() {
+ File configFile = new File(CONFIG_DIR, configFileName);
+ ObjectMapper mapper = new ObjectMapper();
+ networkConfig = new NetworkConfig();
+
+ try {
+ networkConfig = mapper.readValue(configFile,
+ NetworkConfig.class);
+ } catch (JsonParseException e) {
+ String err = String.format("JsonParseException while loading network "
+ + "config from file: %s: %s", configFileName,
+ e.getMessage());
+ throw new NetworkConfigException.ErrorConfig(err);
+ } catch (JsonMappingException e) {
+ String err = String.format(
+ "JsonMappingException while loading network config "
+ + "from file: %s: %s",
+ configFileName,
+ e.getMessage());
+ throw new NetworkConfigException.ErrorConfig(err);
+ } catch (IOException e) {
+ String err = String.format("IOException while loading network config "
+ + "from file: %s %s", configFileName, e.getMessage());
+ throw new NetworkConfigException.ErrorConfig(err);
+ }
+
+ log.info("Network config specifies: {} switches and {} links",
+ (networkConfig.getRestrictSwitches())
+ ? networkConfig.getSwitchConfig().size() : "default allow",
+ (networkConfig.getRestrictLinks())
+ ? networkConfig.getLinkConfig().size() : "default allow");
+ }
+
+ private void parseNetworkConfig() {
+ List<SwitchConfig> swConfList = networkConfig.getSwitchConfig();
+ List<LinkConfig> lkConfList = networkConfig.getLinkConfig();
+ validateSwitchConfig(swConfList);
+ createTypeSpecificSwitchConfig(swConfList);
+ validateLinkConfig(lkConfList);
+ createTypeSpecificLinkConfig(lkConfList);
+ // TODO validate reachability matrix 'names' for configured dpids
+ }
+
+ private void createTypeSpecificSwitchConfig(List<SwitchConfig> swConfList) {
+ for (SwitchConfig swc : swConfList) {
+ nameToDpid.put(swc.getName(), swc.getDpid());
+ String swtype = swc.getType();
+ switch (swtype) {
+ case SEGMENT_ROUTER:
+ SwitchConfig sr = new SegmentRouterConfig(swc);
+ configuredSwitches.put(sr.getDpid(), sr);
+ break;
+ default:
+ throw new NetworkConfigException.UnknownSwitchType(swtype,
+ swc.getName());
+ }
+ }
+ }
+
+ private void createTypeSpecificLinkConfig(List<LinkConfig> lkConfList) {
+ for (LinkConfig lkc : lkConfList) {
+ String lktype = lkc.getType();
+ switch (lktype) {
+ case PKT_LINK:
+ PktLinkConfig pk = new PktLinkConfig(lkc);
+ for (Link lt : pk.getLinkTupleList()) {
+ configuredLinks.put(lt, pk);
+ }
+ break;
+ default:
+ throw new NetworkConfigException.UnknownLinkType(lktype,
+ lkc.getNodeDpid1(), lkc.getNodeDpid2());
+ }
+ }
+ }
+
+ private void validateSwitchConfig(List<SwitchConfig> swConfList) {
+ Set<DeviceId> swDpids = new HashSet<DeviceId>();
+ Set<String> swNames = new HashSet<String>();
+ for (SwitchConfig swc : swConfList) {
+ if (swc.getNodeDpid() == null || swc.getDpid() == null) {
+ throw new NetworkConfigException.DpidNotSpecified(swc.getName());
+ }
+ // ensure both String and DeviceId values of dpid are set
+ if (!swc.getDpid().equals(DeviceId.deviceId(swc.getNodeDpid()))) {
+ throw new NetworkConfigException.SwitchDpidNotConverted(
+ swc.getName());
+ }
+ if (swc.getName() == null) {
+ throw new NetworkConfigException.NameNotSpecified(swc.getDpid());
+ }
+ if (swc.getType() == null) {
+ throw new NetworkConfigException.SwitchTypeNotSpecified(
+ swc.getDpid());
+ }
+ if (!swDpids.add(swc.getDpid())) {
+ throw new NetworkConfigException.DuplicateDpid(swc.getDpid());
+ }
+ if (!swNames.add(swc.getName())) {
+ throw new NetworkConfigException.DuplicateName(swc.getName());
+ }
+ // TODO Add more validations
+ }
+ }
+
+ private void validateLinkConfig(List<LinkConfig> lkConfList) {
+ for (LinkConfig lkc : lkConfList) {
+ if (lkc.getNodeDpid1() == null || lkc.getNodeDpid2() == null) {
+ throw new NetworkConfigException.LinkDpidNotSpecified(
+ lkc.getNodeDpid1(), lkc.getNodeDpid2());
+ }
+ // ensure both String and Long values are set
+ if (!lkc.getDpid1().equals(DeviceId.deviceId(lkc.getNodeDpid1())) ||
+ !lkc.getDpid2().equals(DeviceId.deviceId(lkc.getNodeDpid2()))) {
+ throw new NetworkConfigException.LinkDpidNotConverted(
+ lkc.getNodeDpid1(), lkc.getNodeDpid2());
+ }
+ if (lkc.getType() == null) {
+ throw new NetworkConfigException.LinkTypeNotSpecified(
+ lkc.getNodeDpid1(), lkc.getNodeDpid2());
+ }
+ if (configuredSwitches.get(lkc.getDpid1()) == null) {
+ throw new NetworkConfigException.LinkForUnknownSwitchConfig(
+ lkc.getNodeDpid1());
+ }
+ if (configuredSwitches.get(lkc.getDpid2()) == null) {
+ throw new NetworkConfigException.LinkForUnknownSwitchConfig(
+ lkc.getNodeDpid2());
+ }
+ // TODO add more validations
+ }
+
+ }
+
+ private LinkConfig getConfiguredLink(Link linkTuple) {
+ LinkConfig lkc = null;
+ // first try the unidirectional link with the ports assigned
+ lkc = configuredLinks.get(linkTuple);
+ return lkc;
+ }
+
+
+ /**
+ * Initializes the network configuration manager module by
+ * loading and parsing the network configuration file.
+ */
+ public void init() {
+ loadNetworkConfig();
+ configuredSwitches = new ConcurrentHashMap<DeviceId, SwitchConfig>();
+ configuredLinks = new ConcurrentHashMap<Link, LinkConfig>();
+ nameToDpid = new HashMap<String, DeviceId>();
+ parseNetworkConfig();
+ }
+}
diff --git a/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/config/NetworkConfigService.java b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/config/NetworkConfigService.java
new file mode 100644
index 00000000..56855271
--- /dev/null
+++ b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/config/NetworkConfigService.java
@@ -0,0 +1,256 @@
+package org.onosproject.segmentrouting.config;
+
+import java.util.List;
+
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.Link;
+import org.onosproject.segmentrouting.config.NetworkConfig.LinkConfig;
+import org.onosproject.segmentrouting.config.NetworkConfig.SwitchConfig;
+
+/**
+ * Exposes methods to retrieve network configuration.
+ *
+ * TODO: currently only startup-configuration is exposed and such configuration
+ * cannot be changed at runtime. Need to add runtime support for changes to
+ * configuration (via REST/CLI) in future releases.
+ *
+ * TODO: return immutable objects or defensive copies of network config so that
+ * users of this API do not inadvertently or maliciously change network config.
+ *
+ * @deprecated in Drake; see org.onosproject.net.config
+ */
+@Deprecated
+public interface NetworkConfigService {
+
+ /**
+ * Suggests the action to be taken by the caller given the configuration
+ * associated with the queried network-object (eg. switch, link etc.).
+ */
+ enum NetworkConfigState {
+ /**
+ * Associated network object has been configured to not be allowed in
+ * the network.
+ */
+ DENY,
+
+ /**
+ * Associated network object has been configured to be allowed in the
+ * network.
+ */
+ ACCEPT,
+
+ /**
+ * Associated network object has been configured to be allowed in the
+ * network. In addition, there are configured parameters that should be
+ * added to the object.
+ */
+ ACCEPT_ADD,
+ }
+
+ /**
+ * Returns the configuration outcome (accept, deny etc.), and any configured
+ * parameters to the caller, in response to a query for the configuration
+ * associated with a switch.
+ */
+ class SwitchConfigStatus {
+ private NetworkConfigState configState;
+ private SwitchConfig switchConfig;
+ private String msg;
+
+ SwitchConfigStatus(NetworkConfigState configState,
+ SwitchConfig switchConfig, String msg) {
+ this.configState = configState;
+ this.switchConfig = switchConfig;
+ this.msg = msg;
+ }
+
+ SwitchConfigStatus(NetworkConfigState configState,
+ SwitchConfig switchConfig) {
+ this.configState = configState;
+ this.switchConfig = switchConfig;
+ this.msg = "";
+ }
+
+ /**
+ * Returns the configuration state for the switch.
+ *
+ * @return non-null NetworkConfigState
+ */
+ public NetworkConfigState getConfigState() {
+ return configState;
+ }
+
+ /**
+ * Returns the switch configuration, which may be null if no
+ * configuration exists, or if the configuration state disallows the
+ * switch.
+ *
+ * @return SwitchConfig, the switch configuration, or null
+ */
+ public SwitchConfig getSwitchConfig() {
+ return switchConfig;
+ }
+
+ /**
+ * User readable string typically used to specify the reason why a
+ * switch is being disallowed.
+ *
+ * @return A non-null but possibly empty String
+ */
+ public String getMsg() {
+ return msg;
+ }
+
+ }
+
+ /**
+ * Reserved for future use.
+ *
+ * Returns the configuration outcome (accept, deny etc.), and any configured
+ * parameters to the caller, in response to a query for the configuration
+ * associated with a link.
+ */
+ class LinkConfigStatus {
+ private NetworkConfigState configState;
+ private LinkConfig linkConfig;
+ private String msg;
+
+ LinkConfigStatus(NetworkConfigState configState,
+ LinkConfig linkConfig, String msg) {
+ this.configState = configState;
+ this.linkConfig = linkConfig;
+ this.msg = msg;
+ }
+
+ LinkConfigStatus(NetworkConfigState configState,
+ LinkConfig linkConfig) {
+ this.configState = configState;
+ this.linkConfig = linkConfig;
+ this.msg = "";
+ }
+
+ /**
+ * Returns the configuration state for the link.
+ *
+ * @return non-null NetworkConfigState
+ */
+ public NetworkConfigState getConfigState() {
+ return configState;
+ }
+
+ /**
+ * Returns the link configuration, which may be null if no configuration
+ * exists, or if the configuration state disallows the link.
+ *
+ * @return SwitchConfig, the switch configuration, or null
+ */
+ public LinkConfig getLinkConfig() {
+ return linkConfig;
+ }
+
+ /**
+ * User readable string typically used to specify the reason why a link
+ * is being disallowed.
+ *
+ * @return msg A non-null but possibly empty String
+ */
+ public String getMsg() {
+ return msg;
+ }
+
+ }
+
+ /**
+ * Checks the switch configuration (if any) associated with the 'dpid'.
+ * Determines if the switch should be allowed or denied according to
+ * configuration rules.
+ *
+ * The method always returns a non-null SwitchConfigStatus. The enclosed
+ * ConfigState contains the result of the check. The enclosed SwitchConfig
+ * may or may not be null, depending on the outcome of the check.
+ *
+ * @param dpid device id of the switch to be queried
+ * @return SwitchConfigStatus with outcome of check and associated config.
+ */
+ SwitchConfigStatus checkSwitchConfig(DeviceId dpid);
+
+ /**
+ * Reserved for future use.
+ *
+ * Checks the link configuration (if any) associated with the 'link'.
+ * Determines if the link should be allowed or denied according to
+ * configuration rules. Note that the 'link' is a unidirectional link which
+ * checked against configuration that is typically defined for a
+ * bidirectional link. The caller may make a second call if it wishes to
+ * check the 'reverse' direction.
+ *
+ * Also note that the configuration may not specify ports for a given
+ * bidirectional link. In such cases, the configuration applies to all links
+ * between the two switches. This method will check the given 'link' against
+ * such configuration.
+
+ * The method always returns a non-null LinkConfigStatus. The enclosed
+ * ConfigState contains the result of the check. The enclosed LinkConfig may
+ * or may not be null, depending on the outcome of the check.
+ *
+ * @param linkTuple unidirectional link to be queried
+ * @return LinkConfigStatus with outcome of check and associated config.
+ */
+ LinkConfigStatus checkLinkConfig(Link linkTuple);
+
+ /**
+ * Retrieves a list of switches that have been configured, and have been
+ * determined to be 'allowed' in the network, according to configuration
+ * rules.
+ *
+ * Note that it is possible that there are other switches that are allowed
+ * in the network that have NOT been configured. Such switches will not be a
+ * part of the returned list.
+ *
+ * Also note that it is possible that some switches will not be discovered
+ * and the only way the controller can know about these switches is via
+ * configuration. Such switches will be included in this list. It is up to
+ * the caller to determine which SwitchConfig applies to non-discovered
+ * switches.
+ *
+ * @return a non-null List of SwitchConfig which may be empty
+ */
+ List<SwitchConfig> getConfiguredAllowedSwitches();
+
+ /**
+ * Reserved for future use.
+ *
+ * Retrieves a list of links that have been configured, and have been
+ * determined to be 'allowed' in the network, according to configuration
+ * rules.
+ *
+ * Note that it is possible that there are other links that are allowed in
+ * the network that have NOT been configured. Such links will not be a part
+ * of the returned list.
+ *
+ * Also note that it is possible that some links will not be discovered and
+ * the only way the controller can know about these links is via
+ * configuration. Such links will be included in this list. It is up to the
+ * caller to determine which LinkConfig applies to non-discovered links.
+ *
+ * In addition, note that the LinkConfig applies to the configured
+ * bi-directional link, which may or may not have declared ports. The
+ * associated unidirectional LinkTuple can be retrieved from the
+ * getLinkTupleList() method in the LinkConfig object.
+ *
+ * @return a non-null List of LinkConfig which may be empty
+ */
+ List<LinkConfig> getConfiguredAllowedLinks();
+
+ /**
+ * Retrieves the Dpid associated with a 'name' for a configured switch
+ * object. This method does not check of the switches are 'allowed' by
+ * config.
+ *
+ * @param name device name
+ * @return the Dpid corresponding to a given 'name', or null if no
+ * configured switch was found for the given 'name'.
+ */
+ DeviceId getDpidForName(String name);
+
+}
diff --git a/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/config/PktLinkConfig.java b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/config/PktLinkConfig.java
new file mode 100644
index 00000000..c66ac3c7
--- /dev/null
+++ b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/config/PktLinkConfig.java
@@ -0,0 +1,144 @@
+package org.onosproject.segmentrouting.config;
+
+import java.util.List;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.onosproject.net.Link;
+import org.onosproject.segmentrouting.config.NetworkConfig.LinkConfig;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.fasterxml.jackson.databind.JsonNode;
+
+/**
+ * Reserved for future use.
+ * Configuration for a link between two packet-switches.
+ */
+public class PktLinkConfig extends LinkConfig {
+ protected static final Logger log = LoggerFactory
+ .getLogger(PktLinkConfig.class);
+ private int port1;
+ private int port2;
+ private String nodeName1;
+ private String nodeName2;
+ private List<Link> linkTupleList;
+
+ public PktLinkConfig(LinkConfig lkc) {
+ nodeDpid1 = lkc.getNodeDpid1();
+ nodeDpid2 = lkc.getNodeDpid2();
+ dpid1 = lkc.getDpid1();
+ dpid2 = lkc.getDpid2();
+ type = lkc.getType();
+ allowed = lkc.isAllowed();
+ params = lkc.getParams();
+ publishAttributes = new ConcurrentHashMap<String, String>();
+ parseParams();
+ validateParams();
+ setPublishAttributes();
+ }
+
+ // ********************
+ // Packet Link Configuration
+ // ********************
+
+ public int getPort1() {
+ return port1;
+ }
+
+ public void setPort1(int port1) {
+ this.port1 = port1;
+ }
+
+ public int getPort2() {
+ return port2;
+ }
+
+ public void setPort2(int port2) {
+ this.port2 = port2;
+ }
+
+ public String getNodeName1() {
+ return nodeName1;
+ }
+
+ public void setNodeName1(String nodeName1) {
+ this.nodeName1 = nodeName1;
+ }
+
+ public String getNodeName2() {
+ return nodeName2;
+ }
+
+ public void setNodeName2(String nodeName2) {
+ this.nodeName2 = nodeName2;
+ }
+
+ /**
+ * Returns the two unidirectional links corresponding to the packet-link
+ * configuration. It is possible that the ports in the LinkTuple have
+ * portnumber '0', implying that the configuration applies to all links
+ * between the two switches.
+ *
+ * @return a list of LinkTuple with exactly 2 unidirectional links
+ */
+ public List<Link> getLinkTupleList() {
+ return linkTupleList;
+ }
+
+ private void setPublishAttributes() {
+
+ }
+
+ private void parseParams() {
+ if (params == null) {
+ throw new PktLinkParamsNotSpecified(nodeDpid1, nodeDpid2);
+ }
+ Set<Entry<String, JsonNode>> m = params.entrySet();
+ for (Entry<String, JsonNode> e : m) {
+ String key = e.getKey();
+ JsonNode j = e.getValue();
+ if (key.equals("nodeName1")) {
+ setNodeName1(j.asText());
+ } else if (key.equals("nodeName2")) {
+ setNodeName2(j.asText());
+ } else if (key.equals("port1")) {
+ setPort1(j.asInt());
+ } else if (key.equals("port2")) {
+ setPort2(j.asInt());
+ } else {
+ throw new UnknownPktLinkConfig(key, nodeDpid1, nodeDpid2);
+ }
+ }
+ }
+
+ private void validateParams() {
+ // TODO - wrong-names, duplicate links,
+ // duplicate use of port, is switch-allowed for which link is allowed?
+ // valid port numbers
+ }
+
+ public static class PktLinkParamsNotSpecified extends RuntimeException {
+ private static final long serialVersionUID = 6247582323691265513L;
+
+ public PktLinkParamsNotSpecified(String dpidA, String dpidB) {
+ super();
+ log.error("Params required for packet link - not specified "
+ + "for link between switch1:{} and switch2:{}",
+ dpidA, dpidB);
+ }
+ }
+
+ public static class UnknownPktLinkConfig extends RuntimeException {
+ private static final long serialVersionUID = -5750132094884129179L;
+
+ public UnknownPktLinkConfig(String key, String dpidA, String dpidB) {
+ super();
+ log.error("Unknown packet-link config {} for link between"
+ + " dpid1: {} and dpid2: {}", key,
+ dpidA, dpidB);
+ }
+ }
+
+}
diff --git a/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/config/SegmentRouterConfig.java b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/config/SegmentRouterConfig.java
new file mode 100644
index 00000000..4775c77f
--- /dev/null
+++ b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/config/SegmentRouterConfig.java
@@ -0,0 +1,430 @@
+package org.onosproject.segmentrouting.config;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.onosproject.net.DeviceId;
+import org.onosproject.segmentrouting.config.NetworkConfig.SwitchConfig;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+/**
+ * Manages additional configuration for switches configured as Segment Routers.
+ */
+public class SegmentRouterConfig extends SwitchConfig {
+ protected static final Logger log = LoggerFactory
+ .getLogger(SegmentRouterConfig.class);
+ private String routerIp;
+ private String routerMac;
+ private int nodeSid;
+ private boolean isEdgeRouter;
+ private List<AdjacencySid> adjacencySids;
+ private List<Subnet> subnets;
+
+ public static final String ROUTER_IP = "routerIp";
+ public static final String ROUTER_MAC = "routerMac";
+ public static final String NODE_SID = "nodeSid";
+ public static final String ADJACENCY_SIDS = "adjacencySids";
+ public static final String SUBNETS = "subnets";
+ public static final String ISEDGE = "isEdgeRouter";
+ private static final int SRGB_MAX = 1000;
+
+ /**
+ * Parses and validates the additional configuration parameters applicable
+ * to segment routers.
+ *
+ * @param swc switch configuration
+ */
+ public SegmentRouterConfig(SwitchConfig swc) {
+ this.setName(swc.getName());
+ this.setDpid(swc.getDpid());
+ this.setType(swc.getType());
+ this.setLatitude(swc.getLatitude());
+ this.setLongitude(swc.getLongitude());
+ this.setParams(swc.getParams());
+ this.setAllowed(swc.isAllowed());
+ publishAttributes = new ConcurrentHashMap<String, String>();
+ adjacencySids = new ArrayList<AdjacencySid>();
+ subnets = new ArrayList<Subnet>();
+ parseParams();
+ validateParams();
+ setPublishAttributes();
+ }
+
+ /**
+ * Returns the configured segment router IP address.
+ *
+ * @return ip address in string format
+ */
+ public String getRouterIp() {
+ return routerIp;
+ }
+
+ public void setRouterIp(String routerIp) {
+ this.routerIp = routerIp;
+ }
+
+ /**
+ * Returns the configured segment router mac address.
+ *
+ * @return mac address in string format
+ */
+ public String getRouterMac() {
+ return routerMac;
+ }
+
+ public void setRouterMac(String routerMac) {
+ this.routerMac = routerMac;
+ }
+
+ /**
+ * Returns the configured sID for a segment router.
+ *
+ * @return segment identifier
+ */
+ public int getNodeSid() {
+ return nodeSid;
+ }
+
+ public void setNodeSid(int nodeSid) {
+ this.nodeSid = nodeSid;
+ }
+
+ /**
+ * Returns the flag that indicates the configured segment router
+ * is edge or backbone router.
+ *
+ * @return boolean
+ */
+ public boolean isEdgeRouter() {
+ return isEdgeRouter;
+ }
+
+ public void setIsEdgeRouter(boolean isEdge) {
+ this.isEdgeRouter = isEdge;
+ }
+
+ /**
+ * Class representing segment router adjacency identifier.
+ */
+ public static class AdjacencySid {
+ private int adjSid;
+ private List<Integer> ports;
+
+ public AdjacencySid(int adjSid, List<Integer> ports) {
+ this.ports = ports;
+ this.adjSid = adjSid;
+ }
+
+ /**
+ * Returns the list of ports part of a segment
+ * router adjacency identifier.
+ *
+ * @return list of integers
+ */
+ public List<Integer> getPorts() {
+ return ports;
+ }
+
+ public void setPorts(List<Integer> ports) {
+ this.ports = ports;
+ }
+
+ /**
+ * Returns the configured adjacency id of a segment router.
+ *
+ * @return integer
+ */
+ public int getAdjSid() {
+ return adjSid;
+ }
+
+ public void setAdjSid(int adjSid) {
+ this.adjSid = adjSid;
+ }
+ }
+
+ /**
+ * Returns the configured adjacent segment IDs for a segment router.
+ *
+ * @return list of adjacency identifier
+ */
+ public List<AdjacencySid> getAdjacencySids() {
+ return adjacencySids;
+ }
+
+ public void setAdjacencySids(List<AdjacencySid> adjacencySids) {
+ this.adjacencySids = adjacencySids;
+ }
+
+ /**
+ * Class representing a subnet attached to a segment router.
+ */
+ public static class Subnet {
+ private int portNo;
+ private String subnetIp;
+
+ public Subnet(int portNo, String subnetIp) {
+ this.portNo = portNo;
+ this.subnetIp = subnetIp;
+ }
+
+ /**
+ * Returns the port number of segment router on
+ * which subnet is attached.
+ *
+ * @return integer
+ */
+ public int getPortNo() {
+ return portNo;
+ }
+
+ public void setPortNo(int portNo) {
+ this.portNo = portNo;
+ }
+
+ /**
+ * Returns the configured subnet address.
+ *
+ * @return subnet ip address in string format
+ */
+ public String getSubnetIp() {
+ return subnetIp;
+ }
+
+ public void setSubnetIp(String subnetIp) {
+ this.subnetIp = subnetIp;
+ }
+ }
+
+ /**
+ * Returns the configured subnets for a segment router.
+ *
+ * @return list of subnets
+ */
+ public List<Subnet> getSubnets() {
+ return subnets;
+ }
+
+ public void setSubnets(List<Subnet> subnets) {
+ this.subnets = subnets;
+ }
+
+ // ********************
+ // Helper methods
+ // ********************
+
+ private void parseParams() {
+ if (params == null) {
+ throw new NetworkConfigException.ParamsNotSpecified(name);
+ }
+
+ Set<Entry<String, JsonNode>> m = params.entrySet();
+ for (Entry<String, JsonNode> e : m) {
+ String key = e.getKey();
+ JsonNode j = e.getValue();
+ if (key.equals("routerIp")) {
+ setRouterIp(j.asText());
+ } else if (key.equals("routerMac")) {
+ setRouterMac(j.asText());
+ } else if (key.equals("nodeSid")) {
+ setNodeSid(j.asInt());
+ } else if (key.equals("isEdgeRouter")) {
+ setIsEdgeRouter(j.asBoolean());
+ } else if (key.equals("adjacencySids") || key.equals("subnets")) {
+ getInnerParams(j, key);
+ } else {
+ throw new UnknownSegmentRouterConfig(key, dpid);
+ }
+ }
+ }
+
+ private void getInnerParams(JsonNode j, String innerParam) {
+ Iterator<JsonNode> innerList = j.elements();
+ while (innerList.hasNext()) {
+ Iterator<Entry<String, JsonNode>> f = innerList.next().fields();
+ int portNo = -1;
+ int adjSid = -1;
+ String subnetIp = null;
+ List<Integer> ports = null;
+ while (f.hasNext()) {
+ Entry<String, JsonNode> fe = f.next();
+ if (fe.getKey().equals("portNo")) {
+ portNo = fe.getValue().asInt();
+ } else if (fe.getKey().equals("adjSid")) {
+ adjSid = fe.getValue().asInt();
+ } else if (fe.getKey().equals("subnetIp")) {
+ subnetIp = fe.getValue().asText();
+ } else if (fe.getKey().equals("ports")) {
+ if (fe.getValue().isArray()) {
+ Iterator<JsonNode> i = fe.getValue().elements();
+ ports = new ArrayList<Integer>();
+ while (i.hasNext()) {
+ ports.add(i.next().asInt());
+ }
+ }
+ } else {
+ throw new UnknownSegmentRouterConfig(fe.getKey(), dpid);
+ }
+ }
+ if (innerParam.equals("adjacencySids")) {
+ AdjacencySid ads = new AdjacencySid(adjSid, ports);
+ adjacencySids.add(ads);
+ } else {
+ Subnet sip = new Subnet(portNo, subnetIp);
+ subnets.add(sip);
+ }
+ }
+ }
+
+ private void validateParams() {
+ if (routerIp == null) {
+ throw new IpNotSpecified(dpid);
+ }
+ if (routerMac == null) {
+ throw new MacNotSpecified(dpid);
+ }
+ if (isEdgeRouter && subnets.isEmpty()) {
+ throw new SubnetNotSpecifiedInEdgeRouter(dpid);
+ }
+ if (!isEdgeRouter && !subnets.isEmpty()) {
+ throw new SubnetSpecifiedInBackboneRouter(dpid);
+ }
+ if (nodeSid > SRGB_MAX) {
+ throw new NodeLabelNotInSRGB(nodeSid, dpid);
+ }
+ for (AdjacencySid as : adjacencySids) {
+ int label = as.getAdjSid();
+ List<Integer> plist = as.getPorts();
+ if (label <= SRGB_MAX) {
+ throw new AdjacencyLabelInSRGB(label, dpid);
+ }
+ if (plist.size() <= 1) {
+ throw new AdjacencyLabelNotEnoughPorts(label, dpid);
+ }
+ }
+
+
+ // TODO more validations
+ }
+
+ /**
+ * Setting publishAttributes implies that this is the configuration that
+ * will be added to Topology.Switch object before it is published on the
+ * channel to other controller instances.
+ */
+ private void setPublishAttributes() {
+ publishAttributes.put(ROUTER_IP, routerIp);
+ publishAttributes.put(ROUTER_MAC, routerMac);
+ publishAttributes.put(NODE_SID, String.valueOf(nodeSid));
+ publishAttributes.put(ISEDGE, String.valueOf(isEdgeRouter));
+ ObjectMapper mapper = new ObjectMapper();
+ try {
+ publishAttributes.put(ADJACENCY_SIDS,
+ mapper.writeValueAsString(adjacencySids));
+ publishAttributes.put(SUBNETS,
+ mapper.writeValueAsString(subnets));
+ } catch (JsonProcessingException e) {
+ log.error("Error while writing SR config: {}", e.getCause());
+ } catch (IOException e) {
+ log.error("Error while writing SR config: {}", e.getCause());
+ }
+ }
+
+ // ********************
+ // Exceptions
+ // ********************
+
+ public static class IpNotSpecified extends RuntimeException {
+ private static final long serialVersionUID = -3001502553646331686L;
+
+ public IpNotSpecified(DeviceId dpid) {
+ super();
+ log.error("Router IP address not specified for SR config dpid:{}",
+ dpid);
+ }
+ }
+
+ public static class MacNotSpecified extends RuntimeException {
+ private static final long serialVersionUID = -5850132094884129179L;
+
+ public MacNotSpecified(DeviceId dpid) {
+ super();
+ log.error("Router Mac address not specified for SR config dpid:{}",
+ dpid);
+ }
+ }
+
+ public static class UnknownSegmentRouterConfig extends RuntimeException {
+ private static final long serialVersionUID = -5750132094884129179L;
+
+ public UnknownSegmentRouterConfig(String key, DeviceId dpid) {
+ super();
+ log.error("Unknown Segment Router config {} in dpid: {}", key,
+ dpid);
+ }
+ }
+
+ public static class SubnetNotSpecifiedInEdgeRouter extends RuntimeException {
+ private static final long serialVersionUID = -5855458472668581268L;
+
+ public SubnetNotSpecifiedInEdgeRouter(DeviceId dpid) {
+ super();
+ log.error("Subnet was not specified for edge router in dpid: {}",
+ dpid);
+ }
+ }
+
+ public static class SubnetSpecifiedInBackboneRouter extends RuntimeException {
+ private static final long serialVersionUID = 1L;
+
+ public SubnetSpecifiedInBackboneRouter(DeviceId dpid) {
+ super();
+ log.error("Subnet was specified in backbone router in dpid: {}",
+ dpid);
+ }
+ }
+
+ public static class NodeLabelNotInSRGB extends RuntimeException {
+ private static final long serialVersionUID = -8482670903748519526L;
+
+ public NodeLabelNotInSRGB(int label, DeviceId dpid) {
+ super();
+ log.error("Node sif {} specified in not in global label-base "
+ + "in dpid: {}", label,
+ dpid);
+ }
+ }
+
+ public static class AdjacencyLabelInSRGB extends RuntimeException {
+ private static final long serialVersionUID = -8482670903748519526L;
+
+ public AdjacencyLabelInSRGB(int label, DeviceId dpid) {
+ super();
+ log.error("Adjaceny label {} specified from global label-base "
+ + "in dpid: {}", label,
+ dpid);
+ }
+ }
+
+ public static class AdjacencyLabelNotEnoughPorts extends RuntimeException {
+ private static final long serialVersionUID = -8482670903748519526L;
+
+ public AdjacencyLabelNotEnoughPorts(int label, DeviceId dpid) {
+ super();
+ log.error("Adjaceny label {} must be specified for at least 2 ports. "
+ + "Adjacency labels for single ports are auto-generated "
+ + "in dpid: {}", label,
+ dpid);
+ }
+ }
+}
diff --git a/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/config/package-info.java b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/config/package-info.java
new file mode 100644
index 00000000..fdae9c9e
--- /dev/null
+++ b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/config/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * 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.
+ */
+
+/**
+ * Segment routing network configuration mechanism.
+ */
+package org.onosproject.segmentrouting.config; \ No newline at end of file
diff --git a/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/grouphandler/DefaultEdgeGroupHandler.java b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/grouphandler/DefaultEdgeGroupHandler.java
new file mode 100644
index 00000000..41cf8acc
--- /dev/null
+++ b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/grouphandler/DefaultEdgeGroupHandler.java
@@ -0,0 +1,180 @@
+/*
+ * 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.grouphandler;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.onosproject.core.ApplicationId;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.Link;
+import org.onosproject.net.flowobjective.FlowObjectiveService;
+import org.onosproject.net.link.LinkService;
+import org.onosproject.store.service.EventuallyConsistentMap;
+
+/**
+ * Default ECMP group handler creation module for an edge device.
+ * This component creates a set of ECMP groups for every neighbor
+ * that this device is connected to.
+ * For example, consider a network of 4 devices: D0 (Segment ID: 100),
+ * D1 (Segment ID: 101), D2 (Segment ID: 102) and D3 (Segment ID: 103),
+ * where D0 and D3 are edge devices and D1 and D2 are transit devices.
+ * Assume device D0 is connected to 2 neighbors (D1 and D2 ).
+ * The following groups will be created in D0:
+ * 1) all ports to D1 + with no label push,
+ * 2) all ports to D1 + with label 102 pushed,
+ * 3) all ports to D1 + with label 103 pushed,
+ * 4) all ports to D2 + with no label push,
+ * 5) all ports to D2 + with label 101 pushed,
+ * 6) all ports to D2 + with label 103 pushed,
+ * 7) all ports to D1 and D2 + with label 103 pushed
+ */
+public class DefaultEdgeGroupHandler extends DefaultGroupHandler {
+
+ protected DefaultEdgeGroupHandler(DeviceId deviceId,
+ ApplicationId appId,
+ DeviceProperties config,
+ LinkService linkService,
+ FlowObjectiveService flowObjService,
+ EventuallyConsistentMap<
+ NeighborSetNextObjectiveStoreKey,
+ Integer> nsNextObjStore) {
+ super(deviceId, appId, config, linkService, flowObjService, nsNextObjStore);
+ }
+
+ @Override
+ public void createGroups() {
+ log.debug("Creating default groups "
+ + "for edge device {}", deviceId);
+ Set<DeviceId> neighbors = devicePortMap.keySet();
+ if (neighbors == null || neighbors.isEmpty()) {
+ return;
+ }
+
+ // Create all possible Neighbor sets from this router
+ Set<Set<DeviceId>> powerSet = getPowerSetOfNeighbors(neighbors);
+ log.trace("createGroupsAtEdgeRouter: The size of neighbor powerset "
+ + "for sw {} is {}", deviceId, powerSet.size());
+ Set<NeighborSet> nsSet = new HashSet<NeighborSet>();
+ for (Set<DeviceId> combo : powerSet) {
+ if (combo.isEmpty()) {
+ continue;
+ }
+ List<Integer> groupSegmentIds =
+ getSegmentIdsTobePairedWithNeighborSet(combo);
+ for (Integer sId : groupSegmentIds) {
+ NeighborSet ns = new NeighborSet(combo, sId);
+ log.trace("createGroupsAtEdgeRouter: sw {} "
+ + "combo {} sId {} ns {}",
+ deviceId, combo, sId, ns);
+ nsSet.add(ns);
+ }
+ }
+ log.trace("createGroupsAtEdgeRouter: The neighborset "
+ + "with label for sw {} is {}",
+ deviceId, nsSet);
+
+ createGroupsFromNeighborsets(nsSet);
+ }
+
+ @Override
+ protected void newNeighbor(Link newNeighborLink) {
+ log.debug("New Neighbor: Updating groups "
+ + "for edge device {}", deviceId);
+ // Recompute neighbor power set
+ addNeighborAtPort(newNeighborLink.dst().deviceId(),
+ newNeighborLink.src().port());
+ // Compute new neighbor sets due to the addition of new neighbor
+ Set<NeighborSet> nsSet = computeImpactedNeighborsetForPortEvent(
+ newNeighborLink.dst().deviceId(),
+ devicePortMap.keySet());
+ createGroupsFromNeighborsets(nsSet);
+ }
+
+ @Override
+ protected void newPortToExistingNeighbor(Link newNeighborLink) {
+ /*log.debug("New port to existing neighbor: Updating "
+ + "groups for edge device {}", deviceId);
+ addNeighborAtPort(newNeighborLink.dst().deviceId(),
+ newNeighborLink.src().port());
+ Set<NeighborSet> nsSet = computeImpactedNeighborsetForPortEvent(
+ newNeighborLink.dst().deviceId(),
+ devicePortMap.keySet());
+ for (NeighborSet ns : nsSet) {
+ // Create the new bucket to be updated
+ TrafficTreatment.Builder tBuilder =
+ DefaultTrafficTreatment.builder();
+ tBuilder.setOutput(newNeighborLink.src().port())
+ .setEthDst(deviceConfig.getDeviceMac(
+ newNeighborLink.dst().deviceId()))
+ .setEthSrc(nodeMacAddr);
+ if (ns.getEdgeLabel() != NeighborSet.NO_EDGE_LABEL) {
+ tBuilder.pushMpls()
+ .setMpls(MplsLabel.
+ mplsLabel(ns.getEdgeLabel()));
+ }
+
+ Integer nextId = deviceNextObjectiveIds.get(ns);
+ if (nextId != null) {
+ NextObjective.Builder nextObjBuilder = DefaultNextObjective
+ .builder().withId(nextId)
+ .withType(NextObjective.Type.HASHED).fromApp(appId);
+
+ nextObjBuilder.addTreatment(tBuilder.build());
+
+ NextObjective nextObjective = nextObjBuilder.add();
+ flowObjectiveService.next(deviceId, nextObjective);
+ }
+ }*/
+ }
+
+ @Override
+ protected Set<NeighborSet> computeImpactedNeighborsetForPortEvent(
+ DeviceId impactedNeighbor,
+ Set<DeviceId> updatedNeighbors) {
+ Set<Set<DeviceId>> powerSet = getPowerSetOfNeighbors(updatedNeighbors);
+
+ Set<DeviceId> tmp = new HashSet<DeviceId>();
+ tmp.addAll(updatedNeighbors);
+ tmp.remove(impactedNeighbor);
+ Set<Set<DeviceId>> tmpPowerSet = getPowerSetOfNeighbors(tmp);
+
+ // Compute the impacted neighbor sets
+ powerSet.removeAll(tmpPowerSet);
+
+ Set<NeighborSet> nsSet = new HashSet<NeighborSet>();
+ for (Set<DeviceId> combo : powerSet) {
+ if (combo.isEmpty()) {
+ continue;
+ }
+ List<Integer> groupSegmentIds =
+ getSegmentIdsTobePairedWithNeighborSet(combo);
+ for (Integer sId : groupSegmentIds) {
+ NeighborSet ns = new NeighborSet(combo, sId);
+ log.trace("computeImpactedNeighborsetForPortEvent: sw {} "
+ + "combo {} sId {} ns {}",
+ deviceId, combo, sId, ns);
+ nsSet.add(ns);
+ }
+ }
+ log.trace("computeImpactedNeighborsetForPortEvent: The neighborset "
+ + "with label for sw {} is {}",
+ deviceId, nsSet);
+ return nsSet;
+ }
+
+}
diff --git a/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/grouphandler/DefaultGroupHandler.java b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/grouphandler/DefaultGroupHandler.java
new file mode 100644
index 00000000..a43a0f09
--- /dev/null
+++ b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/grouphandler/DefaultGroupHandler.java
@@ -0,0 +1,539 @@
+/*
+ * 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.grouphandler;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.slf4j.LoggerFactory.getLogger;
+
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Random;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import org.onlab.packet.MacAddress;
+import org.onlab.packet.MplsLabel;
+import org.onlab.util.KryoNamespace;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.Link;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.flow.DefaultTrafficTreatment;
+import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.flowobjective.DefaultNextObjective;
+import org.onosproject.net.flowobjective.FlowObjectiveService;
+import org.onosproject.net.flowobjective.NextObjective;
+import org.onosproject.net.flowobjective.Objective;
+import org.onosproject.net.flowobjective.ObjectiveContext;
+import org.onosproject.net.flowobjective.ObjectiveError;
+import org.onosproject.net.group.DefaultGroupKey;
+import org.onosproject.net.group.GroupKey;
+import org.onosproject.net.link.LinkService;
+import org.onosproject.store.service.EventuallyConsistentMap;
+import org.slf4j.Logger;
+
+/**
+ * Default ECMP group handler creation module. This component creates a set of
+ * ECMP groups for every neighbor that this device is connected to based on
+ * whether the current device is an edge device or a transit device.
+ */
+public class DefaultGroupHandler {
+ protected static final Logger log = getLogger(DefaultGroupHandler.class);
+
+ protected final DeviceId deviceId;
+ protected final ApplicationId appId;
+ protected final DeviceProperties deviceConfig;
+ protected final List<Integer> allSegmentIds;
+ protected final int nodeSegmentId;
+ protected final boolean isEdgeRouter;
+ protected final MacAddress nodeMacAddr;
+ protected LinkService linkService;
+ protected FlowObjectiveService flowObjectiveService;
+
+ protected HashMap<DeviceId, Set<PortNumber>> devicePortMap = new HashMap<>();
+ protected HashMap<PortNumber, DeviceId> portDeviceMap = new HashMap<>();
+ //protected HashMap<NeighborSet, Integer> deviceNextObjectiveIds =
+ // new HashMap<NeighborSet, Integer>();
+ protected EventuallyConsistentMap<
+ NeighborSetNextObjectiveStoreKey, Integer> nsNextObjStore = null;
+ protected Random rand = new Random();
+
+ protected KryoNamespace.Builder kryo = new KryoNamespace.Builder()
+ .register(URI.class).register(HashSet.class)
+ .register(DeviceId.class).register(PortNumber.class)
+ .register(NeighborSet.class).register(PolicyGroupIdentifier.class)
+ .register(PolicyGroupParams.class)
+ .register(GroupBucketIdentifier.class)
+ .register(GroupBucketIdentifier.BucketOutputType.class);
+
+ protected DefaultGroupHandler(DeviceId deviceId, ApplicationId appId,
+ DeviceProperties config,
+ LinkService linkService,
+ FlowObjectiveService flowObjService,
+ EventuallyConsistentMap<
+ NeighborSetNextObjectiveStoreKey,
+ Integer> nsNextObjStore) {
+ this.deviceId = checkNotNull(deviceId);
+ this.appId = checkNotNull(appId);
+ this.deviceConfig = checkNotNull(config);
+ this.linkService = checkNotNull(linkService);
+ allSegmentIds = checkNotNull(config.getAllDeviceSegmentIds());
+ nodeSegmentId = config.getSegmentId(deviceId);
+ isEdgeRouter = config.isEdgeDevice(deviceId);
+ nodeMacAddr = checkNotNull(config.getDeviceMac(deviceId));
+ this.flowObjectiveService = flowObjService;
+ this.nsNextObjStore = nsNextObjStore;
+
+ populateNeighborMaps();
+ }
+
+ /**
+ * Creates a group handler object based on the type of device. If device is
+ * of edge type it returns edge group handler, else it returns transit group
+ * handler.
+ *
+ * @param deviceId device identifier
+ * @param appId application identifier
+ * @param config interface to retrieve the device properties
+ * @param linkService link service object
+ * @param flowObjService flow objective service object
+ * @param nsNextObjStore next objective store map
+ * @return default group handler type
+ */
+ public static DefaultGroupHandler createGroupHandler(DeviceId deviceId,
+ ApplicationId appId,
+ DeviceProperties config,
+ LinkService linkService,
+ FlowObjectiveService flowObjService,
+ EventuallyConsistentMap<NeighborSetNextObjectiveStoreKey,
+ Integer> nsNextObjStore) {
+ if (config.isEdgeDevice(deviceId)) {
+ return new DefaultEdgeGroupHandler(deviceId, appId, config,
+ linkService,
+ flowObjService,
+ nsNextObjStore);
+ } else {
+ return new DefaultTransitGroupHandler(deviceId, appId, config,
+ linkService,
+ flowObjService,
+ nsNextObjStore);
+ }
+ }
+
+ /**
+ * Creates the auto created groups for this device based on the current
+ * snapshot of the topology.
+ */
+ // Empty implementations to be overridden by derived classes
+ public void createGroups() {
+ }
+
+ /**
+ * Performs group creation or update procedures when a new link is
+ * discovered on this device.
+ *
+ * @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, newLink.src().deviceId());
+ return;
+ }
+
+ log.debug("Device {} linkUp at local port {} to neighbor {}", deviceId,
+ newLink.src().port(), newLink.dst().deviceId());
+ addNeighborAtPort(newLink.dst().deviceId(),
+ newLink.src().port());
+ /*if (devicePortMap.get(newLink.dst().deviceId()) == null) {
+ // New Neighbor
+ newNeighbor(newLink);
+ } else {
+ // Old Neighbor
+ newPortToExistingNeighbor(newLink);
+ }*/
+ Set<NeighborSet> nsSet = nsNextObjStore.keySet()
+ .stream()
+ .filter((nsStoreEntry) -> (nsStoreEntry.deviceId().equals(deviceId)))
+ .map((nsStoreEntry) -> (nsStoreEntry.neighborSet()))
+ .filter((ns) -> (ns.getDeviceIds()
+ .contains(newLink.dst().deviceId())))
+ .collect(Collectors.toSet());
+ log.trace("linkUp: nsNextObjStore contents for device {}:",
+ deviceId,
+ nsSet);
+ for (NeighborSet ns : nsSet) {
+ // Create the new bucket to be updated
+ TrafficTreatment.Builder tBuilder =
+ DefaultTrafficTreatment.builder();
+ tBuilder.setOutput(newLink.src().port())
+ .setEthDst(deviceConfig.getDeviceMac(
+ newLink.dst().deviceId()))
+ .setEthSrc(nodeMacAddr);
+ if (ns.getEdgeLabel() != NeighborSet.NO_EDGE_LABEL) {
+ tBuilder.pushMpls()
+ .setMpls(MplsLabel.
+ mplsLabel(ns.getEdgeLabel()));
+ }
+
+ Integer nextId = nsNextObjStore.
+ get(new NeighborSetNextObjectiveStoreKey(deviceId, ns));
+ if (nextId != null) {
+ NextObjective.Builder nextObjBuilder = DefaultNextObjective
+ .builder().withId(nextId)
+ .withType(NextObjective.Type.HASHED).fromApp(appId);
+
+ nextObjBuilder.addTreatment(tBuilder.build());
+
+ log.debug("linkUp in device {}: Adding Bucket "
+ + "with Port {} to next object id {}",
+ deviceId,
+ newLink.src().port(),
+ nextId);
+ NextObjective nextObjective = nextObjBuilder.
+ add(new SRNextObjectiveContext(deviceId));
+ flowObjectiveService.next(deviceId, nextObjective);
+ }
+ }
+ }
+
+ /**
+ * Performs group recovery procedures when a port goes down on this device.
+ *
+ * @param port port number that has gone down
+ */
+ public void portDown(PortNumber port) {
+ if (portDeviceMap.get(port) == null) {
+ log.warn("portDown: unknown port");
+ return;
+ }
+ log.debug("Device {} portDown {} to neighbor {}", deviceId, port,
+ portDeviceMap.get(port));
+ /*Set<NeighborSet> nsSet = computeImpactedNeighborsetForPortEvent(portDeviceMap
+ .get(port),
+ devicePortMap
+ .keySet());*/
+ Set<NeighborSet> nsSet = nsNextObjStore.keySet()
+ .stream()
+ .filter((nsStoreEntry) -> (nsStoreEntry.deviceId().equals(deviceId)))
+ .map((nsStoreEntry) -> (nsStoreEntry.neighborSet()))
+ .filter((ns) -> (ns.getDeviceIds()
+ .contains(portDeviceMap.get(port))))
+ .collect(Collectors.toSet());
+ log.trace("portDown: nsNextObjStore contents for device {}:",
+ deviceId,
+ nsSet);
+ for (NeighborSet ns : nsSet) {
+ // Create the bucket to be removed
+ TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment
+ .builder();
+ tBuilder.setOutput(port)
+ .setEthDst(deviceConfig.getDeviceMac(portDeviceMap
+ .get(port))).setEthSrc(nodeMacAddr);
+ if (ns.getEdgeLabel() != NeighborSet.NO_EDGE_LABEL) {
+ tBuilder.pushMpls().setMpls(MplsLabel.mplsLabel(ns
+ .getEdgeLabel()));
+ }
+
+ Integer nextId = nsNextObjStore.
+ get(new NeighborSetNextObjectiveStoreKey(deviceId, ns));
+ if (nextId != null) {
+ NextObjective.Builder nextObjBuilder = DefaultNextObjective
+ .builder().withType(NextObjective.Type.SIMPLE).withId(nextId).fromApp(appId);
+
+ nextObjBuilder.addTreatment(tBuilder.build());
+
+ log.debug("portDown in device {}: Removing Bucket "
+ + "with Port {} to next object id {}",
+ deviceId,
+ port,
+ nextId);
+ NextObjective nextObjective = nextObjBuilder.
+ remove(new SRNextObjectiveContext(deviceId));
+
+ flowObjectiveService.next(deviceId, nextObjective);
+ }
+
+ }
+
+ devicePortMap.get(portDeviceMap.get(port)).remove(port);
+ portDeviceMap.remove(port);
+ }
+
+ /**
+ * Returns the next objective associated with the neighborset.
+ * If there is no next objective for this neighborset, this API
+ * would create a next objective and return.
+ *
+ * @param ns neighborset
+ * @return int if found or -1
+ */
+ public int getNextObjectiveId(NeighborSet ns) {
+ Integer nextId = nsNextObjStore.
+ get(new NeighborSetNextObjectiveStoreKey(deviceId, ns));
+ if (nextId == null) {
+ log.trace("getNextObjectiveId in device{}: Next objective id "
+ + "not found for {} and creating", deviceId, ns);
+ log.trace("getNextObjectiveId: nsNextObjStore contents for device {}: {}",
+ deviceId,
+ nsNextObjStore.entrySet()
+ .stream()
+ .filter((nsStoreEntry) ->
+ (nsStoreEntry.getKey().deviceId().equals(deviceId)))
+ .collect(Collectors.toList()));
+ createGroupsFromNeighborsets(Collections.singleton(ns));
+ nextId = nsNextObjStore.
+ get(new NeighborSetNextObjectiveStoreKey(deviceId, ns));
+ if (nextId == null) {
+ log.warn("getNextObjectiveId: unable to create next objective");
+ return -1;
+ } else {
+ log.debug("getNextObjectiveId in device{}: Next objective id {} "
+ + "created for {}", deviceId, nextId.intValue(), ns);
+ }
+ } else {
+ log.trace("getNextObjectiveId in device{}: Next objective id {} "
+ + "found for {}", deviceId, nextId.intValue(), ns);
+ }
+ return nextId.intValue();
+ }
+
+ /**
+ * Checks if the next objective ID (group) for the neighbor set exists or not.
+ *
+ * @param ns neighbor set to check
+ * @return true if it exists, false otherwise
+ */
+ public boolean hasNextObjectiveId(NeighborSet ns) {
+ Integer nextId = nsNextObjStore.
+ get(new NeighborSetNextObjectiveStoreKey(deviceId, ns));
+ if (nextId == null) {
+ return false;
+ }
+
+ return true;
+ }
+
+ // Empty implementation
+ protected void newNeighbor(Link newLink) {
+ }
+
+ // Empty implementation
+ protected void newPortToExistingNeighbor(Link newLink) {
+ }
+
+ // Empty implementation
+ protected Set<NeighborSet>
+ computeImpactedNeighborsetForPortEvent(DeviceId impactedNeighbor,
+ Set<DeviceId> updatedNeighbors) {
+ return null;
+ }
+
+ private void populateNeighborMaps() {
+ Set<Link> outgoingLinks = linkService.getDeviceEgressLinks(deviceId);
+ for (Link link : outgoingLinks) {
+ if (link.type() != Link.Type.DIRECT) {
+ continue;
+ }
+ addNeighborAtPort(link.dst().deviceId(), link.src().port());
+ }
+ }
+
+ protected void addNeighborAtPort(DeviceId neighborId,
+ PortNumber portToNeighbor) {
+ // Update DeviceToPort database
+ log.debug("Device {} addNeighborAtPort: neighbor {} at port {}",
+ deviceId, neighborId, portToNeighbor);
+ if (devicePortMap.get(neighborId) != null) {
+ devicePortMap.get(neighborId).add(portToNeighbor);
+ } else {
+ Set<PortNumber> ports = new HashSet<PortNumber>();
+ ports.add(portToNeighbor);
+ devicePortMap.put(neighborId, ports);
+ }
+
+ // Update portToDevice database
+ if (portDeviceMap.get(portToNeighbor) == null) {
+ portDeviceMap.put(portToNeighbor, neighborId);
+ }
+ }
+
+ protected Set<Set<DeviceId>> getPowerSetOfNeighbors(Set<DeviceId> neighbors) {
+ List<DeviceId> list = new ArrayList<DeviceId>(neighbors);
+ Set<Set<DeviceId>> sets = new HashSet<Set<DeviceId>>();
+ // get the number of elements in the neighbors
+ int elements = list.size();
+ // the number of members of a power set is 2^n
+ // including the empty set
+ int powerElements = (1 << elements);
+
+ // run a binary counter for the number of power elements
+ // NOTE: Exclude empty set
+ for (long i = 1; i < powerElements; i++) {
+ Set<DeviceId> neighborSubSet = new HashSet<DeviceId>();
+ for (int j = 0; j < elements; j++) {
+ if ((i >> j) % 2 == 1) {
+ neighborSubSet.add(list.get(j));
+ }
+ }
+ sets.add(neighborSubSet);
+ }
+ return sets;
+ }
+
+ private boolean isSegmentIdSameAsNodeSegmentId(DeviceId deviceId, int sId) {
+ return (deviceConfig.getSegmentId(deviceId) == sId);
+ }
+
+ protected List<Integer> getSegmentIdsTobePairedWithNeighborSet(Set<DeviceId> neighbors) {
+
+ List<Integer> nsSegmentIds = new ArrayList<Integer>();
+
+ // 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) {
+ if (sId.equals(nodeSegmentId)) {
+ continue;
+ }
+ boolean filterOut = false;
+ // Check if the edge label being set is of
+ // any node in the Neighbor set
+ for (DeviceId deviceId : neighbors) {
+ if (isSegmentIdSameAsNodeSegmentId(deviceId, sId)) {
+ filterOut = true;
+ break;
+ }
+ }
+ if (!filterOut) {
+ nsSegmentIds.add(sId);
+ }
+ }
+ return nsSegmentIds;
+ }
+
+ /**
+ * Creates Groups from a set of NeighborSet given.
+ *
+ * @param nsSet a set of NeighborSet
+ */
+ public void createGroupsFromNeighborsets(Set<NeighborSet> nsSet) {
+ for (NeighborSet ns : nsSet) {
+ int nextId = flowObjectiveService.allocateNextId();
+ NextObjective.Builder nextObjBuilder = DefaultNextObjective
+ .builder().withId(nextId)
+ .withType(NextObjective.Type.HASHED).fromApp(appId);
+ for (DeviceId d : ns.getDeviceIds()) {
+ if (devicePortMap.get(d) == null) {
+ log.warn("Device {} is not in the port map yet", d);
+ return;
+ } else if (devicePortMap.get(d).size() == 0) {
+ log.warn("There are no ports for "
+ + "the Device {} in the port map yet", d);
+ return;
+ }
+
+ for (PortNumber sp : devicePortMap.get(d)) {
+ TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment
+ .builder();
+ tBuilder.setOutput(sp)
+ .setEthDst(deviceConfig.getDeviceMac(d))
+ .setEthSrc(nodeMacAddr);
+ if (ns.getEdgeLabel() != NeighborSet.NO_EDGE_LABEL) {
+ tBuilder.pushMpls().setMpls(MplsLabel.mplsLabel(ns
+ .getEdgeLabel()));
+ }
+ nextObjBuilder.addTreatment(tBuilder.build());
+ }
+ }
+
+ NextObjective nextObj = nextObjBuilder.
+ add(new SRNextObjectiveContext(deviceId));
+ flowObjectiveService.next(deviceId, nextObj);
+ log.debug("createGroupsFromNeighborsets: Submited "
+ + "next objective {} in device {}",
+ nextId, deviceId);
+ nsNextObjStore.put(new NeighborSetNextObjectiveStoreKey(deviceId, ns),
+ nextId);
+ }
+ }
+
+ public GroupKey getGroupKey(Object obj) {
+ return new DefaultGroupKey(kryo.build().serialize(obj));
+ }
+
+ /**
+ * Removes groups for the next objective ID given.
+ *
+ * @param objectiveId next objective ID to remove
+ * @return true if succeeds, false otherwise
+ */
+ public boolean removeGroup(int objectiveId) {
+
+ if (nsNextObjStore.containsValue(objectiveId)) {
+ NextObjective.Builder nextObjBuilder = DefaultNextObjective
+ .builder().withId(objectiveId)
+ .withType(NextObjective.Type.HASHED).fromApp(appId);
+ NextObjective nextObjective = nextObjBuilder.
+ remove(new SRNextObjectiveContext(deviceId));
+ flowObjectiveService.next(deviceId, nextObjective);
+
+ for (Map.Entry<NeighborSetNextObjectiveStoreKey, Integer> entry: nsNextObjStore.entrySet()) {
+ if (entry.getValue().equals(objectiveId)) {
+ nsNextObjStore.remove(entry.getKey());
+ break;
+ }
+ }
+ return true;
+ }
+
+ return false;
+ }
+
+ protected static class SRNextObjectiveContext implements ObjectiveContext {
+ final DeviceId deviceId;
+
+ SRNextObjectiveContext(DeviceId deviceId) {
+ this.deviceId = deviceId;
+ }
+ @Override
+ public void onSuccess(Objective objective) {
+ log.debug("Next objective operation successful in device {}",
+ deviceId);
+ }
+
+ @Override
+ public void onError(Objective objective, ObjectiveError error) {
+ log.warn("Next objective {} operation failed with error: {} in device {}",
+ objective, error, deviceId);
+ }
+ }
+}
diff --git a/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/grouphandler/DefaultTransitGroupHandler.java b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/grouphandler/DefaultTransitGroupHandler.java
new file mode 100644
index 00000000..e12426c2
--- /dev/null
+++ b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/grouphandler/DefaultTransitGroupHandler.java
@@ -0,0 +1,185 @@
+/*
+ * 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.grouphandler;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import org.onosproject.core.ApplicationId;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.Link;
+import org.onosproject.net.flowobjective.FlowObjectiveService;
+import org.onosproject.net.link.LinkService;
+import org.onosproject.store.service.EventuallyConsistentMap;
+
+/**
+ * Default ECMP group handler creation module for a transit device.
+ * This component creates a set of ECMP groups for every neighbor
+ * that this device is connected to.
+ * For example, consider a network of 4 devices: D0 (Segment ID: 100),
+ * D1 (Segment ID: 101), D2 (Segment ID: 102) and D3 (Segment ID: 103),
+ * where D0 and D3 are edge devices and D1 and D2 are transit devices.
+ * Assume transit device D1 is connected to 2 neighbors (D0 and D3 ).
+ * The following groups will be created in D1:
+ * 1) all ports to D0 + with no label push,
+ * 2) all ports to D3 + with no label push,
+ */
+public class DefaultTransitGroupHandler extends DefaultGroupHandler {
+
+ protected DefaultTransitGroupHandler(DeviceId deviceId,
+ ApplicationId appId,
+ DeviceProperties config,
+ LinkService linkService,
+ FlowObjectiveService flowObjService,
+ EventuallyConsistentMap<
+ NeighborSetNextObjectiveStoreKey,
+ Integer> nsNextObjStore) {
+ super(deviceId, appId, config, linkService, flowObjService, nsNextObjStore);
+ }
+
+ @Override
+ public void createGroups() {
+ Set<DeviceId> neighbors = devicePortMap.keySet();
+ if (neighbors == null || neighbors.isEmpty()) {
+ return;
+ }
+
+ // Create all possible Neighbor sets from this router
+ // NOTE: Avoid any pairings of edge routers only
+ Set<Set<DeviceId>> sets = getPowerSetOfNeighbors(neighbors);
+ sets = filterEdgeRouterOnlyPairings(sets);
+ log.debug("createGroupsAtTransitRouter: The size of neighbor powerset "
+ + "for sw {} is {}", deviceId, sets.size());
+ Set<NeighborSet> nsSet = new HashSet<NeighborSet>();
+ for (Set<DeviceId> combo : sets) {
+ if (combo.isEmpty()) {
+ continue;
+ }
+ NeighborSet ns = new NeighborSet(combo);
+ log.debug("createGroupsAtTransitRouter: sw {} combo {} ns {}",
+ deviceId, combo, ns);
+ nsSet.add(ns);
+ }
+ log.debug("createGroupsAtTransitRouter: The neighborset with label "
+ + "for sw {} is {}", deviceId, nsSet);
+
+ createGroupsFromNeighborsets(nsSet);
+ }
+
+ @Override
+ protected void newNeighbor(Link newNeighborLink) {
+ log.debug("New Neighbor: Updating groups for "
+ + "transit device {}", deviceId);
+ // Recompute neighbor power set
+ addNeighborAtPort(newNeighborLink.dst().deviceId(),
+ newNeighborLink.src().port());
+ // Compute new neighbor sets due to the addition of new neighbor
+ Set<NeighborSet> nsSet = computeImpactedNeighborsetForPortEvent(
+ newNeighborLink.dst().deviceId(),
+ devicePortMap.keySet());
+ createGroupsFromNeighborsets(nsSet);
+ }
+
+ @Override
+ protected void newPortToExistingNeighbor(Link newNeighborLink) {
+ /*log.debug("New port to existing neighbor: Updating "
+ + "groups for transit device {}", deviceId);
+ addNeighborAtPort(newNeighborLink.dst().deviceId(),
+ newNeighborLink.src().port());
+ Set<NeighborSet> nsSet = computeImpactedNeighborsetForPortEvent(
+ newNeighborLink.dst().deviceId(),
+ devicePortMap.keySet());
+ for (NeighborSet ns : nsSet) {
+ // Create the new bucket to be updated
+ TrafficTreatment.Builder tBuilder =
+ DefaultTrafficTreatment.builder();
+ tBuilder.setOutput(newNeighborLink.src().port())
+ .setEthDst(deviceConfig.getDeviceMac(
+ newNeighborLink.dst().deviceId()))
+ .setEthSrc(nodeMacAddr);
+ if (ns.getEdgeLabel() != NeighborSet.NO_EDGE_LABEL) {
+ tBuilder.pushMpls()
+ .setMpls(MplsLabel.
+ mplsLabel(ns.getEdgeLabel()));
+ }
+
+
+ Integer nextId = deviceNextObjectiveIds.get(ns);
+ if (nextId != null) {
+ NextObjective.Builder nextObjBuilder = DefaultNextObjective
+ .builder().withId(nextId)
+ .withType(NextObjective.Type.HASHED).fromApp(appId);
+
+ nextObjBuilder.addTreatment(tBuilder.build());
+
+ NextObjective nextObjective = nextObjBuilder.add();
+ flowObjectiveService.next(deviceId, nextObjective);
+ }
+ }*/
+ }
+
+ @Override
+ protected Set<NeighborSet> computeImpactedNeighborsetForPortEvent(
+ DeviceId impactedNeighbor,
+ Set<DeviceId> updatedNeighbors) {
+ Set<Set<DeviceId>> powerSet = getPowerSetOfNeighbors(updatedNeighbors);
+
+ Set<DeviceId> tmp = new HashSet<DeviceId>();
+ tmp.addAll(updatedNeighbors);
+ tmp.remove(impactedNeighbor);
+ Set<Set<DeviceId>> tmpPowerSet = getPowerSetOfNeighbors(tmp);
+
+ // Compute the impacted neighbor sets
+ powerSet.removeAll(tmpPowerSet);
+
+ powerSet = filterEdgeRouterOnlyPairings(powerSet);
+ Set<NeighborSet> nsSet = new HashSet<NeighborSet>();
+ for (Set<DeviceId> combo : powerSet) {
+ if (combo.isEmpty()) {
+ continue;
+ }
+ NeighborSet ns = new NeighborSet(combo);
+ log.debug("createGroupsAtTransitRouter: sw {} combo {} ns {}",
+ deviceId, combo, ns);
+ nsSet.add(ns);
+ }
+ log.debug("computeImpactedNeighborsetForPortEvent: The neighborset with label "
+ + "for sw {} is {}", deviceId, nsSet);
+
+ return nsSet;
+ }
+
+ private Set<Set<DeviceId>> filterEdgeRouterOnlyPairings(Set<Set<DeviceId>> sets) {
+ Set<Set<DeviceId>> fiteredSets = new HashSet<Set<DeviceId>>();
+ for (Set<DeviceId> deviceSubSet : sets) {
+ if (deviceSubSet.size() > 1) {
+ boolean avoidEdgeRouterPairing = true;
+ for (DeviceId device : deviceSubSet) {
+ if (!deviceConfig.isEdgeDevice(device)) {
+ avoidEdgeRouterPairing = false;
+ break;
+ }
+ }
+ if (!avoidEdgeRouterPairing) {
+ fiteredSets.add(deviceSubSet);
+ }
+ } else {
+ fiteredSets.add(deviceSubSet);
+ }
+ }
+ return fiteredSets;
+ }
+}
diff --git a/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/grouphandler/DeviceProperties.java b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/grouphandler/DeviceProperties.java
new file mode 100644
index 00000000..816ca7bc
--- /dev/null
+++ b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/grouphandler/DeviceProperties.java
@@ -0,0 +1,57 @@
+/*
+ * 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.grouphandler;
+
+import java.util.List;
+
+import org.onlab.packet.MacAddress;
+import org.onosproject.net.DeviceId;
+
+/**
+ * Mechanism through which group handler module retrieves
+ * the device specific attributes such as segment ID,
+ * Mac address...etc from group handler applications.
+ */
+public interface DeviceProperties {
+ /**
+ * Returns the segment id of a device to be used in group creation.
+ *
+ * @param deviceId device identifier
+ * @return segment id of a device
+ */
+ int getSegmentId(DeviceId deviceId);
+ /**
+ * Returns the Mac address of a device to be used in group creation.
+ *
+ * @param deviceId device identifier
+ * @return mac address of a device
+ */
+ MacAddress getDeviceMac(DeviceId deviceId);
+ /**
+ * Indicates whether a device is edge device or transit/core device.
+ *
+ * @param deviceId device identifier
+ * @return boolean
+ */
+ boolean isEdgeDevice(DeviceId deviceId);
+ /**
+ * Returns all segment IDs to be considered in building auto
+ *
+ * created groups.
+ * @return list of segment IDs
+ */
+ List<Integer> getAllDeviceSegmentIds();
+}
diff --git a/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/grouphandler/GroupBucketIdentifier.java b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/grouphandler/GroupBucketIdentifier.java
new file mode 100644
index 00000000..2ecb4493
--- /dev/null
+++ b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/grouphandler/GroupBucketIdentifier.java
@@ -0,0 +1,69 @@
+/*
+ * 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.grouphandler;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import org.onosproject.net.PortNumber;
+
+/**
+ * Representation of policy group bucket identifier. Not exposed to
+ * the application and only to be used internally.
+ */
+public class GroupBucketIdentifier {
+ private int label;
+ private BucketOutputType type;
+ private PortNumber outPort;
+ private PolicyGroupIdentifier outGroup;
+
+ protected enum BucketOutputType {
+ PORT,
+ GROUP
+ }
+
+ protected GroupBucketIdentifier(int label,
+ PortNumber outPort) {
+ this.label = label;
+ this.type = BucketOutputType.PORT;
+ this.outPort = checkNotNull(outPort);
+ this.outGroup = null;
+ }
+
+ protected GroupBucketIdentifier(int label,
+ PolicyGroupIdentifier outGroup) {
+ this.label = label;
+ this.type = BucketOutputType.GROUP;
+ this.outPort = null;
+ this.outGroup = checkNotNull(outGroup);
+ }
+
+ protected int label() {
+ return this.label;
+ }
+
+ protected BucketOutputType type() {
+ return this.type;
+ }
+
+ protected PortNumber outPort() {
+ return this.outPort;
+ }
+
+ protected PolicyGroupIdentifier outGroup() {
+ return this.outGroup;
+ }
+}
+
diff --git a/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/grouphandler/NeighborSet.java b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/grouphandler/NeighborSet.java
new file mode 100644
index 00000000..7ada3224
--- /dev/null
+++ b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/grouphandler/NeighborSet.java
@@ -0,0 +1,123 @@
+/*
+ * 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.grouphandler;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.util.HashSet;
+import java.util.Objects;
+import java.util.Set;
+
+import org.onosproject.net.DeviceId;
+
+/**
+ * Representation of a set of neighbor switch dpids along with edge node
+ * label. Meant to be used as a lookup-key in a hash-map to retrieve an
+ * ECMP-group that hashes packets to a set of ports connecting to the
+ * neighbors in this set.
+ */
+public class NeighborSet {
+ private final Set<DeviceId> neighbors;
+ private final int edgeLabel;
+ public static final int NO_EDGE_LABEL = -1;
+
+ /**
+ * Constructor with set of neighbors. Edge label is
+ * default to -1.
+ *
+ * @param neighbors set of neighbors to be part of neighbor set
+ */
+ public NeighborSet(Set<DeviceId> neighbors) {
+ checkNotNull(neighbors);
+ this.edgeLabel = NO_EDGE_LABEL;
+ this.neighbors = new HashSet<DeviceId>();
+ this.neighbors.addAll(neighbors);
+ }
+
+ /**
+ * Constructor with set of neighbors and edge label.
+ *
+ * @param neighbors set of neighbors to be part of neighbor set
+ * @param edgeLabel label to be pushed as part of group operation
+ */
+ public NeighborSet(Set<DeviceId> neighbors, int edgeLabel) {
+ checkNotNull(neighbors);
+ this.edgeLabel = edgeLabel;
+ this.neighbors = new HashSet<DeviceId>();
+ this.neighbors.addAll(neighbors);
+ }
+
+ /**
+ * Default constructor for kryo serialization.
+ */
+ public NeighborSet() {
+ this.edgeLabel = NO_EDGE_LABEL;
+ this.neighbors = new HashSet<DeviceId>();
+ }
+
+ /**
+ * Gets the neighbors part of neighbor set.
+ *
+ * @return set of neighbor identifiers
+ */
+ public Set<DeviceId> getDeviceIds() {
+ return neighbors;
+ }
+
+ /**
+ * Gets the label associated with neighbor set.
+ *
+ * @return integer
+ */
+ public int getEdgeLabel() {
+ return edgeLabel;
+ }
+
+ // The list of neighbor ids and label are used for comparison.
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (!(o instanceof NeighborSet)) {
+ return false;
+ }
+ NeighborSet that = (NeighborSet) o;
+ return (this.neighbors.containsAll(that.neighbors) &&
+ that.neighbors.containsAll(this.neighbors) &&
+ (this.edgeLabel == that.edgeLabel));
+ }
+
+ // The list of neighbor ids and label are used for comparison.
+ @Override
+ public int hashCode() {
+ int result = 17;
+ int combinedHash = 0;
+ for (DeviceId d : neighbors) {
+ combinedHash = combinedHash + Objects.hash(d);
+ }
+ result = 31 * result + combinedHash + Objects.hash(edgeLabel);
+
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return " Neighborset Sw: " + neighbors
+ + " and Label: " + edgeLabel;
+ }
+}
diff --git a/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/grouphandler/NeighborSetNextObjectiveStoreKey.java b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/grouphandler/NeighborSetNextObjectiveStoreKey.java
new file mode 100644
index 00000000..9ace5313
--- /dev/null
+++ b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/grouphandler/NeighborSetNextObjectiveStoreKey.java
@@ -0,0 +1,72 @@
+/*
+ * 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.grouphandler;
+
+import java.util.Objects;
+
+import org.onosproject.net.DeviceId;
+
+/**
+ * Class definition of Key for Neighborset to NextObjective store.
+ */
+public class NeighborSetNextObjectiveStoreKey {
+ private final DeviceId deviceId;
+ private final NeighborSet ns;
+
+ public NeighborSetNextObjectiveStoreKey(DeviceId deviceId,
+ NeighborSet ns) {
+ this.deviceId = deviceId;
+ this.ns = ns;
+ }
+
+ public DeviceId deviceId() {
+ return this.deviceId;
+ }
+
+ public NeighborSet neighborSet() {
+ return this.ns;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (!(o instanceof NeighborSetNextObjectiveStoreKey)) {
+ return false;
+ }
+ NeighborSetNextObjectiveStoreKey that =
+ (NeighborSetNextObjectiveStoreKey) o;
+ return (Objects.equals(this.deviceId, that.deviceId) &&
+ Objects.equals(this.ns, that.ns));
+ }
+
+ // The list of neighbor ids and label are used for comparison.
+ @Override
+ public int hashCode() {
+ int result = 17;
+ result = 31 * result + Objects.hashCode(this.deviceId)
+ + Objects.hashCode(this.ns);
+
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return "Device: " + deviceId + " Neighborset: " + ns;
+ }
+}
diff --git a/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/grouphandler/PolicyGroupHandler.java b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/grouphandler/PolicyGroupHandler.java
new file mode 100644
index 00000000..e7e87839
--- /dev/null
+++ b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/grouphandler/PolicyGroupHandler.java
@@ -0,0 +1,352 @@
+/*
+ * 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.grouphandler;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static org.slf4j.LoggerFactory.getLogger;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+
+import org.onlab.packet.MplsLabel;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.segmentrouting.grouphandler.GroupBucketIdentifier.BucketOutputType;
+import org.onosproject.store.service.EventuallyConsistentMap;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.flow.DefaultTrafficTreatment;
+import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.flowobjective.FlowObjectiveService;
+import org.onosproject.net.group.GroupBucket;
+import org.onosproject.net.link.LinkService;
+import org.slf4j.Logger;
+
+/**
+ * A module to create group chains based on the specified device
+ * ports and label stack to be applied on each port.
+ */
+public class PolicyGroupHandler extends DefaultGroupHandler {
+
+ private final Logger log = getLogger(getClass());
+ private HashMap<PolicyGroupIdentifier, PolicyGroupIdentifier> dependentGroups = new HashMap<>();
+
+ /**
+ * Policy group handler constructor.
+ *
+ * @param deviceId device identifier
+ * @param appId application identifier
+ * @param config interface to retrieve the device properties
+ * @param linkService link service object
+ * @param flowObjService flow objective service object
+ * @param nsNextObjStore next objective store map
+ */
+ public PolicyGroupHandler(DeviceId deviceId,
+ ApplicationId appId,
+ DeviceProperties config,
+ LinkService linkService,
+ FlowObjectiveService flowObjService,
+ EventuallyConsistentMap<NeighborSetNextObjectiveStoreKey,
+ Integer> nsNextObjStore) {
+ super(deviceId, appId, config, linkService, flowObjService, nsNextObjStore);
+ }
+
+ public PolicyGroupIdentifier createPolicyGroupChain(String id,
+ List<PolicyGroupParams> params) {
+ List<GroupBucketIdentifier> bucketIds = new ArrayList<>();
+ for (PolicyGroupParams param: params) {
+ List<PortNumber> ports = param.getPorts();
+ if (ports == null) {
+ log.warn("createPolicyGroupChain in sw {} with wrong "
+ + "input parameters", deviceId);
+ return null;
+ }
+
+ int labelStackSize = (param.getLabelStack() != null) ?
+ param.getLabelStack().size() : 0;
+
+ if (labelStackSize > 1) {
+ for (PortNumber sp : ports) {
+ PolicyGroupIdentifier previousGroupkey = null;
+ DeviceId neighbor = portDeviceMap.get(sp);
+ for (int idx = 0; idx < param.getLabelStack().size(); idx++) {
+ int label = param.getLabelStack().get(idx);
+ if (idx == (labelStackSize - 1)) {
+ // Innermost Group
+ GroupBucketIdentifier bucketId =
+ new GroupBucketIdentifier(label,
+ previousGroupkey);
+ bucketIds.add(bucketId);
+ } else if (idx == 0) {
+ // Outermost Group
+ List<GroupBucket> outBuckets = new ArrayList<>();
+ GroupBucketIdentifier bucketId =
+ new GroupBucketIdentifier(label, sp);
+ PolicyGroupIdentifier key = new
+ PolicyGroupIdentifier(id,
+ Collections.singletonList(param),
+ Collections.singletonList(bucketId));
+ TrafficTreatment.Builder tBuilder =
+ DefaultTrafficTreatment.builder();
+ tBuilder.setOutput(sp)
+ .setEthDst(deviceConfig.
+ getDeviceMac(neighbor))
+ .setEthSrc(nodeMacAddr)
+ .pushMpls()
+ .setMpls(MplsLabel.mplsLabel(label));
+ /*outBuckets.add(DefaultGroupBucket.
+ createSelectGroupBucket(tBuilder.build()));
+ GroupDescription desc = new
+ DefaultGroupDescription(deviceId,
+ GroupDescription.Type.INDIRECT,
+ new GroupBuckets(outBuckets));
+ //TODO: BoS*/
+ previousGroupkey = key;
+ //groupService.addGroup(desc);
+ //TODO: Use nextObjective APIs here
+ } else {
+ // Intermediate Groups
+ GroupBucketIdentifier bucketId =
+ new GroupBucketIdentifier(label,
+ previousGroupkey);
+ PolicyGroupIdentifier key = new
+ PolicyGroupIdentifier(id,
+ Collections.singletonList(param),
+ Collections.singletonList(bucketId));
+ // Add to group dependency list
+ dependentGroups.put(previousGroupkey, key);
+ previousGroupkey = key;
+ }
+ }
+ }
+ } else {
+ int label = -1;
+ if (labelStackSize == 1) {
+ label = param.getLabelStack().get(0);
+ }
+ for (PortNumber sp : ports) {
+ GroupBucketIdentifier bucketId =
+ new GroupBucketIdentifier(label, sp);
+ bucketIds.add(bucketId);
+ }
+ }
+ }
+ PolicyGroupIdentifier innermostGroupkey = null;
+ if (!bucketIds.isEmpty()) {
+ innermostGroupkey = new
+ PolicyGroupIdentifier(id,
+ params,
+ bucketIds);
+ // Add to group dependency list
+ boolean fullyResolved = true;
+ for (GroupBucketIdentifier bucketId:bucketIds) {
+ if (bucketId.type() == BucketOutputType.GROUP) {
+ dependentGroups.put(bucketId.outGroup(),
+ innermostGroupkey);
+ fullyResolved = false;
+ }
+ }
+
+ if (fullyResolved) {
+ List<GroupBucket> outBuckets = new ArrayList<>();
+ for (GroupBucketIdentifier bucketId:bucketIds) {
+ DeviceId neighbor = portDeviceMap.
+ get(bucketId.outPort());
+ TrafficTreatment.Builder tBuilder =
+ DefaultTrafficTreatment.builder();
+ tBuilder.setOutput(bucketId.outPort())
+ .setEthDst(deviceConfig.
+ getDeviceMac(neighbor))
+ .setEthSrc(nodeMacAddr);
+ if (bucketId.label() != NeighborSet.NO_EDGE_LABEL) {
+ tBuilder.pushMpls()
+ .setMpls(MplsLabel.mplsLabel(bucketId.label()));
+ }
+ //TODO: BoS
+ /*outBuckets.add(DefaultGroupBucket.
+ createSelectGroupBucket(tBuilder.build()));*/
+ }
+ /*GroupDescription desc = new
+ DefaultGroupDescription(deviceId,
+ GroupDescription.Type.SELECT,
+ new GroupBuckets(outBuckets));
+ groupService.addGroup(desc);*/
+ //TODO: Use nextObjective APIs here
+ }
+ }
+ return innermostGroupkey;
+ }
+
+ //TODO: Use nextObjective APIs to handle the group chains
+ /*@Override
+ protected void handleGroupEvent(GroupEvent event) {
+ if (event.type() == GroupEvent.Type.GROUP_ADDED) {
+ if (dependentGroups.get(event.subject().appCookie()) != null) {
+ PolicyGroupIdentifier dependentGroupKey = dependentGroups.get(event.subject().appCookie());
+ dependentGroups.remove(event.subject().appCookie());
+ boolean fullyResolved = true;
+ for (GroupBucketIdentifier bucketId:
+ dependentGroupKey.bucketIds()) {
+ if (bucketId.type() != BucketOutputType.GROUP) {
+ continue;
+ }
+ if (dependentGroups.containsKey(bucketId.outGroup())) {
+ fullyResolved = false;
+ break;
+ }
+ }
+
+ if (fullyResolved) {
+ List<GroupBucket> outBuckets = new ArrayList<GroupBucket>();
+ for (GroupBucketIdentifier bucketId:
+ dependentGroupKey.bucketIds()) {
+ TrafficTreatment.Builder tBuilder =
+ DefaultTrafficTreatment.builder();
+ if (bucketId.label() != NeighborSet.NO_EDGE_LABEL) {
+ tBuilder.pushMpls()
+ .setMpls(MplsLabel.
+ mplsLabel(bucketId.label()));
+ }
+ //TODO: BoS
+ if (bucketId.type() == BucketOutputType.PORT) {
+ DeviceId neighbor = portDeviceMap.
+ get(bucketId.outPort());
+ tBuilder.setOutput(bucketId.outPort())
+ .setEthDst(deviceConfig.
+ getDeviceMac(neighbor))
+ .setEthSrc(nodeMacAddr);
+ } else {
+ if (groupService.
+ getGroup(deviceId,
+ getGroupKey(bucketId.
+ outGroup())) == null) {
+ throw new IllegalStateException();
+ }
+ GroupId indirectGroupId = groupService.
+ getGroup(deviceId,
+ getGroupKey(bucketId.
+ outGroup())).id();
+ tBuilder.group(indirectGroupId);
+ }
+ outBuckets.add(DefaultGroupBucket.
+ createSelectGroupBucket(tBuilder.build()));
+ }
+ GroupDescription desc = new
+ DefaultGroupDescription(deviceId,
+ GroupDescription.Type.SELECT,
+ new GroupBuckets(outBuckets));
+ groupService.addGroup(desc);
+ }
+ }
+ }
+ }*/
+
+ public PolicyGroupIdentifier generatePolicyGroupKey(String id,
+ List<PolicyGroupParams> params) {
+ List<GroupBucketIdentifier> bucketIds = new ArrayList<>();
+ for (PolicyGroupParams param: params) {
+ List<PortNumber> ports = param.getPorts();
+ if (ports == null) {
+ log.warn("generateGroupKey in sw {} with wrong "
+ + "input parameters", deviceId);
+ return null;
+ }
+
+ int labelStackSize = (param.getLabelStack() != null)
+ ? param.getLabelStack().size() : 0;
+
+ if (labelStackSize > 1) {
+ for (PortNumber sp : ports) {
+ PolicyGroupIdentifier previousGroupkey = null;
+ for (int idx = 0; idx < param.getLabelStack().size(); idx++) {
+ int label = param.getLabelStack().get(idx);
+ if (idx == (labelStackSize - 1)) {
+ // Innermost Group
+ GroupBucketIdentifier bucketId =
+ new GroupBucketIdentifier(label,
+ previousGroupkey);
+ bucketIds.add(bucketId);
+ } else if (idx == 0) {
+ // Outermost Group
+ GroupBucketIdentifier bucketId =
+ new GroupBucketIdentifier(label, sp);
+ PolicyGroupIdentifier key = new
+ PolicyGroupIdentifier(id,
+ Collections.singletonList(param),
+ Collections.singletonList(bucketId));
+ previousGroupkey = key;
+ } else {
+ // Intermediate Groups
+ GroupBucketIdentifier bucketId =
+ new GroupBucketIdentifier(label,
+ previousGroupkey);
+ PolicyGroupIdentifier key = new
+ PolicyGroupIdentifier(id,
+ Collections.singletonList(param),
+ Collections.singletonList(bucketId));
+ previousGroupkey = key;
+ }
+ }
+ }
+ } else {
+ int label = -1;
+ if (labelStackSize == 1) {
+ label = param.getLabelStack().get(0);
+ }
+ for (PortNumber sp : ports) {
+ GroupBucketIdentifier bucketId =
+ new GroupBucketIdentifier(label, sp);
+ bucketIds.add(bucketId);
+ }
+ }
+ }
+ PolicyGroupIdentifier innermostGroupkey = null;
+ if (!bucketIds.isEmpty()) {
+ innermostGroupkey = new
+ PolicyGroupIdentifier(id,
+ params,
+ bucketIds);
+ }
+ return innermostGroupkey;
+ }
+
+ public void removeGroupChain(PolicyGroupIdentifier key) {
+ checkArgument(key != null);
+ List<PolicyGroupIdentifier> groupsToBeDeleted = new ArrayList<>();
+ groupsToBeDeleted.add(key);
+
+ Iterator<PolicyGroupIdentifier> it =
+ groupsToBeDeleted.iterator();
+
+ while (it.hasNext()) {
+ PolicyGroupIdentifier innerMostGroupKey = it.next();
+ for (GroupBucketIdentifier bucketId:
+ innerMostGroupKey.bucketIds()) {
+ if (bucketId.type() != BucketOutputType.GROUP) {
+ groupsToBeDeleted.add(bucketId.outGroup());
+ }
+ }
+ /*groupService.removeGroup(deviceId,
+ getGroupKey(innerMostGroupKey),
+ appId);*/
+ //TODO: Use nextObjective APIs here
+ it.remove();
+ }
+ }
+
+}
diff --git a/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/grouphandler/PolicyGroupIdentifier.java b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/grouphandler/PolicyGroupIdentifier.java
new file mode 100644
index 00000000..44a0a2ce
--- /dev/null
+++ b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/grouphandler/PolicyGroupIdentifier.java
@@ -0,0 +1,90 @@
+/*
+ * 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.grouphandler;
+
+import java.util.List;
+
+/**
+ * Representation of policy based group identifiers.
+ * Opaque to group handler applications and only the outermost
+ * policy group identifier in a chain is visible to the applications.
+ */
+public class PolicyGroupIdentifier {
+ private String id;
+ private List<PolicyGroupParams> inputParams;
+ private List<GroupBucketIdentifier> bucketIds;
+
+ /**
+ * Constructor.
+ *
+ * @param id unique identifier associated with the policy group
+ * @param input policy group params associated with this group
+ * @param bucketIds buckets associated with this group
+ */
+ protected PolicyGroupIdentifier(String id,
+ List<PolicyGroupParams> input,
+ List<GroupBucketIdentifier> bucketIds) {
+ this.id = id;
+ this.inputParams = input;
+ this.bucketIds = bucketIds;
+ }
+
+ /**
+ * Returns the bucket identifier list associated with the policy
+ * group identifier.
+ *
+ * @return list of bucket identifier
+ */
+ protected List<GroupBucketIdentifier> bucketIds() {
+ return this.bucketIds;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = 17;
+ int combinedHash = 0;
+ for (PolicyGroupParams input:inputParams) {
+ combinedHash = combinedHash + input.hashCode();
+ }
+ for (GroupBucketIdentifier bucketId:bucketIds) {
+ combinedHash = combinedHash + bucketId.hashCode();
+ }
+ result = 31 * result + combinedHash;
+
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+
+ if (obj instanceof PolicyGroupIdentifier) {
+ PolicyGroupIdentifier that = (PolicyGroupIdentifier) obj;
+ boolean result = this.id.equals(that.id);
+ result = result &&
+ this.inputParams.containsAll(that.inputParams) &&
+ that.inputParams.containsAll(this.inputParams);
+ result = result &&
+ this.bucketIds.containsAll(that.bucketIds) &&
+ that.bucketIds.containsAll(this.bucketIds);
+ return result;
+ }
+
+ return false;
+ }
+}
diff --git a/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/grouphandler/PolicyGroupParams.java b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/grouphandler/PolicyGroupParams.java
new file mode 100644
index 00000000..5a3f26b5
--- /dev/null
+++ b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/grouphandler/PolicyGroupParams.java
@@ -0,0 +1,92 @@
+/*
+ * 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.grouphandler;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.util.List;
+import java.util.Objects;
+
+import org.onosproject.net.PortNumber;
+
+/**
+ * Representation of parameters used to create policy based groups.
+ */
+public class PolicyGroupParams {
+ private final List<PortNumber> ports;
+ private final List<Integer> labelStack;
+
+ /**
+ * Constructor.
+ *
+ * @param labelStack mpls label stack to be applied on the ports
+ * @param ports ports to be part of the policy group
+ */
+ public PolicyGroupParams(List<Integer> labelStack,
+ List<PortNumber> ports) {
+ this.ports = checkNotNull(ports);
+ this.labelStack = checkNotNull(labelStack);
+ }
+
+ /**
+ * Returns the ports associated with the policy group params.
+ *
+ * @return list of port numbers
+ */
+ public List<PortNumber> getPorts() {
+ return ports;
+ }
+
+ /**
+ * Returns the label stack associated with the policy group params.
+ *
+ * @return list of integers
+ */
+ public List<Integer> getLabelStack() {
+ return labelStack;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = 17;
+ int combinedHash = 0;
+ for (PortNumber port:ports) {
+ combinedHash = combinedHash + port.hashCode();
+ }
+ combinedHash = combinedHash + Objects.hash(labelStack);
+ result = 31 * result + combinedHash;
+
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+
+ if (obj instanceof PolicyGroupParams) {
+ PolicyGroupParams that = (PolicyGroupParams) obj;
+ boolean result = this.labelStack.equals(that.labelStack);
+ result = result &&
+ this.ports.containsAll(that.ports) &&
+ that.ports.containsAll(this.ports);
+ return result;
+ }
+
+ return false;
+ }
+}
diff --git a/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/grouphandler/package-info.java b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/grouphandler/package-info.java
new file mode 100644
index 00000000..1a8d595e
--- /dev/null
+++ b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/grouphandler/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * 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.
+ */
+
+/**
+ * Segment routing group handling.
+ */
+package org.onosproject.segmentrouting.grouphandler; \ No newline at end of file
diff --git a/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/package-info.java b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/package-info.java
new file mode 100644
index 00000000..7b81db8a
--- /dev/null
+++ b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * 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.
+ */
+
+/**
+ * Segment routing application components.
+ */
+package org.onosproject.segmentrouting; \ No newline at end of file
diff --git a/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/web/PolicyCodec.java b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/web/PolicyCodec.java
new file mode 100644
index 00000000..8e508872
--- /dev/null
+++ b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/web/PolicyCodec.java
@@ -0,0 +1,122 @@
+/*
+ * 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.web;
+
+import org.onosproject.codec.CodecContext;
+import org.onosproject.codec.JsonCodec;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import org.onosproject.segmentrouting.Policy;
+import org.onosproject.segmentrouting.TunnelPolicy;
+
+public final class PolicyCodec extends JsonCodec<Policy> {
+
+ // JSON field names
+ private static final String POLICY_ID = "policy_id";
+ private static final String PRIORITY = "priority";
+ private static final String TYPE = "policy_type";
+ private static final String TUNNEL_ID = "tunnel_id";
+ private static final String DST_IP = "dst_ip";
+ private static final String SRC_IP = "src_ip";
+ private static final String PROTO_TYPE = "proto_type";
+ private static final String SRC_PORT = "src_tp_port";
+ private static final String DST_PORT = "dst_tp_port";
+
+ @Override
+ public ObjectNode encode(Policy policy, CodecContext context) {
+ final ObjectNode result = context.mapper().createObjectNode()
+ .put(POLICY_ID, policy.id());
+
+ result.put(PRIORITY, policy.priority());
+ result.put(TYPE, policy.type().toString());
+
+ if (policy.dstIp() != null) {
+ result.put(DST_IP, policy.dstIp());
+ }
+ if (policy.srcIp() != null) {
+ result.put(SRC_IP, policy.srcIp());
+ }
+ if (policy.ipProto() != null) {
+ result.put(PROTO_TYPE, policy.ipProto());
+ }
+
+ int srcPort = policy.srcPort() & 0xffff;
+ if (policy.srcPort() != 0) {
+ result.put(SRC_PORT, srcPort);
+ }
+ int dstPort = policy.dstPort() & 0xffff;
+ if (policy.dstPort() != 0) {
+ result.put(DST_PORT, dstPort);
+ }
+ if (policy.type() == Policy.Type.TUNNEL_FLOW) {
+ result.put(TUNNEL_ID, ((TunnelPolicy) policy).tunnelId());
+ }
+
+ return result;
+ }
+
+ @Override
+ public Policy decode(ObjectNode json, CodecContext context) {
+
+ String pid = json.path(POLICY_ID).asText();
+ String type = json.path(TYPE).asText();
+ int priority = json.path(PRIORITY).asInt();
+ String dstIp = json.path(DST_IP).asText();
+ String srcIp = json.path(SRC_IP).asText();
+ String tunnelId = json.path(TUNNEL_ID).asText();
+ String protoType = json.path(PROTO_TYPE).asText();
+ short srcPort = json.path(SRC_PORT).shortValue();
+ short dstPort = json.path(DST_PORT).shortValue();
+
+ if (json.path(POLICY_ID).isMissingNode() || pid == null) {
+ // TODO: handle errors
+ return null;
+ }
+
+ TunnelPolicy.Builder tpb = TunnelPolicy.builder().setPolicyId(pid);
+ if (!json.path(TYPE).isMissingNode() && type != null &&
+ Policy.Type.valueOf(type).equals(Policy.Type.TUNNEL_FLOW)) {
+
+ if (json.path(TUNNEL_ID).isMissingNode() || tunnelId == null) {
+ return null;
+ }
+
+ tpb.setTunnelId(tunnelId);
+ tpb.setType(Policy.Type.valueOf(type));
+
+ if (!json.path(PRIORITY).isMissingNode()) {
+ tpb.setPriority(priority);
+ }
+ if (dstIp != null) {
+ tpb.setDstIp(dstIp);
+ }
+ if (srcIp != null) {
+ tpb.setSrcIp(srcIp);
+ }
+ if (protoType != null) {
+ tpb.setIpProto(protoType);
+ }
+ if (dstPort != 0) {
+ tpb.setDstPort(dstPort);
+ }
+ if (srcPort != 0) {
+ tpb.setSrcPort(srcPort);
+ }
+ }
+
+ return tpb.build();
+ }
+
+}
diff --git a/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/web/PolicyWebResource.java b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/web/PolicyWebResource.java
new file mode 100644
index 00000000..cdc53a02
--- /dev/null
+++ b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/web/PolicyWebResource.java
@@ -0,0 +1,108 @@
+/*
+ * 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.web;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+import org.onosproject.rest.AbstractWebResource;
+import org.onosproject.segmentrouting.Policy;
+import org.onosproject.segmentrouting.SegmentRoutingService;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.List;
+
+/**
+ * Query, create and remove segment routing plicies.
+ */
+@Path("policy")
+public class PolicyWebResource extends AbstractWebResource {
+
+ private static final PolicyCodec POLICY_CODEC = new PolicyCodec();
+
+ /**
+ * Get all segment routing policies.
+ * Returns an array of segment routing policies.
+ *
+ * @return status of OK
+ */
+ @GET
+ @Produces(MediaType.APPLICATION_JSON)
+ public Response getPolicy() {
+ SegmentRoutingService srService = get(SegmentRoutingService.class);
+ List<Policy> policies = srService.getPolicies();
+ ObjectNode result = new ObjectMapper().createObjectNode();
+ result.set("policy", new PolicyCodec().encode(policies, this));
+
+ return ok(result.toString()).build();
+ }
+
+ /**
+ * Create a new segment routing policy.
+ *
+ * @param input JSON stream for policy to create
+ * @return status of the request - OK if the policy is created,
+ * INTERNAL_SERVER_ERROR if the JSON is invalid or the policy cannot be created
+ * @throws IOException if JSON processing fails
+ */
+ @POST
+ @Consumes(MediaType.APPLICATION_JSON)
+ public Response createPolicy(InputStream input) throws IOException {
+ ObjectMapper mapper = new ObjectMapper();
+ ObjectNode policyJson = (ObjectNode) mapper.readTree(input);
+ SegmentRoutingService srService = get(SegmentRoutingService.class);
+ Policy policyInfo = POLICY_CODEC.decode(policyJson, this);
+
+ if (policyInfo.type() == Policy.Type.TUNNEL_FLOW) {
+ srService.createPolicy(policyInfo);
+ return Response.ok().build();
+ } else {
+ return Response.serverError().build();
+ }
+ }
+
+ /**
+ * Delete a segment routing policy.
+ *
+ * @param input JSON stream for policy to delete
+ * @return status of the request - OK if the policy is removed,
+ * INTERNAL_SERVER_ERROR otherwise
+ * @throws IOException if JSON is invalid
+ */
+ @DELETE
+ @Consumes(MediaType.APPLICATION_JSON)
+ public Response removePolicy(InputStream input) throws IOException {
+ ObjectMapper mapper = new ObjectMapper();
+ ObjectNode policyJson = (ObjectNode) mapper.readTree(input);
+ SegmentRoutingService srService = get(SegmentRoutingService.class);
+ Policy policyInfo = POLICY_CODEC.decode(policyJson, this);
+ // TODO: Check the result
+ srService.removePolicy(policyInfo);
+
+ return Response.ok().build();
+
+ }
+
+}
diff --git a/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/web/TunnelCodec.java b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/web/TunnelCodec.java
new file mode 100644
index 00000000..2f85afd5
--- /dev/null
+++ b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/web/TunnelCodec.java
@@ -0,0 +1,66 @@
+/*
+ * 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.web;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import org.onosproject.codec.CodecContext;
+import org.onosproject.codec.JsonCodec;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import org.onosproject.segmentrouting.DefaultTunnel;
+import org.onosproject.segmentrouting.Tunnel;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public final class TunnelCodec extends JsonCodec<Tunnel> {
+
+ // JSON field names
+ private static final String TUNNEL_ID = "tunnel_id";
+ private static final String GROUP_ID = "group_id";
+ private static final String LABEL_PATH = "label_path";
+
+ @Override
+ public ObjectNode encode(Tunnel tunnel, CodecContext context) {
+ final ObjectNode result = context.mapper().createObjectNode()
+ .put(TUNNEL_ID, tunnel.id());
+
+ result.put(GROUP_ID, tunnel.groupId());
+
+ final ArrayNode jsonLabelIds = result.putArray(LABEL_PATH);
+
+ tunnel.labelIds().forEach(label -> jsonLabelIds.add(label.intValue()));
+
+ return result;
+ }
+
+ @Override
+ public DefaultTunnel decode(ObjectNode json, CodecContext context) {
+
+ String tid = json.path(TUNNEL_ID).asText();
+ List<Integer> labels = new ArrayList<>();
+
+ if (!json.path(LABEL_PATH).isMissingNode()) {
+ ArrayNode labelArray = (ArrayNode) json.path(LABEL_PATH);
+ for (JsonNode o : labelArray) {
+ labels.add(o.asInt());
+ }
+ }
+
+ return new DefaultTunnel(tid, labels);
+ }
+
+}
diff --git a/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/web/TunnelWebResource.java b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/web/TunnelWebResource.java
new file mode 100644
index 00000000..fb30308a
--- /dev/null
+++ b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/web/TunnelWebResource.java
@@ -0,0 +1,102 @@
+/*
+ * 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.web;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+import org.onosproject.rest.AbstractWebResource;
+import org.onosproject.segmentrouting.SegmentRoutingService;
+import org.onosproject.segmentrouting.Tunnel;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.List;
+
+/**
+ * Query, create and remove segment routing tunnels.
+ */
+@Path("tunnel")
+public class TunnelWebResource extends AbstractWebResource {
+
+ private static final TunnelCodec TUNNEL_CODEC = new TunnelCodec();
+
+ /**
+ * Get all segment routing tunnels.
+ * Returns an array of segment routing tunnels.
+ *
+ * @return status of OK
+ */
+ @GET
+ @Produces(MediaType.APPLICATION_JSON)
+ public Response getTunnel() {
+ SegmentRoutingService srService = get(SegmentRoutingService.class);
+ List<Tunnel> tunnels = srService.getTunnels();
+ ObjectNode result = new ObjectMapper().createObjectNode();
+ result.set("tunnel", new TunnelCodec().encode(tunnels, this));
+
+ return ok(result.toString()).build();
+ }
+
+ /**
+ * Create a new segment routing tunnel.
+ *
+ * @param input JSON stream for tunnel to create
+ * @return status of the request - OK if the tunnel is created,
+ * INTERNAL_SERVER_ERROR if the JSON is invalid or the tunnel cannot be created
+ * @throws IOException if the JSON is invalid
+ */
+ @POST
+ @Consumes(MediaType.APPLICATION_JSON)
+ public Response createTunnel(InputStream input) throws IOException {
+ ObjectMapper mapper = new ObjectMapper();
+ ObjectNode tunnelJson = (ObjectNode) mapper.readTree(input);
+ SegmentRoutingService srService = get(SegmentRoutingService.class);
+ Tunnel tunnelInfo = TUNNEL_CODEC.decode(tunnelJson, this);
+ srService.createTunnel(tunnelInfo);
+
+ return Response.ok().build();
+ }
+
+ /**
+ * Delete a segment routing tunnel.
+ *
+ * @param input JSON stream for tunnel to delete
+ * @return status of the request - OK if the tunnel is removed,
+ * INTERNAL_SERVER_ERROR otherwise
+ * @throws IOException if JSON is invalid
+ */
+ @DELETE
+ @Consumes(MediaType.APPLICATION_JSON)
+ public Response removeTunnel(InputStream input) throws IOException {
+ ObjectMapper mapper = new ObjectMapper();
+ ObjectNode tunnelJson = (ObjectNode) mapper.readTree(input);
+ SegmentRoutingService srService = get(SegmentRoutingService.class);
+ Tunnel tunnelInfo = TUNNEL_CODEC.decode(tunnelJson, this);
+ srService.removeTunnel(tunnelInfo);
+
+ return Response.ok().build();
+ }
+
+}
diff --git a/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/web/package-info.java b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/web/package-info.java
new file mode 100644
index 00000000..85c63de5
--- /dev/null
+++ b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/web/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * 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.
+ */
+
+/**
+ * Set of resources implementing the segment routing application REST API.
+ */
+package org.onosproject.segmentrouting.web; \ No newline at end of file
diff --git a/framework/src/onos/apps/segmentrouting/src/main/resources/OSGI-INF/blueprint/shell-config.xml b/framework/src/onos/apps/segmentrouting/src/main/resources/OSGI-INF/blueprint/shell-config.xml
new file mode 100644
index 00000000..3e47bf8f
--- /dev/null
+++ b/framework/src/onos/apps/segmentrouting/src/main/resources/OSGI-INF/blueprint/shell-config.xml
@@ -0,0 +1,40 @@
+<!--
+ ~ 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.
+ -->
+<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0">
+
+ <command-bundle xmlns="http://karaf.apache.org/xmlns/shell/v1.1.0">
+ <command>
+ <action class="org.onosproject.segmentrouting.cli.TunnelListCommand"/>
+ </command>
+ <command>
+ <action class="org.onosproject.segmentrouting.cli.PolicyListCommand"/>
+ </command>
+ <command>
+ <action class="org.onosproject.segmentrouting.cli.PolicyAddCommand"/>
+ </command>
+ <command>
+ <action class="org.onosproject.segmentrouting.cli.PolicyRemoveCommand"/>
+ </command>
+ <command>
+ <action class="org.onosproject.segmentrouting.cli.TunnelAddCommand"/>
+ </command>
+ <command>
+ <action class="org.onosproject.segmentrouting.cli.TunnelRemoveCommand"/>
+ </command>
+ </command-bundle>
+</blueprint>
+
+
diff --git a/framework/src/onos/apps/segmentrouting/src/main/webapp/WEB-INF/web.xml b/framework/src/onos/apps/segmentrouting/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 00000000..94c0d7de
--- /dev/null
+++ b/framework/src/onos/apps/segmentrouting/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ 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.
+ -->
+<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee"
+ xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
+ xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
+ id="ONOS" version="2.5">
+ <display-name>Segment Routing REST API v1.0</display-name>
+
+ <servlet>
+ <servlet-name>JAX-RS Service</servlet-name>
+ <servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
+ <init-param>
+ <param-name>com.sun.jersey.config.property.resourceConfigClass</param-name>
+ <param-value>com.sun.jersey.api.core.ClassNamesResourceConfig</param-value>
+ </init-param>
+ <init-param>
+ <param-name>com.sun.jersey.config.property.classnames</param-name>
+ <param-value>
+ org.onosproject.segmentrouting.web.TunnelWebResource,
+ org.onosproject.segmentrouting.web.PolicyWebResource
+ </param-value>
+ </init-param>
+ <load-on-startup>1</load-on-startup>
+ </servlet>
+
+ <servlet-mapping>
+ <servlet-name>JAX-RS Service</servlet-name>
+ <url-pattern>/*</url-pattern>
+ </servlet-mapping>
+</web-app>