[AETHER-598] Updates onos dependency to 2.2.4 and fix api/impl bundles issue.

API and Impl bundles both have `org.onosproject.segmentrouting.config` package
which creates issues during the app activation

Change-Id: I087c9a037b86ba522cfe568c43c102705e6ca4f2
diff --git a/impl/src/main/java/org/onosproject/segmentrouting/DeviceConfiguration.java b/impl/src/main/java/org/onosproject/segmentrouting/DeviceConfiguration.java
new file mode 100644
index 0000000..3251e7a
--- /dev/null
+++ b/impl/src/main/java/org/onosproject/segmentrouting/DeviceConfiguration.java
@@ -0,0 +1,881 @@
+/*
+ * Copyright 2015-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.segmentrouting;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import com.google.common.collect.HashMultimap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Multimaps;
+import com.google.common.collect.SetMultimap;
+import com.google.common.collect.Sets;
+import org.onlab.packet.Ip4Address;
+import org.onlab.packet.Ip6Address;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.IpPrefix;
+import org.onlab.packet.MacAddress;
+import org.onlab.packet.VlanId;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.HostId;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.config.ConfigException;
+import org.onosproject.net.config.basics.BasicDeviceConfig;
+import org.onosproject.net.config.basics.InterfaceConfig;
+import org.onosproject.net.host.InterfaceIpAddress;
+import org.onosproject.net.intf.Interface;
+import org.onosproject.routeservice.Route;
+import org.onosproject.segmentrouting.config.DeviceConfigNotFoundException;
+import org.onosproject.segmentrouting.config.DeviceProperties;
+import org.onosproject.segmentrouting.config.SegmentRoutingAppConfig;
+import org.onosproject.segmentrouting.config.SegmentRoutingDeviceConfig;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Segment Routing configuration component that reads the
+ * segment routing related configuration from Network Configuration Manager
+ * component and organizes in more accessible formats.
+ */
+public class DeviceConfiguration implements DeviceProperties {
+
+    private static final String NO_SUBNET = "No subnet configured on {}";
+
+    private static final Logger log = LoggerFactory.getLogger(DeviceConfiguration.class);
+    private final List<Integer> allSegmentIds = new ArrayList<>();
+    private final Map<DeviceId, SegmentRouterInfo> deviceConfigMap = new ConcurrentHashMap<>();
+    private SegmentRoutingManager srManager;
+
+    private class SegmentRouterInfo {
+        int ipv4NodeSid = -1;
+        int ipv6NodeSid = -1;
+        DeviceId deviceId;
+        Ip4Address ipv4Loopback;
+        Ip6Address ipv6Loopback;
+        MacAddress mac;
+        boolean isEdge;
+        SetMultimap<PortNumber, IpAddress> gatewayIps;
+        SetMultimap<PortNumber, IpPrefix> subnets;
+        Map<Integer, Set<Integer>> adjacencySids;
+        DeviceId pairDeviceId;
+        PortNumber pairLocalPort;
+        int pwRoutingLabel;
+
+        public SegmentRouterInfo() {
+            gatewayIps = Multimaps.synchronizedSetMultimap(HashMultimap.create());
+            subnets = Multimaps.synchronizedSetMultimap(HashMultimap.create());
+        }
+    }
+
+    /**
+     * Constructs device configuration for all Segment Router devices,
+     * organizing the data into various maps for easier access.
+     *
+     * @param srManager Segment Routing Manager
+     */
+    public DeviceConfiguration(SegmentRoutingManager srManager) {
+        this.srManager = srManager;
+        updateConfig();
+    }
+
+    public void updateConfig() {
+        // Read config from device subject, excluding gatewayIps and subnets.
+        Set<DeviceId> deviceSubjects =
+                srManager.cfgService.getSubjects(DeviceId.class, SegmentRoutingDeviceConfig.class);
+        deviceSubjects.forEach(subject -> {
+            BasicDeviceConfig basicDeviceConfig = srManager.cfgService.addConfig(subject, BasicDeviceConfig.class);
+            if (!basicDeviceConfig.purgeOnDisconnection()) {
+                // Setting purge on disconnection flag for the device SR has control over.
+                // addConfig returns a config if it exists or creates a new one.
+                log.info("PurgeOnDisconnection set to true for device {}", subject);
+                basicDeviceConfig.purgeOnDisconnection(true);
+                srManager.cfgService.applyConfig(subject, BasicDeviceConfig.class, basicDeviceConfig.node());
+            }
+            SegmentRoutingDeviceConfig config =
+                    srManager.cfgService.getConfig(subject, SegmentRoutingDeviceConfig.class);
+            SegmentRouterInfo info = new SegmentRouterInfo();
+            info.deviceId = subject;
+            info.ipv4NodeSid = config.nodeSidIPv4();
+            info.ipv6NodeSid = config.nodeSidIPv6();
+            info.ipv4Loopback = config.routerIpv4();
+            info.ipv6Loopback = config.routerIpv6();
+            info.mac = config.routerMac();
+            info.isEdge = config.isEdgeRouter();
+            info.adjacencySids = config.adjacencySids();
+            info.pairDeviceId = config.pairDeviceId();
+            info.pairLocalPort = config.pairLocalPort();
+            info.pwRoutingLabel = info.ipv4NodeSid + 1000;
+            deviceConfigMap.put(info.deviceId, info);
+            log.debug("Read device config for device: {}", info.deviceId);
+            /*
+             * IPv6 sid is not inserted. this part of the code is not used for now.
+             */
+            allSegmentIds.add(info.ipv4NodeSid);
+        });
+
+        // Read gatewayIps and subnets from port subject. Ignore suppressed ports.
+        Set<ConnectPoint> portSubjects = srManager.cfgService
+                .getSubjects(ConnectPoint.class, InterfaceConfig.class);
+        portSubjects.stream()
+                .filter(subject -> deviceConfigMap.containsKey(subject.deviceId()))
+                .filter(subject -> !isSuppressedPort(subject)).forEach(subject -> {
+            InterfaceConfig config =
+                    srManager.cfgService.getConfig(subject, InterfaceConfig.class);
+            Set<Interface> networkInterfaces;
+            try {
+                networkInterfaces = config.getInterfaces();
+            } catch (ConfigException e) {
+                log.error("Error loading port configuration");
+                return;
+            }
+            networkInterfaces.forEach(networkInterface -> {
+                VlanId vlanId = networkInterface.vlan();
+                ConnectPoint connectPoint = networkInterface.connectPoint();
+                DeviceId dpid = connectPoint.deviceId();
+                PortNumber port = connectPoint.port();
+                MacAddress mac = networkInterface.mac();
+                SegmentRouterInfo info = deviceConfigMap.get(dpid);
+
+                // skip if there is no corresponding device for this ConenctPoint
+                if (info != null) {
+                    // Extract subnet information
+                    List<InterfaceIpAddress> interfaceAddresses = networkInterface.ipAddressesList();
+                    interfaceAddresses.forEach(interfaceAddress -> {
+                        // Do not add /0, /32 and /128 to gateway IP list
+                        int prefixLength = interfaceAddress.subnetAddress().prefixLength();
+                        IpPrefix ipPrefix = interfaceAddress.subnetAddress();
+                        if (ipPrefix.isIp4()) {
+                            if (prefixLength != 0 && prefixLength != IpPrefix.MAX_INET_MASK_LENGTH) {
+                                info.gatewayIps.put(port, interfaceAddress.ipAddress());
+                            }
+                            info.subnets.put(port, interfaceAddress.subnetAddress());
+                        } else {
+                            if (prefixLength != 0 && prefixLength != IpPrefix.MAX_INET6_MASK_LENGTH) {
+                                info.gatewayIps.put(port, interfaceAddress.ipAddress());
+                            }
+                            info.subnets.put(port, interfaceAddress.subnetAddress());
+                        }
+                    });
+
+                    // Override interface mac with router mac
+                    if (!mac.equals(info.mac)) {
+                        ArrayNode array = (ArrayNode) config.node();
+                        for (JsonNode intfNode : array) {
+                            ObjectNode objNode = (ObjectNode) intfNode;
+                            objNode.put(InterfaceConfig.MAC, info.mac.toString());
+                        }
+                        srManager.cfgService.applyConfig(connectPoint, InterfaceConfig.class, array);
+                    }
+                }
+            });
+            // We register the connect point with the NRS.
+            srManager.registerConnectPoint(subject);
+        });
+    }
+
+    public Collection<DeviceId> getRouters() {
+        return deviceConfigMap.keySet();
+    }
+
+    @Override
+    public boolean isConfigured(DeviceId deviceId) {
+        return deviceConfigMap.get(deviceId) != null;
+    }
+
+    @Override
+    public int getIPv4SegmentId(DeviceId deviceId) throws DeviceConfigNotFoundException {
+        SegmentRouterInfo srinfo = deviceConfigMap.get(deviceId);
+        if (srinfo != null) {
+            log.trace("getIPv4SegmentId for device{} is {}", deviceId, srinfo.ipv4NodeSid);
+            return srinfo.ipv4NodeSid;
+        } else {
+            String message = "getIPv4SegmentId fails for device: " + deviceId + ".";
+            throw new DeviceConfigNotFoundException(message);
+        }
+    }
+
+    @Override
+    public int getIPv6SegmentId(DeviceId deviceId) throws DeviceConfigNotFoundException {
+        SegmentRouterInfo srinfo = deviceConfigMap.get(deviceId);
+        if (srinfo != null) {
+            log.trace("getIPv6SegmentId for device{} is {}", deviceId, srinfo.ipv6NodeSid);
+            return srinfo.ipv6NodeSid;
+        } else {
+            String message = "getIPv6SegmentId fails for device: " + deviceId + ".";
+            throw new DeviceConfigNotFoundException(message);
+        }
+    }
+
+    @Override
+    public int getPWRoutingLabel(DeviceId deviceId) throws DeviceConfigNotFoundException {
+        SegmentRouterInfo srinfo = deviceConfigMap.get(deviceId);
+        if (srinfo != null) {
+            log.trace("pwRoutingLabel for device{} is {}", deviceId, srinfo.pwRoutingLabel);
+            return srinfo.pwRoutingLabel;
+        } else {
+            String message = "getPWRoutingLabel fails for device: " + deviceId + ".";
+            throw new DeviceConfigNotFoundException(message);
+        }
+    }
+
+    /**
+     * Returns the IPv4 Node segment id of a segment router given its Router mac address.
+     *
+     * @param routerMac router mac address
+     * @return node segment id, or -1 if not found in config
+     */
+    public int getIPv4SegmentId(MacAddress routerMac) {
+        for (Map.Entry<DeviceId, SegmentRouterInfo> entry:
+                    deviceConfigMap.entrySet()) {
+            if (entry.getValue().mac.equals(routerMac)) {
+                return entry.getValue().ipv4NodeSid;
+            }
+        }
+
+        return -1;
+    }
+
+    /**
+     * Returns the IPv6 Node segment id of a segment router given its Router mac address.
+     *
+     * @param routerMac router mac address
+     * @return node segment id, or -1 if not found in config
+     */
+    public int getIPv6SegmentId(MacAddress routerMac) {
+        for (Map.Entry<DeviceId, SegmentRouterInfo> entry:
+                deviceConfigMap.entrySet()) {
+            if (entry.getValue().mac.equals(routerMac)) {
+                return entry.getValue().ipv6NodeSid;
+            }
+        }
+
+        return -1;
+    }
+
+    /**
+     * Returns the IPv4 Node segment id of a segment router given its Router ip address.
+     *
+     * @param routerAddress router ip address
+     * @return node segment id, or -1 if not found in config
+     */
+    public int getIPv4SegmentId(Ip4Address routerAddress) {
+        for (Map.Entry<DeviceId, SegmentRouterInfo> entry: deviceConfigMap.entrySet()) {
+            Ip4Address ipv4Loopback = entry.getValue().ipv4Loopback;
+            if (ipv4Loopback == null) {
+                continue;
+            }
+            if (entry.getValue().ipv4Loopback.equals(routerAddress)) {
+                if (entry.getValue().ipv4NodeSid == -1) {
+                    continue;
+                }
+                return entry.getValue().ipv4NodeSid;
+            }
+        }
+
+        return -1;
+    }
+
+    /**
+     * Returns the IPv6 Node segment id of a segment router given its Router ip address.
+     *
+     * @param routerAddress router ip address
+     * @return node segment id, or -1 if not found in config
+     */
+    public int getIPv6SegmentId(Ip6Address routerAddress) {
+        for (Map.Entry<DeviceId, SegmentRouterInfo> entry: deviceConfigMap.entrySet()) {
+            Ip6Address ipv6Loopback = entry.getValue().ipv6Loopback;
+            if (ipv6Loopback == null) {
+                continue;
+            }
+            if (entry.getValue().ipv6Loopback.equals(routerAddress)) {
+                if (entry.getValue().ipv6NodeSid == -1) {
+                    continue;
+                }
+                return entry.getValue().ipv6NodeSid;
+            }
+        }
+
+        return -1;
+    }
+
+    @Override
+    public MacAddress getDeviceMac(DeviceId deviceId) throws DeviceConfigNotFoundException {
+        SegmentRouterInfo srinfo = deviceConfigMap.get(deviceId);
+        if (srinfo != null) {
+            return srinfo.mac;
+        } else {
+            String message = "getDeviceMac fails for device: " + deviceId + ".";
+            throw new DeviceConfigNotFoundException(message);
+        }
+    }
+
+    @Override
+    public Ip4Address getRouterIpv4(DeviceId deviceId) throws DeviceConfigNotFoundException {
+        SegmentRouterInfo srinfo = deviceConfigMap.get(deviceId);
+        if (srinfo != null) {
+            log.trace("getRouterIpv4 for device{} is {}", deviceId, srinfo.ipv4Loopback);
+            return srinfo.ipv4Loopback;
+        } else {
+            String message = "getRouterIpv4 fails for device: " + deviceId + ".";
+            throw new DeviceConfigNotFoundException(message);
+        }
+    }
+
+    @Override
+    public Ip6Address getRouterIpv6(DeviceId deviceId) throws DeviceConfigNotFoundException {
+        SegmentRouterInfo srinfo = deviceConfigMap.get(deviceId);
+        if (srinfo != null) {
+            log.trace("getRouterIpv6 for device{} is {}", deviceId, srinfo.ipv6Loopback);
+            return srinfo.ipv6Loopback;
+        } else {
+            String message = "getRouterIpv6 fails for device: " + deviceId + ".";
+            throw new DeviceConfigNotFoundException(message);
+        }
+    }
+
+    /**
+     * Gets router ip address based on the destination ip address.
+     *
+     * @param destIpAddress the destination ip address
+     * @param routerDeviceId the device id
+     * @return the ip address of the routes
+     */
+    public IpAddress getRouterIpAddress(IpAddress destIpAddress, DeviceId routerDeviceId) {
+        IpAddress routerIpAddress;
+        try {
+            routerIpAddress = destIpAddress.isIp4() ? getRouterIpv4(routerDeviceId) :
+                    getRouterIpv6(routerDeviceId);
+        } catch (DeviceConfigNotFoundException e) {
+            routerIpAddress = null;
+        }
+        return routerIpAddress;
+    }
+
+    @Override
+    public boolean isEdgeDevice(DeviceId deviceId) throws DeviceConfigNotFoundException {
+        SegmentRouterInfo srinfo = deviceConfigMap.get(deviceId);
+        if (srinfo != null) {
+            log.trace("isEdgeDevice for device{} is {}", deviceId, srinfo.isEdge);
+            return srinfo.isEdge;
+        } else {
+            String message = "isEdgeDevice fails for device: " + deviceId + ".";
+            throw new DeviceConfigNotFoundException(message);
+        }
+    }
+
+    @Override
+    public List<Integer> getAllDeviceSegmentIds() {
+        return allSegmentIds;
+    }
+
+    @Override
+    public Map<IpPrefix, List<PortNumber>> getSubnetPortsMap(DeviceId deviceId)
+            throws DeviceConfigNotFoundException {
+        SegmentRouterInfo srinfo = deviceConfigMap.get(deviceId);
+        if (srinfo == null) {
+            String message = "getSubnetPortsMap fails for device: " + deviceId + ".";
+            throw new DeviceConfigNotFoundException(message);
+        }
+        // Construct subnet-port mapping from port-subnet mapping
+        SetMultimap<PortNumber, IpPrefix> portSubnetMap = srinfo.subnets;
+        Map<IpPrefix, List<PortNumber>> subnetPortMap = new HashMap<>();
+
+        portSubnetMap.entries().forEach(entry -> {
+            PortNumber port = entry.getKey();
+            IpPrefix subnet = entry.getValue();
+
+            if (subnet.prefixLength() == IpPrefix.MAX_INET_MASK_LENGTH ||
+                    subnet.prefixLength() == IpPrefix.MAX_INET6_MASK_LENGTH) {
+                return;
+            }
+
+            if (subnetPortMap.containsKey(subnet)) {
+                subnetPortMap.get(subnet).add(port);
+            } else {
+                ArrayList<PortNumber> ports = new ArrayList<>();
+                ports.add(port);
+                subnetPortMap.put(subnet, ports);
+            }
+        });
+        return subnetPortMap;
+    }
+
+    /**
+     * Returns the device identifier or data plane identifier (dpid)
+     * of a segment router given its segment id.
+     *
+     * @param sid segment id
+     * @return deviceId device identifier
+     */
+    public DeviceId getDeviceId(int sid) {
+        for (Map.Entry<DeviceId, SegmentRouterInfo> entry:
+            deviceConfigMap.entrySet()) {
+            if (entry.getValue().ipv4NodeSid == sid ||
+                    entry.getValue().ipv6NodeSid == sid) {
+                return entry.getValue().deviceId;
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * Returns the device identifier or data plane identifier (dpid)
+     * of a segment router given its router ip address.
+     *
+     * @param ipAddress router ip address
+     * @return deviceId device identifier
+     */
+    public DeviceId getDeviceId(Ip4Address ipAddress) {
+        for (Map.Entry<DeviceId, SegmentRouterInfo> entry:
+            deviceConfigMap.entrySet()) {
+            if (entry.getValue().ipv4Loopback.equals(ipAddress)) {
+                return entry.getValue().deviceId;
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * Returns the device identifier or data plane identifier (dpid)
+     * of a segment router given its router ipv6 address.
+     *
+     * @param ipAddress router ipv6 address
+     * @return deviceId device identifier
+     */
+    public DeviceId getDeviceId(Ip6Address ipAddress) {
+        for (Map.Entry<DeviceId, SegmentRouterInfo> entry:
+                deviceConfigMap.entrySet()) {
+            if (entry.getValue().ipv6Loopback.equals(ipAddress)) {
+                return entry.getValue().deviceId;
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * Returns the configured port ip addresses for a segment router.
+     * These addresses serve as gateway IP addresses for the subnets configured
+     * on those ports.
+     *
+     * @param deviceId device identifier
+     * @return immutable set of ip addresses configured on the ports or null if not found
+     */
+    public Set<IpAddress> getPortIPs(DeviceId deviceId) {
+        SegmentRouterInfo srinfo = deviceConfigMap.get(deviceId);
+        if (srinfo != null) {
+            log.trace("getSubnetGatewayIps for device{} is {}", deviceId,
+                      srinfo.gatewayIps.values());
+            return ImmutableSet.copyOf(srinfo.gatewayIps.values());
+        }
+        return null;
+    }
+
+    /**
+     * Returns configured subnets for a segment router.
+     *
+     * @param deviceId device identifier
+     * @return list of ip prefixes or null if not found
+     */
+    public Set<IpPrefix> getConfiguredSubnets(DeviceId deviceId) {
+        Set<IpPrefix> subnets = srManager.interfaceService.getInterfaces().stream()
+                .filter(intf -> Objects.equals(deviceId, intf.connectPoint().deviceId()))
+                .flatMap(intf -> intf.ipAddressesList().stream())
+                .map(InterfaceIpAddress::subnetAddress)
+                .collect(Collectors.toSet());
+
+        if (subnets.isEmpty()) {
+            log.debug(NO_SUBNET, deviceId);
+            return Collections.emptySet();
+        }
+        return subnets;
+    }
+
+    /**
+     * Returns all subnets for a segment router, including subnets learnt from route service.
+     *
+     * @param deviceId device identifier
+     * @return set of ip prefixes or null if not found
+     * @deprecated use getBatchedSubnets(DeviceId deviceId) instead
+     */
+    @Deprecated
+    public Set<IpPrefix> getSubnets(DeviceId deviceId) {
+        SegmentRouterInfo srinfo = deviceConfigMap.get(deviceId);
+        if (srinfo != null && srinfo.subnets != null) {
+            // Note: ImmutableSet.Builder.addAll calls the iterator of parameter internally,
+            //       which is not protected by SynchronizedCollection mutex.
+            ImmutableSet.Builder<IpPrefix> builder = ImmutableSet.builder();
+            srinfo.subnets.forEach((k, v) -> builder.add(v));
+            return builder.build();
+        }
+        return null;
+    }
+
+    /**
+     * Returns batches of all subnets reachable via given next hop
+     * <p>
+     * First batch includes FPM and STATIC routes
+     * Second batch includes all other type of routes obtained from routeService, including DHCP routes.
+     *
+     * @param hostId next hop host id
+     * @return list of subnet batches, each batch includes a set of prefixes.
+     */
+    // TODO Querying routeService directly may be expensive. Some kind of reverse lookup cache should be developed.
+    public List<Set<IpPrefix>> getBatchedSubnets(HostId hostId) {
+        Set<IpPrefix> high = Sets.newHashSet();
+        Set<IpPrefix> low = Sets.newHashSet();
+
+        srManager.routeService.getRouteTables().stream()
+                .map(tableId -> srManager.routeService.getResolvedRoutes(tableId))
+                .flatMap(Collection::stream)
+                .forEach(resolvedRoute -> {
+                    // Continue if next hop is not what we are looking for
+                    if (!Objects.equals(hostId.mac(), resolvedRoute.nextHopMac()) ||
+                            !Objects.equals(hostId.vlanId(), resolvedRoute.nextHopVlan())) {
+                        return;
+                    }
+                    // Prioritize STATIC and FPM among others
+                    if (resolvedRoute.route().source() == Route.Source.STATIC ||
+                            resolvedRoute.route().source() == Route.Source.FPM) {
+                        high.add(resolvedRoute.prefix());
+                    } else {
+                        low.add(resolvedRoute.prefix());
+                    }
+                });
+        return Stream.of(high, low).filter(set -> !set.isEmpty()).collect(Collectors.toList());
+    }
+
+    /**
+     * Returns batches of all subnets reachable on the given device.
+     * <p>
+     * First batch includes configured subnets, FPM and STATIC routes
+     * Second batch includes all other type of routes obtained from routeService, including DHCP routes.
+     *
+     * @param deviceId device identifier
+     * @return list of subnet batches, each batch includes a set of prefixes.
+     */
+    // TODO Querying routeService directly may be expensive. Some kind of reverse lookup cache should be developed.
+    public List<Set<IpPrefix>> getBatchedSubnets(DeviceId deviceId) {
+        Set<IpPrefix> high = Sets.newHashSet();
+        Set<IpPrefix> low = Sets.newHashSet();
+
+        high.addAll(getConfiguredSubnets(deviceId));
+        srManager.routeService.getRouteTables().stream()
+                .map(tableId -> srManager.routeService.getResolvedRoutes(tableId))
+                .flatMap(Collection::stream)
+                .forEach(resolvedRoute -> {
+                    // Continue to next resolved route if none of the next hop attaches to given device
+                    if (srManager.nextHopLocations(resolvedRoute).stream()
+                            .noneMatch(cp -> Objects.equals(deviceId, cp.deviceId()))) {
+                        return;
+                    }
+                    // Prioritize STATIC and FPM among others
+                    if (resolvedRoute.route().source() == Route.Source.STATIC ||
+                            resolvedRoute.route().source() == Route.Source.FPM) {
+                        high.add(resolvedRoute.prefix());
+                    } else {
+                        low.add(resolvedRoute.prefix());
+                    }
+                });
+        return Lists.newArrayList(high, low);
+    }
+
+    /**
+     * Returns the subnet configuration of given device and port.
+     *
+     * @param deviceId Device ID
+     * @param port Port number
+     * @return The subnets configured on given port or empty set if
+     *         the port is unconfigured or suppressed.
+     */
+    public Set<IpPrefix> getPortSubnets(DeviceId deviceId, PortNumber port) {
+        ConnectPoint connectPoint = new ConnectPoint(deviceId, port);
+
+        if (isSuppressedPort(connectPoint)) {
+            return Collections.emptySet();
+        }
+
+        Set<IpPrefix> subnets = srManager.interfaceService.getInterfacesByPort(connectPoint).stream()
+                .flatMap(intf -> intf.ipAddressesList().stream())
+                .map(InterfaceIpAddress::subnetAddress)
+                .collect(Collectors.toSet());
+
+        if (subnets.isEmpty()) {
+            log.debug(NO_SUBNET, connectPoint);
+            return Collections.emptySet();
+        }
+        return subnets;
+    }
+
+    /**
+     * Returns all ports that has a subnet that contains any of the given IP addresses.
+     *
+     * @param ips a set of IP addresses
+     * @return a set of connect point that has a subnet that contains any of the given IP addresses
+     */
+    public Set<ConnectPoint> getPortByIps(Set<IpAddress> ips) {
+        return srManager.interfaceService.getInterfaces().stream()
+                .filter(intf -> intf.ipAddressesList().stream().anyMatch(intfAddress ->
+                            ips.stream().anyMatch(ip -> intfAddress.subnetAddress().contains(ip))))
+                .map(Interface::connectPoint)
+                .collect(Collectors.toSet());
+    }
+
+    /**
+     * Returns all the connect points of the segment routers that have the
+     * specified ip address in their subnets.
+     *
+     * @param destIpAddress target ip address
+     * @return connect points of the segment routers
+     */
+    public Set<ConnectPoint> getConnectPointsForASubnetHost(IpAddress destIpAddress) {
+        return srManager.interfaceService.getMatchingInterfaces(destIpAddress).stream()
+                .map(Interface::connectPoint)
+                .collect(Collectors.toSet());
+    }
+
+    /**
+     * 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) {
+        Interface matchIntf = srManager.interfaceService.getMatchingInterface(destIpAddress);
+
+        if (matchIntf == null) {
+            log.debug("No router was found for {}", destIpAddress);
+            return null;
+        }
+
+        DeviceId routerDeviceId = matchIntf.connectPoint().deviceId();
+        SegmentRouterInfo srInfo = deviceConfigMap.get(routerDeviceId);
+        if (srInfo == null) {
+            log.debug("No device config was found for {}", routerDeviceId);
+            return null;
+        }
+
+        return srInfo.ipv4Loopback;
+    }
+
+    /**
+     * Returns the router ipv6 address of segment router that has the
+     * specified ip address in its subnets.
+     *
+     * @param destIpAddress target ip address
+     * @return router ip address
+     */
+    public Ip6Address getRouterIpAddressForASubnetHost(Ip6Address destIpAddress) {
+        Interface matchIntf = srManager.interfaceService.getMatchingInterface(destIpAddress);
+
+        if (matchIntf == null) {
+            log.debug("No router was found for {}", destIpAddress);
+            return null;
+        }
+
+        DeviceId routerDeviceId = matchIntf.connectPoint().deviceId();
+        SegmentRouterInfo srInfo = deviceConfigMap.get(routerDeviceId);
+        if (srInfo == null) {
+            log.debug("No device config was found for {}", routerDeviceId);
+            return null;
+        }
+
+        return srInfo.ipv6Loopback;
+    }
+
+    /**
+     * 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 or null if not found
+     */
+    public MacAddress getRouterMacForAGatewayIp(IpAddress gatewayIpAddress) {
+        for (Map.Entry<DeviceId, SegmentRouterInfo> entry:
+                deviceConfigMap.entrySet()) {
+            if (entry.getValue().gatewayIps.
+                    values().contains(gatewayIpAddress)) {
+                return entry.getValue().mac;
+            }
+        }
+
+        log.debug("Cannot find a router for {}", gatewayIpAddress);
+        return null;
+    }
+
+    /**
+     * Checks if the host IP is in any of the subnet defined in the router with the
+     * device ID given.
+     *
+     * @param deviceId device identification of the router
+     * @param hostIp   host IP address to check
+     * @return true if the given IP is within any of the subnet defined in the router,
+     * false if no subnet is defined in the router or if the host is not
+     * within any subnet defined in the router
+     */
+    public boolean inSameSubnet(DeviceId deviceId, IpAddress hostIp) {
+        Set<IpPrefix> subnets = getConfiguredSubnets(deviceId);
+        if (subnets == null) {
+            return false;
+        }
+
+        for (IpPrefix subnet: subnets) {
+            // Exclude /0 since it is a special case used for default route
+            if (subnet.prefixLength() != 0 && subnet.contains(hostIp)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Checks if the IP is in the subnet defined on given connect point.
+     *
+     * @param connectPoint Connect point
+     * @param ip The IP address to check
+     * @return True if the IP belongs to the subnet.
+     *         False if the IP does not belong to the subnet, or
+     *         there is no subnet configuration on given connect point.
+     */
+    public boolean inSameSubnet(ConnectPoint connectPoint, IpAddress ip) {
+        return getPortSubnets(connectPoint.deviceId(), connectPoint.port()).stream()
+                .anyMatch(ipPrefix -> ipPrefix.contains(ip));
+    }
+
+    /**
+     * Returns the ports corresponding to the adjacency Sid given.
+     *
+     * @param deviceId device identification of the router
+     * @param sid adjacency Sid
+     * @return set of port numbers
+     */
+    public Set<Integer> getPortsForAdjacencySid(DeviceId deviceId, int sid) {
+        SegmentRouterInfo srinfo = deviceConfigMap.get(deviceId);
+        return srinfo != null ?
+                ImmutableSet.copyOf(srinfo.adjacencySids.get(sid)) :
+                ImmutableSet.copyOf(new HashSet<>());
+    }
+
+    /**
+     * Check if the Sid given is whether adjacency Sid of the router device or not.
+     *
+     * @param deviceId device identification of the router
+     * @param sid Sid to check
+     * @return true if the Sid given is the adjacency Sid of the device,
+     * otherwise false
+     */
+    public boolean isAdjacencySid(DeviceId deviceId, int sid) {
+        SegmentRouterInfo srinfo = deviceConfigMap.get(deviceId);
+        return srinfo != null && srinfo.adjacencySids.containsKey(sid);
+    }
+
+    /**
+     * Add subnet to specific connect point.
+     *
+     * @param cp connect point
+     * @param ipPrefix subnet being added to the device
+     */
+    public void addSubnet(ConnectPoint cp, IpPrefix ipPrefix) {
+        checkNotNull(cp);
+        checkNotNull(ipPrefix);
+        SegmentRouterInfo srinfo = deviceConfigMap.get(cp.deviceId());
+        if (srinfo == null) {
+            log.warn("Device {} is not configured. Abort.", cp.deviceId());
+            return;
+        }
+        srinfo.subnets.put(cp.port(), ipPrefix);
+    }
+
+    /**
+     * Remove subnet from specific connect point.
+     *
+     * @param cp connect point
+     * @param ipPrefix subnet being removed to the device
+     */
+    public void removeSubnet(ConnectPoint cp, IpPrefix ipPrefix) {
+        checkNotNull(cp);
+        checkNotNull(ipPrefix);
+        SegmentRouterInfo srinfo = deviceConfigMap.get(cp.deviceId());
+        if (srinfo == null) {
+            log.warn("Device {} is not configured. Abort.", cp.deviceId());
+            return;
+        }
+        srinfo.subnets.remove(cp.port(), ipPrefix);
+    }
+
+    private boolean isSuppressedPort(ConnectPoint connectPoint) {
+        SegmentRoutingAppConfig appConfig = srManager.cfgService
+                .getConfig(srManager.appId(), SegmentRoutingAppConfig.class);
+        if (appConfig != null && appConfig.suppressSubnet().contains(connectPoint)) {
+            log.info("Interface configuration on port {} is ignored", connectPoint);
+            return true;
+        }
+        return false;
+    }
+
+    public boolean isPairedEdge(DeviceId deviceId) throws DeviceConfigNotFoundException {
+        if (!isEdgeDevice(deviceId)) {
+            return false;
+        }
+        SegmentRouterInfo srinfo = deviceConfigMap.get(deviceId);
+        return (srinfo.pairDeviceId == null) ? false : true;
+    }
+
+    public DeviceId getPairDeviceId(DeviceId deviceId) throws DeviceConfigNotFoundException {
+        SegmentRouterInfo srinfo = deviceConfigMap.get(deviceId);
+        if (srinfo != null) {
+            return srinfo.pairDeviceId;
+        } else {
+            String message = "getPairDeviceId fails for device: " + deviceId + ".";
+            throw new DeviceConfigNotFoundException(message);
+        }
+    }
+
+    public PortNumber getPairLocalPort(DeviceId deviceId)
+            throws DeviceConfigNotFoundException {
+        SegmentRouterInfo srinfo = deviceConfigMap.get(deviceId);
+        if (srinfo != null) {
+            return srinfo.pairLocalPort;
+        } else {
+            String message = "getPairLocalPort fails for device: " + deviceId + ".";
+            throw new DeviceConfigNotFoundException(message);
+        }
+    }
+
+    public boolean isPairLocalPort(DeviceId devId, PortNumber pnum) {
+        return pnum.equals(srManager.getPairLocalPort(devId).orElse(null));
+    }
+}