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

Change-Id: Idfa67903e59e1c4cac4da430f89cd4c50e821420
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..3e541f0
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="UTF-8"?>

+<!--

+  ~ Copyright 2014 Open Networking Laboratory

+  ~

+  ~ Licensed under the Apache License, Version 2.0 (the "License");

+  ~ you may not use this file except in compliance with the License.

+  ~ You may obtain a copy of the License at

+  ~

+  ~     http://www.apache.org/licenses/LICENSE-2.0

+  ~

+  ~ Unless required by applicable law or agreed to in writing, software

+  ~ distributed under the License is distributed on an "AS IS" BASIS,

+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+  ~ See the License for the specific language governing permissions and

+  ~ limitations under the License.

+  --><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

+    <modelVersion>4.0.0</modelVersion>

+    <parent>

+    <artifactId>onos-apps</artifactId>

+    <groupId>org.onosproject</groupId>

+    <version>1.2.0-SNAPSHOT</version>

+  </parent>

+

+    <artifactId>onos-app-segmentrouting</artifactId>

+    <packaging>bundle</packaging>

+

+    <description>ONOS OSGi bundle archetype</description>

+    <url>http://onosproject.org</url>

+

+    <dependencies>

+        <dependency>

+            <groupId>org.onosproject</groupId>

+            <artifactId>onos-app-grouphandler</artifactId>

+            <version>1.2.0-SNAPSHOT</version>

+        </dependency>

+    </dependencies>

+

+</project>

diff --git a/src/main/java/org/onosproject/segmentrouting/ArpHandler.java b/src/main/java/org/onosproject/segmentrouting/ArpHandler.java
new file mode 100644
index 0000000..5b94518
--- /dev/null
+++ b/src/main/java/org/onosproject/segmentrouting/ArpHandler.java
@@ -0,0 +1,222 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.segmentrouting;
+
+import org.onlab.packet.ARP;
+import org.onlab.packet.Ethernet;
+import org.onlab.packet.Ip4Address;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.IpPrefix;
+import org.onlab.packet.MacAddress;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.Host;
+import org.onosproject.net.Port;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.flow.DefaultTrafficTreatment;
+import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.packet.DefaultOutboundPacket;
+import org.onosproject.net.packet.InboundPacket;
+import org.onosproject.net.HostId;
+import org.onosproject.net.packet.OutboundPacket;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.nio.ByteBuffer;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+public class ArpHandler {
+
+    private static Logger log = LoggerFactory.getLogger(ArpHandler.class);
+
+    private SegmentRoutingManager srManager;
+    private NetworkConfigHandler config;
+
+    /**
+     * Creates an ArpHandler object.
+     *
+     * @param srManager SegmentRoutingManager object
+     */
+    public ArpHandler(SegmentRoutingManager srManager) {
+        this.srManager = srManager;
+        this.config = checkNotNull(srManager.networkConfigHandler);
+    }
+
+    /**
+     * Processes incoming ARP packets.
+     * If it is an ARP request to router itself or known hosts,
+     * then it sends ARP response.
+     * If it is an ARP request to unknown hosts in its own subnet,
+     * then it flood the ARP request to the ports.
+     * If it is an ARP response, then set a flow rule for the host
+     * and forward any IP packets to the host in the packet buffer to the host.
+     *
+     * @param pkt incoming packet
+     */
+    public void processPacketIn(InboundPacket pkt) {
+
+        Ethernet ethernet = pkt.parsed();
+        ARP arp = (ARP) ethernet.getPayload();
+
+        ConnectPoint connectPoint = pkt.receivedFrom();
+        PortNumber inPort = connectPoint.port();
+        DeviceId deviceId = connectPoint.deviceId();
+        byte[] senderMacAddressByte = arp.getSenderHardwareAddress();
+        Ip4Address hostIpAddress = Ip4Address.valueOf(arp.getSenderProtocolAddress());
+
+        srManager.routingRulePopulator.populateIpRuleForHost(deviceId, hostIpAddress, MacAddress.
+                valueOf(senderMacAddressByte), inPort);
+
+        if (arp.getOpCode() == ARP.OP_REQUEST) {
+            handleArpRequest(deviceId, connectPoint, ethernet);
+        } else {
+            srManager.ipHandler.forwardPackets(deviceId, hostIpAddress);
+        }
+    }
+
+    private void handleArpRequest(DeviceId deviceId, ConnectPoint inPort, Ethernet payload) {
+
+        ARP arpRequest = (ARP) payload.getPayload();
+        HostId targetHostId = HostId.hostId(MacAddress.valueOf(
+                arpRequest.getTargetHardwareAddress()));
+
+        // ARP request for router
+        if (isArpReqForRouter(deviceId, arpRequest)) {
+            Ip4Address targetAddress = Ip4Address.valueOf(arpRequest.getTargetProtocolAddress());
+            sendArpResponse(arpRequest, config.getRouterMac(targetAddress));
+
+        // ARP request for known hosts
+        } else if (srManager.hostService.getHost(targetHostId) != null) {
+            MacAddress targetMac = srManager.hostService.getHost(targetHostId).mac();
+            sendArpResponse(arpRequest, targetMac);
+
+        // ARP request for unknown host in the subnet
+        } else if (isArpReqForSubnet(deviceId, arpRequest)) {
+            flood(payload, inPort);
+        }
+    }
+
+
+    private boolean isArpReqForRouter(DeviceId deviceId, ARP arpRequest) {
+        Ip4Address gatewayIpAddress = config.getGatewayIpAddress(deviceId);
+        if (gatewayIpAddress != null) {
+            Ip4Address targetProtocolAddress = Ip4Address.valueOf(arpRequest
+                    .getTargetProtocolAddress());
+            if (gatewayIpAddress.equals(targetProtocolAddress)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private boolean isArpReqForSubnet(DeviceId deviceId, ARP arpRequest) {
+        String subnetInfo = config.getSubnetInfo(deviceId);
+        if (subnetInfo != null) {
+            IpPrefix prefix = IpPrefix.valueOf(subnetInfo);
+            if (prefix.contains(Ip4Address.valueOf(arpRequest.getTargetProtocolAddress()))) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Sends an APR request for the target IP address to all ports except in-port.
+     *
+     * @param deviceId Switch device ID
+     * @param targetAddress target IP address for ARP
+     * @param inPort in-port
+     */
+    public void sendArpRequest(DeviceId deviceId, IpAddress targetAddress, ConnectPoint inPort) {
+
+        byte[] senderMacAddress = config.getRouterMacAddress(deviceId).toBytes();
+        byte[] senderIpAddress = config.getRouterIpAddress(deviceId)
+                .getIp4Prefix().address().toOctets();
+
+        ARP arpRequest = new ARP();
+        arpRequest.setHardwareType(ARP.HW_TYPE_ETHERNET)
+                  .setProtocolType(ARP.PROTO_TYPE_IP)
+                  .setHardwareAddressLength(
+                        (byte) Ethernet.DATALAYER_ADDRESS_LENGTH)
+                  .setProtocolAddressLength((byte) Ip4Address.BYTE_LENGTH)
+                  .setOpCode(ARP.OP_REQUEST)
+                  .setSenderHardwareAddress(senderMacAddress)
+                  .setTargetHardwareAddress(MacAddress.ZERO.toBytes())
+                  .setSenderProtocolAddress(senderIpAddress)
+                  .setTargetProtocolAddress(targetAddress.toOctets());
+
+        Ethernet eth = new Ethernet();
+        eth.setDestinationMACAddress(MacAddress.BROADCAST.toBytes())
+                .setSourceMACAddress(senderMacAddress)
+                .setEtherType(Ethernet.TYPE_ARP).setPayload(arpRequest);
+
+        flood(eth, inPort);
+    }
+
+    private void sendArpResponse(ARP arpRequest, MacAddress targetMac) {
+
+        ARP arpReply = new ARP();
+        arpReply.setHardwareType(ARP.HW_TYPE_ETHERNET)
+                .setProtocolType(ARP.PROTO_TYPE_IP)
+                .setHardwareAddressLength(
+                        (byte) Ethernet.DATALAYER_ADDRESS_LENGTH)
+                .setProtocolAddressLength((byte) Ip4Address.BYTE_LENGTH)
+                .setOpCode(ARP.OP_REPLY)
+                .setSenderHardwareAddress(targetMac.toBytes())
+                .setSenderProtocolAddress(arpRequest.getTargetProtocolAddress())
+                .setTargetHardwareAddress(arpRequest.getSenderHardwareAddress())
+                .setTargetProtocolAddress(arpRequest.getSenderProtocolAddress());
+
+        Ethernet eth = new Ethernet();
+        eth.setDestinationMACAddress(arpRequest.getSenderHardwareAddress())
+                .setSourceMACAddress(targetMac.toBytes())
+                .setEtherType(Ethernet.TYPE_ARP).setPayload(arpReply);
+
+
+        HostId dstId = HostId.hostId(MacAddress.valueOf(
+                arpReply.getTargetHardwareAddress()));
+        Host dst = srManager.hostService.getHost(dstId);
+        if (dst == null) {
+            log.warn("Cannot send ARP response to unknown device");
+            return;
+        }
+
+        TrafficTreatment treatment = DefaultTrafficTreatment.builder().
+                setOutput(dst.location().port()).build();
+        OutboundPacket packet = new DefaultOutboundPacket(dst.location().deviceId(),
+                treatment, ByteBuffer.wrap(eth.serialize()));
+
+        srManager.packetService.emit(packet);
+    }
+
+    private void flood(Ethernet request, ConnectPoint inPort) {
+        TrafficTreatment.Builder builder;
+        ByteBuffer buf = ByteBuffer.wrap(request.serialize());
+
+        for (Port port: srManager.deviceService.getPorts(inPort.deviceId())) {
+            if (!port.number().equals(inPort.port()) &&
+                    port.number().toLong() > 0) {
+                builder = DefaultTrafficTreatment.builder();
+                builder.setOutput(port.number());
+                srManager.packetService.emit(new DefaultOutboundPacket(inPort.deviceId(),
+                        builder.build(), buf));
+            }
+        }
+    }
+
+}
diff --git a/src/main/java/org/onosproject/segmentrouting/DefaultRoutingHandler.java b/src/main/java/org/onosproject/segmentrouting/DefaultRoutingHandler.java
new file mode 100644
index 0000000..3684ec9
--- /dev/null
+++ b/src/main/java/org/onosproject/segmentrouting/DefaultRoutingHandler.java
@@ -0,0 +1,228 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.segmentrouting;
+
+import org.onlab.packet.IpPrefix;
+import org.onosproject.net.Device;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.MastershipRole;
+import org.onosproject.net.flow.FlowRule;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Set;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+public class DefaultRoutingHandler {
+
+    private static Logger log = LoggerFactory.getLogger(DefaultRoutingHandler.class);
+
+    private SegmentRoutingManager srManager;
+    private RoutingRulePopulator rulePopulator;
+    private NetworkConfigHandler config;
+    private Status populationStatus;
+
+    /**
+     * Represents the default routing population status.
+     */
+    public enum Status {
+        // population process is not started yet.
+        IDLE,
+
+        // population process started.
+        STARTED,
+
+        // population process was aborted due to errors, mostly for groups not found.
+        ABORTED,
+
+        // population process was finished successfully.
+        SUCCEEDED
+    }
+
+    /**
+     * Creates a DefaultRoutingHandler object.
+     *
+     * @param srManager SegmentRoutingManager object
+     */
+    public DefaultRoutingHandler(SegmentRoutingManager srManager) {
+        this.srManager = srManager;
+        this.rulePopulator = checkNotNull(srManager.routingRulePopulator);
+        this.config = checkNotNull(srManager.networkConfigHandler);
+        this.populationStatus = Status.IDLE;
+    }
+
+    /**
+     * Populates all routing rules to all connected routers, including default
+     * routing rules, adjacency rules, and policy rules if any.
+     *
+     * @return true if it succeeds in populating all rules, otherwise false
+     */
+    public boolean populateAllRoutingRules() {
+
+        populationStatus = Status.STARTED;
+        log.info("Starts to populate routing rules");
+
+        for (Device sw : srManager.deviceService.getDevices()) {
+            if (srManager.mastershipService.
+                    getLocalRole(sw.id()) != MastershipRole.MASTER) {
+                continue;
+            }
+
+            ECMPShortestPathGraph ecmpSPG = new ECMPShortestPathGraph(sw.id(), srManager);
+            if (!populateEcmpRoutingRules(sw, ecmpSPG)) {
+                populationStatus = Status.ABORTED;
+                log.debug("Abort routing rule population");
+                return false;
+            }
+
+            // TODO: Set adjacency routing rule for all switches
+        }
+
+        populationStatus = Status.SUCCEEDED;
+        log.info("Completes routing rule population");
+        return true;
+    }
+
+    private boolean populateEcmpRoutingRules(Device sw,
+                                             ECMPShortestPathGraph ecmpSPG) {
+
+        HashMap<Integer, HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>>> switchVia =
+                ecmpSPG.getAllLearnedSwitchesAndVia();
+        for (Integer itrIdx : switchVia.keySet()) {
+            HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>> swViaMap =
+                    switchVia.get(itrIdx);
+            for (DeviceId targetSw : swViaMap.keySet()) {
+                DeviceId destSw = sw.id();
+                Set<DeviceId> nextHops = new HashSet<>();
+
+                for (ArrayList<DeviceId> via : swViaMap.get(targetSw)) {
+                    if (via.isEmpty()) {
+                        nextHops.add(destSw);
+                    } else {
+                        nextHops.add(via.get(0));
+                    }
+                }
+                if (!populateEcmpRoutingRulePartial(targetSw, destSw, nextHops)) {
+                    return false;
+                }
+            }
+        }
+
+        return true;
+    }
+
+    private boolean populateEcmpRoutingRulePartial(DeviceId targetSw, DeviceId destSw,
+                                Set<DeviceId> nextHops) {
+        boolean result;
+
+        if (nextHops.isEmpty()) {
+            nextHops.add(destSw);
+        }
+
+        // If both target switch and dest switch are edge routers, then set IP rule
+        // for both subnet and router IP.
+        if (config.isEdgeRouter(targetSw) && config.isEdgeRouter(destSw)) {
+            String subnets = config.getSubnetInfo(destSw);
+            result = rulePopulator.populateIpRuleForSubnet(targetSw, subnets, destSw, nextHops);
+            if (!result) {
+                return false;
+            }
+
+            IpPrefix routerIp = config.getRouterIpAddress(destSw);
+            result = rulePopulator.populateIpRuleForRouter(targetSw, routerIp, destSw, nextHops);
+            if (!result) {
+                return false;
+            }
+
+        // If the target switch is an edge router, then set IP rules for the router IP.
+        } else if (config.isEdgeRouter(targetSw)) {
+            IpPrefix routerIp = config.getRouterIpAddress(destSw);
+            result = rulePopulator.populateIpRuleForRouter(targetSw, routerIp, destSw, nextHops);
+            if (!result) {
+                return false;
+            }
+
+        // If the target switch is an transit router, then set MPLS rules only.
+        } else if (config.isTransitRouter(targetSw)) {
+            result = rulePopulator.populateMplsRule(targetSw, destSw, nextHops);
+            if (!result) {
+                return false;
+            }
+        } else {
+            log.warn("The switch {} is neither an edge router nor a transit router.", targetSw);
+            return false;
+        }
+
+        return true;
+    }
+
+    /**
+     * Populates table miss entries for all tables, and pipeline rules for
+     * VLAN and TACM tables.
+     *
+     * @param deviceId Switch ID to set the rules
+     */
+    public void populateTtpRules(DeviceId deviceId) {
+
+        rulePopulator.populateTableMissEntry(deviceId, FlowRule.Type.VLAN,
+                true, false, false, FlowRule.Type.DEFAULT);
+        rulePopulator.populateTableMissEntry(deviceId, FlowRule.Type.ETHER,
+                true, false, false, FlowRule.Type.DEFAULT);
+        rulePopulator.populateTableMissEntry(deviceId, FlowRule.Type.IP,
+                false, true, true, FlowRule.Type.ACL);
+        rulePopulator.populateTableMissEntry(deviceId, FlowRule.Type.MPLS,
+                false, true, true, FlowRule.Type.ACL);
+        rulePopulator.populateTableMissEntry(deviceId, FlowRule.Type.ACL,
+                false, false, false, FlowRule.Type.DEFAULT);
+
+        rulePopulator.populateTableVlan(deviceId);
+        rulePopulator.populateTableTMac(deviceId);
+    }
+
+    /**
+     * Start the flow rule population process if it was never started.
+     * The process finishes successfully when all flow rules are set and
+     * stops with ABORTED status when any groups required for flows is not
+     * set yet.
+     */
+    public void startPopulationProcess() {
+        synchronized (populationStatus) {
+            if (populationStatus == Status.IDLE ||
+                    populationStatus == Status.SUCCEEDED) {
+                populationStatus = Status.STARTED;
+                populateAllRoutingRules();
+            }
+        }
+    }
+
+    /**
+     * Resume the flow rule population process if it was aborted for any reason.
+     * Mostly the process is aborted when the groups required are not set yet.
+     */
+    public void resumePopulationProcess() {
+        synchronized (populationStatus) {
+            if (populationStatus == Status.ABORTED) {
+                populationStatus = Status.STARTED;
+                // TODO: we need to restart from the point aborted instead of restarting.
+                populateAllRoutingRules();
+            }
+        }
+    }
+}
diff --git a/src/main/java/org/onosproject/segmentrouting/DeviceConfiguration.java b/src/main/java/org/onosproject/segmentrouting/DeviceConfiguration.java
new file mode 100644
index 0000000..17cfd96
--- /dev/null
+++ b/src/main/java/org/onosproject/segmentrouting/DeviceConfiguration.java
@@ -0,0 +1,190 @@
+package org.onosproject.segmentrouting;
+
+import org.onlab.packet.Ip4Address;
+import org.onlab.packet.MacAddress;
+import org.onosproject.grouphandler.DeviceProperties;
+import org.onosproject.net.DeviceId;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class DeviceConfiguration implements DeviceProperties {
+
+    private static final Logger log = LoggerFactory
+            .getLogger(DeviceConfiguration.class);
+    private final List<Integer> allSegmentIds =
+            Arrays.asList(101, 102, 103, 104, 105, 106);
+    private HashMap<DeviceId, Integer> deviceSegmentIdMap =
+            new HashMap<DeviceId, Integer>() {
+                {
+                    put(DeviceId.deviceId("of:0000000000000001"), 101);
+                    put(DeviceId.deviceId("of:0000000000000002"), 102);
+                    put(DeviceId.deviceId("of:0000000000000003"), 103);
+                    put(DeviceId.deviceId("of:0000000000000004"), 104);
+                    put(DeviceId.deviceId("of:0000000000000005"), 105);
+                    put(DeviceId.deviceId("of:0000000000000006"), 106);
+                }
+            };
+    private final HashMap<DeviceId, MacAddress> deviceMacMap =
+            new HashMap<DeviceId, MacAddress>() {
+                {
+                    put(DeviceId.deviceId("of:0000000000000001"),
+                            MacAddress.valueOf("00:00:00:00:00:01"));
+                    put(DeviceId.deviceId("of:0000000000000002"),
+                            MacAddress.valueOf("00:00:00:00:00:02"));
+                    put(DeviceId.deviceId("of:0000000000000003"),
+                            MacAddress.valueOf("00:00:00:00:00:03"));
+                    put(DeviceId.deviceId("of:0000000000000004"),
+                            MacAddress.valueOf("00:00:00:00:00:04"));
+                    put(DeviceId.deviceId("of:0000000000000005"),
+                            MacAddress.valueOf("00:00:00:00:00:05"));
+                    put(DeviceId.deviceId("of:0000000000000006"),
+                            MacAddress.valueOf("00:00:00:00:00:06"));
+                }
+            };
+
+    private final HashMap<DeviceId, Ip4Address> deviceIpMap =
+            new HashMap<DeviceId, Ip4Address>() {
+                {
+                    put(DeviceId.deviceId("of:0000000000000001"),
+                            Ip4Address.valueOf("192.168.0.1"));
+                    put(DeviceId.deviceId("of:0000000000000002"),
+                            Ip4Address.valueOf("192.168.0.2"));
+                    put(DeviceId.deviceId("of:0000000000000003"),
+                            Ip4Address.valueOf("192.168.0.3"));
+                    put(DeviceId.deviceId("of:0000000000000004"),
+                            Ip4Address.valueOf("192.168.0.4"));
+                    put(DeviceId.deviceId("of:0000000000000005"),
+                            Ip4Address.valueOf("192.168.0.5"));
+                    put(DeviceId.deviceId("of:0000000000000006"),
+                            Ip4Address.valueOf("192.168.0.6"));
+                }
+            };
+
+    @Override
+    public int getSegmentId(DeviceId deviceId) {
+        if (deviceSegmentIdMap.get(deviceId) != null) {
+            log.debug("getSegmentId for device{} is {}",
+                    deviceId,
+                    deviceSegmentIdMap.get(deviceId));
+            return deviceSegmentIdMap.get(deviceId);
+        } else {
+            throw new IllegalStateException();
+        }
+    }
+
+
+    @Override
+    public MacAddress getDeviceMac(DeviceId deviceId) {
+        if (deviceMacMap.get(deviceId) != null) {
+            log.debug("getDeviceMac for device{} is {}",
+                    deviceId,
+                    deviceMacMap.get(deviceId));
+            return deviceMacMap.get(deviceId);
+        } else {
+            throw new IllegalStateException();
+        }
+    }
+
+
+    @Override
+    public boolean isEdgeDevice(DeviceId deviceId) {
+        if (deviceId.equals(DeviceId.deviceId("of:0000000000000001"))
+                || deviceId.equals(DeviceId.deviceId("of:0000000000000006"))) {
+            return true;
+        }
+
+        return false;
+    }
+
+    @Override
+    public List<Integer> getAllDeviceSegmentIds() {
+        return allSegmentIds;
+    }
+
+
+    /**
+     * Returns Segment ID for the router with the MAC address given.
+     *
+     * @param targetMac Mac address for the router
+     * @return Segment ID for the router with the MAC address
+     */
+    public int getSegmentId(MacAddress targetMac) {
+        for (Map.Entry<DeviceId, MacAddress> entry: deviceMacMap.entrySet()) {
+            if (entry.getValue().equals(targetMac)) {
+                return deviceSegmentIdMap.get(entry.getKey());
+            }
+        }
+
+        return -1;
+    }
+
+    /**
+     * Returns Segment ID for the router withe IP address given.
+     *
+     * @param targetAddress IP address of the router
+     * @return Segment ID for the router with the IP address
+     */
+    public int getSegmentId(Ip4Address targetAddress) {
+        for (Map.Entry<DeviceId, Ip4Address> entry: deviceIpMap.entrySet()) {
+            if (entry.getValue().equals(targetAddress)) {
+                return deviceSegmentIdMap.get(entry.getKey());
+            }
+        }
+
+        return -1;
+    }
+
+    /**
+     * Returns Router IP address for the router with the device ID given.
+     *
+     * @param deviceId device ID of the router
+     * @return IP address of the router
+     */
+    public Ip4Address getRouterIp(DeviceId deviceId) {
+        if (deviceIpMap.get(deviceId) != null) {
+            log.debug("getDeviceIp for device{} is {}",
+                    deviceId,
+                    deviceIpMap.get(deviceId));
+            return deviceIpMap.get(deviceId);
+        } else {
+            throw new IllegalStateException();
+        }
+    }
+
+    /**
+     * Returns the Device ID of the router with the Segment ID given.
+     *
+     * @param sid Segment ID of the router
+     * @return Device ID of the router
+     */
+    public DeviceId getDeviceId(int sid) {
+        for (Map.Entry<DeviceId, Integer> entry: deviceSegmentIdMap.entrySet()) {
+            if (entry.getValue() == sid) {
+                return entry.getKey();
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * Returns the Device ID of the router with the IP address given.
+     *
+     * @param ipAddress IP address of the router
+     * @return Device ID of the router
+     */
+    public DeviceId getDeviceId(Ip4Address ipAddress) {
+        for (Map.Entry<DeviceId, Ip4Address> entry: deviceIpMap.entrySet()) {
+            if (entry.getValue().equals(ipAddress)) {
+                return entry.getKey();
+            }
+        }
+
+        return null;
+    }
+}
diff --git a/src/main/java/org/onosproject/segmentrouting/ECMPShortestPathGraph.java b/src/main/java/org/onosproject/segmentrouting/ECMPShortestPathGraph.java
new file mode 100644
index 0000000..cd28fcf
--- /dev/null
+++ b/src/main/java/org/onosproject/segmentrouting/ECMPShortestPathGraph.java
@@ -0,0 +1,374 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.segmentrouting;
+
+import org.onosproject.net.DefaultLink;
+import org.onosproject.net.DefaultPath;
+import org.onosproject.net.Device;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.Link;
+import org.onosproject.net.Path;
+import org.onosproject.net.provider.ProviderId;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * This class creates bandwidth constrained breadth first tree and returns paths
+ * from root Device to leaf Devicees which satisfies the bandwidth condition. If
+ * bandwidth parameter is not specified, the normal breadth first tree will be
+ * calculated. The paths are snapshot paths at the point of the class
+ * instantiation.
+ */
+public class ECMPShortestPathGraph {
+    LinkedList<DeviceId> deviceQueue = new LinkedList<>();
+    LinkedList<Integer> distanceQueue = new LinkedList<>();
+    HashMap<DeviceId, Integer> deviceSearched = new HashMap<>();
+    HashMap<DeviceId, ArrayList<Link>> upstreamLinks = new HashMap<>();
+    HashMap<DeviceId, ArrayList<Path>> paths = new HashMap<>();
+    HashMap<Integer, ArrayList<DeviceId>> distanceDeviceMap = new HashMap<>();
+    DeviceId rootDevice;
+    private SegmentRoutingManager srManager;
+    private static final Logger log = LoggerFactory
+            .getLogger(ECMPShortestPathGraph.class);
+
+    /**
+     * Constructor.
+     *
+     * @param rootDevice root of the BFS tree
+     * @param linkListToAvoid link list to avoid
+     * @param deviceIdListToAvoid device list to avoid
+     */
+    public ECMPShortestPathGraph(DeviceId rootDevice, List<String> deviceIdListToAvoid,
+                                 List<Link> linkListToAvoid) {
+        this.rootDevice = rootDevice;
+        calcECMPShortestPathGraph(deviceIdListToAvoid, linkListToAvoid);
+    }
+
+    /**
+     * Constructor.
+     *
+     * @param rootDevice root of the BFS tree
+     * @param srManager SegmentRoutingManager object
+     */
+    public ECMPShortestPathGraph(DeviceId rootDevice, SegmentRoutingManager srManager) {
+        this.rootDevice = rootDevice;
+        this.srManager = srManager;
+        calcECMPShortestPathGraph();
+    }
+
+    /**
+     * Calculates the BFS tree using any provided constraints and Intents.
+     */
+    private void calcECMPShortestPathGraph() {
+        deviceQueue.add(rootDevice);
+        int currDistance = 0;
+        distanceQueue.add(currDistance);
+        deviceSearched.put(rootDevice, currDistance);
+        while (!deviceQueue.isEmpty()) {
+            DeviceId sw = deviceQueue.poll();
+            DeviceId prevSw = null;
+            currDistance = distanceQueue.poll();
+
+            for (Link link : srManager.linkService.getDeviceEgressLinks(sw)) {
+                DeviceId reachedDevice = link.dst().deviceId();
+                if ((prevSw != null)
+                        && (prevSw.equals(reachedDevice))) {
+                    /* Ignore LAG links between the same set of Devicees */
+                    continue;
+                } else  {
+                    prevSw = reachedDevice;
+                }
+
+                Integer distance = deviceSearched.get(reachedDevice);
+                if ((distance != null) && (distance.intValue() < (currDistance + 1))) {
+                    continue;
+                }
+                if (distance == null) {
+                    /* First time visiting this Device node */
+                    deviceQueue.add(reachedDevice);
+                    distanceQueue.add(currDistance + 1);
+                    deviceSearched.put(reachedDevice, currDistance + 1);
+
+                    ArrayList<DeviceId> distanceSwArray = distanceDeviceMap
+                            .get(currDistance + 1);
+                    if (distanceSwArray == null) {
+                        distanceSwArray = new ArrayList<DeviceId>();
+                        distanceSwArray.add(reachedDevice);
+                        distanceDeviceMap.put(currDistance + 1, distanceSwArray);
+                    } else {
+                        distanceSwArray.add(reachedDevice);
+                    }
+                }
+
+                ArrayList<Link> upstreamLinkArray =
+                        upstreamLinks.get(reachedDevice);
+                if (upstreamLinkArray == null) {
+                    upstreamLinkArray = new ArrayList<Link>();
+                    upstreamLinkArray.add(copyDefaultLink(link));
+                    //upstreamLinkArray.add(link);
+                    upstreamLinks.put(reachedDevice, upstreamLinkArray);
+                } else {
+                    /* ECMP links */
+                    upstreamLinkArray.add(copyDefaultLink(link));
+                }
+            }
+        }
+    }
+
+    /**
+     * Calculates the BFS tree using any provided constraints and Intents.
+     */
+    private void calcECMPShortestPathGraph(List<String> deviceIdListToAvoid, List<Link> linksToAvoid) {
+        deviceQueue.add(rootDevice);
+        int currDistance = 0;
+        distanceQueue.add(currDistance);
+        deviceSearched.put(rootDevice, currDistance);
+        boolean foundLinkToAvoid = false;
+        while (!deviceQueue.isEmpty()) {
+            DeviceId sw = deviceQueue.poll();
+            DeviceId prevSw = null;
+            currDistance = distanceQueue.poll();
+            for (Link link : srManager.linkService.getDeviceEgressLinks(sw)) {
+                for (Link linkToAvoid: linksToAvoid) {
+                    // TODO: equls should work
+                    //if (link.equals(linkToAvoid)) {
+                    if (linkContains(link, linksToAvoid)) {
+                        foundLinkToAvoid = true;
+                        break;
+                    }
+                }
+                if (foundLinkToAvoid) {
+                    foundLinkToAvoid = false;
+                    continue;
+                }
+                DeviceId reachedDevice = link.dst().deviceId();
+                if (deviceIdListToAvoid.contains(reachedDevice.toString())) {
+                    continue;
+                }
+                if ((prevSw != null)
+                        && (prevSw.equals(reachedDevice))) {
+                    /* Ignore LAG links between the same set of Devicees */
+                    continue;
+                } else {
+                    prevSw = reachedDevice;
+                }
+
+                Integer distance = deviceSearched.get(reachedDevice);
+                if ((distance != null) && (distance.intValue() < (currDistance + 1))) {
+                    continue;
+                }
+                if (distance == null) {
+                    /* First time visiting this Device node */
+                    deviceQueue.add(reachedDevice);
+                    distanceQueue.add(currDistance + 1);
+                    deviceSearched.put(reachedDevice, currDistance + 1);
+
+                    ArrayList<DeviceId> distanceSwArray = distanceDeviceMap
+                            .get(currDistance + 1);
+                    if (distanceSwArray == null) {
+                        distanceSwArray = new ArrayList<DeviceId>();
+                        distanceSwArray.add(reachedDevice);
+                        distanceDeviceMap.put(currDistance + 1, distanceSwArray);
+                    } else {
+                        distanceSwArray.add(reachedDevice);
+                    }
+                }
+
+                ArrayList<Link> upstreamLinkArray =
+                        upstreamLinks.get(reachedDevice);
+                if (upstreamLinkArray == null) {
+                    upstreamLinkArray = new ArrayList<Link>();
+                    upstreamLinkArray.add(copyDefaultLink(link));
+                    upstreamLinks.put(reachedDevice, upstreamLinkArray);
+                } else {
+                    /* ECMP links */
+                    upstreamLinkArray.add(copyDefaultLink(link));
+                }
+            }
+        }
+    }
+
+
+    private boolean linkContains(Link link, List<Link> links) {
+
+        DeviceId srcDevice1 = link.src().deviceId();
+        DeviceId dstDevice1 = link.dst().deviceId();
+        long srcPort1 = link.src().port().toLong();
+        long dstPort1 = link.dst().port().toLong();
+
+        for (Link link2: links) {
+            DeviceId srcDevice2 = link2.src().deviceId();
+            DeviceId dstDevice2 = link2.dst().deviceId();
+            long srcPort2 = link2.src().port().toLong();
+            long dstPort2 = link2.dst().port().toLong();
+
+            if (srcDevice1.toString().equals(srcDevice2.toString())
+                    && dstDevice1.toString().equals(dstDevice2.toString())
+                    && srcPort1 == srcPort2 && dstPort1 == dstPort2) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    private void getDFSPaths(DeviceId dstDeviceDeviceId, Path path, ArrayList<Path> paths) {
+        DeviceId rootDeviceDeviceId = rootDevice;
+        for (Link upstreamLink : upstreamLinks.get(dstDeviceDeviceId)) {
+            /* Deep clone the path object */
+            Path sofarPath;
+            ArrayList<Link> sofarLinks = new ArrayList<Link>();
+            if (path != null && !path.links().isEmpty()) {
+                sofarLinks.addAll(path.links());
+            }
+            sofarLinks.add(upstreamLink);
+            sofarPath = new DefaultPath(ProviderId.NONE, sofarLinks, 0);
+            if (upstreamLink.src().deviceId().equals(rootDeviceDeviceId)) {
+                paths.add(sofarPath);
+                return;
+            } else {
+                getDFSPaths(upstreamLink.src().deviceId(), sofarPath, paths);
+            }
+        }
+    }
+
+    /**
+     * Return root Device for the graph.
+     *
+     * @return root Device
+     */
+    public DeviceId getRootDevice() {
+        return rootDevice;
+    }
+
+    /**
+     * Return the computed ECMP paths from the root Device to a given Device in
+     * the network.
+     *
+     * @param targetDevice the target Device
+     * @return the list of ECMP Paths from the root Device to the target Device
+     */
+    public ArrayList<Path> getECMPPaths(DeviceId targetDevice) {
+        ArrayList<Path> pathArray = paths.get(targetDevice);
+        if (pathArray == null && deviceSearched.containsKey(
+                targetDevice)) {
+            pathArray = new ArrayList<>();
+            DeviceId sw = targetDevice;
+            getDFSPaths(sw, null, pathArray);
+            paths.put(targetDevice, pathArray);
+        }
+        return pathArray;
+    }
+
+    /**
+     * Return the complete info of the computed ECMP paths for each Device
+     * learned in multiple iterations from the root Device.
+     *
+     * @return the hash table of Devicees learned in multiple Dijkstra
+     *         iterations and corresponding ECMP paths to it from the root
+     *         Device
+     */
+    public HashMap<Integer, HashMap<DeviceId,
+            ArrayList<Path>>> getCompleteLearnedDeviceesAndPaths() {
+
+        HashMap<Integer, HashMap<DeviceId, ArrayList<Path>>> pathGraph = new
+                HashMap<Integer, HashMap<DeviceId, ArrayList<Path>>>();
+
+        for (Integer itrIndx : distanceDeviceMap.keySet()) {
+            HashMap<DeviceId, ArrayList<Path>> swMap = new
+                    HashMap<DeviceId, ArrayList<Path>>();
+            for (DeviceId sw : distanceDeviceMap.get(itrIndx)) {
+                swMap.put(sw, getECMPPaths(sw));
+            }
+            pathGraph.put(itrIndx, swMap);
+        }
+
+        return pathGraph;
+    }
+
+    /**
+     * Return the complete info of the computed ECMP paths for each Device
+     * learned in multiple iterations from the root Device.
+     *
+     * @return the hash table of Devicees learned in multiple Dijkstra
+     *         iterations and corresponding ECMP paths in terms of Devicees to
+     *         be traversed to it from the root Device
+     */
+    public HashMap<Integer, HashMap<DeviceId,
+            ArrayList<ArrayList<DeviceId>>>> getAllLearnedSwitchesAndVia() {
+
+        HashMap<Integer, HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>>> deviceViaMap =
+                new HashMap<Integer, HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>>>();
+
+        for (Integer itrIndx : distanceDeviceMap.keySet()) {
+            HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>> swMap =
+                    new HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>>();
+
+            for (DeviceId sw : distanceDeviceMap.get(itrIndx)) {
+                ArrayList<ArrayList<DeviceId>> swViaArray = new ArrayList<>();
+                for (Path path : getECMPPaths(sw)) {
+                    ArrayList<DeviceId> swVia = new ArrayList<>();
+                    for (Link link : path.links()) {
+                        if (link.src().deviceId().equals(rootDevice)) {
+                            /* No need to add the root Device again in
+                             * the Via list
+                             */
+                            continue;
+                        }
+                        swVia.add(link.src().deviceId());
+                    }
+                    swViaArray.add(swVia);
+                }
+                swMap.put(sw, swViaArray);
+            }
+            deviceViaMap.put(itrIndx, swMap);
+        }
+        return deviceViaMap;
+    }
+
+
+    private Link copyDefaultLink(Link link) {
+        DefaultLink src = (DefaultLink) link;
+        DefaultLink defaultLink = new DefaultLink(src.providerId(), src.src(),
+                src.dst(), src.type(), src.annotations());
+
+        return defaultLink;
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder sBuilder = new StringBuilder();
+        for (Device device: srManager.deviceService.getDevices()) {
+            if (device.id() != rootDevice) {
+                sBuilder.append("Paths from" + rootDevice + " to " + device.id() + "\r\n");
+                ArrayList<Path> paths = getECMPPaths(device.id());
+                if (paths != null) {
+                    for (Path path : paths) {
+                        for (Link link : path.links()) {
+                            sBuilder.append(" : " + link.src() + " -> " + link.dst());
+                        }
+                    }
+                }
+            }
+        }
+        return sBuilder.toString();
+    }
+}
+
diff --git a/src/main/java/org/onosproject/segmentrouting/IcmpHandler.java b/src/main/java/org/onosproject/segmentrouting/IcmpHandler.java
new file mode 100644
index 0000000..3bfb888
--- /dev/null
+++ b/src/main/java/org/onosproject/segmentrouting/IcmpHandler.java
@@ -0,0 +1,167 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.segmentrouting;
+
+import java.nio.ByteBuffer;
+
+import org.onlab.packet.Ethernet;
+import org.onlab.packet.ICMP;
+import org.onlab.packet.IPv4;
+import org.onlab.packet.Ip4Address;
+import org.onlab.packet.IpPrefix;
+import org.onlab.packet.MPLS;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.flow.DefaultTrafficTreatment;
+import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.packet.DefaultOutboundPacket;
+import org.onosproject.net.packet.InboundPacket;
+import org.onosproject.net.packet.OutboundPacket;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+public class IcmpHandler {
+
+    private static Logger log = LoggerFactory.getLogger(IcmpHandler.class);
+    private SegmentRoutingManager srManager;
+    private NetworkConfigHandler config;
+
+    /**
+     * Creates an IcmpHandler object.
+     *
+     * @param srManager SegmentRoutingManager object
+     */
+    public IcmpHandler(SegmentRoutingManager srManager) {
+        this.srManager = srManager;
+        this.config = checkNotNull(srManager.networkConfigHandler);
+    }
+
+    /**
+     * Process incoming ICMP packet.
+     * If it is an ICMP request to router or known host, then sends an ICMP response.
+     * If it is an ICMP packet to known host and forward the packet to the host.
+     * If it is an ICMP packet to unknown host in a subnet, then sends an ARP request
+     * to the subnet.
+     *
+     * @param pkt
+     */
+    public void processPacketIn(InboundPacket pkt) {
+
+        Ethernet ethernet = pkt.parsed();
+        IPv4 ipv4 = (IPv4) ethernet.getPayload();
+
+        ConnectPoint connectPoint = pkt.receivedFrom();
+        DeviceId deviceId = connectPoint.deviceId();
+        Ip4Address destinationAddress =
+                Ip4Address.valueOf(ipv4.getDestinationAddress());
+        Ip4Address gatewayIpAddress = config.getGatewayIpAddress(deviceId);
+        IpPrefix routerIpPrefix = config.getRouterIpAddress(deviceId);
+        Ip4Address routerIpAddress = routerIpPrefix.getIp4Prefix().address();
+
+        // ICMP to the router IP or gateway IP
+        if (((ICMP) ipv4.getPayload()).getIcmpType() == ICMP.TYPE_ECHO_REQUEST &&
+                (destinationAddress.equals(routerIpAddress) ||
+                        gatewayIpAddress.equals(destinationAddress))) {
+            sendICMPResponse(ethernet, connectPoint);
+            // TODO: do we need to set the flow rule again ??
+
+        // ICMP for any known host
+        } else if (!srManager.hostService.getHostsByIp(destinationAddress).isEmpty()) {
+            srManager.ipHandler.forwardPackets(deviceId, destinationAddress);
+
+        // ICMP for an unknown host in the subnet of the router
+        } else if (config.inSameSubnet(deviceId, destinationAddress)) {
+            srManager.arpHandler.sendArpRequest(deviceId, destinationAddress, connectPoint);
+
+        // ICMP for an unknown host
+        } else {
+            log.debug("ICMP request for unknown host {} ", destinationAddress);
+            // Do nothing
+        }
+    }
+
+    private void sendICMPResponse(Ethernet icmpRequest, ConnectPoint outport) {
+
+        Ethernet icmpReplyEth = new Ethernet();
+
+        IPv4 icmpRequestIpv4 = (IPv4) icmpRequest.getPayload();
+        IPv4 icmpReplyIpv4 = new IPv4();
+
+        int destAddress = icmpRequestIpv4.getDestinationAddress();
+        icmpReplyIpv4.setDestinationAddress(icmpRequestIpv4.getSourceAddress());
+        icmpReplyIpv4.setSourceAddress(destAddress);
+        icmpReplyIpv4.setTtl((byte) 64);
+        icmpReplyIpv4.setChecksum((short) 0);
+
+        ICMP icmpReply = (ICMP) icmpRequestIpv4.getPayload().clone();
+        icmpReply.setIcmpType(ICMP.TYPE_ECHO_REPLY);
+        icmpReply.setIcmpCode(ICMP.SUBTYPE_ECHO_REPLY);
+        icmpReply.setChecksum((short) 0);
+
+        icmpReplyIpv4.setPayload(icmpReply);
+
+        icmpReplyEth.setPayload(icmpReplyIpv4);
+        icmpReplyEth.setEtherType(Ethernet.TYPE_IPV4);
+        icmpReplyEth.setDestinationMACAddress(icmpRequest.getSourceMACAddress());
+        icmpReplyEth.setSourceMACAddress(icmpRequest.getDestinationMACAddress());
+        icmpReplyEth.setVlanID(icmpRequest.getVlanID());
+
+        Ip4Address destIpAddress = Ip4Address.valueOf(icmpReplyIpv4.getDestinationAddress());
+        Ip4Address destRouterAddress = config.getDestinationRouterAddress(destIpAddress);
+        int sid = config.getMplsId(destRouterAddress);
+        if (sid < 0) {
+            log.warn("Cannot find the Segment ID for {}", destAddress);
+            return;
+        }
+
+        sendPacketOut(outport, icmpReplyEth, sid);
+
+    }
+
+    private void sendPacketOut(ConnectPoint outport, Ethernet payload, int sid) {
+
+        IPv4 ipPacket = (IPv4) payload.getPayload();
+        Ip4Address destIpAddress = Ip4Address.valueOf(ipPacket.getDestinationAddress());
+
+        if (sid == -1 || config.getMplsId(payload.getDestinationMAC()) == sid ||
+                config.inSameSubnet(outport.deviceId(), destIpAddress)) {
+            TrafficTreatment treatment = DefaultTrafficTreatment.builder().
+                    setOutput(outport.port()).build();
+            OutboundPacket packet = new DefaultOutboundPacket(outport.deviceId(),
+                    treatment, ByteBuffer.wrap(payload.serialize()));
+            srManager.packetService.emit(packet);
+        } else {
+            log.warn("Send a MPLS packet as a ICMP response");
+            TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+                    .setOutput(outport.port())
+                    .build();
+
+            payload.setEtherType(Ethernet.MPLS_UNICAST);
+            MPLS mplsPkt = new MPLS();
+            mplsPkt.setLabel(sid);
+            mplsPkt.setTtl(((IPv4) payload.getPayload()).getTtl());
+            mplsPkt.setPayload(payload.getPayload());
+            payload.setPayload(mplsPkt);
+
+            OutboundPacket packet = new DefaultOutboundPacket(outport.deviceId(),
+                    treatment, ByteBuffer.wrap(payload.serialize()));
+
+            srManager.packetService.emit(packet);
+        }
+    }
+}
diff --git a/src/main/java/org/onosproject/segmentrouting/IpHandler.java b/src/main/java/org/onosproject/segmentrouting/IpHandler.java
new file mode 100644
index 0000000..cca8692
--- /dev/null
+++ b/src/main/java/org/onosproject/segmentrouting/IpHandler.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.segmentrouting;
+
+import org.onlab.packet.Ethernet;
+import org.onlab.packet.IPv4;
+import org.onlab.packet.Ip4Address;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.Host;
+import org.onosproject.net.flow.DefaultTrafficTreatment;
+import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.packet.DefaultOutboundPacket;
+import org.onosproject.net.packet.InboundPacket;
+import org.onosproject.net.packet.OutboundPacket;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.nio.ByteBuffer;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentLinkedQueue;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+public class IpHandler {
+
+    private static Logger log = LoggerFactory.getLogger(IpHandler.class);
+    private SegmentRoutingManager srManager;
+    private NetworkConfigHandler config;
+    private ConcurrentHashMap<Ip4Address, ConcurrentLinkedQueue<IPv4>> ipPacketQueue;
+
+    /**
+     * Creates an IpHandler object.
+     *
+     * @param srManager SegmentRoutingManager object
+     */
+    public IpHandler(SegmentRoutingManager srManager) {
+        this.srManager = srManager;
+        this.config = checkNotNull(srManager.networkConfigHandler);
+        ipPacketQueue = new ConcurrentHashMap<Ip4Address, ConcurrentLinkedQueue<IPv4>>();
+    }
+
+    /**
+     * Processes incoming IP packets.
+     *
+     * If it is an IP packet for known host, then forward it to the host.
+     * If it is an IP packet for unknown host in subnet, then send an ARP request
+     * to the subnet.
+     *
+     * @param pkt incoming packet
+     */
+    public void processPacketIn(InboundPacket pkt) {
+        Ethernet ethernet = pkt.parsed();
+        IPv4 ipv4 = (IPv4) ethernet.getPayload();
+
+        ConnectPoint connectPoint = pkt.receivedFrom();
+        DeviceId deviceId = connectPoint.deviceId();
+        Ip4Address destinationAddress =
+                Ip4Address.valueOf(ipv4.getDestinationAddress());
+
+        // IP packet for know hosts
+        if (!srManager.hostService.getHostsByIp(destinationAddress).isEmpty()) {
+            forwardPackets(deviceId, destinationAddress);
+
+        // IP packet for unknown host in the subnet of the router
+        } else if (config.inSameSubnet(deviceId, destinationAddress)) {
+            srManager.arpHandler.sendArpRequest(deviceId, destinationAddress, connectPoint);
+
+        // IP packets for unknown host
+        } else {
+            log.debug("ICMP request for unknown host {} which is not in the subnet",
+                    destinationAddress);
+            // Do nothing
+        }
+    }
+
+    /**
+     * Adds the IP packet to a buffer.
+     * The packets are forwarded to corresponding destination when the destination
+     * MAC address is known via ARP response.
+     *
+     * @param ipPacket IP packet to add to the buffer
+     */
+    public void addToPacketBuffer(IPv4 ipPacket) {
+
+        // Better not buffer TPC packets due to out-of-order packet transfer
+        if (ipPacket.getProtocol() == IPv4.PROTOCOL_TCP) {
+            return;
+        }
+
+        Ip4Address destIpAddress = Ip4Address.valueOf(ipPacket.getDestinationAddress());
+
+        if (ipPacketQueue.get(destIpAddress) == null) {
+            ConcurrentLinkedQueue<IPv4> queue = new ConcurrentLinkedQueue<IPv4>();
+            queue.add(ipPacket);
+            ipPacketQueue.put(destIpAddress, queue);
+        } else {
+            ipPacketQueue.get(destIpAddress).add(ipPacket);
+        }
+    }
+
+    /**
+     * Forwards IP packets in the buffer to the destination IP address.
+     * It is called when the controller finds the destination MAC address
+     * via ARP responsees.
+     *
+     * @param deviceId switch device ID
+     * @param destIpAddress destination IP address
+     */
+    public void forwardPackets(DeviceId deviceId, Ip4Address destIpAddress) {
+        for (IPv4 ipPacket : ipPacketQueue.get(destIpAddress)) {
+            Ip4Address destAddress = Ip4Address.valueOf(ipPacket.getDestinationAddress());
+            if (ipPacket != null && config.inSameSubnet(deviceId, destAddress)) {
+                ipPacket.setTtl((byte) (ipPacket.getTtl() - 1));
+                ipPacket.setChecksum((short) 0);
+                for (Host dest: srManager.hostService.getHostsByIp(destIpAddress)) {
+                    Ethernet eth = new Ethernet();
+                    eth.setDestinationMACAddress(dest.mac());
+                    eth.setSourceMACAddress(config.getRouterMacAddress(
+                            deviceId));
+                    eth.setEtherType(Ethernet.TYPE_IPV4);
+                    eth.setPayload(ipPacket);
+
+                    TrafficTreatment treatment = DefaultTrafficTreatment.builder().
+                            setOutput(dest.location().port()).build();
+                    OutboundPacket packet = new DefaultOutboundPacket(deviceId,
+                            treatment, ByteBuffer.wrap(eth.serialize()));
+                    srManager.packetService.emit(packet);
+                }
+            }
+        }
+    }
+}
diff --git a/src/main/java/org/onosproject/segmentrouting/NetworkConfigHandler.java b/src/main/java/org/onosproject/segmentrouting/NetworkConfigHandler.java
new file mode 100644
index 0000000..2cb5867
--- /dev/null
+++ b/src/main/java/org/onosproject/segmentrouting/NetworkConfigHandler.java
@@ -0,0 +1,206 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.segmentrouting;
+
+import com.google.common.collect.Lists;
+import org.onlab.packet.Ip4Address;
+import org.onlab.packet.Ip4Prefix;
+import org.onlab.packet.IpPrefix;
+import org.onlab.packet.MacAddress;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.Link;
+import org.onosproject.net.PortNumber;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.net.URI;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * This class is temporary class and used only for test.
+ * It will be replaced with "real" Network Config Manager.
+ */
+
+public class NetworkConfigHandler {
+
+    private static Logger log = LoggerFactory.getLogger(NetworkConfigHandler.class);
+    private SegmentRoutingManager srManager;
+    private DeviceConfiguration deviceConfig = new DeviceConfiguration();
+
+    public NetworkConfigHandler(SegmentRoutingManager srManager) {
+        this.srManager = srManager;
+    }
+
+    public Ip4Address getGatewayIpAddress(DeviceId deviceId) {
+
+        if (deviceId.uri().equals(URI.create("of:0000000000000001"))) {
+            return Ip4Address.valueOf("10.0.1.128");
+        } else if (deviceId.uri().equals(URI.create("of:0000000000000006"))) {
+            return Ip4Address.valueOf("7.7.7.128");
+        }
+
+        log.warn("No gateway Ip address was found for {}", deviceId);
+        return Ip4Address.valueOf("0.0.0.0");
+    }
+
+    public IpPrefix getRouterIpAddress(DeviceId deviceId) {
+
+        return IpPrefix.valueOf(deviceConfig.getRouterIp(deviceId), 32);
+    }
+
+    public MacAddress getRouterMacAddress(DeviceId deviceId) {
+        return deviceConfig.getDeviceMac(deviceId);
+    }
+
+    public boolean inSameSubnet(DeviceId deviceId, Ip4Address destIp) {
+
+        String subnetInfo = getSubnetInfo(deviceId);
+        if (subnetInfo == null) {
+            return false;
+        }
+
+        IpPrefix prefix = IpPrefix.valueOf(subnetInfo);
+        if (prefix.contains(destIp)) {
+            return true;
+        }
+
+        return false;
+    }
+
+    public boolean inSameSubnet(Ip4Address address, int sid) {
+        DeviceId deviceId = deviceConfig.getDeviceId(sid);
+        if (deviceId == null) {
+            log.warn("Cannot find a device for SID {}", sid);
+            return false;
+        }
+
+        String subnetInfo = getSubnetInfo(deviceId);
+        if (subnetInfo == null) {
+            log.warn("Cannot find the subnet info for {}", deviceId);
+            return false;
+        }
+
+        Ip4Prefix subnet = Ip4Prefix.valueOf(subnetInfo);
+        if (subnet.contains(address)) {
+            return true;
+        }
+
+        return false;
+
+    }
+
+    public String getSubnetInfo(DeviceId deviceId) {
+        // TODO : supports multiple subnet
+        if (deviceId.uri().equals(URI.create("of:0000000000000001"))) {
+            return "10.0.1.1/24";
+        } else if (deviceId.uri().equals(URI.create("of:0000000000000006"))) {
+            return "7.7.7.7/24";
+        } else {
+            log.error("Switch {} is not an edge router", deviceId);
+            return null;
+        }
+    }
+
+    public int getMplsId(DeviceId deviceId) {
+        return deviceConfig.getSegmentId(deviceId);
+    }
+
+    public int getMplsId(MacAddress mac) {
+        return deviceConfig.getSegmentId(mac);
+    }
+
+    public int getMplsId(Ip4Address address) {
+        return deviceConfig.getSegmentId(address);
+    }
+
+    public boolean isEcmpNotSupportedInTransit(DeviceId deviceId) {
+        return false;
+    }
+
+    public boolean isTransitRouter(DeviceId deviceId) {
+        return true;
+    }
+
+
+    public boolean isEdgeRouter(DeviceId deviceId) {
+        if (deviceId.uri().equals(URI.create("of:0000000000000001"))
+                || deviceId.uri().equals(URI.create("of:0000000000000006"))) {
+            return true;
+        }
+
+        return false;
+    }
+
+    private List<PortNumber> getPortsToNeighbors(DeviceId deviceId, List<DeviceId> fwdSws) {
+
+        List<PortNumber> portNumbers = Lists.newArrayList();
+
+        Set<Link> links = srManager.linkService.getDeviceEgressLinks(deviceId);
+        for (Link link: links) {
+            for (DeviceId swId: fwdSws) {
+                if (link.dst().deviceId().equals(swId)) {
+                    portNumbers.add(link.src().port());
+                    break;
+                }
+            }
+        }
+
+        return portNumbers;
+    }
+
+    public List<PortNumber> getPortsToDevice(DeviceId deviceId) {
+        List<PortNumber> portNumbers = Lists.newArrayList();
+
+        Set<Link> links = srManager.linkService.getDeviceEgressLinks(deviceId);
+        for (Link link: links) {
+            if (link.dst().deviceId().equals(deviceId)) {
+                portNumbers.add(link.src().port());
+            }
+        }
+
+        return portNumbers;
+    }
+
+
+    public Ip4Address getDestinationRouterAddress(Ip4Address destIpAddress) {
+        // TODO: need to check the subnet info
+        if (destIpAddress.toString().equals("10.0.1.1")) {
+            return Ip4Address.valueOf("192.168.0.1");
+        } else if (destIpAddress.toString().equals("7.7.7.7")) {
+            return Ip4Address.valueOf("192.168.0.6");
+        } else {
+            log.warn("No router was found for {}", destIpAddress);
+            return null;
+        }
+
+    }
+
+    public DeviceId getDeviceId(Ip4Address ip4Address) {
+        return deviceConfig.getDeviceId(ip4Address);
+    }
+
+    public MacAddress getRouterMac(Ip4Address targetAddress) {
+        if (targetAddress.toString().equals("10.0.1.128")) {
+            return MacAddress.valueOf("00:00:00:00:00:01");
+        } else if (targetAddress.toString().equals("7.7.7.128")) {
+            return MacAddress.valueOf("00:00:00:00:00:06");
+        } else {
+            log.warn("Cannot find a router for {}", targetAddress);
+            return null;
+        }
+    }
+}
diff --git a/src/main/java/org/onosproject/segmentrouting/RoutingRulePopulator.java b/src/main/java/org/onosproject/segmentrouting/RoutingRulePopulator.java
new file mode 100644
index 0000000..063517e
--- /dev/null
+++ b/src/main/java/org/onosproject/segmentrouting/RoutingRulePopulator.java
@@ -0,0 +1,402 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.segmentrouting;
+
+import org.onlab.packet.Ethernet;
+import org.onlab.packet.Ip4Address;
+import org.onlab.packet.IpPrefix;
+import org.onlab.packet.MacAddress;
+
+import org.onlab.packet.MplsLabel;
+import org.onosproject.grouphandler.NeighborSet;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.Link;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.flow.DefaultFlowRule;
+import org.onosproject.net.flow.DefaultTrafficSelector;
+import org.onosproject.net.flow.DefaultTrafficTreatment;
+import org.onosproject.net.flow.FlowRule;
+import org.onosproject.net.flow.TrafficSelector;
+import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.group.DefaultGroupKey;
+import org.onosproject.net.group.Group;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Set;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+public class RoutingRulePopulator {
+
+    private static final Logger log = LoggerFactory.getLogger(RoutingRulePopulator.class);
+
+    private SegmentRoutingManager srManager;
+    private NetworkConfigHandler config;
+
+    /**
+     * Creates a RoutingRulePopulator object.
+     *
+     * @param srManager
+     */
+    public RoutingRulePopulator(SegmentRoutingManager srManager) {
+        this.srManager = srManager;
+        this.config = checkNotNull(srManager.networkConfigHandler);
+    }
+
+    /**
+     * Populates IP flow rules for specific hosts directly connected to the switch.
+     *
+     * @param deviceId switch ID to set the rules
+     * @param hostIp host IP address
+     * @param hostMac host MAC address
+     * @param outPort port where the host is connected
+     */
+    public void populateIpRuleForHost(DeviceId deviceId, Ip4Address hostIp,
+                                      MacAddress hostMac, PortNumber outPort) {
+        TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder();
+        TrafficTreatment.Builder tbuilder = DefaultTrafficTreatment.builder();
+
+        sbuilder.matchIPDst(IpPrefix.valueOf(hostIp, 32));
+        sbuilder.matchEthType(Ethernet.TYPE_IPV4);
+
+        tbuilder.setEthDst(hostMac)
+                .setEthSrc(config.getRouterMacAddress(deviceId))
+                .setOutput(outPort);
+
+        TrafficTreatment treatment = tbuilder.build();
+        TrafficSelector selector = sbuilder.build();
+
+        FlowRule f = new DefaultFlowRule(deviceId, selector, treatment, 100,
+                srManager.appId, 600, false, FlowRule.Type.IP);
+
+        srManager.flowRuleService.applyFlowRules(f);
+        log.debug("Flow rule {} is set to switch {}", f, deviceId);
+    }
+
+    /**
+     * Populates IP flow rules for the subnets of the destination router.
+     *
+     * @param deviceId switch ID to set the rules
+     * @param subnetInfo subnet information
+     * @param destSw destination switch ID
+     * @param nextHops next hop switch ID list
+     * @return true if all rules are set successfully, false otherwise
+     */
+    public boolean populateIpRuleForSubnet(DeviceId deviceId, String subnetInfo,
+                                           DeviceId destSw, Set<DeviceId> nextHops) {
+
+        List<IpPrefix> subnets = extractSubnet(subnetInfo);
+        for (IpPrefix subnet: subnets) {
+            if (!populateIpRuleForRouter(deviceId, subnet, destSw, nextHops)) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * Populates IP flow rules for the router IP address.
+     *
+     * @param deviceId device ID to set the rules
+     * @param ipPrefix the IP address of the destination router
+     * @param destSw device ID of the destination router
+     * @param nextHops next hop switch ID list
+     * @return true if all rules are set successfully, false otherwise
+     */
+    public boolean populateIpRuleForRouter(DeviceId deviceId, IpPrefix ipPrefix,
+                                           DeviceId destSw, Set<DeviceId> nextHops) {
+
+        TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder();
+        TrafficTreatment.Builder tbuilder = DefaultTrafficTreatment.builder();
+
+        sbuilder.matchIPDst(ipPrefix);
+        sbuilder.matchEthType(Ethernet.TYPE_IPV4);
+
+        NeighborSet ns = null;
+
+        //If the next hop is the same as the final destination, then MPLS label is not set.
+        if (nextHops.size() == 1 && nextHops.toArray()[0].equals(destSw)) {
+            tbuilder.decNwTtl();
+            ns = new NeighborSet(nextHops);
+        } else {
+            tbuilder.copyTtlOut();
+            ns = new NeighborSet(nextHops, config.getMplsId(destSw));
+        }
+
+        DefaultGroupKey groupKey = (DefaultGroupKey) srManager.getGroupKey(ns);
+        if (groupKey == null) {
+            log.warn("Group key is not found for ns {}", ns);
+            return false;
+        }
+        Group group = srManager.groupService.getGroup(deviceId, groupKey);
+        if (group != null) {
+            tbuilder.group(group.id());
+        } else {
+            log.warn("No group found for NeighborSet {} from {} to {}",
+                    ns, deviceId, destSw);
+            return false;
+        }
+
+        TrafficTreatment treatment = tbuilder.build();
+        TrafficSelector selector = sbuilder.build();
+
+        FlowRule f = new DefaultFlowRule(deviceId, selector, treatment, 100,
+                srManager.appId, 600, false, FlowRule.Type.IP);
+
+        srManager.flowRuleService.applyFlowRules(f);
+        log.debug("IP flow rule {} is set to switch {}", f, deviceId);
+
+        return true;
+    }
+
+
+    /**
+     * Populates MPLS flow rules to all transit routers.
+     *
+     * @param deviceId device ID of the switch to set the rules
+     * @param destSwId destination switch device ID
+     * @param nextHops next hops switch ID list
+     * @return true if all rules are set successfully, false otherwise
+     */
+    public boolean populateMplsRule(DeviceId deviceId, DeviceId destSwId, Set<DeviceId> nextHops) {
+
+        TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder();
+        Collection<TrafficTreatment> treatments = new ArrayList<>();
+
+        // TODO Handle the case of Bos == false
+        sbuilder.matchMplsLabel(MplsLabel.mplsLabel(config.getMplsId(destSwId)));
+        sbuilder.matchEthType(Ethernet.MPLS_UNICAST);
+
+        //If the next hop is the destination router, do PHP
+        if (nextHops.size() == 1 && destSwId.equals(nextHops.toArray()[0])) {
+            TrafficTreatment treatmentBos =
+                    getMplsTreatment(deviceId, destSwId, nextHops, true, true);
+            TrafficTreatment treatment =
+                    getMplsTreatment(deviceId, destSwId, nextHops, true, false);
+            if (treatmentBos != null) {
+                treatments.add(treatmentBos);
+            } else {
+                log.warn("Failed to set MPLS rules.");
+                return false;
+            }
+        } else {
+            TrafficTreatment treatmentBos =
+                    getMplsTreatment(deviceId, destSwId, nextHops, false, true);
+            TrafficTreatment treatment =
+                    getMplsTreatment(deviceId, destSwId, nextHops, false, false);
+
+            if (treatmentBos != null) {
+                treatments.add(treatmentBos);
+            } else {
+                log.warn("Failed to set MPLS rules.");
+                return false;
+            }
+        }
+
+        TrafficSelector selector = sbuilder.build();
+        for (TrafficTreatment treatment: treatments) {
+            FlowRule f = new DefaultFlowRule(deviceId, selector, treatment, 100,
+                    srManager.appId, 600, false, FlowRule.Type.MPLS);
+            srManager.flowRuleService.applyFlowRules(f);
+            log.debug("MPLS rule {} is set to {}", f, deviceId);
+        }
+
+        return true;
+    }
+
+
+    private TrafficTreatment getMplsTreatment(DeviceId deviceId, DeviceId destSw,
+                                             Set<DeviceId> nextHops,
+                                             boolean phpRequired, boolean isBos) {
+
+        TrafficTreatment.Builder tbuilder = DefaultTrafficTreatment.builder();
+
+        if (phpRequired) {
+            tbuilder.copyTtlIn();
+            if (isBos) {
+                tbuilder.popMpls(Ethernet.TYPE_IPV4)
+                        .decNwTtl();
+            } else {
+                tbuilder.popMpls(Ethernet.MPLS_UNICAST)
+                .decMplsTtl();
+            }
+        } else {
+            tbuilder.decMplsTtl();
+        }
+
+        if (config.isEcmpNotSupportedInTransit(deviceId)
+                && config.isTransitRouter(deviceId)) {
+            Link link = selectOneLink(deviceId, nextHops);
+            if (link == null) {
+                log.warn("No link from {} to {}", deviceId, nextHops);
+                return null;
+            }
+            tbuilder.setEthSrc(config.getRouterMacAddress(deviceId))
+                    .setEthDst(config.getRouterMacAddress(link.dst().deviceId()))
+                    .setOutput(link.src().port());
+        } else {
+            NeighborSet ns = new NeighborSet(nextHops);
+            DefaultGroupKey groupKey = (DefaultGroupKey) srManager.getGroupKey(ns);
+            if (groupKey == null) {
+                log.warn("Group key is not found for ns {}", ns);
+                return null;
+            }
+            Group group = srManager.groupService.getGroup(deviceId, groupKey);
+            if (group != null) {
+                tbuilder.group(group.id());
+            } else {
+                log.warn("No group found for ns {} key {} in {}", ns,
+                        srManager.getGroupKey(ns), deviceId);
+                return null;
+            }
+        }
+
+        return tbuilder.build();
+    }
+
+    /**
+     * Populates VLAN flows rules.
+     * All packets are forwarded to TMAC table.
+     *
+     * @param deviceId switch ID to set the rules
+     */
+    public void populateTableVlan(DeviceId deviceId) {
+        TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder();
+        TrafficTreatment.Builder tbuilder = DefaultTrafficTreatment.builder();
+
+        tbuilder.transition(FlowRule.Type.ETHER);
+
+        TrafficTreatment treatment = tbuilder.build();
+        TrafficSelector selector = sbuilder.build();
+
+        FlowRule f = new DefaultFlowRule(deviceId, selector, treatment, 100,
+                srManager.appId, 600, false, FlowRule.Type.VLAN);
+
+        srManager.flowRuleService.applyFlowRules(f);
+
+        log.debug("Vlan flow rule {} is set to switch {}", f, deviceId);
+    }
+
+    /**
+     * Populates TMAC table rules.
+     * IP packets are forwarded to IP table.
+     * MPLS packets are forwarded to MPLS table.
+     *
+     * @param deviceId switch ID to set the rules
+     */
+    public void populateTableTMac(DeviceId deviceId) {
+
+        // flow rule for IP packets
+        TrafficSelector selectorIp = DefaultTrafficSelector.builder()
+                .matchEthType(Ethernet.TYPE_IPV4)
+                .matchEthDst(config.getRouterMacAddress(deviceId))
+                .build();
+        TrafficTreatment treatmentIp = DefaultTrafficTreatment.builder()
+                .transition(FlowRule.Type.IP)
+                .build();
+
+        FlowRule flowIp = new DefaultFlowRule(deviceId, selectorIp, treatmentIp, 100,
+                srManager.appId, 600, false, FlowRule.Type.ETHER);
+
+        srManager.flowRuleService.applyFlowRules(flowIp);
+
+        // flow rule for MPLS packets
+        TrafficSelector selectorMpls = DefaultTrafficSelector.builder()
+                .matchEthType(Ethernet.MPLS_UNICAST)
+                .matchEthDst(config.getRouterMacAddress(deviceId))
+                .build();
+        TrafficTreatment treatmentMpls = DefaultTrafficTreatment.builder()
+                .transition(FlowRule.Type.MPLS)
+                .build();
+
+        FlowRule flowMpls = new DefaultFlowRule(deviceId, selectorMpls, treatmentMpls, 100,
+                srManager.appId, 600, false, FlowRule.Type.ETHER);
+
+        srManager.flowRuleService.applyFlowRules(flowMpls);
+
+    }
+
+    /**
+     * Populates a table miss entry.
+     *
+     * @param deviceId switch ID to set rules
+     * @param tableToAdd table to set the rules
+     * @param toControllerNow flag to send packets to controller immediately
+     * @param toControllerWrite flag to send packets to controller at the end of pipeline
+     * @param toTable flag to send packets to a specific table
+     * @param tableToSend table type to send packets when the toTable flag is set
+     */
+    public void populateTableMissEntry(DeviceId deviceId, FlowRule.Type tableToAdd, boolean toControllerNow,
+                                       boolean toControllerWrite,
+                                       boolean toTable, FlowRule.Type tableToSend) {
+        // TODO: Change arguments to EnumSet
+        TrafficSelector selector = DefaultTrafficSelector.builder()
+                .build();
+        TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
+
+        if (toControllerNow) {
+            tBuilder.setOutput(PortNumber.CONTROLLER);
+        }
+
+        if (toControllerWrite) {
+            tBuilder.deferred().setOutput(PortNumber.CONTROLLER);
+        }
+
+        if (toTable) {
+            tBuilder.transition(tableToSend);
+        }
+
+        FlowRule flow = new DefaultFlowRule(deviceId, selector, tBuilder.build(), 0,
+                srManager.appId, 600, false, tableToAdd);
+
+        srManager.flowRuleService.applyFlowRules(flow);
+
+    }
+
+
+    private List<IpPrefix> extractSubnet(String subnetInfo) {
+        List<IpPrefix> subnetIpPrefixes = new ArrayList<>();
+
+        // TODO: refactoring required depending on the format of the subnet info
+        IpPrefix prefix = IpPrefix.valueOf(subnetInfo);
+        if (prefix == null) {
+            log.error("Wrong ip prefix type {}", subnetInfo);
+        } else {
+            subnetIpPrefixes.add(prefix);
+        }
+
+        return subnetIpPrefixes;
+    }
+
+    private Link selectOneLink(DeviceId srcId, Set<DeviceId> destIds) {
+
+        Set<Link> links = srManager.linkService.getDeviceEgressLinks(srcId);
+        DeviceId destId = (DeviceId) destIds.toArray()[0];
+        for (Link link: links) {
+            if (link.dst().deviceId().equals(destId)) {
+                return link;
+            }
+        }
+
+        return null;
+    }
+
+}
diff --git a/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java b/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java
new file mode 100644
index 0000000..a7a3b11
--- /dev/null
+++ b/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java
@@ -0,0 +1,342 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.segmentrouting;
+
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.onlab.packet.Ethernet;
+import org.onlab.packet.IPv4;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreService;
+import org.onosproject.event.Event;
+import org.onosproject.grouphandler.DefaultGroupHandler;
+import org.onosproject.grouphandler.NeighborSet;
+import org.onosproject.mastership.MastershipService;
+import org.onosproject.net.Device;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.Link;
+import org.onosproject.net.MastershipRole;
+import org.onosproject.net.Port;
+import org.onosproject.net.device.DeviceEvent;
+import org.onosproject.net.device.DeviceListener;
+import org.onosproject.net.device.DeviceService;
+import org.onosproject.net.flow.FlowRuleService;
+import org.onosproject.net.group.Group;
+import org.onosproject.net.group.GroupEvent;
+import org.onosproject.net.group.GroupKey;
+import org.onosproject.net.group.GroupListener;
+import org.onosproject.net.group.GroupService;
+import org.onosproject.net.host.HostService;
+import org.onosproject.net.intent.IntentService;
+import org.onosproject.net.link.LinkEvent;
+import org.onosproject.net.link.LinkListener;
+import org.onosproject.net.link.LinkService;
+import org.onosproject.net.packet.InboundPacket;
+import org.onosproject.net.packet.PacketContext;
+import org.onosproject.net.packet.PacketProcessor;
+import org.onosproject.net.packet.PacketService;
+import org.onosproject.net.topology.TopologyService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
+
+@SuppressWarnings("ALL")
+@Component(immediate = true)
+public class SegmentRoutingManager {
+
+    private static Logger log = LoggerFactory.getLogger(SegmentRoutingManager.class);
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected CoreService coreService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected TopologyService topologyService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected PacketService packetService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected IntentService intentService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected HostService hostService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected DeviceService deviceService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected FlowRuleService flowRuleService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected LinkService linkService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected GroupService groupService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected MastershipService mastershipService;
+
+    protected NetworkConfigHandler networkConfigHandler = new NetworkConfigHandler(this);
+    protected ArpHandler arpHandler = new ArpHandler(this);
+    protected IcmpHandler icmpHandler = new IcmpHandler(this);
+    protected IpHandler ipHandler = new IpHandler(this);
+    protected RoutingRulePopulator routingRulePopulator = new RoutingRulePopulator(this);
+    protected ApplicationId appId;
+
+    private DefaultRoutingHandler defaultRoutingHandler = new DefaultRoutingHandler(this);
+    private DeviceConfiguration deviceConfiguration = new DeviceConfiguration();
+    private InternalPacketProcessor processor = new InternalPacketProcessor();
+    private InternalEventHandler eventHandler = new InternalEventHandler();
+
+    private ScheduledExecutorService executorService = Executors.newScheduledThreadPool(1);
+
+    private static ScheduledFuture<?> eventHandlerFuture = null;
+    private ConcurrentLinkedQueue<Event> eventQueue = new ConcurrentLinkedQueue<Event>();
+    private Map<DeviceId, DefaultGroupHandler> groupHandlerMap
+            = new ConcurrentHashMap<DeviceId, DefaultGroupHandler>();
+
+    private static int numOfEvents = 0;
+    private static int numOfHandlerExecution = 0;
+    private static int numOfHandlerScheduled = 0;
+
+    @Activate
+    protected void activate() {
+        appId = coreService.registerApplication("org.onosproject.segmentrouting");
+        packetService.addProcessor(processor, PacketProcessor.ADVISOR_MAX + 2);
+        linkService.addListener(new InternalLinkListener());
+        groupService.addListener(new InternalGroupListener());
+        deviceService.addListener(new InternalDeviceListener());
+
+        for (Device device: deviceService.getDevices()) {
+            if (mastershipService.
+                    getLocalRole(device.id()) == MastershipRole.MASTER) {
+                DefaultGroupHandler groupHandler =
+                    DefaultGroupHandler.createGroupHandler(device.id(),
+                            appId, deviceConfiguration, linkService, groupService);
+                groupHandler.createGroups();
+                groupHandlerMap.put(device.id(), groupHandler);
+                log.debug("Initiating default group handling for {}", device.id());
+
+                defaultRoutingHandler.startPopulationProcess();
+            } else {
+                log.debug("Activate: Local role {} "
+                                + "is not MASTER for device {}",
+                        mastershipService.
+                                getLocalRole(device.id()),
+                        device.id());
+            }
+        }
+
+        log.info("Started");
+    }
+
+    @Deactivate
+    protected void deactivate() {
+        packetService.removeProcessor(processor);
+        processor = null;
+        log.info("Stopped");
+    }
+
+    /**
+     * Returns the GrouopKey object for the device and the NighborSet given.
+     *
+     * @param ns NeightborSet object for the GroupKey
+     * @return GroupKey object for the NeighborSet
+     */
+    public GroupKey getGroupKey(NeighborSet ns) {
+
+        for (DefaultGroupHandler groupHandler: groupHandlerMap.values()) {
+            return groupHandler.getGroupKey(ns);
+        }
+
+        return null;
+    }
+
+    private class InternalPacketProcessor implements PacketProcessor {
+
+        @Override
+        public void process(PacketContext context) {
+
+            if (context.isHandled()) {
+                return;
+            }
+
+            InboundPacket pkt = context.inPacket();
+            Ethernet ethernet = pkt.parsed();
+
+            if (ethernet.getEtherType() == Ethernet.TYPE_ARP) {
+                arpHandler.processPacketIn(pkt);
+            } else if (ethernet.getEtherType() == Ethernet.TYPE_IPV4) {
+                IPv4 ipPacket = (IPv4) ethernet.getPayload();
+                ipHandler.addToPacketBuffer(ipPacket);
+                if (ipPacket.getProtocol() == IPv4.PROTOCOL_ICMP) {
+                    icmpHandler.processPacketIn(pkt);
+                } else {
+                    ipHandler.processPacketIn(pkt);
+                }
+            }
+        }
+    }
+
+    private class InternalLinkListener implements LinkListener {
+        @Override
+        public void event(LinkEvent event) {
+            if (event.type() == LinkEvent.Type.LINK_ADDED ||
+                    event.type() == LinkEvent.Type.LINK_REMOVED) {
+                scheduleEventHandlerIfNotScheduled(event);
+            }
+        }
+    }
+
+    private class InternalDeviceListener implements DeviceListener {
+
+        @Override
+        public void event(DeviceEvent event) {
+            if (mastershipService.
+                    getLocalRole(event.subject().id()) != MastershipRole.MASTER) {
+                log.debug("Local role {} is not MASTER for device {}",
+                        mastershipService.
+                                getLocalRole(event.subject().id()),
+                        event.subject().id());
+                return;
+            }
+
+            switch (event.type()) {
+            case DEVICE_ADDED:
+            case PORT_REMOVED:
+                scheduleEventHandlerIfNotScheduled(event);
+                break;
+            default:
+            }
+        }
+    }
+
+    private class InternalGroupListener implements GroupListener {
+
+        @Override
+        public void event(GroupEvent event) {
+            switch (event.type()) {
+                case GROUP_ADDED:
+                    scheduleEventHandlerIfNotScheduled(event);
+                    break;
+                case GROUP_ADD_REQUESTED:
+                    log.info("Group add requested");
+                    break;
+                case GROUP_UPDATED:
+                    break;
+                default:
+                    log.warn("Unhandled group event type: {}", event.type());
+            }
+        }
+    }
+
+    private void scheduleEventHandlerIfNotScheduled(Event event) {
+
+        eventQueue.add(event);
+        numOfEvents++;
+        if (eventHandlerFuture == null ||
+                eventHandlerFuture.isDone()) {
+            eventHandlerFuture = executorService.schedule(eventHandler,
+                    100, TimeUnit.MILLISECONDS);
+            numOfHandlerScheduled++;
+        }
+
+        log.trace("numOfEvents {}, numOfEventHanlderScheduled {}", numOfEvents,
+                numOfHandlerScheduled);
+
+    }
+
+    private class InternalEventHandler implements Runnable {
+
+        public void run() {
+            numOfHandlerExecution++;
+            while (!eventQueue.isEmpty()) {
+                Event event = eventQueue.poll();
+                if (event.type() == LinkEvent.Type.LINK_ADDED) {
+                    processLinkAdded((Link) event.subject());
+                } else if (event.type() == LinkEvent.Type.LINK_REMOVED) {
+                    processLinkRemoved((Link) event.subject());
+                } else if (event.type() == GroupEvent.Type.GROUP_ADDED) {
+                    processGroupAdded((Group) event.subject());
+                } else if (event.type() == DeviceEvent.Type.DEVICE_ADDED) {
+                    processDeviceAdded((Device) event.subject());
+                } else if (event.type() == DeviceEvent.Type.PORT_REMOVED) {
+                    processPortRemoved((Device) event.subject(),
+                            ((DeviceEvent) event).port());
+                } else {
+                    log.warn("Unhandled event type: {}", event.type());
+                }
+            }
+            log.debug("numOfHandlerExecution {} numOfEventHanlderScheduled {} numOfEvents {}",
+                    numOfHandlerExecution, numOfHandlerScheduled, numOfEvents);
+        }
+    }
+
+
+
+    private void processLinkAdded(Link link) {
+        log.debug("A new link {} was added", link.toString());
+
+        if (mastershipService.
+                getLocalRole(link.src().deviceId()) == MastershipRole.MASTER) {
+            DefaultGroupHandler groupHandler =
+                    groupHandlerMap.get(link.src().deviceId());
+            if (groupHandler != null) {
+                groupHandler.linkUp(link);
+            }
+        }
+        defaultRoutingHandler.startPopulationProcess();
+    }
+
+    private void processLinkRemoved(Link link) {
+        log.debug("A link {} was removed", link.toString());
+        defaultRoutingHandler.startPopulationProcess();
+    }
+
+
+    private void processGroupAdded(Group group) {
+        log.debug("A new group with ID {} was added", group.id());
+        defaultRoutingHandler.resumePopulationProcess();
+    }
+
+    private void processDeviceAdded(Device device) {
+        log.debug("A new device with ID {} was added", device.id());
+        defaultRoutingHandler.populateTtpRules(device.id());
+        DefaultGroupHandler dgh = DefaultGroupHandler.createGroupHandler(
+                device.id(), appId, new DeviceConfiguration(), linkService, groupService);
+        dgh.createGroups();
+        groupHandlerMap.put(device.id(), dgh);
+    }
+
+    private void processPortRemoved(Device device, Port port) {
+        log.debug("Port {} was removed", port.toString());
+        DefaultGroupHandler groupHandler =
+                groupHandlerMap.get(device.id());
+        if (groupHandler != null) {
+            groupHandler.portDown(port.number());
+        }
+    }
+}