ONOS-685: Network Configuration Manager support for Segment Routing application

Change-Id: Ia15bfd24559dd5542633c8b76d500b2d31362340
diff --git a/src/main/java/org/onosproject/segmentrouting/ArpHandler.java b/src/main/java/org/onosproject/segmentrouting/ArpHandler.java
index 5b94518..b696525 100644
--- a/src/main/java/org/onosproject/segmentrouting/ArpHandler.java
+++ b/src/main/java/org/onosproject/segmentrouting/ArpHandler.java
@@ -19,7 +19,6 @@
 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;
@@ -36,7 +35,7 @@
 import org.slf4j.LoggerFactory;
 
 import java.nio.ByteBuffer;
-
+import java.util.List;
 import static com.google.common.base.Preconditions.checkNotNull;
 
 public class ArpHandler {
@@ -112,11 +111,11 @@
 
 
     private boolean isArpReqForRouter(DeviceId deviceId, ARP arpRequest) {
-        Ip4Address gatewayIpAddress = config.getGatewayIpAddress(deviceId);
-        if (gatewayIpAddress != null) {
+        List<Ip4Address> gatewayIpAddresses = config.getGatewayIpAddress(deviceId);
+        if (gatewayIpAddresses != null) {
             Ip4Address targetProtocolAddress = Ip4Address.valueOf(arpRequest
                     .getTargetProtocolAddress());
-            if (gatewayIpAddress.equals(targetProtocolAddress)) {
+            if (gatewayIpAddresses.contains(targetProtocolAddress)) {
                 return true;
             }
         }
@@ -124,15 +123,11 @@
     }
 
     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;
+        return config.getSubnetInfo(deviceId).stream()
+                     .anyMatch((prefix)->
+                     prefix.contains(Ip4Address.
+                                     valueOf(arpRequest.
+                                             getTargetProtocolAddress())));
     }
 
     /**
diff --git a/src/main/java/org/onosproject/segmentrouting/DefaultRoutingHandler.java b/src/main/java/org/onosproject/segmentrouting/DefaultRoutingHandler.java
index 3684ec9..58d3262 100644
--- a/src/main/java/org/onosproject/segmentrouting/DefaultRoutingHandler.java
+++ b/src/main/java/org/onosproject/segmentrouting/DefaultRoutingHandler.java
@@ -15,6 +15,7 @@
  */
 package org.onosproject.segmentrouting;
 
+import org.onlab.packet.Ip4Prefix;
 import org.onlab.packet.IpPrefix;
 import org.onosproject.net.Device;
 import org.onosproject.net.DeviceId;
@@ -26,6 +27,7 @@
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.List;
 import java.util.Set;
 
 import static com.google.common.base.Preconditions.checkNotNull;
@@ -139,8 +141,11 @@
         // 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);
+            List<Ip4Prefix> subnets = config.getSubnetInfo(destSw);
+            result = rulePopulator.populateIpRuleForSubnet(targetSw,
+                                                           subnets,
+                                                           destSw,
+                                                           nextHops);
             if (!result) {
                 return false;
             }
diff --git a/src/main/java/org/onosproject/segmentrouting/DeviceConfiguration.java b/src/main/java/org/onosproject/segmentrouting/DeviceConfiguration.java
index 17cfd96..0dfb3fd 100644
--- a/src/main/java/org/onosproject/segmentrouting/DeviceConfiguration.java
+++ b/src/main/java/org/onosproject/segmentrouting/DeviceConfiguration.java
@@ -1,171 +1,218 @@
 package org.onosproject.segmentrouting;
 
 import org.onlab.packet.Ip4Address;
+import org.onlab.packet.Ip4Prefix;
+import org.onlab.packet.IpPrefix;
 import org.onlab.packet.MacAddress;
-import org.onosproject.grouphandler.DeviceProperties;
+import org.onosproject.segmentrouting.grouphandler.DeviceProperties;
 import org.onosproject.net.DeviceId;
+import org.onosproject.net.PortNumber;
+import org.onosproject.segmentrouting.config.NetworkConfig.SwitchConfig;
+import org.onosproject.segmentrouting.config.NetworkConfigManager;
+import org.onosproject.segmentrouting.config.SegmentRouterConfig;
+import org.onosproject.segmentrouting.config.SegmentRouterConfig.Subnet;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import java.util.Arrays;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
+/**
+ * Segment Routing configuration component that reads the
+ * segment routing related configuration from Network Configuration Manager
+ * component and organizes in more accessible formats.
+ *
+ * TODO: Merge multiple Segment Routing configuration wrapper classes into one.
+ */
 public class DeviceConfiguration implements DeviceProperties {
 
     private static final Logger log = LoggerFactory
             .getLogger(DeviceConfiguration.class);
-    private final List<Integer> allSegmentIds =
-            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 List<Integer> allSegmentIds = new ArrayList<Integer>();
+    private final HashMap<DeviceId, SegmentRouterInfo> deviceConfigMap = new HashMap<>();
+    private final NetworkConfigManager configService;
 
-    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"));
-                }
-            };
+    private class SegmentRouterInfo {
+        int nodeSid;
+        DeviceId deviceId;
+        Ip4Address ip;
+        MacAddress mac;
+        boolean isEdge;
+        HashMap<PortNumber, Ip4Address> gatewayIps;
+        HashMap<PortNumber, Ip4Prefix> subnets;
+    }
 
+    /**
+     * Constructor. Reads all the configuration for all devices of type
+     * Segment Router and organizes into various maps for easier access.
+     *
+     * @param configService handle to network configuration manager
+     * component from where the relevant configuration is retrieved.
+     */
+    public DeviceConfiguration(NetworkConfigManager configService) {
+        this.configService = checkNotNull(configService);
+        List<SwitchConfig> allSwitchCfg =
+                this.configService.getConfiguredAllowedSwitches();
+        for (SwitchConfig cfg : allSwitchCfg) {
+            if (!(cfg instanceof SegmentRouterConfig)) {
+                continue;
+            }
+            SegmentRouterInfo info = new SegmentRouterInfo();
+            info.nodeSid = ((SegmentRouterConfig) cfg).getNodeSid();
+            info.deviceId = ((SegmentRouterConfig) cfg).getDpid();
+            info.mac = MacAddress.valueOf(((
+                    SegmentRouterConfig) cfg).getRouterMac());
+            String routerIp = ((SegmentRouterConfig) cfg).getRouterIp();
+            Ip4Prefix prefix = checkNotNull(IpPrefix.valueOf(routerIp).getIp4Prefix());
+            info.ip = prefix.address();
+            info.isEdge = ((SegmentRouterConfig) cfg).isEdgeRouter();
+            info.subnets = new HashMap<>();
+            info.gatewayIps = new HashMap<PortNumber, Ip4Address>();
+            for (Subnet s: ((SegmentRouterConfig) cfg).getSubnets()) {
+                info.subnets.put(PortNumber.portNumber(s.getPortNo()),
+                                 Ip4Prefix.valueOf(s.getSubnetIp()));
+                String gatewayIp = s.getSubnetIp().
+                        substring(0, s.getSubnetIp().indexOf('/'));
+                info.gatewayIps.put(PortNumber.portNumber(s.getPortNo()),
+                                    Ip4Address.valueOf(gatewayIp));
+            }
+            this.deviceConfigMap.put(info.deviceId, info);
+            this.allSegmentIds.add(info.nodeSid);
+        }
+    }
+
+    /**
+     * Returns the segment id of a segment router.
+     *
+     * @param deviceId device identifier
+     * @return segment id
+     */
     @Override
     public int getSegmentId(DeviceId deviceId) {
-        if (deviceSegmentIdMap.get(deviceId) != null) {
+        if (deviceConfigMap.get(deviceId) != null) {
             log.debug("getSegmentId for device{} is {}",
                     deviceId,
-                    deviceSegmentIdMap.get(deviceId));
-            return deviceSegmentIdMap.get(deviceId);
+                    deviceConfigMap.get(deviceId).nodeSid);
+            return deviceConfigMap.get(deviceId).nodeSid;
         } else {
             throw new IllegalStateException();
         }
     }
 
+    /**
+     * Returns the segment id of a segment router given its mac address.
+     *
+     * @param routerMac router mac address
+     * @return segment id
+     */
+    public int getSegmentId(MacAddress routerMac) {
+        for (Map.Entry<DeviceId, SegmentRouterInfo> entry:
+                    deviceConfigMap.entrySet()) {
+            if (entry.getValue().mac.equals(routerMac)) {
+                return entry.getValue().nodeSid;
+            }
+        }
 
+        return -1;
+    }
+
+    /**
+     * Returns the segment id of a segment router given its router ip address.
+     *
+     * @param routerAddress router ip address
+     * @return segment id
+     */
+    public int getSegmentId(Ip4Address routerAddress) {
+        for (Map.Entry<DeviceId, SegmentRouterInfo> entry:
+            deviceConfigMap.entrySet()) {
+            if (entry.getValue().ip.equals(routerAddress)) {
+                return entry.getValue().nodeSid;
+            }
+        }
+
+        return -1;
+    }
+
+    /**
+     * Returns the router mac of a segment router.
+     *
+     * @param deviceId device identifier
+     * @return router mac address
+     */
     @Override
     public MacAddress getDeviceMac(DeviceId deviceId) {
-        if (deviceMacMap.get(deviceId) != null) {
+        if (deviceConfigMap.get(deviceId) != null) {
             log.debug("getDeviceMac for device{} is {}",
                     deviceId,
-                    deviceMacMap.get(deviceId));
-            return deviceMacMap.get(deviceId);
+                    deviceConfigMap.get(deviceId).mac);
+            return deviceConfigMap.get(deviceId).mac;
         } 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;
+    /**
+     * Returns the router ip address of a segment router.
+     *
+     * @param deviceId device identifier
+     * @return router ip address
+     */
+    public Ip4Address getRouterIp(DeviceId deviceId) {
+        if (deviceConfigMap.get(deviceId) != null) {
+            log.debug("getDeviceIp for device{} is {}",
+                    deviceId,
+                    deviceConfigMap.get(deviceId).ip);
+            return deviceConfigMap.get(deviceId).ip;
+        } else {
+            throw new IllegalStateException();
         }
-
-        return false;
     }
 
+    /**
+     * Indicates if the segment router is a edge router or
+     * a transit/back bone router.
+     *
+     * @param deviceId device identifier
+     * @return boolean
+     */
+    @Override
+    public boolean isEdgeDevice(DeviceId deviceId) {
+        if (deviceConfigMap.get(deviceId) != null) {
+            log.debug("isEdgeDevice for device{} is {}",
+                    deviceId,
+                    deviceConfigMap.get(deviceId).isEdge);
+            return deviceConfigMap.get(deviceId).isEdge;
+        } else {
+            throw new IllegalStateException();
+        }
+    }
+
+    /**
+     * Returns the segment ids of all configured segment routers.
+     *
+     * @return list of segment ids
+     */
     @Override
     public List<Integer> getAllDeviceSegmentIds() {
         return allSegmentIds;
     }
 
-
     /**
-     * Returns Segment ID for the router with the MAC address given.
+     * Returns the device identifier or data plane identifier (dpid)
+     * of a segment router given its segment id.
      *
-     * @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
+     * @param sid segment id
+     * @return deviceId device identifier
      */
     public DeviceId getDeviceId(int sid) {
-        for (Map.Entry<DeviceId, Integer> entry: deviceSegmentIdMap.entrySet()) {
-            if (entry.getValue() == sid) {
-                return entry.getKey();
+        for (Map.Entry<DeviceId, SegmentRouterInfo> entry:
+            deviceConfigMap.entrySet()) {
+            if (entry.getValue().nodeSid == sid) {
+                return entry.getValue().deviceId;
             }
         }
 
@@ -173,18 +220,97 @@
     }
 
     /**
-     * Returns the Device ID of the router with the IP address given.
+     * Returns the device identifier or data plane identifier (dpid)
+     * of a segment router given its router ip address.
      *
-     * @param ipAddress IP address of the router
-     * @return Device ID of the router
+     * @param ipAddress router ip address
+     * @return deviceId device identifier
      */
     public DeviceId getDeviceId(Ip4Address ipAddress) {
-        for (Map.Entry<DeviceId, Ip4Address> entry: deviceIpMap.entrySet()) {
-            if (entry.getValue().equals(ipAddress)) {
-                return entry.getKey();
+        for (Map.Entry<DeviceId, SegmentRouterInfo> entry:
+            deviceConfigMap.entrySet()) {
+            if (entry.getValue().ip.equals(ipAddress)) {
+                return entry.getValue().deviceId;
             }
         }
 
         return null;
     }
+
+    /**
+     * Returns the configured subnet gateway ip addresses for a segment router.
+     *
+     * @param deviceId device identifier
+     * @return list of ip addresses
+     */
+    public List<Ip4Address> getSubnetGatewayIps(DeviceId deviceId) {
+        if (deviceConfigMap.get(deviceId) != null) {
+            log.debug("getSubnetGatewayIps for device{} is {}",
+                    deviceId,
+                    deviceConfigMap.get(deviceId).gatewayIps.values());
+            return new ArrayList<Ip4Address>(deviceConfigMap.
+                    get(deviceId).gatewayIps.values());
+        } else {
+            return null;
+        }
+    }
+
+    /**
+     * Returns the configured subnet prefixes for a segment router.
+     *
+     * @param deviceId device identifier
+     * @return list of ip prefixes
+     */
+    public List<Ip4Prefix> getSubnets(DeviceId deviceId) {
+        if (deviceConfigMap.get(deviceId) != null) {
+            log.debug("getSubnets for device{} is {}",
+                    deviceId,
+                    deviceConfigMap.get(deviceId).subnets.values());
+            return new ArrayList<Ip4Prefix>(deviceConfigMap.
+                    get(deviceId).subnets.values());
+        } else {
+            return null;
+        }
+    }
+
+    /**
+     * Returns the router ip address of segment router that has the
+     * specified ip address in its subnets.
+     *
+     * @param destIpAddress target ip address
+     * @return router ip address
+     */
+    public Ip4Address getRouterIpAddressForASubnetHost(Ip4Address destIpAddress) {
+        for (Map.Entry<DeviceId, SegmentRouterInfo> entry:
+                    deviceConfigMap.entrySet()) {
+            for (Ip4Prefix prefix:entry.getValue().subnets.values()) {
+                if (prefix.contains(destIpAddress)) {
+                    return entry.getValue().ip;
+                }
+            }
+        }
+
+        log.debug("No router was found for {}", destIpAddress);
+        return null;
+    }
+
+    /**
+     * Returns the router mac address of segment router that has the
+     * specified ip address as one of its subnet gateway ip address.
+     *
+     * @param gatewayIpAddress router gateway ip address
+     * @return router mac address
+     */
+    public MacAddress getRouterMacForAGatewayIp(Ip4Address gatewayIpAddress) {
+        for (Map.Entry<DeviceId, SegmentRouterInfo> entry:
+                deviceConfigMap.entrySet()) {
+            if (entry.getValue().gatewayIps.
+                    values().contains(gatewayIpAddress)) {
+                return entry.getValue().mac;
+            }
+        }
+
+        log.debug("Cannot find a router for {}", gatewayIpAddress);
+        return null;
+    }
 }
diff --git a/src/main/java/org/onosproject/segmentrouting/IcmpHandler.java b/src/main/java/org/onosproject/segmentrouting/IcmpHandler.java
index 3bfb888..45fa794 100644
--- a/src/main/java/org/onosproject/segmentrouting/IcmpHandler.java
+++ b/src/main/java/org/onosproject/segmentrouting/IcmpHandler.java
@@ -16,7 +16,7 @@
 package org.onosproject.segmentrouting;
 
 import java.nio.ByteBuffer;
-
+import java.util.List;
 import org.onlab.packet.Ethernet;
 import org.onlab.packet.ICMP;
 import org.onlab.packet.IPv4;
@@ -69,14 +69,14 @@
         DeviceId deviceId = connectPoint.deviceId();
         Ip4Address destinationAddress =
                 Ip4Address.valueOf(ipv4.getDestinationAddress());
-        Ip4Address gatewayIpAddress = config.getGatewayIpAddress(deviceId);
+        List<Ip4Address> gatewayIpAddresses = 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))) {
+                        gatewayIpAddresses.contains(destinationAddress))) {
             sendICMPResponse(ethernet, connectPoint);
             // TODO: do we need to set the flow rule again ??
 
diff --git a/src/main/java/org/onosproject/segmentrouting/NetworkConfigHandler.java b/src/main/java/org/onosproject/segmentrouting/NetworkConfigHandler.java
index 2cb5867..ad210bb 100644
--- a/src/main/java/org/onosproject/segmentrouting/NetworkConfigHandler.java
+++ b/src/main/java/org/onosproject/segmentrouting/NetworkConfigHandler.java
@@ -16,6 +16,7 @@
 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;
@@ -26,39 +27,33 @@
 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.
+ *
+ * TODO: Knock off this wrapper and directly use DeviceConfiguration class
  */
 
 public class NetworkConfigHandler {
 
     private static Logger log = LoggerFactory.getLogger(NetworkConfigHandler.class);
     private SegmentRoutingManager srManager;
-    private DeviceConfiguration deviceConfig = new DeviceConfiguration();
+    private DeviceConfiguration deviceConfig;
 
-    public NetworkConfigHandler(SegmentRoutingManager srManager) {
+    public NetworkConfigHandler(SegmentRoutingManager srManager,
+                                DeviceConfiguration deviceConfig) {
         this.srManager = srManager;
+        this.deviceConfig = deviceConfig;
     }
 
-    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 List<Ip4Address> getGatewayIpAddress(DeviceId deviceId) {
+        return this.deviceConfig.getSubnetGatewayIps(deviceId);
     }
 
     public IpPrefix getRouterIpAddress(DeviceId deviceId) {
-
         return IpPrefix.valueOf(deviceConfig.getRouterIp(deviceId), 32);
     }
 
@@ -68,17 +63,13 @@
 
     public boolean inSameSubnet(DeviceId deviceId, Ip4Address destIp) {
 
-        String subnetInfo = getSubnetInfo(deviceId);
-        if (subnetInfo == null) {
+        List<Ip4Prefix> subnets = getSubnetInfo(deviceId);
+        if (subnets == null) {
             return false;
         }
 
-        IpPrefix prefix = IpPrefix.valueOf(subnetInfo);
-        if (prefix.contains(destIp)) {
-            return true;
-        }
-
-        return false;
+        return subnets.stream()
+               .anyMatch((subnet) -> subnet.contains(destIp));
     }
 
     public boolean inSameSubnet(Ip4Address address, int sid) {
@@ -88,43 +79,23 @@
             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;
-
+        return inSameSubnet(deviceId, address);
     }
 
-    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 List<Ip4Prefix> getSubnetInfo(DeviceId deviceId) {
+        return deviceConfig.getSubnets(deviceId);
     }
 
     public int getMplsId(DeviceId deviceId) {
         return deviceConfig.getSegmentId(deviceId);
     }
 
-    public int getMplsId(MacAddress mac) {
-        return deviceConfig.getSegmentId(mac);
+    public int getMplsId(MacAddress routerMac) {
+        return deviceConfig.getSegmentId(routerMac);
     }
 
-    public int getMplsId(Ip4Address address) {
-        return deviceConfig.getSegmentId(address);
+    public int getMplsId(Ip4Address routerIpAddress) {
+        return deviceConfig.getSegmentId(routerIpAddress);
     }
 
     public boolean isEcmpNotSupportedInTransit(DeviceId deviceId) {
@@ -132,17 +103,12 @@
     }
 
     public boolean isTransitRouter(DeviceId deviceId) {
-        return true;
+        return !(deviceConfig.isEdgeDevice(deviceId));
     }
 
 
     public boolean isEdgeRouter(DeviceId deviceId) {
-        if (deviceId.uri().equals(URI.create("of:0000000000000001"))
-                || deviceId.uri().equals(URI.create("of:0000000000000006"))) {
-            return true;
-        }
-
-        return false;
+        return deviceConfig.isEdgeDevice(deviceId);
     }
 
     private List<PortNumber> getPortsToNeighbors(DeviceId deviceId, List<DeviceId> fwdSws) {
@@ -177,16 +143,7 @@
 
 
     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;
-        }
-
+        return deviceConfig.getRouterIpAddressForASubnetHost(destIpAddress);
     }
 
     public DeviceId getDeviceId(Ip4Address ip4Address) {
@@ -194,13 +151,6 @@
     }
 
     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;
-        }
+        return deviceConfig.getRouterMacForAGatewayIp(targetAddress);
     }
 }
diff --git a/src/main/java/org/onosproject/segmentrouting/RoutingRulePopulator.java b/src/main/java/org/onosproject/segmentrouting/RoutingRulePopulator.java
index 063517e..337b1f3 100644
--- a/src/main/java/org/onosproject/segmentrouting/RoutingRulePopulator.java
+++ b/src/main/java/org/onosproject/segmentrouting/RoutingRulePopulator.java
@@ -17,11 +17,11 @@
 
 import org.onlab.packet.Ethernet;
 import org.onlab.packet.Ip4Address;
+import org.onlab.packet.Ip4Prefix;
 import org.onlab.packet.IpPrefix;
 import org.onlab.packet.MacAddress;
-
 import org.onlab.packet.MplsLabel;
-import org.onosproject.grouphandler.NeighborSet;
+import org.onosproject.segmentrouting.grouphandler.NeighborSet;
 import org.onosproject.net.DeviceId;
 import org.onosproject.net.Link;
 import org.onosproject.net.PortNumber;
@@ -94,15 +94,15 @@
      * Populates IP flow rules for the subnets of the destination router.
      *
      * @param deviceId switch ID to set the rules
-     * @param subnetInfo subnet information
+     * @param subnets subnet information
      * @param destSw destination switch ID
      * @param nextHops next hop switch ID list
      * @return true if all rules are set successfully, false otherwise
      */
-    public boolean populateIpRuleForSubnet(DeviceId deviceId, String subnetInfo,
+    public boolean populateIpRuleForSubnet(DeviceId deviceId, List<Ip4Prefix> subnets,
                                            DeviceId destSw, Set<DeviceId> nextHops) {
 
-        List<IpPrefix> subnets = extractSubnet(subnetInfo);
+        //List<IpPrefix> subnets = extractSubnet(subnetInfo);
         for (IpPrefix subnet: subnets) {
             if (!populateIpRuleForRouter(deviceId, subnet, destSw, nextHops)) {
                 return false;
@@ -371,21 +371,6 @@
 
     }
 
-
-    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);
diff --git a/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java b/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java
index a7a3b11..b5e1181 100644
--- a/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java
+++ b/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java
@@ -25,8 +25,8 @@
 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.segmentrouting.grouphandler.DefaultGroupHandler;
+import org.onosproject.segmentrouting.grouphandler.NeighborSet;
 import org.onosproject.mastership.MastershipService;
 import org.onosproject.net.Device;
 import org.onosproject.net.DeviceId;
@@ -52,6 +52,7 @@
 import org.onosproject.net.packet.PacketProcessor;
 import org.onosproject.net.packet.PacketService;
 import org.onosproject.net.topology.TopologyService;
+import org.onosproject.segmentrouting.config.NetworkConfigManager;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -98,16 +99,15 @@
 
     @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 NetworkConfigHandler networkConfigHandler = null;
+    protected ArpHandler arpHandler = null;
+    protected IcmpHandler icmpHandler = null;
+    protected IpHandler ipHandler = null;
+    protected RoutingRulePopulator routingRulePopulator = null;
     protected ApplicationId appId;
 
-    private DefaultRoutingHandler defaultRoutingHandler = new DefaultRoutingHandler(this);
-    private DeviceConfiguration deviceConfiguration = new DeviceConfiguration();
+    private DefaultRoutingHandler defaultRoutingHandler = null;
+    private DeviceConfiguration deviceConfiguration = null;
     private InternalPacketProcessor processor = new InternalPacketProcessor();
     private InternalEventHandler eventHandler = new InternalEventHandler();
 
@@ -118,6 +118,8 @@
     private Map<DeviceId, DefaultGroupHandler> groupHandlerMap
             = new ConcurrentHashMap<DeviceId, DefaultGroupHandler>();
 
+    private NetworkConfigManager networkConfigService = new NetworkConfigManager();;
+
     private static int numOfEvents = 0;
     private static int numOfHandlerExecution = 0;
     private static int numOfHandlerScheduled = 0;
@@ -125,6 +127,16 @@
     @Activate
     protected void activate() {
         appId = coreService.registerApplication("org.onosproject.segmentrouting");
+        networkConfigService.init();
+        deviceConfiguration = new DeviceConfiguration(networkConfigService);
+        networkConfigHandler = new NetworkConfigHandler(this,
+                                                        deviceConfiguration);
+        arpHandler = new ArpHandler(this);
+        icmpHandler = new IcmpHandler(this);
+        ipHandler = new IpHandler(this);
+        routingRulePopulator = new RoutingRulePopulator(this);
+        defaultRoutingHandler = new DefaultRoutingHandler(this);
+
         packetService.addProcessor(processor, PacketProcessor.ADVISOR_MAX + 2);
         linkService.addListener(new InternalLinkListener());
         groupService.addListener(new InternalGroupListener());
@@ -271,6 +283,7 @@
 
     private class InternalEventHandler implements Runnable {
 
+        @Override
         public void run() {
             numOfHandlerExecution++;
             while (!eventQueue.isEmpty()) {
@@ -326,7 +339,11 @@
         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);
+                device.id(),
+                appId,
+                deviceConfiguration,
+                linkService,
+                groupService);
         dgh.createGroups();
         groupHandlerMap.put(device.id(), dgh);
     }
diff --git a/src/main/java/org/onosproject/segmentrouting/config/NetworkConfig.java b/src/main/java/org/onosproject/segmentrouting/config/NetworkConfig.java
new file mode 100644
index 0000000..6ae0779
--- /dev/null
+++ b/src/main/java/org/onosproject/segmentrouting/config/NetworkConfig.java
@@ -0,0 +1,336 @@
+package org.onosproject.segmentrouting.config;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import org.onosproject.net.DeviceId;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.databind.JsonNode;
+
+/**
+ * Public class corresponding to JSON described data model. Defines the network
+ * configuration at startup.
+ */
+public class NetworkConfig {
+    protected static final Logger log = LoggerFactory.getLogger(NetworkConfig.class);
+
+    @SuppressWarnings("unused")
+    private String comment;
+
+    private Boolean restrictSwitches;
+    private Boolean restrictLinks;
+    private List<SwitchConfig> switches;
+    private List<LinkConfig> links;
+
+    /**
+     * Default constructor.
+     */
+    public NetworkConfig() {
+        switches = new ArrayList<SwitchConfig>();
+        links = new ArrayList<LinkConfig>();
+    }
+
+    @JsonProperty("comment")
+    public void setComment(String c) {
+        log.trace("NetworkConfig: comment={}", c);
+        comment = c;
+    }
+
+    @JsonProperty("restrictSwitches")
+    public void setRestrictSwitches(boolean rs) {
+        log.trace("NetworkConfig: restrictSwitches={}", rs);
+        restrictSwitches = rs;
+    }
+
+    /**
+     * Returns default restrict configuration for switches.
+     *
+     * @return boolean
+     */
+    public Boolean getRestrictSwitches() {
+        return restrictSwitches;
+    }
+
+    @JsonProperty("restrictLinks")
+    public void setRestrictLinks(boolean rl) {
+        log.trace("NetworkConfig: restrictLinks={}", rl);
+        restrictLinks = rl;
+    }
+
+    /**
+     * Returns default restrict configuration for links.
+     *
+     * @return boolean
+     */
+    public Boolean getRestrictLinks() {
+        return restrictLinks;
+    }
+
+    /**
+     * Returns configuration for switches.
+     *
+     * @return list of switch configuration
+     */
+    public List<SwitchConfig> getSwitchConfig() {
+        return switches;
+    }
+
+    @JsonProperty("switchConfig")
+    public void setSwitchConfig(List<SwitchConfig> switches2) {
+        log.trace("NetworkConfig: switchConfig={}", switches2);
+        this.switches = switches2;
+    }
+
+    /**
+     * Java class corresponding to JSON described switch
+     * configuration data model.
+     */
+    public static class SwitchConfig {
+        protected String nodeDpid;
+        protected String name;
+        protected String type;
+        protected boolean allowed;
+        protected double latitude;
+        protected double longitude;
+        protected Map<String, JsonNode> params;
+        protected Map<String, String> publishAttributes;
+        protected DeviceId dpid;
+
+        /**
+         * Returns the configured "name" of a switch.
+         *
+         * @return string
+         */
+        public String getName() {
+            return name;
+        }
+
+        @JsonProperty("name")
+        public void setName(String name) {
+            log.trace("SwitchConfig: name={}", name);
+            this.name = name;
+        }
+
+        /**
+         * Returns the data plane identifier of a switch.
+         *
+         * @return ONOS device identifier
+         */
+        public DeviceId getDpid() {
+            return dpid;
+        }
+
+        public void setDpid(DeviceId dpid) {
+            this.dpid = dpid;
+            this.nodeDpid = dpid.toString();
+        }
+
+        /**
+         * Returns the data plane identifier of a switch.
+         *
+         * @return string
+         */
+        public String getNodeDpid() {
+            return nodeDpid;
+        }
+
+        // mapper sets both DeviceId and string fields for dpid
+        @JsonProperty("nodeDpid")
+        public void setNodeDpid(String nodeDpid) {
+            log.trace("SwitchConfig: nodeDpid={}", nodeDpid);
+            this.nodeDpid = nodeDpid;
+            this.dpid = DeviceId.deviceId(nodeDpid);
+        }
+
+        /**
+         * Returns the type of a switch.
+         *
+         * @return string
+         */
+        public String getType() {
+            return type;
+        }
+
+        @JsonProperty("type")
+        public void setType(String type) {
+            log.trace("SwitchConfig: type={}", type);
+            this.type = type;
+        }
+
+        /**
+         * Returns the latitude of a switch.
+         *
+         * @return double
+         */
+        public double getLatitude() {
+            return latitude;
+        }
+
+        @JsonProperty("latitude")
+        public void setLatitude(double latitude) {
+            log.trace("SwitchConfig: latitude={}", latitude);
+            this.latitude = latitude;
+        }
+
+        /**
+         * Returns the longitude of a switch.
+         *
+         * @return double
+         */
+        public double getLongitude() {
+            return longitude;
+        }
+
+        @JsonProperty("longitude")
+        public void setLongitude(double longitude) {
+            log.trace("SwitchConfig: longitude={}", longitude);
+            this.longitude = longitude;
+        }
+
+        /**
+         * Returns the allowed flag for a switch.
+         *
+         * @return boolean
+         */
+        public boolean isAllowed() {
+            return allowed;
+        }
+
+        @JsonProperty("allowed")
+        public void setAllowed(boolean allowed) {
+            this.allowed = allowed;
+        }
+
+        /**
+         * Returns the additional configured parameters of a switch.
+         *
+         * @return key value map
+         */
+        public Map<String, JsonNode> getParams() {
+            return params;
+        }
+
+        @JsonProperty("params")
+        public void setParams(Map<String, JsonNode> params) {
+            this.params = params;
+        }
+
+        /**
+         * Reserved for future use.
+         *
+         * @return key value map
+         */
+        public Map<String, String> getPublishAttributes() {
+            return publishAttributes;
+        }
+
+        @JsonProperty("publishAttributes")
+        public void setPublishAttributes(Map<String, String> publishAttributes) {
+            this.publishAttributes = publishAttributes;
+        }
+
+    }
+
+    @JsonProperty("linkConfig")
+    public void setLinkConfig(List<LinkConfig> links2) {
+        this.links = links2;
+    }
+
+    /**
+     * Reserved for future use.
+     *
+     * @return list of configured link configuration
+     */
+    public List<LinkConfig> getLinkConfig() {
+        return links;
+    }
+
+    /**
+     * Reserved for future use.
+     */
+    public static class LinkConfig {
+        protected String type;
+        protected Boolean allowed;
+        protected DeviceId dpid1;
+        protected DeviceId dpid2;
+        protected String nodeDpid1;
+        protected String nodeDpid2;
+        protected Map<String, JsonNode> params;
+        protected Map<String, String> publishAttributes;
+
+        public String getType() {
+            return type;
+        }
+
+        public void setType(String type) {
+            this.type = type;
+        }
+
+        public Boolean isAllowed() {
+            return allowed;
+        }
+
+        public void setAllowed(Boolean allowed) {
+            this.allowed = allowed;
+        }
+
+        public String getNodeDpid1() {
+            return nodeDpid1;
+        }
+
+        // mapper sets both long and string fields for dpid
+        public void setNodeDpid1(String nodeDpid1) {
+            this.nodeDpid1 = nodeDpid1;
+            this.dpid1 = DeviceId.deviceId(nodeDpid1);
+        }
+
+        public String getNodeDpid2() {
+            return nodeDpid2;
+        }
+
+        // mapper sets both long and string fields for dpid
+        public void setNodeDpid2(String nodeDpid2) {
+            this.nodeDpid2 = nodeDpid2;
+            this.dpid2 = DeviceId.deviceId(nodeDpid2);
+        }
+
+        public DeviceId getDpid1() {
+            return dpid1;
+        }
+
+        public void setDpid1(DeviceId dpid1) {
+            this.dpid1 = dpid1;
+            this.nodeDpid1 = dpid1.toString();
+        }
+
+        public DeviceId getDpid2() {
+            return dpid2;
+        }
+
+        public void setDpid2(DeviceId dpid2) {
+            this.dpid2 = dpid2;
+            this.nodeDpid2 = dpid2.toString();
+        }
+
+        public Map<String, JsonNode> getParams() {
+            return params;
+        }
+
+        public void setParams(Map<String, JsonNode> params) {
+            this.params = params;
+        }
+
+        public Map<String, String> getPublishAttributes() {
+            return publishAttributes;
+        }
+
+        public void setPublishAttributes(Map<String, String> publishAttributes) {
+            this.publishAttributes = publishAttributes;
+        }
+    }
+}
+
diff --git a/src/main/java/org/onosproject/segmentrouting/config/NetworkConfigException.java b/src/main/java/org/onosproject/segmentrouting/config/NetworkConfigException.java
new file mode 100644
index 0000000..9194221
--- /dev/null
+++ b/src/main/java/org/onosproject/segmentrouting/config/NetworkConfigException.java
@@ -0,0 +1,200 @@
+package org.onosproject.segmentrouting.config;
+
+import org.onosproject.net.DeviceId;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * NetworkConfigExceptions specifies a set of unchecked runtime exceptions that
+ * can be thrown by the {@link NetworkConfigManager}. It indicates errors that
+ * must be fixed in the config file before controller execution can proceed.
+ */
+public class NetworkConfigException extends RuntimeException {
+
+    private static final long serialVersionUID = 4959684709803000652L;
+    protected static final Logger log = LoggerFactory
+            .getLogger(NetworkConfigException.class);
+
+    /**
+     * Exception for duplicate device identifier configuration.
+     */
+    public static class DuplicateDpid extends RuntimeException {
+        private static final long serialVersionUID = 5491113234592145335L;
+
+        public DuplicateDpid(DeviceId dpid) {
+            super();
+            log.error("Duplicate dpid found in switch-config Dpid:{}",
+                    dpid);
+        }
+    }
+
+    /**
+     * Exception for duplicate device name configuration.
+     */
+    public static class DuplicateName extends RuntimeException {
+        private static final long serialVersionUID = -4090171438031376129L;
+
+        public DuplicateName(String name) {
+            super();
+            log.error("Duplicate name found in switch-config name:{}", name);
+        }
+    }
+
+    /**
+     * Exception for unspecified device identifier for a switch.
+     */
+    public static class DpidNotSpecified extends RuntimeException {
+        private static final long serialVersionUID = -8494418855597117254L;
+
+        public DpidNotSpecified(String name) {
+            super();
+            log.error("Dpid not specified for switch-config name:{}", name);
+        }
+    }
+
+    /**
+     * Exception for unspecified device name for a switch.
+     */
+    public static class NameNotSpecified extends RuntimeException {
+        private static final long serialVersionUID = -3518881744110422891L;
+
+        public NameNotSpecified(DeviceId dpid) {
+            super();
+            log.error("Name not specified for switch-config dpid:{}",
+                    dpid);
+        }
+    }
+
+    /**
+     * Exception for unspecified device type for a switch.
+     */
+    public static class SwitchTypeNotSpecified extends RuntimeException {
+        private static final long serialVersionUID = 2527453336226053753L;
+
+        public SwitchTypeNotSpecified(DeviceId dpid) {
+            super();
+            log.error("Switch type not specified for switch-config dpid:{}",
+                    dpid);
+        }
+    }
+
+    /**
+     * Exception for unknown device type configured for a switch.
+     */
+    public static class UnknownSwitchType extends RuntimeException {
+        private static final long serialVersionUID = 7758418165512249170L;
+
+        public UnknownSwitchType(String type, String name) {
+            super();
+            log.error("Unknown switch type {} for switch name:{}", type, name);
+        }
+    }
+
+    /**
+     * Exception for missing required parameter configuration for a switch.
+     */
+    public static class ParamsNotSpecified extends RuntimeException {
+        private static final long serialVersionUID = 6247582323691265513L;
+
+        public ParamsNotSpecified(String name) {
+            super();
+            log.error("Params required - not specified for switch:{}", name);
+        }
+    }
+
+    /**
+     * Reserved for future use.
+     */
+    public static class LinkTypeNotSpecified extends RuntimeException {
+        private static final long serialVersionUID = -2089470389588542215L;
+
+        public LinkTypeNotSpecified(String dpid1, String dpid2) {
+            super();
+            log.error("Link type not specified for link-config between "
+                    + "dpid1:{} and dpid2:{}", dpid1, dpid2);
+        }
+    }
+
+    /**
+     * Reserved for future use.
+     */
+    public static class LinkDpidNotSpecified extends RuntimeException {
+        private static final long serialVersionUID = -5701825916378616004L;
+
+        public LinkDpidNotSpecified(String dpid1, String dpid2) {
+            super();
+            if (dpid1 == null) {
+                log.error("nodeDpid1 not specified for link-config ");
+            }
+            if (dpid2 == null) {
+                log.error("nodeDpid2 not specified for link-config ");
+            }
+        }
+    }
+
+    /**
+     * Reserved for future use.
+     */
+    public static class LinkForUnknownSwitchConfig extends RuntimeException {
+        private static final long serialVersionUID = -2910458439881964094L;
+
+        public LinkForUnknownSwitchConfig(String dpid) {
+            super();
+            log.error("Link configuration was specified for a switch-dpid {} "
+                    + "that has not been configured", dpid);
+        }
+    }
+
+    /**
+     * Reserved for future use.
+     */
+    public static class UnknownLinkType extends RuntimeException {
+        private static final long serialVersionUID = -5505376193106542305L;
+
+        public UnknownLinkType(String linktype, String dpid1, String dpid2) {
+            super();
+            log.error("unknown link type {} for links between dpid1:{} "
+                    + "and dpid2:{}", linktype, dpid1, dpid2);
+        }
+    }
+
+    /**
+     * Exception for generic configuration errors.
+     */
+    public static class ErrorConfig extends RuntimeException {
+        private static final long serialVersionUID = -2827406314700193147L;
+
+        public ErrorConfig(String errorMsg) {
+            super();
+            log.error(errorMsg);
+        }
+
+    }
+
+    /**
+     * Reserved for future use.
+     */
+    public static class SwitchDpidNotConverted extends RuntimeException {
+        private static final long serialVersionUID = 5640347104590170426L;
+
+        public SwitchDpidNotConverted(String name) {
+            super();
+            log.error("Switch dpid specified as a HexString {} does not match "
+                    + "with long value", name);
+        }
+    }
+
+    /**
+     * Reserved for future use.
+     */
+    public static class LinkDpidNotConverted extends RuntimeException {
+        private static final long serialVersionUID = 2397245646094080774L;
+
+        public LinkDpidNotConverted(String dpid1, String dpid2) {
+            log.error("Dpids expressed as HexStrings for links between dpid1:{} "
+                    + "and dpid2:{} do not match with long values", dpid1, dpid2);
+        }
+    }
+
+}
+
diff --git a/src/main/java/org/onosproject/segmentrouting/config/NetworkConfigManager.java b/src/main/java/org/onosproject/segmentrouting/config/NetworkConfigManager.java
new file mode 100644
index 0000000..44e4f1c
--- /dev/null
+++ b/src/main/java/org/onosproject/segmentrouting/config/NetworkConfigManager.java
@@ -0,0 +1,323 @@
+package org.onosproject.segmentrouting.config;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.Link;
+import org.onosproject.segmentrouting.config.NetworkConfig.LinkConfig;
+import org.onosproject.segmentrouting.config.NetworkConfig.SwitchConfig;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.fasterxml.jackson.core.JsonParseException;
+import com.fasterxml.jackson.databind.JsonMappingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+/**
+ * NetworkConfigManager manages all network configuration for switches, links
+ * and any other state that needs to be configured for correct network
+ * operation.
+ *
+ */
+public class NetworkConfigManager implements NetworkConfigService {
+    protected static final Logger log = LoggerFactory
+            .getLogger(NetworkConfigManager.class);
+    private static final String CONFIG_DIR = "../config";
+    private static final String DEFAULT_CONFIG_FILE = "segmentrouting.conf";
+    private final String configFileName = DEFAULT_CONFIG_FILE;
+    /**
+     * JSON Config file needs to use one of the following types for defining the
+     * kind of switch or link it wishes to configure.
+     */
+    public static final String SEGMENT_ROUTER = "Router_SR";
+
+    public static final String PKT_LINK = "pktLink";
+
+    NetworkConfig networkConfig;
+    private ConcurrentMap<DeviceId, SwitchConfig> configuredSwitches;
+    private ConcurrentMap<Link, LinkConfig> configuredLinks;
+    private Map<String, DeviceId> nameToDpid;
+
+    @Override
+    public SwitchConfigStatus checkSwitchConfig(DeviceId dpid) {
+        SwitchConfig swc = configuredSwitches.get(dpid);
+        if (networkConfig.getRestrictSwitches()) {
+            // default deny behavior
+            if (swc == null) {
+                // switch is not configured - we deny this switch
+                return new SwitchConfigStatus(NetworkConfigState.DENY, null,
+                        "Switch not configured, in network denying switches by default.");
+            }
+            if (swc.isAllowed()) {
+                // switch is allowed in config, return configured attributes
+                return new SwitchConfigStatus(NetworkConfigState.ACCEPT_ADD, swc);
+            } else {
+                // switch has been configured off (administratively down)
+                return new SwitchConfigStatus(NetworkConfigState.DENY, null,
+                        "Switch configured down (allowed=false).");
+            }
+        } else {
+            // default allow behavior
+            if (swc == null) {
+                // no config to add
+                return new SwitchConfigStatus(NetworkConfigState.ACCEPT, null);
+            }
+            if (swc.isAllowed()) {
+                // switch is allowed in config, return configured attributes
+                return new SwitchConfigStatus(NetworkConfigState.ACCEPT_ADD, swc);
+            } else {
+                // switch has been configured off (administratively down)
+                return new SwitchConfigStatus(NetworkConfigState.DENY, null,
+                        "Switch configured down (allowed=false).");
+            }
+        }
+
+    }
+
+    @Override
+    public LinkConfigStatus checkLinkConfig(Link linkTuple) {
+        LinkConfig lkc = getConfiguredLink(linkTuple);
+        // links are always disallowed if any one of the nodes that make up the
+        // link are disallowed
+        DeviceId linkNode1 = linkTuple.src().deviceId();
+        SwitchConfigStatus scs1 = checkSwitchConfig(linkNode1);
+        if (scs1.getConfigState() == NetworkConfigState.DENY) {
+            return new LinkConfigStatus(NetworkConfigState.DENY, null,
+                    "Link-node: " + linkNode1 + " denied by config: " + scs1.getMsg());
+        }
+        DeviceId linkNode2 = linkTuple.dst().deviceId();
+        SwitchConfigStatus scs2 = checkSwitchConfig(linkNode2);
+        if (scs2.getConfigState() == NetworkConfigState.DENY) {
+            return new LinkConfigStatus(NetworkConfigState.DENY, null,
+                    "Link-node: " + linkNode2 + " denied by config: " + scs2.getMsg());
+        }
+        if (networkConfig.getRestrictLinks()) {
+            // default deny behavior
+            if (lkc == null) {
+                // link is not configured - we deny this link
+                return new LinkConfigStatus(NetworkConfigState.DENY, null,
+                        "Link not configured, in network denying links by default.");
+            }
+            if (lkc.isAllowed()) {
+                // link is allowed in config, return configured attributes
+                return new LinkConfigStatus(NetworkConfigState.ACCEPT_ADD, lkc);
+            } else {
+                // link has been configured off (administratively down)
+                return new LinkConfigStatus(NetworkConfigState.DENY, null,
+                        "Link configured down (allowed=false).");
+            }
+        } else {
+            // default allow behavior
+            if (lkc == null) {
+                // no config to add
+                return new LinkConfigStatus(NetworkConfigState.ACCEPT, null);
+            }
+            if (lkc.isAllowed()) {
+                // link is allowed in config, return configured attributes
+                return new LinkConfigStatus(NetworkConfigState.ACCEPT_ADD, lkc);
+            } else {
+                // link has been configured off (administratively down)
+                return new LinkConfigStatus(NetworkConfigState.DENY, null,
+                        "Link configured down (allowed=false).");
+            }
+        }
+
+    }
+
+    @Override
+    public List<SwitchConfig> getConfiguredAllowedSwitches() {
+        List<SwitchConfig> allowed = new ArrayList<SwitchConfig>();
+        for (SwitchConfig swc : configuredSwitches.values()) {
+            if (swc.isAllowed()) {
+                allowed.add(swc);
+            }
+        }
+        return allowed;
+    }
+
+    @Override
+    public List<LinkConfig> getConfiguredAllowedLinks() {
+        List<LinkConfig> allowed = new ArrayList<LinkConfig>();
+        for (LinkConfig lkc : configuredLinks.values()) {
+            if (lkc.isAllowed()) {
+                allowed.add(lkc);
+            }
+        }
+        return allowed;
+    }
+
+    @Override
+    public DeviceId getDpidForName(String name) {
+        if (nameToDpid.get(name) != null) {
+            return nameToDpid.get(name);
+        }
+        return null;
+    }
+
+    // **************
+    // Private methods
+    // **************
+
+    private void loadNetworkConfig() {
+        File configFile = new File(CONFIG_DIR, configFileName);
+        ObjectMapper mapper = new ObjectMapper();
+        networkConfig = new NetworkConfig();
+
+        try {
+            networkConfig = mapper.readValue(configFile,
+                                             NetworkConfig.class);
+        } catch (JsonParseException e) {
+            String err = String.format("JsonParseException while loading network "
+                    + "config from file: %s: %s", configFileName,
+                    e.getMessage());
+            throw new NetworkConfigException.ErrorConfig(err);
+        } catch (JsonMappingException e) {
+            String err = String.format(
+                    "JsonMappingException while loading network config "
+                            + "from file: %s: %s",
+                            configFileName,
+                            e.getMessage());
+            throw new NetworkConfigException.ErrorConfig(err);
+        } catch (IOException e) {
+            String err = String.format("IOException while loading network config "
+                    + "from file: %s %s", configFileName, e.getMessage());
+            throw new NetworkConfigException.ErrorConfig(err);
+        }
+
+        log.info("Network config specifies: {} switches and {} links",
+                (networkConfig.getRestrictSwitches())
+                        ? networkConfig.getSwitchConfig().size() : "default allow",
+                        (networkConfig.getRestrictLinks())
+                        ? networkConfig.getLinkConfig().size() : "default allow");
+    }
+
+    private void parseNetworkConfig() {
+        List<SwitchConfig> swConfList = networkConfig.getSwitchConfig();
+        List<LinkConfig> lkConfList = networkConfig.getLinkConfig();
+        validateSwitchConfig(swConfList);
+        createTypeSpecificSwitchConfig(swConfList);
+        validateLinkConfig(lkConfList);
+        createTypeSpecificLinkConfig(lkConfList);
+        // TODO validate reachability matrix 'names' for configured dpids
+    }
+
+    private void createTypeSpecificSwitchConfig(List<SwitchConfig> swConfList) {
+        for (SwitchConfig swc : swConfList) {
+            nameToDpid.put(swc.getName(), swc.getDpid());
+            String swtype = swc.getType();
+            switch (swtype) {
+            case SEGMENT_ROUTER:
+                SwitchConfig sr = new SegmentRouterConfig(swc);
+                configuredSwitches.put(sr.getDpid(), sr);
+                break;
+            default:
+                throw new NetworkConfigException.UnknownSwitchType(swtype,
+                        swc.getName());
+            }
+        }
+    }
+
+    private void createTypeSpecificLinkConfig(List<LinkConfig> lkConfList) {
+        for (LinkConfig lkc : lkConfList) {
+            String lktype = lkc.getType();
+            switch (lktype) {
+            case PKT_LINK:
+                PktLinkConfig pk = new PktLinkConfig(lkc);
+                for (Link lt : pk.getLinkTupleList()) {
+                    configuredLinks.put(lt, pk);
+                }
+                break;
+            default:
+                throw new NetworkConfigException.UnknownLinkType(lktype,
+                        lkc.getNodeDpid1(), lkc.getNodeDpid2());
+            }
+        }
+    }
+
+    private void validateSwitchConfig(List<SwitchConfig> swConfList) {
+        Set<DeviceId> swDpids = new HashSet<DeviceId>();
+        Set<String> swNames = new HashSet<String>();
+        for (SwitchConfig swc : swConfList) {
+            if (swc.getNodeDpid() == null || swc.getDpid() == null) {
+                throw new NetworkConfigException.DpidNotSpecified(swc.getName());
+            }
+            // ensure both String and DeviceId values of dpid are set
+            if (!swc.getDpid().equals(DeviceId.deviceId(swc.getNodeDpid()))) {
+                throw new NetworkConfigException.SwitchDpidNotConverted(
+                        swc.getName());
+            }
+            if (swc.getName() == null) {
+                throw new NetworkConfigException.NameNotSpecified(swc.getDpid());
+            }
+            if (swc.getType() == null) {
+                throw new NetworkConfigException.SwitchTypeNotSpecified(
+                        swc.getDpid());
+            }
+            if (!swDpids.add(swc.getDpid())) {
+                throw new NetworkConfigException.DuplicateDpid(swc.getDpid());
+            }
+            if (!swNames.add(swc.getName())) {
+                throw new NetworkConfigException.DuplicateName(swc.getName());
+            }
+            // TODO Add more validations
+        }
+    }
+
+    private void validateLinkConfig(List<LinkConfig> lkConfList) {
+        for (LinkConfig lkc : lkConfList) {
+            if (lkc.getNodeDpid1() == null || lkc.getNodeDpid2() == null) {
+                throw new NetworkConfigException.LinkDpidNotSpecified(
+                        lkc.getNodeDpid1(), lkc.getNodeDpid2());
+            }
+            // ensure both String and Long values are set
+            if (!lkc.getDpid1().equals(DeviceId.deviceId(lkc.getNodeDpid1())) ||
+                    !lkc.getDpid2().equals(DeviceId.deviceId(lkc.getNodeDpid2()))) {
+                throw new NetworkConfigException.LinkDpidNotConverted(
+                        lkc.getNodeDpid1(), lkc.getNodeDpid2());
+            }
+            if (lkc.getType() == null) {
+                throw new NetworkConfigException.LinkTypeNotSpecified(
+                        lkc.getNodeDpid1(), lkc.getNodeDpid2());
+            }
+            if (configuredSwitches.get(lkc.getDpid1()) == null) {
+                throw new NetworkConfigException.LinkForUnknownSwitchConfig(
+                        lkc.getNodeDpid1());
+            }
+            if (configuredSwitches.get(lkc.getDpid2()) == null) {
+                throw new NetworkConfigException.LinkForUnknownSwitchConfig(
+                        lkc.getNodeDpid2());
+            }
+            // TODO add more validations
+        }
+
+    }
+
+    private LinkConfig getConfiguredLink(Link linkTuple) {
+        LinkConfig lkc = null;
+        // first try the unidirectional link with the ports assigned
+        lkc = configuredLinks.get(linkTuple);
+        return lkc;
+    }
+
+
+    /**
+     * Initializes the network configuration manager module by
+     * loading and parsing the network configuration file.
+     */
+    public void init() {
+        loadNetworkConfig();
+        configuredSwitches = new ConcurrentHashMap<DeviceId, SwitchConfig>();
+        configuredLinks = new ConcurrentHashMap<Link, LinkConfig>();
+        nameToDpid = new HashMap<String, DeviceId>();
+        parseNetworkConfig();
+    }
+}
diff --git a/src/main/java/org/onosproject/segmentrouting/config/NetworkConfigService.java b/src/main/java/org/onosproject/segmentrouting/config/NetworkConfigService.java
new file mode 100644
index 0000000..722d50c
--- /dev/null
+++ b/src/main/java/org/onosproject/segmentrouting/config/NetworkConfigService.java
@@ -0,0 +1,253 @@
+package org.onosproject.segmentrouting.config;
+
+import java.util.List;
+
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.Link;
+import org.onosproject.segmentrouting.config.NetworkConfig.LinkConfig;
+import org.onosproject.segmentrouting.config.NetworkConfig.SwitchConfig;
+
+/**
+ * Exposes methods to retrieve network configuration.
+ *
+ * TODO: currently only startup-configuration is exposed and such configuration
+ * cannot be changed at runtime. Need to add runtime support for changes to
+ * configuration (via REST/CLI) in future releases.
+ *
+ * TODO: return immutable objects or defensive copies of network config so that
+ * users of this API do not inadvertently or maliciously change network config.
+ */
+public interface NetworkConfigService {
+
+    /**
+     * Suggests the action to be taken by the caller given the configuration
+     * associated with the queried network-object (eg. switch, link etc.).
+     */
+    public enum NetworkConfigState {
+        /**
+         * Associated network object has been configured to not be allowed in
+         * the network.
+         */
+        DENY,
+
+        /**
+         * Associated network object has been configured to be allowed in the
+         * network.
+         */
+        ACCEPT,
+
+        /**
+         * Associated network object has been configured to be allowed in the
+         * network. In addition, there are configured parameters that should be
+         * added to the object.
+         */
+        ACCEPT_ADD,
+    }
+
+    /**
+     * Returns the configuration outcome (accept, deny etc.), and any configured
+     * parameters to the caller, in response to a query for the configuration
+     * associated with a switch.
+     */
+    public class SwitchConfigStatus {
+        private NetworkConfigState configState;
+        private SwitchConfig switchConfig;
+        private String msg;
+
+        SwitchConfigStatus(NetworkConfigState configState,
+                SwitchConfig switchConfig, String msg) {
+            this.configState = configState;
+            this.switchConfig = switchConfig;
+            this.msg = msg;
+        }
+
+        SwitchConfigStatus(NetworkConfigState configState,
+                SwitchConfig switchConfig) {
+            this.configState = configState;
+            this.switchConfig = switchConfig;
+            this.msg = "";
+        }
+
+        /**
+         * Returns the configuration state for the switch.
+         *
+         * @return non-null NetworkConfigState
+         */
+        public NetworkConfigState getConfigState() {
+            return configState;
+        }
+
+        /**
+         * Returns the switch configuration, which may be null if no
+         * configuration exists, or if the configuration state disallows the
+         * switch.
+         *
+         * @return SwitchConfig, the switch configuration, or null
+         */
+        public SwitchConfig getSwitchConfig() {
+            return switchConfig;
+        }
+
+        /**
+         * User readable string typically used to specify the reason why a
+         * switch is being disallowed.
+         *
+         * @return A non-null but possibly empty String
+         */
+        public String getMsg() {
+            return msg;
+        }
+
+    }
+
+    /**
+     * Reserved for future use.
+     *
+     * Returns the configuration outcome (accept, deny etc.), and any configured
+     * parameters to the caller, in response to a query for the configuration
+     * associated with a link.
+     */
+    public class LinkConfigStatus {
+        private NetworkConfigState configState;
+        private LinkConfig linkConfig;
+        private String msg;
+
+        LinkConfigStatus(NetworkConfigState configState,
+                LinkConfig linkConfig, String msg) {
+            this.configState = configState;
+            this.linkConfig = linkConfig;
+            this.msg = msg;
+        }
+
+        LinkConfigStatus(NetworkConfigState configState,
+                LinkConfig linkConfig) {
+            this.configState = configState;
+            this.linkConfig = linkConfig;
+            this.msg = "";
+        }
+
+        /**
+         * Returns the configuration state for the link.
+         *
+         * @return non-null NetworkConfigState
+         */
+        public NetworkConfigState getConfigState() {
+            return configState;
+        }
+
+        /**
+         * Returns the link configuration, which may be null if no configuration
+         * exists, or if the configuration state disallows the link.
+         *
+         * @return SwitchConfig, the switch configuration, or null
+         */
+        public LinkConfig getLinkConfig() {
+            return linkConfig;
+        }
+
+        /**
+         * User readable string typically used to specify the reason why a link
+         * is being disallowed.
+         *
+         * @return msg A non-null but possibly empty String
+         */
+        public String getMsg() {
+            return msg;
+        }
+
+    }
+
+    /**
+     * Checks the switch configuration (if any) associated with the 'dpid'.
+     * Determines if the switch should be allowed or denied according to
+     * configuration rules.
+     *
+     * The method always returns a non-null SwitchConfigStatus. The enclosed
+     * ConfigState contains the result of the check. The enclosed SwitchConfig
+     * may or may not be null, depending on the outcome of the check.
+     *
+     * @param dpid device id of the switch to be queried
+     * @return SwitchConfigStatus with outcome of check and associated config.
+     */
+    public SwitchConfigStatus checkSwitchConfig(DeviceId dpid);
+
+    /**
+     * Reserved for future use.
+     *
+     * Checks the link configuration (if any) associated with the 'link'.
+     * Determines if the link should be allowed or denied according to
+     * configuration rules. Note that the 'link' is a unidirectional link which
+     * checked against configuration that is typically defined for a
+     * bidirectional link. The caller may make a second call if it wishes to
+     * check the 'reverse' direction.
+     *
+     * Also note that the configuration may not specify ports for a given
+     * bidirectional link. In such cases, the configuration applies to all links
+     * between the two switches. This method will check the given 'link' against
+     * such configuration.
+
+     * The method always returns a non-null LinkConfigStatus. The enclosed
+     * ConfigState contains the result of the check. The enclosed LinkConfig may
+     * or may not be null, depending on the outcome of the check.
+     *
+     * @param linkTuple unidirectional link to be queried
+     * @return LinkConfigStatus with outcome of check and associated config.
+     */
+    public LinkConfigStatus checkLinkConfig(Link linkTuple);
+
+    /**
+     * Retrieves a list of switches that have been configured, and have been
+     * determined to be 'allowed' in the network, according to configuration
+     * rules.
+     *
+     * Note that it is possible that there are other switches that are allowed
+     * in the network that have NOT been configured. Such switches will not be a
+     * part of the returned list.
+     *
+     * Also note that it is possible that some switches will not be discovered
+     * and the only way the controller can know about these switches is via
+     * configuration. Such switches will be included in this list. It is up to
+     * the caller to determine which SwitchConfig applies to non-discovered
+     * switches.
+     *
+     * @return a non-null List of SwitchConfig which may be empty
+     */
+    public List<SwitchConfig> getConfiguredAllowedSwitches();
+
+    /**
+     * Reserved for future use.
+     *
+     * Retrieves a list of links that have been configured, and have been
+     * determined to be 'allowed' in the network, according to configuration
+     * rules.
+     *
+     * Note that it is possible that there are other links that are allowed in
+     * the network that have NOT been configured. Such links will not be a part
+     * of the returned list.
+     *
+     * Also note that it is possible that some links will not be discovered and
+     * the only way the controller can know about these links is via
+     * configuration. Such links will be included in this list. It is up to the
+     * caller to determine which LinkConfig applies to non-discovered links.
+     *
+     * In addition, note that the LinkConfig applies to the configured
+     * bi-directional link, which may or may not have declared ports. The
+     * associated unidirectional LinkTuple can be retrieved from the
+     * getLinkTupleList() method in the LinkConfig object.
+     *
+     * @return a non-null List of LinkConfig which may be empty
+     */
+    public List<LinkConfig> getConfiguredAllowedLinks();
+
+    /**
+     * Retrieves the Dpid associated with a 'name' for a configured switch
+     * object. This method does not check of the switches are 'allowed' by
+     * config.
+     *
+     * @param name device name
+     * @return the Dpid corresponding to a given 'name', or null if no
+     *         configured switch was found for the given 'name'.
+     */
+    public DeviceId getDpidForName(String name);
+
+}
diff --git a/src/main/java/org/onosproject/segmentrouting/config/PktLinkConfig.java b/src/main/java/org/onosproject/segmentrouting/config/PktLinkConfig.java
new file mode 100644
index 0000000..c66ac3c
--- /dev/null
+++ b/src/main/java/org/onosproject/segmentrouting/config/PktLinkConfig.java
@@ -0,0 +1,144 @@
+package org.onosproject.segmentrouting.config;
+
+import java.util.List;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.onosproject.net.Link;
+import org.onosproject.segmentrouting.config.NetworkConfig.LinkConfig;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.fasterxml.jackson.databind.JsonNode;
+
+/**
+ * Reserved for future use.
+ * Configuration for a link between two packet-switches.
+ */
+public class PktLinkConfig extends LinkConfig {
+    protected static final Logger log = LoggerFactory
+            .getLogger(PktLinkConfig.class);
+    private int port1;
+    private int port2;
+    private String nodeName1;
+    private String nodeName2;
+    private List<Link> linkTupleList;
+
+    public PktLinkConfig(LinkConfig lkc) {
+        nodeDpid1 = lkc.getNodeDpid1();
+        nodeDpid2 = lkc.getNodeDpid2();
+        dpid1 = lkc.getDpid1();
+        dpid2 = lkc.getDpid2();
+        type = lkc.getType();
+        allowed = lkc.isAllowed();
+        params = lkc.getParams();
+        publishAttributes = new ConcurrentHashMap<String, String>();
+        parseParams();
+        validateParams();
+        setPublishAttributes();
+    }
+
+    // ********************
+    // Packet Link Configuration
+    // ********************
+
+    public int getPort1() {
+        return port1;
+    }
+
+    public void setPort1(int port1) {
+        this.port1 = port1;
+    }
+
+    public int getPort2() {
+        return port2;
+    }
+
+    public void setPort2(int port2) {
+        this.port2 = port2;
+    }
+
+    public String getNodeName1() {
+        return nodeName1;
+    }
+
+    public void setNodeName1(String nodeName1) {
+        this.nodeName1 = nodeName1;
+    }
+
+    public String getNodeName2() {
+        return nodeName2;
+    }
+
+    public void setNodeName2(String nodeName2) {
+        this.nodeName2 = nodeName2;
+    }
+
+    /**
+     * Returns the two unidirectional links corresponding to the packet-link
+     * configuration. It is possible that the ports in the LinkTuple have
+     * portnumber '0', implying that the configuration applies to all links
+     * between the two switches.
+     *
+     * @return a list of LinkTuple with exactly 2 unidirectional links
+     */
+    public List<Link> getLinkTupleList() {
+        return linkTupleList;
+    }
+
+    private void setPublishAttributes() {
+
+    }
+
+    private void parseParams() {
+        if (params == null) {
+            throw new PktLinkParamsNotSpecified(nodeDpid1, nodeDpid2);
+        }
+        Set<Entry<String, JsonNode>> m = params.entrySet();
+        for (Entry<String, JsonNode> e : m) {
+            String key = e.getKey();
+            JsonNode j = e.getValue();
+            if (key.equals("nodeName1")) {
+                setNodeName1(j.asText());
+            } else if (key.equals("nodeName2")) {
+                setNodeName2(j.asText());
+            } else if (key.equals("port1")) {
+                setPort1(j.asInt());
+            } else if (key.equals("port2")) {
+                setPort2(j.asInt());
+            } else {
+                throw new UnknownPktLinkConfig(key, nodeDpid1, nodeDpid2);
+            }
+        }
+    }
+
+    private void validateParams() {
+        // TODO - wrong-names, duplicate links,
+        // duplicate use of port, is switch-allowed for which link is allowed?
+        // valid port numbers
+    }
+
+    public static class PktLinkParamsNotSpecified extends RuntimeException {
+        private static final long serialVersionUID = 6247582323691265513L;
+
+        public PktLinkParamsNotSpecified(String dpidA, String dpidB) {
+            super();
+            log.error("Params required for packet link - not specified "
+                    + "for link between switch1:{} and switch2:{}",
+                    dpidA, dpidB);
+        }
+    }
+
+    public static class UnknownPktLinkConfig extends RuntimeException {
+        private static final long serialVersionUID = -5750132094884129179L;
+
+        public UnknownPktLinkConfig(String key, String dpidA, String dpidB) {
+            super();
+            log.error("Unknown packet-link config {} for link between"
+                    + " dpid1: {} and dpid2: {}", key,
+                    dpidA, dpidB);
+        }
+    }
+
+}
diff --git a/src/main/java/org/onosproject/segmentrouting/config/SegmentRouterConfig.java b/src/main/java/org/onosproject/segmentrouting/config/SegmentRouterConfig.java
new file mode 100644
index 0000000..4775c77
--- /dev/null
+++ b/src/main/java/org/onosproject/segmentrouting/config/SegmentRouterConfig.java
@@ -0,0 +1,430 @@
+package org.onosproject.segmentrouting.config;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.onosproject.net.DeviceId;
+import org.onosproject.segmentrouting.config.NetworkConfig.SwitchConfig;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+/**
+ * Manages additional configuration for switches configured as Segment Routers.
+ */
+public class SegmentRouterConfig extends SwitchConfig {
+    protected static final Logger log = LoggerFactory
+            .getLogger(SegmentRouterConfig.class);
+    private String routerIp;
+    private String routerMac;
+    private int nodeSid;
+    private boolean isEdgeRouter;
+    private List<AdjacencySid> adjacencySids;
+    private List<Subnet> subnets;
+
+    public static final String ROUTER_IP = "routerIp";
+    public static final String ROUTER_MAC = "routerMac";
+    public static final String NODE_SID = "nodeSid";
+    public static final String ADJACENCY_SIDS = "adjacencySids";
+    public static final String SUBNETS = "subnets";
+    public static final String ISEDGE = "isEdgeRouter";
+    private static final int SRGB_MAX = 1000;
+
+    /**
+     * Parses and validates the additional configuration parameters applicable
+     * to segment routers.
+     *
+     * @param swc switch configuration
+     */
+    public SegmentRouterConfig(SwitchConfig swc) {
+        this.setName(swc.getName());
+        this.setDpid(swc.getDpid());
+        this.setType(swc.getType());
+        this.setLatitude(swc.getLatitude());
+        this.setLongitude(swc.getLongitude());
+        this.setParams(swc.getParams());
+        this.setAllowed(swc.isAllowed());
+        publishAttributes = new ConcurrentHashMap<String, String>();
+        adjacencySids = new ArrayList<AdjacencySid>();
+        subnets = new ArrayList<Subnet>();
+        parseParams();
+        validateParams();
+        setPublishAttributes();
+    }
+
+    /**
+     * Returns the configured segment router IP address.
+     *
+     * @return ip address in string format
+     */
+    public String getRouterIp() {
+        return routerIp;
+    }
+
+    public void setRouterIp(String routerIp) {
+        this.routerIp = routerIp;
+    }
+
+    /**
+     * Returns the configured segment router mac address.
+     *
+     * @return mac address in string format
+     */
+    public String getRouterMac() {
+        return routerMac;
+    }
+
+    public void setRouterMac(String routerMac) {
+        this.routerMac = routerMac;
+    }
+
+    /**
+     * Returns the configured sID for a segment router.
+     *
+     * @return segment identifier
+     */
+    public int getNodeSid() {
+        return nodeSid;
+    }
+
+    public void setNodeSid(int nodeSid) {
+        this.nodeSid = nodeSid;
+    }
+
+    /**
+     * Returns the flag that indicates the configured segment router
+     * is edge or backbone router.
+     *
+     * @return boolean
+     */
+    public boolean isEdgeRouter() {
+        return isEdgeRouter;
+    }
+
+    public void setIsEdgeRouter(boolean isEdge) {
+        this.isEdgeRouter = isEdge;
+    }
+
+    /**
+     * Class representing segment router adjacency identifier.
+     */
+    public static class AdjacencySid {
+        private int adjSid;
+        private List<Integer> ports;
+
+        public AdjacencySid(int adjSid, List<Integer> ports) {
+            this.ports = ports;
+            this.adjSid = adjSid;
+        }
+
+        /**
+         * Returns the list of ports part of a segment
+         * router adjacency identifier.
+         *
+         * @return list of integers
+         */
+        public List<Integer> getPorts() {
+            return ports;
+        }
+
+        public void setPorts(List<Integer> ports) {
+            this.ports = ports;
+        }
+
+        /**
+         * Returns the configured adjacency id of a segment router.
+         *
+         * @return integer
+         */
+        public int getAdjSid() {
+            return adjSid;
+        }
+
+        public void setAdjSid(int adjSid) {
+            this.adjSid = adjSid;
+        }
+    }
+
+    /**
+     * Returns the configured adjacent segment IDs for a segment router.
+     *
+     * @return list of adjacency identifier
+     */
+    public List<AdjacencySid> getAdjacencySids() {
+        return adjacencySids;
+    }
+
+    public void setAdjacencySids(List<AdjacencySid> adjacencySids) {
+        this.adjacencySids = adjacencySids;
+    }
+
+    /**
+     * Class representing a subnet attached to a segment router.
+     */
+    public static class Subnet {
+        private int portNo;
+        private String subnetIp;
+
+        public Subnet(int portNo, String subnetIp) {
+            this.portNo = portNo;
+            this.subnetIp = subnetIp;
+        }
+
+        /**
+         * Returns the port number of segment router on
+         * which subnet is attached.
+         *
+         * @return integer
+         */
+        public int getPortNo() {
+            return portNo;
+        }
+
+        public void setPortNo(int portNo) {
+            this.portNo = portNo;
+        }
+
+        /**
+         * Returns the configured subnet address.
+         *
+         * @return subnet ip address in string format
+         */
+        public String getSubnetIp() {
+            return subnetIp;
+        }
+
+        public void setSubnetIp(String subnetIp) {
+            this.subnetIp = subnetIp;
+        }
+    }
+
+    /**
+     * Returns the configured subnets for a segment router.
+     *
+     * @return list of subnets
+     */
+    public List<Subnet> getSubnets() {
+        return subnets;
+    }
+
+    public void setSubnets(List<Subnet> subnets) {
+        this.subnets = subnets;
+    }
+
+    // ********************
+    // Helper methods
+    // ********************
+
+    private void parseParams() {
+        if (params == null) {
+            throw new NetworkConfigException.ParamsNotSpecified(name);
+        }
+
+        Set<Entry<String, JsonNode>> m = params.entrySet();
+        for (Entry<String, JsonNode> e : m) {
+            String key = e.getKey();
+            JsonNode j = e.getValue();
+            if (key.equals("routerIp")) {
+                setRouterIp(j.asText());
+            } else if (key.equals("routerMac")) {
+                setRouterMac(j.asText());
+            } else if (key.equals("nodeSid")) {
+                setNodeSid(j.asInt());
+            } else if (key.equals("isEdgeRouter")) {
+                setIsEdgeRouter(j.asBoolean());
+            } else if (key.equals("adjacencySids") || key.equals("subnets")) {
+                getInnerParams(j, key);
+            } else {
+                throw new UnknownSegmentRouterConfig(key, dpid);
+            }
+        }
+    }
+
+    private void getInnerParams(JsonNode j, String innerParam) {
+        Iterator<JsonNode> innerList = j.elements();
+        while (innerList.hasNext()) {
+            Iterator<Entry<String, JsonNode>> f = innerList.next().fields();
+            int portNo = -1;
+            int adjSid = -1;
+            String subnetIp = null;
+            List<Integer> ports = null;
+            while (f.hasNext()) {
+                Entry<String, JsonNode> fe = f.next();
+                if (fe.getKey().equals("portNo")) {
+                    portNo = fe.getValue().asInt();
+                } else if (fe.getKey().equals("adjSid")) {
+                    adjSid = fe.getValue().asInt();
+                } else if (fe.getKey().equals("subnetIp")) {
+                    subnetIp = fe.getValue().asText();
+                } else if (fe.getKey().equals("ports")) {
+                    if (fe.getValue().isArray()) {
+                        Iterator<JsonNode> i = fe.getValue().elements();
+                        ports = new ArrayList<Integer>();
+                        while (i.hasNext()) {
+                            ports.add(i.next().asInt());
+                        }
+                    }
+                } else {
+                    throw new UnknownSegmentRouterConfig(fe.getKey(), dpid);
+                }
+            }
+            if (innerParam.equals("adjacencySids")) {
+                AdjacencySid ads = new AdjacencySid(adjSid, ports);
+                adjacencySids.add(ads);
+            } else {
+                Subnet sip = new Subnet(portNo, subnetIp);
+                subnets.add(sip);
+            }
+        }
+    }
+
+    private void validateParams() {
+        if (routerIp == null) {
+            throw new IpNotSpecified(dpid);
+        }
+        if (routerMac == null) {
+            throw new MacNotSpecified(dpid);
+        }
+        if (isEdgeRouter && subnets.isEmpty()) {
+            throw new SubnetNotSpecifiedInEdgeRouter(dpid);
+        }
+        if (!isEdgeRouter && !subnets.isEmpty()) {
+            throw new SubnetSpecifiedInBackboneRouter(dpid);
+        }
+        if (nodeSid > SRGB_MAX) {
+            throw new NodeLabelNotInSRGB(nodeSid, dpid);
+        }
+        for (AdjacencySid as : adjacencySids) {
+            int label = as.getAdjSid();
+            List<Integer> plist = as.getPorts();
+            if (label <= SRGB_MAX) {
+                throw new AdjacencyLabelInSRGB(label, dpid);
+            }
+            if (plist.size() <= 1) {
+                throw new AdjacencyLabelNotEnoughPorts(label, dpid);
+            }
+        }
+
+
+        // TODO more validations
+    }
+
+    /**
+     * Setting publishAttributes implies that this is the configuration that
+     * will be added to Topology.Switch object before it is published on the
+     * channel to other controller instances.
+     */
+    private void setPublishAttributes() {
+        publishAttributes.put(ROUTER_IP, routerIp);
+        publishAttributes.put(ROUTER_MAC, routerMac);
+        publishAttributes.put(NODE_SID, String.valueOf(nodeSid));
+        publishAttributes.put(ISEDGE, String.valueOf(isEdgeRouter));
+        ObjectMapper mapper = new ObjectMapper();
+        try {
+            publishAttributes.put(ADJACENCY_SIDS,
+                    mapper.writeValueAsString(adjacencySids));
+            publishAttributes.put(SUBNETS,
+                    mapper.writeValueAsString(subnets));
+        } catch (JsonProcessingException e) {
+            log.error("Error while writing SR config: {}", e.getCause());
+        } catch (IOException e) {
+            log.error("Error while writing SR config: {}", e.getCause());
+        }
+    }
+
+    // ********************
+    // Exceptions
+    // ********************
+
+    public static class IpNotSpecified extends RuntimeException {
+        private static final long serialVersionUID = -3001502553646331686L;
+
+        public IpNotSpecified(DeviceId dpid) {
+            super();
+            log.error("Router IP address not specified for SR config dpid:{}",
+                    dpid);
+        }
+    }
+
+    public static class MacNotSpecified extends RuntimeException {
+        private static final long serialVersionUID = -5850132094884129179L;
+
+        public MacNotSpecified(DeviceId dpid) {
+            super();
+            log.error("Router Mac address not specified for SR config dpid:{}",
+                    dpid);
+        }
+    }
+
+    public static class UnknownSegmentRouterConfig extends RuntimeException {
+        private static final long serialVersionUID = -5750132094884129179L;
+
+        public UnknownSegmentRouterConfig(String key, DeviceId dpid) {
+            super();
+            log.error("Unknown Segment Router config {} in dpid: {}", key,
+                    dpid);
+        }
+    }
+
+    public static class SubnetNotSpecifiedInEdgeRouter extends RuntimeException {
+        private static final long serialVersionUID = -5855458472668581268L;
+
+        public SubnetNotSpecifiedInEdgeRouter(DeviceId dpid) {
+            super();
+            log.error("Subnet was not specified for edge router in dpid: {}",
+                    dpid);
+        }
+    }
+
+    public static class SubnetSpecifiedInBackboneRouter extends RuntimeException {
+        private static final long serialVersionUID = 1L;
+
+        public SubnetSpecifiedInBackboneRouter(DeviceId dpid) {
+            super();
+            log.error("Subnet was specified in backbone router in dpid: {}",
+                    dpid);
+        }
+    }
+
+    public static class NodeLabelNotInSRGB extends RuntimeException {
+        private static final long serialVersionUID = -8482670903748519526L;
+
+        public NodeLabelNotInSRGB(int label, DeviceId dpid) {
+            super();
+            log.error("Node sif {} specified in not in global label-base "
+                    + "in dpid: {}", label,
+                    dpid);
+        }
+    }
+
+    public static class AdjacencyLabelInSRGB extends RuntimeException {
+        private static final long serialVersionUID = -8482670903748519526L;
+
+        public AdjacencyLabelInSRGB(int label, DeviceId dpid) {
+            super();
+            log.error("Adjaceny label {} specified from global label-base "
+                    + "in dpid: {}", label,
+                    dpid);
+        }
+    }
+
+    public static class AdjacencyLabelNotEnoughPorts extends RuntimeException {
+        private static final long serialVersionUID = -8482670903748519526L;
+
+        public AdjacencyLabelNotEnoughPorts(int label, DeviceId dpid) {
+            super();
+            log.error("Adjaceny label {} must be specified for at least 2 ports. "
+                    + "Adjacency labels for single ports are auto-generated "
+                    + "in dpid: {}", label,
+                    dpid);
+        }
+    }
+}
diff --git a/src/main/java/org/onosproject/segmentrouting/grouphandler/DefaultEdgeGroupHandler.java b/src/main/java/org/onosproject/segmentrouting/grouphandler/DefaultEdgeGroupHandler.java
new file mode 100644
index 0000000..0cd5743
--- /dev/null
+++ b/src/main/java/org/onosproject/segmentrouting/grouphandler/DefaultEdgeGroupHandler.java
@@ -0,0 +1,182 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.segmentrouting.grouphandler;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.onlab.packet.MplsLabel;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.Link;
+import org.onosproject.net.flow.DefaultTrafficTreatment;
+import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.group.DefaultGroupBucket;
+import org.onosproject.net.group.GroupBucket;
+import org.onosproject.net.group.GroupBuckets;
+import org.onosproject.net.group.GroupService;
+import org.onosproject.net.link.LinkService;
+
+/**
+ * Default ECMP group handler creation module for an edge device.
+ * This component creates a set of ECMP groups for every neighbor
+ * that this device is connected to.
+ * For example, consider a network of 4 devices: D0 (Segment ID: 100),
+ * D1 (Segment ID: 101), D2 (Segment ID: 102) and D3 (Segment ID: 103),
+ * where D0 and D3 are edge devices and D1 and D2 are transit devices.
+ * Assume device D0 is connected to 2 neighbors (D1 and D2 ).
+ * The following groups will be created in D0:
+ * 1) all ports to D1 + with no label push,
+ * 2) all ports to D1 + with label 102 pushed,
+ * 3) all ports to D1 + with label 103 pushed,
+ * 4) all ports to D2 + with no label push,
+ * 5) all ports to D2 + with label 101 pushed,
+ * 6) all ports to D2 + with label 103 pushed,
+ * 7) all ports to D1 and D2 + with label 103 pushed
+ */
+public class DefaultEdgeGroupHandler extends DefaultGroupHandler {
+
+    protected DefaultEdgeGroupHandler(DeviceId deviceId,
+                                  ApplicationId appId,
+                                  DeviceProperties config,
+                                  LinkService linkService,
+                                  GroupService groupService) {
+        super(deviceId, appId, config, linkService, groupService);
+    }
+
+    @Override
+    public void createGroups() {
+        log.debug("Creating default groups "
+                + "for edge device {}", deviceId);
+        Set<DeviceId> neighbors = devicePortMap.keySet();
+        if (neighbors == null || neighbors.isEmpty()) {
+            return;
+        }
+
+        // Create all possible Neighbor sets from this router
+        Set<Set<DeviceId>> powerSet = getPowerSetOfNeighbors(neighbors);
+        log.trace("createGroupsAtEdgeRouter: The size of neighbor powerset "
+                + "for sw {} is {}", deviceId, powerSet.size());
+        Set<NeighborSet> nsSet = new HashSet<NeighborSet>();
+        for (Set<DeviceId> combo : powerSet) {
+            if (combo.isEmpty()) {
+                continue;
+            }
+            List<Integer> groupSegmentIds =
+                    getSegmentIdsTobePairedWithNeighborSet(combo);
+            for (Integer sId : groupSegmentIds) {
+                NeighborSet ns = new NeighborSet(combo, sId);
+                log.trace("createGroupsAtEdgeRouter: sw {} "
+                        + "combo {} sId {} ns {}",
+                        deviceId, combo, sId, ns);
+                nsSet.add(ns);
+            }
+        }
+        log.trace("createGroupsAtEdgeRouter: The neighborset "
+                + "with label for sw {} is {}",
+                deviceId, nsSet);
+
+        createGroupsFromNeighborsets(nsSet);
+    }
+
+    @Override
+    protected void newNeighbor(Link newNeighborLink) {
+        log.debug("New Neighbor: Updating groups "
+                + "for edge device {}", deviceId);
+        // Recompute neighbor power set
+        addNeighborAtPort(newNeighborLink.dst().deviceId(),
+                          newNeighborLink.src().port());
+        // Compute new neighbor sets due to the addition of new neighbor
+        Set<NeighborSet> nsSet = computeImpactedNeighborsetForPortEvent(
+                                             newNeighborLink.dst().deviceId(),
+                                             devicePortMap.keySet());
+        createGroupsFromNeighborsets(nsSet);
+    }
+
+    @Override
+    protected void newPortToExistingNeighbor(Link newNeighborLink) {
+        log.debug("New port to existing neighbor: Updating "
+                + "groups for edge device {}", deviceId);
+        addNeighborAtPort(newNeighborLink.dst().deviceId(),
+                          newNeighborLink.src().port());
+        Set<NeighborSet> nsSet = computeImpactedNeighborsetForPortEvent(
+                                              newNeighborLink.dst().deviceId(),
+                                              devicePortMap.keySet());
+        for (NeighborSet ns : nsSet) {
+            // Create the new bucket to be updated
+            TrafficTreatment.Builder tBuilder =
+                    DefaultTrafficTreatment.builder();
+            tBuilder.setOutput(newNeighborLink.src().port())
+                    .setEthDst(deviceConfig.getDeviceMac(
+                          newNeighborLink.dst().deviceId()))
+                    .setEthSrc(nodeMacAddr);
+            if (ns.getEdgeLabel() != NeighborSet.NO_EDGE_LABEL) {
+                tBuilder.pushMpls()
+                        .setMpls(MplsLabel.
+                                 mplsLabel(ns.getEdgeLabel()));
+            }
+            GroupBucket updatedBucket = DefaultGroupBucket.
+                    createSelectGroupBucket(tBuilder.build());
+            GroupBuckets updatedBuckets = new GroupBuckets(
+                                        Arrays.asList(updatedBucket));
+            log.debug("newPortToExistingNeighborAtEdgeRouter: "
+                    + "groupService.addBucketsToGroup for neighborset{}", ns);
+            groupService.addBucketsToGroup(deviceId,
+                                           getGroupKey(ns),
+                                           updatedBuckets,
+                                           getGroupKey(ns),
+                                           appId);
+        }
+    }
+
+    @Override
+    protected Set<NeighborSet> computeImpactedNeighborsetForPortEvent(
+                                            DeviceId impactedNeighbor,
+                                            Set<DeviceId> updatedNeighbors) {
+        Set<Set<DeviceId>> powerSet = getPowerSetOfNeighbors(updatedNeighbors);
+
+        Set<DeviceId> tmp = new HashSet<DeviceId>();
+        tmp.addAll(updatedNeighbors);
+        tmp.remove(impactedNeighbor);
+        Set<Set<DeviceId>> tmpPowerSet = getPowerSetOfNeighbors(tmp);
+
+        // Compute the impacted neighbor sets
+        powerSet.removeAll(tmpPowerSet);
+
+        Set<NeighborSet> nsSet = new HashSet<NeighborSet>();
+        for (Set<DeviceId> combo : powerSet) {
+            if (combo.isEmpty()) {
+                continue;
+            }
+            List<Integer> groupSegmentIds =
+                    getSegmentIdsTobePairedWithNeighborSet(combo);
+            for (Integer sId : groupSegmentIds) {
+                NeighborSet ns = new NeighborSet(combo, sId);
+                log.trace("computeImpactedNeighborsetForPortEvent: sw {} "
+                        + "combo {} sId {} ns {}",
+                        deviceId, combo, sId, ns);
+                nsSet.add(ns);
+            }
+        }
+        log.trace("computeImpactedNeighborsetForPortEvent: The neighborset "
+                + "with label for sw {} is {}",
+                deviceId, nsSet);
+        return nsSet;
+    }
+
+}
diff --git a/src/main/java/org/onosproject/segmentrouting/grouphandler/DefaultGroupHandler.java b/src/main/java/org/onosproject/segmentrouting/grouphandler/DefaultGroupHandler.java
new file mode 100644
index 0000000..0ac3728
--- /dev/null
+++ b/src/main/java/org/onosproject/segmentrouting/grouphandler/DefaultGroupHandler.java
@@ -0,0 +1,412 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.segmentrouting.grouphandler;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.slf4j.LoggerFactory.getLogger;
+
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.onlab.packet.MacAddress;
+import org.onlab.packet.MplsLabel;
+import org.onlab.util.KryoNamespace;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.Link;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.flow.DefaultTrafficTreatment;
+import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.group.DefaultGroupBucket;
+import org.onosproject.net.group.DefaultGroupDescription;
+import org.onosproject.net.group.DefaultGroupKey;
+import org.onosproject.net.group.Group;
+import org.onosproject.net.group.GroupBucket;
+import org.onosproject.net.group.GroupBuckets;
+import org.onosproject.net.group.GroupDescription;
+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.link.LinkService;
+import org.slf4j.Logger;
+
+/**
+ * Default ECMP group handler creation module. This
+ * component creates a set of ECMP groups for every neighbor
+ * that this device is connected to based on whether the
+ * current device is an edge device or a transit device.
+ */
+public class DefaultGroupHandler {
+    protected final Logger log = getLogger(getClass());
+
+    protected final DeviceId deviceId;
+    protected final ApplicationId appId;
+    protected final DeviceProperties deviceConfig;
+    protected final List<Integer> allSegmentIds;
+    protected final int nodeSegmentId;
+    protected final boolean isEdgeRouter;
+    protected final MacAddress nodeMacAddr;
+    protected LinkService linkService;
+    protected GroupService groupService;
+
+    protected HashMap<DeviceId, Set<PortNumber>> devicePortMap =
+            new HashMap<DeviceId, Set<PortNumber>>();
+    protected HashMap<PortNumber, DeviceId> portDeviceMap =
+            new HashMap<PortNumber, DeviceId>();
+
+    private GroupListener listener = new InternalGroupListener();
+    protected KryoNamespace.Builder kryo = new KryoNamespace.Builder()
+    .register(URI.class)
+    .register(HashSet.class)
+    .register(DeviceId.class)
+    .register(PortNumber.class)
+    .register(NeighborSet.class)
+    .register(PolicyGroupIdentifier.class)
+    .register(PolicyGroupParams.class)
+    .register(GroupBucketIdentifier.class)
+    .register(GroupBucketIdentifier.BucketOutputType.class);
+
+    protected DefaultGroupHandler(DeviceId deviceId,
+                               ApplicationId appId,
+                               DeviceProperties config,
+                               LinkService linkService,
+                               GroupService groupService) {
+        this.deviceId = checkNotNull(deviceId);
+        this.appId = checkNotNull(appId);
+        this.deviceConfig = checkNotNull(config);
+        this.linkService = checkNotNull(linkService);
+        this.groupService = checkNotNull(groupService);
+        allSegmentIds = checkNotNull(config.getAllDeviceSegmentIds());
+        nodeSegmentId = config.getSegmentId(deviceId);
+        isEdgeRouter = config.isEdgeDevice(deviceId);
+        nodeMacAddr = checkNotNull(config.getDeviceMac(deviceId));
+
+        this.groupService.addListener(listener);
+
+        populateNeighborMaps();
+    }
+
+    /**
+     * Creates a group handler object based on the type of device. If
+     * device is of edge type it returns edge group handler, else it
+     * returns transit group handler.
+     *
+     * @param deviceId device identifier
+     * @param appId application identifier
+     * @param config interface to retrieve the device properties
+     * @param linkService link service object
+     * @param groupService group service object
+     * @return default group handler type
+     */
+    public static DefaultGroupHandler createGroupHandler(DeviceId deviceId,
+                                                  ApplicationId appId,
+                                                  DeviceProperties config,
+                                                  LinkService linkService,
+                                                  GroupService groupService) {
+        if (config.isEdgeDevice(deviceId)) {
+            return new DefaultEdgeGroupHandler(deviceId,
+                                               appId,
+                                               config,
+                                               linkService,
+                                               groupService);
+        } else {
+            return new DefaultTransitGroupHandler(deviceId,
+                                                  appId,
+                                                  config,
+                                                  linkService,
+                                                  groupService);
+        }
+    }
+
+    /**
+     * Creates the auto created groups for this device based on the
+     * current snapshot of the topology.
+     */
+    //Empty implementations to be overridden by derived classes
+    public void createGroups() {
+    }
+
+    /**
+     * Performs group creation or update procedures when a new link
+     * is discovered on this device.
+     *
+     * @param newLink new neighbor link
+     */
+    public void linkUp(Link newLink) {
+
+        if (newLink.type() != Link.Type.DIRECT) {
+            log.warn("linkUp: unknown link type");
+            return;
+        }
+
+
+        if (!newLink.src().deviceId().equals(deviceId)) {
+            log.warn("linkUp: deviceId{} doesn't match with link src{}",
+                     deviceId,
+                     newLink.src().deviceId());
+            return;
+        }
+
+        log.debug("Device {} linkUp at local port {} to neighbor {}",
+                  deviceId, newLink.src().port(), newLink.dst().deviceId());
+        if (devicePortMap.get(newLink.dst().deviceId()) == null) {
+            // New Neighbor
+            newNeighbor(newLink);
+        } else {
+            // Old Neighbor
+            newPortToExistingNeighbor(newLink);
+        }
+    }
+
+    /**
+     * Performs group recovery procedures when a port goes down
+     * on this device.
+     *
+     * @param port port number that has gone down
+     */
+    public void portDown(PortNumber port) {
+        if (portDeviceMap.get(port) == null) {
+            log.warn("portDown: unknown port");
+            return;
+        }
+        log.debug("Device {} portDown {} to neighbor {}",
+                  deviceId, port, portDeviceMap.get(port));
+        Set<NeighborSet> nsSet = computeImpactedNeighborsetForPortEvent(
+                                                     portDeviceMap.get(port),
+                                                     devicePortMap.keySet());
+        for (NeighborSet ns : nsSet) {
+            // Create the bucket to be removed
+            TrafficTreatment.Builder tBuilder =
+                    DefaultTrafficTreatment.builder();
+            tBuilder.setOutput(port)
+                    .setEthDst(deviceConfig.getDeviceMac(
+                         portDeviceMap.get(port)))
+                    .setEthSrc(nodeMacAddr);
+            if (ns.getEdgeLabel() != NeighborSet.NO_EDGE_LABEL) {
+                tBuilder.pushMpls()
+                    .setMpls(MplsLabel.mplsLabel(ns.getEdgeLabel()));
+            }
+            GroupBucket removeBucket = DefaultGroupBucket.
+                    createSelectGroupBucket(tBuilder.build());
+            GroupBuckets removeBuckets = new GroupBuckets(
+                                               Arrays.asList(removeBucket));
+            log.debug("portDown in device{}: "
+                    + "groupService.removeBucketsFromGroup "
+                    + "for neighborset{}", deviceId, ns);
+            groupService.removeBucketsFromGroup(deviceId,
+                                                getGroupKey(ns),
+                                                removeBuckets,
+                                                getGroupKey(ns),
+                                                appId);
+        }
+
+        devicePortMap.get(portDeviceMap.get(port)).remove(port);
+        portDeviceMap.remove(port);
+    }
+
+    /**
+     * Returns a group associated with the key.
+     *
+     * @param key cookie associated with the group
+     * @return group if found or null
+     */
+    public Group getGroup(GroupKey key) {
+        return groupService.getGroup(deviceId, key);
+    }
+
+    //Empty implementation
+    protected void newNeighbor(Link newLink) {
+    }
+
+    //Empty implementation
+    protected void newPortToExistingNeighbor(Link newLink) {
+    }
+
+    //Empty implementation
+    protected Set<NeighborSet> computeImpactedNeighborsetForPortEvent(
+                                    DeviceId impactedNeighbor,
+                                    Set<DeviceId> updatedNeighbors) {
+        return null;
+    }
+
+    private void populateNeighborMaps() {
+        Set<Link> outgoingLinks = linkService.getDeviceEgressLinks(deviceId);
+        for (Link link:outgoingLinks) {
+            if (link.type() != Link.Type.DIRECT) {
+                continue;
+            }
+            addNeighborAtPort(link.dst().deviceId(), link.src().port());
+        }
+    }
+
+    protected void addNeighborAtPort(DeviceId neighborId, PortNumber portToNeighbor) {
+        // Update DeviceToPort database
+        log.debug("Device {} addNeighborAtPort: neighbor {} at port {}",
+                  deviceId, neighborId, portToNeighbor);
+        if (devicePortMap.get(neighborId) != null) {
+            devicePortMap.get(neighborId).add(portToNeighbor);
+        } else {
+            Set<PortNumber> ports = new HashSet<PortNumber>();
+            ports.add(portToNeighbor);
+            devicePortMap.put(neighborId, ports);
+        }
+
+        // Update portToDevice database
+        if (portDeviceMap.get(portToNeighbor) == null) {
+            portDeviceMap.put(portToNeighbor, neighborId);
+        }
+    }
+
+    protected Set<Set<DeviceId>>
+            getPowerSetOfNeighbors(Set<DeviceId> neighbors) {
+        List<DeviceId> list = new ArrayList<DeviceId>(neighbors);
+        Set<Set<DeviceId>> sets = new HashSet<Set<DeviceId>>();
+        // get the number of elements in the neighbors
+        int elements = list.size();
+        // the number of members of a power set is 2^n
+        // including the empty set
+        int powerElements = (1 << elements);
+
+        // run a binary counter for the number of power elements
+        // NOTE: Exclude empty set
+        for (long i = 1; i < powerElements; i++) {
+            Set<DeviceId> neighborSubSet = new HashSet<DeviceId>();
+            for (int j = 0; j < elements; j++) {
+                if ((i >> j) % 2 == 1) {
+                    neighborSubSet.add(list.get(j));
+                }
+            }
+            sets.add(neighborSubSet);
+        }
+        return sets;
+    }
+
+    private boolean isSegmentIdSameAsNodeSegmentId(DeviceId deviceId, int sId) {
+        return (deviceConfig.getSegmentId(deviceId) == sId);
+    }
+
+    protected List<Integer> getSegmentIdsTobePairedWithNeighborSet(
+                                   Set<DeviceId> neighbors) {
+
+        List<Integer> nsSegmentIds = new ArrayList<Integer>();
+
+        // Always pair up with no edge label
+        //If (neighbors.size() == 1) {
+        nsSegmentIds.add(-1);
+        //}
+
+        // Filter out SegmentIds matching with the
+        // nodes in the combo
+        for (Integer sId : allSegmentIds) {
+            if (sId.equals(nodeSegmentId)) {
+                continue;
+            }
+            boolean filterOut = false;
+            // Check if the edge label being set is of
+            // any node in the Neighbor set
+            for (DeviceId deviceId : neighbors) {
+                if (isSegmentIdSameAsNodeSegmentId(deviceId, sId)) {
+                    filterOut = true;
+                    break;
+                }
+            }
+            if (!filterOut) {
+                nsSegmentIds.add(sId);
+            }
+        }
+        return nsSegmentIds;
+    }
+
+    protected void createGroupsFromNeighborsets(Set<NeighborSet> nsSet) {
+        for (NeighborSet ns : nsSet) {
+            // Create the bucket array from the neighbor set
+            List<GroupBucket> buckets = new ArrayList<GroupBucket>();
+            for (DeviceId d : ns.getDeviceIds()) {
+                for (PortNumber sp : devicePortMap.get(d)) {
+                    TrafficTreatment.Builder tBuilder =
+                            DefaultTrafficTreatment.builder();
+                    tBuilder.setOutput(sp)
+                            .setEthDst(deviceConfig.getDeviceMac(d))
+                            .setEthSrc(nodeMacAddr);
+                    if (ns.getEdgeLabel() != NeighborSet.NO_EDGE_LABEL) {
+                        tBuilder.pushMpls()
+                                .setMpls(MplsLabel.
+                                         mplsLabel(ns.getEdgeLabel()));
+                    }
+                    buckets.add(DefaultGroupBucket.createSelectGroupBucket(
+                                                                tBuilder.build()));
+                }
+            }
+            GroupBuckets groupBuckets = new GroupBuckets(buckets);
+            GroupDescription newGroupDesc = new DefaultGroupDescription(
+                                      deviceId,
+                                      Group.Type.SELECT,
+                                      groupBuckets,
+                                      getGroupKey(ns),
+                                      appId);
+            log.debug("createGroupsFromNeighborsets: "
+                    + "groupService.addGroup for neighborset{}", ns);
+            groupService.addGroup(newGroupDesc);
+        }
+    }
+
+    protected void handleGroupEvent(GroupEvent event) {
+        switch (event.type()) {
+        case GROUP_ADDED:
+            log.debug("Received GROUP_ADDED from group service "
+                    + "for device {} with group key{} with id{}",
+                      event.subject().deviceId(),
+                      event.subject().appCookie(),
+                      event.subject().id());
+            break;
+        case GROUP_UPDATED:
+            log.trace("Received GROUP_UPDATED from group service "
+                    + "for device {} with group key{} with id{}",
+                      event.subject().deviceId(),
+                      event.subject().appCookie(),
+                      event.subject().id());
+            break;
+        case GROUP_REMOVED:
+            log.debug("Received GROUP_REMOVED from group service "
+                    + "for device {} with group key{} with id{}",
+                      event.subject().deviceId(),
+                      event.subject().appCookie(),
+                      event.subject().id());
+            break;
+        default:
+            break;
+        }
+    }
+
+    private class InternalGroupListener implements GroupListener {
+
+        @Override
+        public void event(GroupEvent event) {
+            handleGroupEvent(event);
+        }
+    }
+
+    public GroupKey getGroupKey(Object obj) {
+        return new DefaultGroupKey(kryo.build().serialize(obj));
+    }
+
+}
diff --git a/src/main/java/org/onosproject/segmentrouting/grouphandler/DefaultTransitGroupHandler.java b/src/main/java/org/onosproject/segmentrouting/grouphandler/DefaultTransitGroupHandler.java
new file mode 100644
index 0000000..e053622
--- /dev/null
+++ b/src/main/java/org/onosproject/segmentrouting/grouphandler/DefaultTransitGroupHandler.java
@@ -0,0 +1,186 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.segmentrouting.grouphandler;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.onlab.packet.MplsLabel;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.Link;
+import org.onosproject.net.flow.DefaultTrafficTreatment;
+import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.group.DefaultGroupBucket;
+import org.onosproject.net.group.GroupBucket;
+import org.onosproject.net.group.GroupBuckets;
+import org.onosproject.net.group.GroupService;
+import org.onosproject.net.link.LinkService;
+
+/**
+ * Default ECMP group handler creation module for a transit device.
+ * This component creates a set of ECMP groups for every neighbor
+ * that this device is connected to.
+ * For example, consider a network of 4 devices: D0 (Segment ID: 100),
+ * D1 (Segment ID: 101), D2 (Segment ID: 102) and D3 (Segment ID: 103),
+ * where D0 and D3 are edge devices and D1 and D2 are transit devices.
+ * Assume transit device D1 is connected to 2 neighbors (D0 and D3 ).
+ * The following groups will be created in D1:
+ * 1) all ports to D0 + with no label push,
+ * 2) all ports to D3 + with no label push,
+ */
+public class DefaultTransitGroupHandler extends DefaultGroupHandler {
+
+    protected DefaultTransitGroupHandler(DeviceId deviceId,
+                                  ApplicationId appId,
+                                  DeviceProperties config,
+                                  LinkService linkService,
+                                  GroupService groupService) {
+        super(deviceId, appId, config, linkService, groupService);
+    }
+
+    @Override
+    public void createGroups() {
+        Set<DeviceId> neighbors = devicePortMap.keySet();
+        if (neighbors == null || neighbors.isEmpty()) {
+            return;
+        }
+
+        // Create all possible Neighbor sets from this router
+        // NOTE: Avoid any pairings of edge routers only
+        Set<Set<DeviceId>> sets = getPowerSetOfNeighbors(neighbors);
+        sets = filterEdgeRouterOnlyPairings(sets);
+        log.debug("createGroupsAtTransitRouter: The size of neighbor powerset "
+                + "for sw {} is {}", deviceId, sets.size());
+        Set<NeighborSet> nsSet = new HashSet<NeighborSet>();
+        for (Set<DeviceId> combo : sets) {
+            if (combo.isEmpty()) {
+                continue;
+            }
+            NeighborSet ns = new NeighborSet(combo);
+            log.debug("createGroupsAtTransitRouter: sw {} combo {} ns {}",
+                      deviceId, combo, ns);
+            nsSet.add(ns);
+        }
+        log.debug("createGroupsAtTransitRouter: The neighborset with label "
+                + "for sw {} is {}", deviceId, nsSet);
+
+        createGroupsFromNeighborsets(nsSet);
+    }
+
+    @Override
+    protected void newNeighbor(Link newNeighborLink) {
+        log.debug("New Neighbor: Updating groups for "
+                + "transit device {}", deviceId);
+        // Recompute neighbor power set
+        addNeighborAtPort(newNeighborLink.dst().deviceId(),
+                          newNeighborLink.src().port());
+        // Compute new neighbor sets due to the addition of new neighbor
+        Set<NeighborSet> nsSet = computeImpactedNeighborsetForPortEvent(
+                                             newNeighborLink.dst().deviceId(),
+                                             devicePortMap.keySet());
+        createGroupsFromNeighborsets(nsSet);
+    }
+
+    @Override
+    protected void newPortToExistingNeighbor(Link newNeighborLink) {
+        log.debug("New port to existing neighbor: Updating "
+                + "groups for transit device {}", deviceId);
+        addNeighborAtPort(newNeighborLink.dst().deviceId(),
+                          newNeighborLink.src().port());
+        Set<NeighborSet> nsSet = computeImpactedNeighborsetForPortEvent(
+                                              newNeighborLink.dst().deviceId(),
+                                              devicePortMap.keySet());
+        for (NeighborSet ns : nsSet) {
+            // Create the new bucket to be updated
+            TrafficTreatment.Builder tBuilder =
+                    DefaultTrafficTreatment.builder();
+            tBuilder.setOutput(newNeighborLink.src().port())
+                    .setEthDst(deviceConfig.getDeviceMac(
+                          newNeighborLink.dst().deviceId()))
+                    .setEthSrc(nodeMacAddr);
+            if (ns.getEdgeLabel() != NeighborSet.NO_EDGE_LABEL) {
+                tBuilder.pushMpls()
+                        .setMpls(MplsLabel.
+                                 mplsLabel(ns.getEdgeLabel()));
+            }
+            GroupBucket updatedBucket = DefaultGroupBucket.
+                    createSelectGroupBucket(tBuilder.build());
+            GroupBuckets updatedBuckets = new GroupBuckets(
+                                        Arrays.asList(updatedBucket));
+            log.debug("newPortToExistingNeighborAtEdgeRouter: "
+                    + "groupService.addBucketsToGroup for neighborset{}", ns);
+            groupService.addBucketsToGroup(deviceId,
+                                           getGroupKey(ns),
+                                           updatedBuckets,
+                                           getGroupKey(ns),
+                                           appId);
+        }
+    }
+
+    @Override
+    protected Set<NeighborSet> computeImpactedNeighborsetForPortEvent(
+                                            DeviceId impactedNeighbor,
+                                            Set<DeviceId> updatedNeighbors) {
+        Set<Set<DeviceId>> powerSet = getPowerSetOfNeighbors(updatedNeighbors);
+
+        Set<DeviceId> tmp = new HashSet<DeviceId>();
+        tmp.addAll(updatedNeighbors);
+        tmp.remove(impactedNeighbor);
+        Set<Set<DeviceId>> tmpPowerSet = getPowerSetOfNeighbors(tmp);
+
+        // Compute the impacted neighbor sets
+        powerSet.removeAll(tmpPowerSet);
+
+        powerSet = filterEdgeRouterOnlyPairings(powerSet);
+        Set<NeighborSet> nsSet = new HashSet<NeighborSet>();
+        for (Set<DeviceId> combo : powerSet) {
+            if (combo.isEmpty()) {
+                continue;
+            }
+            NeighborSet ns = new NeighborSet(combo);
+            log.debug("createGroupsAtTransitRouter: sw {} combo {} ns {}",
+                      deviceId, combo, ns);
+            nsSet.add(ns);
+        }
+        log.debug("computeImpactedNeighborsetForPortEvent: The neighborset with label "
+                + "for sw {} is {}", deviceId, nsSet);
+
+        return nsSet;
+    }
+
+    private Set<Set<DeviceId>> filterEdgeRouterOnlyPairings(Set<Set<DeviceId>> sets) {
+        Set<Set<DeviceId>> fiteredSets = new HashSet<Set<DeviceId>>();
+        for (Set<DeviceId> deviceSubSet : sets) {
+            if (deviceSubSet.size() > 1) {
+                boolean avoidEdgeRouterPairing = true;
+                for (DeviceId device : deviceSubSet) {
+                    if (!deviceConfig.isEdgeDevice(device)) {
+                        avoidEdgeRouterPairing = false;
+                        break;
+                    }
+                }
+                if (!avoidEdgeRouterPairing) {
+                    fiteredSets.add(deviceSubSet);
+                }
+            } else {
+                fiteredSets.add(deviceSubSet);
+            }
+        }
+        return fiteredSets;
+    }
+}
diff --git a/src/main/java/org/onosproject/segmentrouting/grouphandler/DeviceProperties.java b/src/main/java/org/onosproject/segmentrouting/grouphandler/DeviceProperties.java
new file mode 100644
index 0000000..816ca7b
--- /dev/null
+++ b/src/main/java/org/onosproject/segmentrouting/grouphandler/DeviceProperties.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.segmentrouting.grouphandler;
+
+import java.util.List;
+
+import org.onlab.packet.MacAddress;
+import org.onosproject.net.DeviceId;
+
+/**
+ * Mechanism through which group handler module retrieves
+ * the device specific attributes such as segment ID,
+ * Mac address...etc from group handler applications.
+ */
+public interface DeviceProperties {
+    /**
+     * Returns the segment id of a device to be used in group creation.
+     *
+     * @param deviceId device identifier
+     * @return segment id of a device
+     */
+    int getSegmentId(DeviceId deviceId);
+    /**
+     * Returns the Mac address of a device to be used in group creation.
+     *
+     * @param deviceId device identifier
+     * @return mac address of a device
+     */
+    MacAddress getDeviceMac(DeviceId deviceId);
+    /**
+     * Indicates whether a device is edge device or transit/core device.
+     *
+     * @param deviceId device identifier
+     * @return boolean
+     */
+    boolean isEdgeDevice(DeviceId deviceId);
+    /**
+     * Returns all segment IDs to be considered in building auto
+     *
+     * created groups.
+     * @return list of segment IDs
+     */
+    List<Integer> getAllDeviceSegmentIds();
+}
diff --git a/src/main/java/org/onosproject/segmentrouting/grouphandler/GroupBucketIdentifier.java b/src/main/java/org/onosproject/segmentrouting/grouphandler/GroupBucketIdentifier.java
new file mode 100644
index 0000000..2ecb449
--- /dev/null
+++ b/src/main/java/org/onosproject/segmentrouting/grouphandler/GroupBucketIdentifier.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.segmentrouting.grouphandler;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import org.onosproject.net.PortNumber;
+
+/**
+ * Representation of policy group bucket identifier. Not exposed to
+ * the application and only to be used internally.
+ */
+public class GroupBucketIdentifier {
+    private int label;
+    private BucketOutputType type;
+    private PortNumber outPort;
+    private PolicyGroupIdentifier outGroup;
+
+    protected enum BucketOutputType {
+        PORT,
+        GROUP
+    }
+
+    protected GroupBucketIdentifier(int label,
+                                    PortNumber outPort) {
+        this.label = label;
+        this.type = BucketOutputType.PORT;
+        this.outPort = checkNotNull(outPort);
+        this.outGroup = null;
+    }
+
+    protected GroupBucketIdentifier(int label,
+                                    PolicyGroupIdentifier outGroup) {
+        this.label = label;
+        this.type = BucketOutputType.GROUP;
+        this.outPort = null;
+        this.outGroup = checkNotNull(outGroup);
+    }
+
+    protected int label() {
+        return this.label;
+    }
+
+    protected BucketOutputType type() {
+        return this.type;
+    }
+
+    protected PortNumber outPort() {
+        return this.outPort;
+    }
+
+    protected PolicyGroupIdentifier outGroup() {
+        return this.outGroup;
+    }
+}
+
diff --git a/src/main/java/org/onosproject/segmentrouting/grouphandler/NeighborSet.java b/src/main/java/org/onosproject/segmentrouting/grouphandler/NeighborSet.java
new file mode 100644
index 0000000..7ada322
--- /dev/null
+++ b/src/main/java/org/onosproject/segmentrouting/grouphandler/NeighborSet.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.segmentrouting.grouphandler;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.util.HashSet;
+import java.util.Objects;
+import java.util.Set;
+
+import org.onosproject.net.DeviceId;
+
+/**
+ * Representation of a set of neighbor switch dpids along with edge node
+ * label. Meant to be used as a lookup-key in a hash-map to retrieve an
+ * ECMP-group that hashes packets to a set of ports connecting to the
+ * neighbors in this set.
+ */
+public class NeighborSet {
+    private final Set<DeviceId> neighbors;
+    private final int edgeLabel;
+    public static final int NO_EDGE_LABEL = -1;
+
+    /**
+     * Constructor with set of neighbors. Edge label is
+     * default to -1.
+     *
+     * @param neighbors set of neighbors to be part of neighbor set
+     */
+    public NeighborSet(Set<DeviceId> neighbors) {
+        checkNotNull(neighbors);
+        this.edgeLabel = NO_EDGE_LABEL;
+        this.neighbors = new HashSet<DeviceId>();
+        this.neighbors.addAll(neighbors);
+    }
+
+    /**
+     * Constructor with set of neighbors and edge label.
+     *
+     * @param neighbors set of neighbors to be part of neighbor set
+     * @param edgeLabel label to be pushed as part of group operation
+     */
+    public NeighborSet(Set<DeviceId> neighbors, int edgeLabel) {
+        checkNotNull(neighbors);
+        this.edgeLabel = edgeLabel;
+        this.neighbors = new HashSet<DeviceId>();
+        this.neighbors.addAll(neighbors);
+    }
+
+    /**
+     * Default constructor for kryo serialization.
+     */
+    public NeighborSet() {
+        this.edgeLabel = NO_EDGE_LABEL;
+        this.neighbors = new HashSet<DeviceId>();
+    }
+
+    /**
+     * Gets the neighbors part of neighbor set.
+     *
+     * @return set of neighbor identifiers
+     */
+    public Set<DeviceId> getDeviceIds() {
+        return neighbors;
+    }
+
+    /**
+     * Gets the label associated with neighbor set.
+     *
+     * @return integer
+     */
+    public int getEdgeLabel() {
+        return edgeLabel;
+    }
+
+    // The list of neighbor ids and label are used for comparison.
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (!(o instanceof NeighborSet)) {
+            return false;
+        }
+        NeighborSet that = (NeighborSet) o;
+        return (this.neighbors.containsAll(that.neighbors) &&
+                that.neighbors.containsAll(this.neighbors) &&
+                (this.edgeLabel == that.edgeLabel));
+    }
+
+    // The list of neighbor ids and label are used for comparison.
+    @Override
+    public int hashCode() {
+        int result = 17;
+        int combinedHash = 0;
+        for (DeviceId d : neighbors) {
+            combinedHash = combinedHash + Objects.hash(d);
+        }
+        result = 31 * result + combinedHash + Objects.hash(edgeLabel);
+
+        return result;
+    }
+
+    @Override
+    public String toString() {
+        return " Neighborset Sw: " + neighbors
+                + " and Label: " + edgeLabel;
+    }
+}
diff --git a/src/main/java/org/onosproject/segmentrouting/grouphandler/PolicyGroupHandler.java b/src/main/java/org/onosproject/segmentrouting/grouphandler/PolicyGroupHandler.java
new file mode 100644
index 0000000..9f35c74
--- /dev/null
+++ b/src/main/java/org/onosproject/segmentrouting/grouphandler/PolicyGroupHandler.java
@@ -0,0 +1,353 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.segmentrouting.grouphandler;
+
+import static org.slf4j.LoggerFactory.getLogger;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+
+import org.onlab.packet.MplsLabel;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.GroupId;
+import org.onosproject.segmentrouting.grouphandler.GroupBucketIdentifier.BucketOutputType;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.flow.DefaultTrafficTreatment;
+import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.group.DefaultGroupBucket;
+import org.onosproject.net.group.DefaultGroupDescription;
+import org.onosproject.net.group.GroupBucket;
+import org.onosproject.net.group.GroupBuckets;
+import org.onosproject.net.group.GroupDescription;
+import org.onosproject.net.group.GroupEvent;
+import org.onosproject.net.group.GroupService;
+import org.onosproject.net.link.LinkService;
+import org.slf4j.Logger;
+
+/**
+ * A module to create group chains based on the specified device
+ * ports and label stack to be applied on each port.
+ */
+public class PolicyGroupHandler extends DefaultGroupHandler {
+
+    private final Logger log = getLogger(getClass());
+    private HashMap<PolicyGroupIdentifier, PolicyGroupIdentifier> dependentGroups =
+            new HashMap<PolicyGroupIdentifier, PolicyGroupIdentifier>();
+
+    /**
+     * Policy group handler constructor.
+     *
+     * @param deviceId device identifier
+     * @param appId application identifier
+     * @param config interface to retrieve the device properties
+     * @param linkService link service object
+     * @param groupService group service object
+     */
+    public PolicyGroupHandler(DeviceId deviceId,
+                              ApplicationId appId,
+                              DeviceProperties config,
+                              LinkService linkService,
+                              GroupService groupService) {
+        super(deviceId, appId, config, linkService, groupService);
+    }
+
+    public PolicyGroupIdentifier createPolicyGroupChain(String id,
+                                                        List<PolicyGroupParams> params) {
+        List<GroupBucketIdentifier> bucketIds = new ArrayList<GroupBucketIdentifier>();
+        for (PolicyGroupParams param: params) {
+            List<PortNumber> ports = param.getPorts();
+            if (ports == null) {
+                log.warn("createPolicyGroupChain in sw {} with wrong "
+                        + "input parameters", deviceId);
+                return null;
+            }
+
+            int labelStackSize = (param.getLabelStack() != null) ?
+                                      param.getLabelStack().size() : 0;
+
+            if (labelStackSize > 1) {
+                for (PortNumber sp : ports) {
+                    PolicyGroupIdentifier previousGroupkey = null;
+                    DeviceId neighbor = portDeviceMap.get(sp);
+                    for (int idx = 0; idx < param.getLabelStack().size(); idx++) {
+                        int label = param.getLabelStack().get(idx).intValue();
+                        if (idx == (labelStackSize - 1)) {
+                            // Innermost Group
+                            GroupBucketIdentifier bucketId =
+                                    new GroupBucketIdentifier(label,
+                                                              previousGroupkey);
+                            bucketIds.add(bucketId);
+                        } else if (idx == 0) {
+                            // Outermost Group
+                            List<GroupBucket> outBuckets = new ArrayList<GroupBucket>();
+                            GroupBucketIdentifier bucketId =
+                                    new GroupBucketIdentifier(label, sp);
+                            PolicyGroupIdentifier key = new
+                                    PolicyGroupIdentifier(id,
+                                                          Arrays.asList(param),
+                                                          Arrays.asList(bucketId));
+                            TrafficTreatment.Builder tBuilder =
+                                    DefaultTrafficTreatment.builder();
+                            tBuilder.setOutput(sp)
+                                    .setEthDst(deviceConfig.
+                                               getDeviceMac(neighbor))
+                                    .setEthSrc(nodeMacAddr)
+                                    .pushMpls()
+                                    .setMpls(MplsLabel.mplsLabel(label));
+                            outBuckets.add(DefaultGroupBucket.
+                                           createSelectGroupBucket(tBuilder.build()));
+                            GroupDescription desc = new
+                                    DefaultGroupDescription(deviceId,
+                                                            GroupDescription.Type.INDIRECT,
+                                                            new GroupBuckets(outBuckets));
+                            //TODO: BoS
+                            previousGroupkey = key;
+                            groupService.addGroup(desc);
+                        } else {
+                            // Intermediate Groups
+                            GroupBucketIdentifier bucketId =
+                                    new GroupBucketIdentifier(label,
+                                                              previousGroupkey);
+                            PolicyGroupIdentifier key = new
+                                    PolicyGroupIdentifier(id,
+                                                          Arrays.asList(param),
+                                                          Arrays.asList(bucketId));
+                            // Add to group dependency list
+                            dependentGroups.put(previousGroupkey, key);
+                            previousGroupkey = key;
+                        }
+                    }
+                }
+            } else {
+                int label = -1;
+                if (labelStackSize == 1) {
+                    label = param.getLabelStack().get(0).intValue();
+                }
+                for (PortNumber sp : ports) {
+                    GroupBucketIdentifier bucketId =
+                            new GroupBucketIdentifier(label, sp);
+                    bucketIds.add(bucketId);
+                }
+            }
+        }
+        PolicyGroupIdentifier innermostGroupkey = null;
+        if (!bucketIds.isEmpty()) {
+            innermostGroupkey = new
+                    PolicyGroupIdentifier(id,
+                                          params,
+                                          bucketIds);
+            // Add to group dependency list
+            boolean fullyResolved = true;
+            for (GroupBucketIdentifier bucketId:bucketIds) {
+                if (bucketId.type() == BucketOutputType.GROUP) {
+                    dependentGroups.put(bucketId.outGroup(),
+                                        innermostGroupkey);
+                    fullyResolved = false;
+                }
+            }
+
+            if (fullyResolved) {
+                List<GroupBucket> outBuckets = new ArrayList<GroupBucket>();
+                for (GroupBucketIdentifier bucketId:bucketIds) {
+                    DeviceId neighbor = portDeviceMap.
+                            get(bucketId.outPort());
+                    TrafficTreatment.Builder tBuilder =
+                            DefaultTrafficTreatment.builder();
+                    tBuilder.setOutput(bucketId.outPort())
+                            .setEthDst(deviceConfig.
+                                       getDeviceMac(neighbor))
+                            .setEthSrc(nodeMacAddr);
+                    if (bucketId.label() != NeighborSet.NO_EDGE_LABEL) {
+                        tBuilder.pushMpls()
+                            .setMpls(MplsLabel.mplsLabel(bucketId.label()));
+                    }
+                    //TODO: BoS
+                    outBuckets.add(DefaultGroupBucket.
+                                   createSelectGroupBucket(tBuilder.build()));
+                }
+                GroupDescription desc = new
+                        DefaultGroupDescription(deviceId,
+                                                GroupDescription.Type.SELECT,
+                                                new GroupBuckets(outBuckets));
+                groupService.addGroup(desc);
+            }
+        }
+        return innermostGroupkey;
+    }
+
+    @Override
+    protected void handleGroupEvent(GroupEvent event) {
+        if (event.type() == GroupEvent.Type.GROUP_ADDED) {
+            if (dependentGroups.get(event.subject().appCookie()) != null) {
+                PolicyGroupIdentifier dependentGroupKey = dependentGroups.get(event.subject().appCookie());
+                dependentGroups.remove(event.subject().appCookie());
+                boolean fullyResolved = true;
+                for (GroupBucketIdentifier bucketId:
+                            dependentGroupKey.bucketIds()) {
+                    if (bucketId.type() != BucketOutputType.GROUP) {
+                        continue;
+                    }
+                    if (dependentGroups.containsKey(bucketId.outGroup())) {
+                        fullyResolved = false;
+                        break;
+                    }
+                }
+
+                if (fullyResolved) {
+                    List<GroupBucket> outBuckets = new ArrayList<GroupBucket>();
+                    for (GroupBucketIdentifier bucketId:
+                                dependentGroupKey.bucketIds()) {
+                        TrafficTreatment.Builder tBuilder =
+                                DefaultTrafficTreatment.builder();
+                        if (bucketId.label() != NeighborSet.NO_EDGE_LABEL) {
+                            tBuilder.pushMpls()
+                                    .setMpls(MplsLabel.
+                                             mplsLabel(bucketId.label()));
+                        }
+                        //TODO: BoS
+                        if (bucketId.type() == BucketOutputType.PORT) {
+                            DeviceId neighbor = portDeviceMap.
+                                        get(bucketId.outPort());
+                            tBuilder.setOutput(bucketId.outPort())
+                                    .setEthDst(deviceConfig.
+                                               getDeviceMac(neighbor))
+                                     .setEthSrc(nodeMacAddr);
+                        } else {
+                            if (groupService.
+                                    getGroup(deviceId,
+                                             getGroupKey(bucketId.
+                                                       outGroup())) == null) {
+                                throw new IllegalStateException();
+                            }
+                            GroupId indirectGroupId = groupService.
+                                    getGroup(deviceId,
+                                             getGroupKey(bucketId.
+                                                         outGroup())).id();
+                            tBuilder.group(indirectGroupId);
+                        }
+                        outBuckets.add(DefaultGroupBucket.
+                                       createSelectGroupBucket(tBuilder.build()));
+                    }
+                    GroupDescription desc = new
+                            DefaultGroupDescription(deviceId,
+                                                    GroupDescription.Type.SELECT,
+                                                    new GroupBuckets(outBuckets));
+                    groupService.addGroup(desc);
+                }
+            }
+        }
+    }
+
+    public PolicyGroupIdentifier generatePolicyGroupKey(String id,
+                                   List<PolicyGroupParams> params) {
+        List<GroupBucketIdentifier> bucketIds = new ArrayList<GroupBucketIdentifier>();
+        for (PolicyGroupParams param: params) {
+            List<PortNumber> ports = param.getPorts();
+            if (ports == null) {
+                log.warn("generateGroupKey in sw {} with wrong "
+                        + "input parameters", deviceId);
+                return null;
+            }
+
+            int labelStackSize = (param.getLabelStack() != null)
+                    ? param.getLabelStack().size() : 0;
+
+            if (labelStackSize > 1) {
+                for (PortNumber sp : ports) {
+                    PolicyGroupIdentifier previousGroupkey = null;
+                    for (int idx = 0; idx < param.getLabelStack().size(); idx++) {
+                        int label = param.getLabelStack().get(idx).intValue();
+                        if (idx == (labelStackSize - 1)) {
+                            // Innermost Group
+                            GroupBucketIdentifier bucketId =
+                                    new GroupBucketIdentifier(label,
+                                                              previousGroupkey);
+                            bucketIds.add(bucketId);
+                        } else if (idx == 0) {
+                            // Outermost Group
+                            GroupBucketIdentifier bucketId =
+                                    new GroupBucketIdentifier(label, sp);
+                            PolicyGroupIdentifier key = new
+                                    PolicyGroupIdentifier(id,
+                                                          Arrays.asList(param),
+                                                          Arrays.asList(bucketId));
+                            previousGroupkey = key;
+                        } else {
+                            // Intermediate Groups
+                            GroupBucketIdentifier bucketId =
+                                    new GroupBucketIdentifier(label,
+                                                              previousGroupkey);
+                            PolicyGroupIdentifier key = new
+                                    PolicyGroupIdentifier(id,
+                                                          Arrays.asList(param),
+                                                          Arrays.asList(bucketId));
+                            previousGroupkey = key;
+                        }
+                    }
+                }
+            } else {
+                int label = -1;
+                if (labelStackSize == 1) {
+                    label = param.getLabelStack().get(0).intValue();
+                }
+                for (PortNumber sp : ports) {
+                    GroupBucketIdentifier bucketId =
+                            new GroupBucketIdentifier(label, sp);
+                    bucketIds.add(bucketId);
+                }
+            }
+        }
+        PolicyGroupIdentifier innermostGroupkey = null;
+        if (!bucketIds.isEmpty()) {
+            innermostGroupkey = new
+                    PolicyGroupIdentifier(id,
+                                          params,
+                                          bucketIds);
+        }
+        return innermostGroupkey;
+    }
+
+    public void removeGroupChain(PolicyGroupIdentifier key) {
+        if (!(key instanceof PolicyGroupIdentifier)) {
+            throw new IllegalArgumentException();
+        }
+        List<PolicyGroupIdentifier> groupsToBeDeleted =
+                new ArrayList<PolicyGroupIdentifier>();
+        groupsToBeDeleted.add(key);
+
+        Iterator<PolicyGroupIdentifier> it =
+                groupsToBeDeleted.iterator();
+
+        while (it.hasNext()) {
+            PolicyGroupIdentifier innerMostGroupKey = it.next();
+            for (GroupBucketIdentifier bucketId:
+                        innerMostGroupKey.bucketIds()) {
+                if (bucketId.type() != BucketOutputType.GROUP) {
+                    groupsToBeDeleted.add(bucketId.outGroup());
+                }
+            }
+            groupService.removeGroup(deviceId,
+                                     getGroupKey(innerMostGroupKey),
+                                     appId);
+            it.remove();
+        }
+    }
+
+}
\ No newline at end of file
diff --git a/src/main/java/org/onosproject/segmentrouting/grouphandler/PolicyGroupIdentifier.java b/src/main/java/org/onosproject/segmentrouting/grouphandler/PolicyGroupIdentifier.java
new file mode 100644
index 0000000..44a0a2c
--- /dev/null
+++ b/src/main/java/org/onosproject/segmentrouting/grouphandler/PolicyGroupIdentifier.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.segmentrouting.grouphandler;
+
+import java.util.List;
+
+/**
+ * Representation of policy based group identifiers.
+ * Opaque to group handler applications and only the outermost
+ * policy group identifier in a chain is visible to the applications.
+ */
+public class PolicyGroupIdentifier {
+    private String id;
+    private List<PolicyGroupParams> inputParams;
+    private List<GroupBucketIdentifier> bucketIds;
+
+    /**
+     * Constructor.
+     *
+     * @param id unique identifier associated with the policy group
+     * @param input policy group params associated with this group
+     * @param bucketIds buckets associated with this group
+     */
+    protected PolicyGroupIdentifier(String id,
+                                 List<PolicyGroupParams> input,
+                                 List<GroupBucketIdentifier> bucketIds) {
+        this.id = id;
+        this.inputParams = input;
+        this.bucketIds = bucketIds;
+    }
+
+    /**
+     * Returns the bucket identifier list associated with the policy
+     * group identifier.
+     *
+     * @return list of bucket identifier
+     */
+    protected List<GroupBucketIdentifier> bucketIds() {
+        return this.bucketIds;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = 17;
+        int combinedHash = 0;
+        for (PolicyGroupParams input:inputParams) {
+            combinedHash = combinedHash + input.hashCode();
+        }
+        for (GroupBucketIdentifier bucketId:bucketIds) {
+            combinedHash = combinedHash + bucketId.hashCode();
+        }
+        result = 31 * result + combinedHash;
+
+        return result;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+
+        if (obj instanceof PolicyGroupIdentifier) {
+            PolicyGroupIdentifier that = (PolicyGroupIdentifier) obj;
+            boolean result = this.id.equals(that.id);
+            result = result &&
+                    this.inputParams.containsAll(that.inputParams) &&
+                    that.inputParams.containsAll(this.inputParams);
+            result = result &&
+                    this.bucketIds.containsAll(that.bucketIds) &&
+                    that.bucketIds.containsAll(this.bucketIds);
+            return result;
+        }
+
+        return false;
+    }
+}
diff --git a/src/main/java/org/onosproject/segmentrouting/grouphandler/PolicyGroupParams.java b/src/main/java/org/onosproject/segmentrouting/grouphandler/PolicyGroupParams.java
new file mode 100644
index 0000000..5a3f26b
--- /dev/null
+++ b/src/main/java/org/onosproject/segmentrouting/grouphandler/PolicyGroupParams.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.segmentrouting.grouphandler;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.util.List;
+import java.util.Objects;
+
+import org.onosproject.net.PortNumber;
+
+/**
+ * Representation of parameters used to create policy based groups.
+ */
+public class PolicyGroupParams {
+    private final List<PortNumber> ports;
+    private final List<Integer> labelStack;
+
+    /**
+     * Constructor.
+     *
+     * @param labelStack mpls label stack to be applied on the ports
+     * @param ports ports to be part of the policy group
+     */
+    public PolicyGroupParams(List<Integer> labelStack,
+                             List<PortNumber> ports) {
+        this.ports = checkNotNull(ports);
+        this.labelStack = checkNotNull(labelStack);
+    }
+
+    /**
+     * Returns the ports associated with the policy group params.
+     *
+     * @return list of port numbers
+     */
+    public List<PortNumber> getPorts() {
+        return ports;
+    }
+
+    /**
+     * Returns the label stack associated with the policy group params.
+     *
+     * @return list of integers
+     */
+    public List<Integer> getLabelStack() {
+        return labelStack;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = 17;
+        int combinedHash = 0;
+        for (PortNumber port:ports) {
+            combinedHash = combinedHash + port.hashCode();
+        }
+        combinedHash = combinedHash + Objects.hash(labelStack);
+        result = 31 * result + combinedHash;
+
+        return result;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+
+        if (obj instanceof PolicyGroupParams) {
+            PolicyGroupParams that = (PolicyGroupParams) obj;
+            boolean result = this.labelStack.equals(that.labelStack);
+            result = result &&
+                    this.ports.containsAll(that.ports) &&
+                    that.ports.containsAll(this.ports);
+            return result;
+        }
+
+        return false;
+    }
+}