Added IPv6 routing support to SDN-IP, and integrated it with BGP:
 * The routing entries as received from BGP can be either IPv4 or IPv6
 * The CLI prints the IPv4 and IPv6 routes
 * The BGP peering is still over IPv4, so configuration-wise the IPv6
   routes from the eBGP peers have to be configured to use the IPv4 peering.
 * The integration/testing with the IPv6 Network Discovery Protocol is not
   done yet.
 * The integration/testing with IPv6 intents is not done yet.

Also:
 * Moved nested class BgpSessionManager.BgpRouteSelector out of the
   BgpSessionManager class.
 * Code cleanup.

Change-Id: I9f2dbe4395a72d353bbf215a8a14b01b53c3423f
diff --git a/apps/sdnip/src/main/java/org/onosproject/sdnip/IntentSynchronizer.java b/apps/sdnip/src/main/java/org/onosproject/sdnip/IntentSynchronizer.java
index d6b7412..0a0e569 100644
--- a/apps/sdnip/src/main/java/org/onosproject/sdnip/IntentSynchronizer.java
+++ b/apps/sdnip/src/main/java/org/onosproject/sdnip/IntentSynchronizer.java
@@ -29,7 +29,7 @@
 import java.util.concurrent.Semaphore;
 
 import org.apache.commons.lang3.tuple.Pair;
-import org.onlab.packet.Ip4Prefix;
+import org.onlab.packet.IpPrefix;
 import org.onosproject.core.ApplicationId;
 import org.onosproject.net.flow.criteria.Criteria.IPCriterion;
 import org.onosproject.net.flow.criteria.Criterion;
@@ -55,7 +55,7 @@
     private final ApplicationId appId;
     private final IntentService intentService;
     private final Map<IntentKey, PointToPointIntent> peerIntents;
-    private final Map<Ip4Prefix, MultiPointToSinglePointIntent> routeIntents;
+    private final Map<IpPrefix, MultiPointToSinglePointIntent> routeIntents;
 
     //
     // State to deal with SDN-IP Leader election and pushing Intents
@@ -182,7 +182,7 @@
     public Collection<MultiPointToSinglePointIntent> getRouteIntents() {
         List<MultiPointToSinglePointIntent> result = new LinkedList<>();
 
-        for (Map.Entry<Ip4Prefix, MultiPointToSinglePointIntent> entry :
+        for (Map.Entry<IpPrefix, MultiPointToSinglePointIntent> entry :
             routeIntents.entrySet()) {
             result.add(entry.getValue());
         }
@@ -247,12 +247,12 @@
      * Updates multi-point-to-single-point route intents.
      *
      * @param submitIntents the intents to submit
-     * @param withdrawPrefixes the IPv4 matching prefixes for the intents
-     * to withdraw
+     * @param withdrawPrefixes the IPv4 or IPv6 matching prefixes for the
+     * intents to withdraw
      */
     void updateRouteIntents(
-                Collection<Pair<Ip4Prefix, MultiPointToSinglePointIntent>> submitIntents,
-                Collection<Ip4Prefix> withdrawPrefixes) {
+                Collection<Pair<IpPrefix, MultiPointToSinglePointIntent>> submitIntents,
+                Collection<IpPrefix> withdrawPrefixes) {
 
         //
         // NOTE: Semantically, we MUST withdraw existing intents before
@@ -269,7 +269,7 @@
             //
             IntentOperations.Builder withdrawBuilder =
                 IntentOperations.builder(appId);
-            for (Ip4Prefix prefix : withdrawPrefixes) {
+            for (IpPrefix prefix : withdrawPrefixes) {
                 intent = routeIntents.remove(prefix);
                 if (intent == null) {
                     log.trace("SDN-IP No intent in routeIntents to delete " +
@@ -287,9 +287,9 @@
             //
             IntentOperations.Builder submitBuilder =
                 IntentOperations.builder(appId);
-            for (Pair<Ip4Prefix, MultiPointToSinglePointIntent> pair :
+            for (Pair<IpPrefix, MultiPointToSinglePointIntent> pair :
                      submitIntents) {
-                Ip4Prefix prefix = pair.getLeft();
+                IpPrefix prefix = pair.getLeft();
                 intent = pair.getRight();
                 MultiPointToSinglePointIntent oldIntent =
                     routeIntents.put(prefix, intent);
@@ -382,18 +382,23 @@
                     // Find the IP prefix
                     Criterion c =
                         mp2pIntent.selector().getCriterion(Criterion.Type.IPV4_DST);
+                    if (c == null) {
+                        // Try IPv6
+                        c =
+                            mp2pIntent.selector().getCriterion(Criterion.Type.IPV6_DST);
+                    }
                     if (c != null && c instanceof IPCriterion) {
                         IPCriterion ipCriterion = (IPCriterion) c;
-                        Ip4Prefix ip4Prefix = ipCriterion.ip().getIp4Prefix();
-                        if (ip4Prefix == null) {
+                        IpPrefix ipPrefix = ipCriterion.ip();
+                        if (ipPrefix == null) {
                             continue;
                         }
                         log.trace("SDN-IP Intent Synchronizer: updating " +
                                   "in-memory Route Intent for prefix {}",
-                                  ip4Prefix);
-                        routeIntents.put(ip4Prefix, mp2pIntent);
+                                  ipPrefix);
+                        routeIntents.put(ipPrefix, mp2pIntent);
                     } else {
-                        log.warn("SDN-IP no IPV4_DST criterion found for Intent {}",
+                        log.warn("SDN-IP no IPV4_DST or IPV6_DST criterion found for Intent {}",
                                  mp2pIntent.id());
                     }
                     continue;
diff --git a/apps/sdnip/src/main/java/org/onosproject/sdnip/RouteEntry.java b/apps/sdnip/src/main/java/org/onosproject/sdnip/RouteEntry.java
index 13ee240..3cffb7e 100644
--- a/apps/sdnip/src/main/java/org/onosproject/sdnip/RouteEntry.java
+++ b/apps/sdnip/src/main/java/org/onosproject/sdnip/RouteEntry.java
@@ -19,8 +19,8 @@
 
 import java.util.Objects;
 
-import org.onlab.packet.Ip4Address;
-import org.onlab.packet.Ip4Prefix;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.IpPrefix;
 
 import com.google.common.base.MoreObjects;
 
@@ -28,8 +28,8 @@
  * Represents a route entry for an IP prefix.
  */
 public class RouteEntry {
-    private final Ip4Prefix prefix;             // The IP prefix
-    private final Ip4Address nextHop;           // Next-hop IP address
+    private final IpPrefix prefix;              // The IP prefix
+    private final IpAddress nextHop;            // Next-hop IP address
 
     /**
      * Class constructor.
@@ -37,17 +37,26 @@
      * @param prefix the IP prefix of the route
      * @param nextHop the next hop IP address for the route
      */
-    public RouteEntry(Ip4Prefix prefix, Ip4Address nextHop) {
+    public RouteEntry(IpPrefix prefix, IpAddress nextHop) {
         this.prefix = checkNotNull(prefix);
         this.nextHop = checkNotNull(nextHop);
     }
 
     /**
+     * Returns the IP version of the route.
+     *
+     * @return the IP version of the route
+     */
+    public IpAddress.Version version() {
+        return nextHop.version();
+    }
+
+    /**
      * Returns the IP prefix of the route.
      *
      * @return the IP prefix of the route
      */
-    public Ip4Prefix prefix() {
+    public IpPrefix prefix() {
         return prefix;
     }
 
@@ -56,27 +65,32 @@
      *
      * @return the next hop IP address for the route
      */
-    public Ip4Address nextHop() {
+    public IpAddress nextHop() {
         return nextHop;
     }
 
     /**
-     * Creates the binary string representation of an IPv4 prefix.
+     * Creates the binary string representation of an IP prefix.
+     * The prefix can be either IPv4 or IPv6.
      * The string length is equal to the prefix length.
      *
-     * @param ip4Prefix the IPv4 prefix to use
+     * @param ipPrefix the IP prefix to use
      * @return the binary string representation
      */
-    static String createBinaryString(Ip4Prefix ip4Prefix) {
-        if (ip4Prefix.prefixLength() == 0) {
+    static String createBinaryString(IpPrefix ipPrefix) {
+        if (ipPrefix.prefixLength() == 0) {
             return "";
         }
 
-        StringBuilder result = new StringBuilder(ip4Prefix.prefixLength());
-        long value = ip4Prefix.address().toInt() & 0xffffffffL;
-        for (int i = 0; i < ip4Prefix.prefixLength(); i++) {
-            long mask = 1 << (Ip4Prefix.MAX_MASK_LENGTH - 1 - i);
-            result.append(((value & mask) == 0) ? "0" : "1");
+        byte[] octets = ipPrefix.address().toOctets();
+        StringBuilder result = new StringBuilder(ipPrefix.prefixLength());
+        for (int i = 0; i < ipPrefix.prefixLength(); i++) {
+            int byteOffset = i / Byte.SIZE;
+            int bitOffset = i % Byte.SIZE;
+            int mask = 1 << (Byte.SIZE - 1 - bitOffset);
+            byte value = octets[byteOffset];
+            boolean isSet = ((value & mask) != 0);
+            result.append(isSet ? "1" : "0");
         }
         return result.toString();
     }
diff --git a/apps/sdnip/src/main/java/org/onosproject/sdnip/Router.java b/apps/sdnip/src/main/java/org/onosproject/sdnip/Router.java
index 44cc3bf..d34a395 100644
--- a/apps/sdnip/src/main/java/org/onosproject/sdnip/Router.java
+++ b/apps/sdnip/src/main/java/org/onosproject/sdnip/Router.java
@@ -30,9 +30,9 @@
 
 import org.apache.commons.lang3.tuple.Pair;
 import org.onlab.packet.Ethernet;
-import org.onlab.packet.Ip4Address;
-import org.onlab.packet.Ip4Prefix;
 import org.onlab.packet.IpAddress;
+import org.onlab.packet.IpPrefix;
+import org.onlab.packet.Ip4Address;
 import org.onlab.packet.MacAddress;
 import org.onosproject.core.ApplicationId;
 import org.onosproject.net.ConnectPoint;
@@ -67,23 +67,20 @@
 public class Router implements RouteListener {
 
     private static final Logger log = LoggerFactory.getLogger(Router.class);
-    // For routes announced by local BGP daemon in SDN network,
-    // the next hop will be 0.0.0.0.
-    private static final Ip4Address LOCAL_NEXT_HOP =
-        Ip4Address.valueOf("0.0.0.0");
 
     // Store all route updates in a radix tree.
     // The key in this tree is the binary string of prefix of the route.
-    private InvertedRadixTree<RouteEntry> ribTable;
+    private InvertedRadixTree<RouteEntry> ribTable4;
+    private InvertedRadixTree<RouteEntry> ribTable6;
 
     // Stores all incoming route updates in a queue.
     private final BlockingQueue<Collection<RouteUpdate>> routeUpdatesQueue;
 
-    // The Ip4Address is the next hop address of each route update.
-    private final SetMultimap<Ip4Address, RouteEntry> routesWaitingOnArp;
+    // The IpAddress is the next hop address of each route update.
+    private final SetMultimap<IpAddress, RouteEntry> routesWaitingOnArp;
 
     // The IPv4 address to MAC address mapping
-    private final Map<Ip4Address, MacAddress> ip2Mac;
+    private final Map<IpAddress, MacAddress> ip2Mac;
 
     private final ApplicationId appId;
     private final IntentSynchronizer intentSynchronizer;
@@ -114,11 +111,13 @@
 
         this.hostListener = new InternalHostListener();
 
-        ribTable = new ConcurrentInvertedRadixTree<>(
+        ribTable4 = new ConcurrentInvertedRadixTree<>(
+                new DefaultByteArrayNodeFactory());
+        ribTable6 = new ConcurrentInvertedRadixTree<>(
                 new DefaultByteArrayNodeFactory());
         routeUpdatesQueue = new LinkedBlockingQueue<>();
         routesWaitingOnArp = Multimaps.synchronizedSetMultimap(
-                HashMultimap.<Ip4Address, RouteEntry>create());
+                HashMultimap.<IpAddress, RouteEntry>create());
         ip2Mac = new ConcurrentHashMap<>();
 
         bgpUpdatesExecutor = Executors.newSingleThreadExecutor(
@@ -151,7 +150,9 @@
 
         synchronized (this) {
             // Cleanup all local state
-            ribTable = new ConcurrentInvertedRadixTree<>(
+            ribTable4 = new ConcurrentInvertedRadixTree<>(
+                new DefaultByteArrayNodeFactory());
+            ribTable6 = new ConcurrentInvertedRadixTree<>(
                 new DefaultByteArrayNodeFactory());
             routeUpdatesQueue.clear();
             routesWaitingOnArp.clear();
@@ -195,15 +196,103 @@
     }
 
     /**
+     * Gets all IPv4 routes from the RIB.
+     *
+     * @return all IPv4 routes from the RIB
+     */
+    public Collection<RouteEntry> getRoutes4() {
+        Iterator<KeyValuePair<RouteEntry>> it =
+            ribTable4.getKeyValuePairsForKeysStartingWith("").iterator();
+
+        List<RouteEntry> routes = new LinkedList<>();
+
+        while (it.hasNext()) {
+            KeyValuePair<RouteEntry> entry = it.next();
+            routes.add(entry.getValue());
+        }
+
+        return routes;
+    }
+
+    /**
+     * Gets all IPv6 routes from the RIB.
+     *
+     * @return all IPv6 routes from the RIB
+     */
+    public Collection<RouteEntry> getRoutes6() {
+        Iterator<KeyValuePair<RouteEntry>> it =
+            ribTable6.getKeyValuePairsForKeysStartingWith("").iterator();
+
+        List<RouteEntry> routes = new LinkedList<>();
+
+        while (it.hasNext()) {
+            KeyValuePair<RouteEntry> entry = it.next();
+            routes.add(entry.getValue());
+        }
+
+        return routes;
+    }
+
+    /**
+     * Finds a route in the RIB for a prefix. The prefix can be either IPv4 or
+     * IPv6.
+     *
+     * @param prefix the prefix to use
+     * @return the route if found, otherwise null
+     */
+    RouteEntry findRibRoute(IpPrefix prefix) {
+        String binaryString = RouteEntry.createBinaryString(prefix);
+        if (prefix.version() == Ip4Address.VERSION) {
+            // IPv4
+            return ribTable4.getValueForExactKey(binaryString);
+        }
+        // IPv6
+        return ribTable6.getValueForExactKey(binaryString);
+    }
+
+    /**
+     * Adds a route to the RIB. The route can be either IPv4 or IPv6.
+     *
+     * @param routeEntry the route entry to use
+     */
+    void addRibRoute(RouteEntry routeEntry) {
+        if (routeEntry.prefix().version() == Ip4Address.VERSION) {
+            // IPv4
+            ribTable4.put(RouteEntry.createBinaryString(routeEntry.prefix()),
+                          routeEntry);
+        } else {
+            // IPv6
+            ribTable6.put(RouteEntry.createBinaryString(routeEntry.prefix()),
+                          routeEntry);
+        }
+    }
+
+    /**
+     * Removes a route for a prefix from the RIB. The prefix can be either IPv4
+     * or IPv6.
+     *
+     * @param prefix the prefix to use
+     * @return true if the rotue was found and removed, otherwise false
+     */
+    boolean removeRibRoute(IpPrefix prefix) {
+        if (prefix.version() == Ip4Address.VERSION) {
+            // IPv4
+            return ribTable4.remove(RouteEntry.createBinaryString(prefix));
+        }
+        // IPv6
+        return ribTable6.remove(RouteEntry.createBinaryString(prefix));
+    }
+
+    /**
      * Processes route updates.
      *
      * @param routeUpdates the route updates to process
      */
     void processRouteUpdates(Collection<RouteUpdate> routeUpdates) {
         synchronized (this) {
-            Collection<Pair<Ip4Prefix, MultiPointToSinglePointIntent>>
+            Collection<Pair<IpPrefix, MultiPointToSinglePointIntent>>
                 submitIntents = new LinkedList<>();
-            Collection<Ip4Prefix> withdrawPrefixes = new LinkedList<>();
+            Collection<IpPrefix> withdrawPrefixes = new LinkedList<>();
             MultiPointToSinglePointIntent intent;
 
             for (RouteUpdate update : routeUpdates) {
@@ -249,28 +338,32 @@
      */
     private MultiPointToSinglePointIntent processRouteAdd(
                 RouteEntry routeEntry,
-                Collection<Ip4Prefix> withdrawPrefixes) {
+                Collection<IpPrefix> withdrawPrefixes) {
         log.debug("Processing route add: {}", routeEntry);
 
-        Ip4Prefix prefix = routeEntry.prefix();
-        Ip4Address nextHop = null;
-        RouteEntry foundRouteEntry =
-            ribTable.put(RouteEntry.createBinaryString(prefix), routeEntry);
-        if (foundRouteEntry != null) {
-            nextHop = foundRouteEntry.nextHop();
+        // Find the old next-hop if we are updating an old route entry
+        IpAddress oldNextHop = null;
+        RouteEntry oldRouteEntry = findRibRoute(routeEntry.prefix());
+        if (oldRouteEntry != null) {
+            oldNextHop = oldRouteEntry.nextHop();
         }
 
-        if (nextHop != null && !nextHop.equals(routeEntry.nextHop())) {
-            // There was an existing nexthop for this prefix. This update
-            // supersedes that, so we need to remove the old flows for this
-            // prefix from the switches
+        // Add the new route to the RIB
+        addRibRoute(routeEntry);
+
+        if (oldNextHop != null) {
+            if (oldNextHop.equals(routeEntry.nextHop())) {
+                return null;            // No change
+            }
+            //
+            // Update an existing nexthop for the prefix.
+            // We need to remove the old flows for this prefix from the
+            // switches before the new flows are added.
+            //
             withdrawPrefixes.add(routeEntry.prefix());
         }
-        if (nextHop != null && nextHop.equals(routeEntry.nextHop())) {
-            return null;
-        }
 
-        if (routeEntry.nextHop().equals(LOCAL_NEXT_HOP)) {
+        if (routeEntry.nextHop().isZero()) {
             // Route originated by SDN domain
             // We don't handle these at the moment
             log.debug("Own route {} to {}",
@@ -321,8 +414,8 @@
      * @return the generated intent, or null if no intent should be submitted
      */
     private MultiPointToSinglePointIntent generateRouteIntent(
-                Ip4Prefix prefix,
-                Ip4Address nextHopIpAddress,
+                IpPrefix prefix,
+                IpAddress nextHopIpAddress,
                 MacAddress nextHopMacAddress) {
 
         // Find the attachment point (egress interface) of the next hop
@@ -362,10 +455,18 @@
         }
 
         // Match the destination IP prefix at the first hop
-        TrafficSelector selector = DefaultTrafficSelector.builder()
+        TrafficSelector selector;
+        if (prefix.version() == Ip4Address.VERSION) {
+            selector = DefaultTrafficSelector.builder()
                 .matchEthType(Ethernet.TYPE_IPV4)
                 .matchIPDst(prefix)
                 .build();
+        } else {
+            selector = DefaultTrafficSelector.builder()
+                .matchEthType(Ethernet.TYPE_IPV6)
+                .matchIPDst(prefix)
+                .build();
+        }
 
         // Rewrite the destination MAC address
         TrafficTreatment treatment = DefaultTrafficTreatment.builder()
@@ -389,11 +490,11 @@
      * intents will be withdrawn
      */
     private void processRouteDelete(RouteEntry routeEntry,
-                                    Collection<Ip4Prefix> withdrawPrefixes) {
+                                    Collection<IpPrefix> withdrawPrefixes) {
         log.debug("Processing route delete: {}", routeEntry);
-        Ip4Prefix prefix = routeEntry.prefix();
+        boolean isRemoved = removeRibRoute(routeEntry.prefix());
 
-        if (ribTable.remove(RouteEntry.createBinaryString(prefix))) {
+        if (isRemoved) {
             //
             // Only withdraw intents if an entry was actually removed from the
             // tree. If no entry was removed, the <prefix, nexthop> wasn't
@@ -415,26 +516,26 @@
      * @param ipAddress the IP address that an event was received for
      * @param macAddress the most recently known MAC address for the IP address
      */
-    private void updateMac(Ip4Address ipAddress, MacAddress macAddress) {
-        log.debug("Received updated MAC info: {} => {}", ipAddress, macAddress);
+    private void updateMac(IpAddress ipAddress, MacAddress macAddress) {
+        log.debug("Received updated MAC info: {} => {}", ipAddress,
+                  macAddress);
 
-        // We synchronize on this to prevent changes to the radix tree
+        //
+        // We synchronize on "this" to prevent changes to the Radix tree
         // while we're pushing intents. If the tree changes, the
-        // tree and intents could get out of sync.
+        // tree and the intents could get out of sync.
+        //
         synchronized (this) {
-            Collection<Pair<Ip4Prefix, MultiPointToSinglePointIntent>>
+            Collection<Pair<IpPrefix, MultiPointToSinglePointIntent>>
                 submitIntents = new LinkedList<>();
             MultiPointToSinglePointIntent intent;
 
             Set<RouteEntry> routesToPush =
-                    routesWaitingOnArp.removeAll(ipAddress);
+                routesWaitingOnArp.removeAll(ipAddress);
 
             for (RouteEntry routeEntry : routesToPush) {
                 // These will always be adds
-                Ip4Prefix prefix = routeEntry.prefix();
-                String binaryString = RouteEntry.createBinaryString(prefix);
-                RouteEntry foundRouteEntry =
-                    ribTable.getValueForExactKey(binaryString);
+                RouteEntry foundRouteEntry = findRibRoute(routeEntry.prefix());
                 if (foundRouteEntry != null &&
                     foundRouteEntry.nextHop().equals(routeEntry.nextHop())) {
                     // We only push prefix flows if the prefix is still in the
@@ -442,10 +543,11 @@
                     // update.
                     // The prefix could have been removed while we were waiting
                     // for the ARP, or the next hop could have changed.
-                    intent = generateRouteIntent(prefix, ipAddress,
-                                                 macAddress);
+                    intent = generateRouteIntent(routeEntry.prefix(),
+                                                 ipAddress, macAddress);
                     if (intent != null) {
-                        submitIntents.add(Pair.of(prefix, intent));
+                        submitIntents.add(Pair.of(routeEntry.prefix(),
+                                                  intent));
                     }
                 } else {
                     log.debug("{} has been revoked before the MAC was resolved",
@@ -454,7 +556,7 @@
             }
 
             if (!submitIntents.isEmpty()) {
-                Collection<Ip4Prefix> withdrawPrefixes = new LinkedList<>();
+                Collection<IpPrefix> withdrawPrefixes = new LinkedList<>();
                 intentSynchronizer.updateRouteIntents(submitIntents,
                                                       withdrawPrefixes);
             }
@@ -464,25 +566,6 @@
     }
 
     /**
-     * Gets the SDN-IP routes.
-     *
-     * @return the SDN-IP routes
-     */
-    public Collection<RouteEntry> getRoutes() {
-        Iterator<KeyValuePair<RouteEntry>> it =
-            ribTable.getKeyValuePairsForKeysStartingWith("").iterator();
-
-        List<RouteEntry> routes = new LinkedList<>();
-
-        while (it.hasNext()) {
-            KeyValuePair<RouteEntry> entry = it.next();
-            routes.add(entry.getValue());
-        }
-
-        return routes;
-    }
-
-    /**
      * Listener for host events.
      */
     class InternalHostListener implements HostListener {
@@ -495,21 +578,13 @@
             case HOST_ADDED:
                 // FALLTHROUGH
             case HOST_UPDATED:
-                for (IpAddress ip : host.ipAddresses()) {
-                    Ip4Address ip4Address = ip.getIp4Address();
-                    if (ip4Address == null) {
-                        continue;
-                    }
-                    updateMac(ip4Address, host.mac());
+                for (IpAddress ipAddress : host.ipAddresses()) {
+                    updateMac(ipAddress, host.mac());
                 }
                 break;
             case HOST_REMOVED:
-                for (IpAddress ip : host.ipAddresses()) {
-                    Ip4Address ip4Address = ip.getIp4Address();
-                    if (ip4Address == null) {
-                        continue;
-                    }
-                    ip2Mac.remove(ip4Address);
+                for (IpAddress ipAddress : host.ipAddresses()) {
+                    ip2Mac.remove(ipAddress);
                 }
                 break;
             default:
diff --git a/apps/sdnip/src/main/java/org/onosproject/sdnip/SdnIp.java b/apps/sdnip/src/main/java/org/onosproject/sdnip/SdnIp.java
index 066500b..2b95129 100644
--- a/apps/sdnip/src/main/java/org/onosproject/sdnip/SdnIp.java
+++ b/apps/sdnip/src/main/java/org/onosproject/sdnip/SdnIp.java
@@ -164,13 +164,23 @@
     }
 
     @Override
-    public Collection<BgpRouteEntry> getBgpRoutes() {
-        return bgpSessionManager.getBgpRoutes();
+    public Collection<BgpRouteEntry> getBgpRoutes4() {
+        return bgpSessionManager.getBgpRoutes4();
     }
 
     @Override
-    public Collection<RouteEntry> getRoutes() {
-        return router.getRoutes();
+    public Collection<BgpRouteEntry> getBgpRoutes6() {
+        return bgpSessionManager.getBgpRoutes6();
+    }
+
+    @Override
+    public Collection<RouteEntry> getRoutes4() {
+        return router.getRoutes4();
+    }
+
+    @Override
+    public Collection<RouteEntry> getRoutes6() {
+        return router.getRoutes6();
     }
 
     @Override
diff --git a/apps/sdnip/src/main/java/org/onosproject/sdnip/SdnIpService.java b/apps/sdnip/src/main/java/org/onosproject/sdnip/SdnIpService.java
index cfa089a..aa71f02 100644
--- a/apps/sdnip/src/main/java/org/onosproject/sdnip/SdnIpService.java
+++ b/apps/sdnip/src/main/java/org/onosproject/sdnip/SdnIpService.java
@@ -32,18 +32,32 @@
     public Collection<BgpSession> getBgpSessions();
 
     /**
-     * Gets the BGP routes.
+     * Gets the selected IPv4 BGP routes among all BGP sessions.
      *
-     * @return the BGP routes
+     * @return the selected IPv4 BGP routes among all BGP sessions
      */
-    public Collection<BgpRouteEntry> getBgpRoutes();
+    public Collection<BgpRouteEntry> getBgpRoutes4();
 
     /**
-     * Gets all the routes known to SDN-IP.
+     * Gets the selected IPv6 BGP routes among all BGP sessions.
      *
-     * @return the SDN-IP routes
+     * @return the selected IPv6 BGP routes among all BGP sessions
      */
-    public Collection<RouteEntry> getRoutes();
+    public Collection<BgpRouteEntry> getBgpRoutes6();
+
+    /**
+     * Gets all IPv4 routes known to SDN-IP.
+     *
+     * @return the SDN-IP IPv4 routes
+     */
+    public Collection<RouteEntry> getRoutes4();
+
+    /**
+     * Gets all IPv6 routes known to SDN-IP.
+     *
+     * @return the SDN-IP IPv6 routes
+     */
+    public Collection<RouteEntry> getRoutes6();
 
     /**
      * Changes whether this SDN-IP instance is the primary or not based on the
diff --git a/apps/sdnip/src/main/java/org/onosproject/sdnip/bgp/BgpRouteEntry.java b/apps/sdnip/src/main/java/org/onosproject/sdnip/bgp/BgpRouteEntry.java
index 05d97db..67ef5b1 100644
--- a/apps/sdnip/src/main/java/org/onosproject/sdnip/bgp/BgpRouteEntry.java
+++ b/apps/sdnip/src/main/java/org/onosproject/sdnip/bgp/BgpRouteEntry.java
@@ -20,8 +20,9 @@
 import java.util.ArrayList;
 import java.util.Objects;
 
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.IpPrefix;
 import org.onlab.packet.Ip4Address;
-import org.onlab.packet.Ip4Prefix;
 import org.onosproject.sdnip.RouteEntry;
 import org.onosproject.sdnip.bgp.BgpConstants.Update;
 
@@ -48,8 +49,8 @@
      * @param asPath the AS path
      * @param localPref the route local preference
      */
-    public BgpRouteEntry(BgpSession bgpSession, Ip4Prefix prefix,
-                         Ip4Address nextHop, byte origin,
+    public BgpRouteEntry(BgpSession bgpSession, IpPrefix prefix,
+                         IpAddress nextHop, byte origin,
                          BgpRouteEntry.AsPath asPath, long localPref) {
         super(prefix, nextHop);
         this.bgpSession = checkNotNull(bgpSession);
diff --git a/apps/sdnip/src/main/java/org/onosproject/sdnip/bgp/BgpRouteSelector.java b/apps/sdnip/src/main/java/org/onosproject/sdnip/bgp/BgpRouteSelector.java
new file mode 100644
index 0000000..1569c07
--- /dev/null
+++ b/apps/sdnip/src/main/java/org/onosproject/sdnip/bgp/BgpRouteSelector.java
@@ -0,0 +1,202 @@
+/*
+ * 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.sdnip.bgp;
+
+import java.util.Collection;
+import java.util.LinkedList;
+
+import org.onlab.packet.IpPrefix;
+import org.onosproject.sdnip.RouteUpdate;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Class to receive and process the BGP routes from each BGP Session/Peer.
+ */
+class BgpRouteSelector {
+    private static final Logger log =
+        LoggerFactory.getLogger(BgpRouteSelector.class);
+
+    private BgpSessionManager bgpSessionManager;
+
+    /**
+     * Constructor.
+     *
+     * @param bgpSessionManager the BGP Session Manager to use
+     */
+    BgpRouteSelector(BgpSessionManager bgpSessionManager) {
+        this.bgpSessionManager = bgpSessionManager;
+    }
+
+    /**
+     * Processes route entry updates: added/updated and deleted route
+     * entries.
+     *
+     * @param bgpSession the BGP session the route entry updates were
+     * received on
+     * @param addedBgpRouteEntries the added/updated route entries to process
+     * @param deletedBgpRouteEntries the deleted route entries to process
+     */
+    synchronized void routeUpdates(BgpSession bgpSession,
+                        Collection<BgpRouteEntry> addedBgpRouteEntries,
+                        Collection<BgpRouteEntry> deletedBgpRouteEntries) {
+        Collection<RouteUpdate> routeUpdates = new LinkedList<>();
+        RouteUpdate routeUpdate;
+
+        if (bgpSessionManager.isShutdown()) {
+            return;         // Ignore any leftover updates if shutdown
+        }
+        // Process the deleted route entries
+        for (BgpRouteEntry bgpRouteEntry : deletedBgpRouteEntries) {
+            routeUpdate = processDeletedRoute(bgpSession, bgpRouteEntry);
+            if (routeUpdate != null) {
+                routeUpdates.add(routeUpdate);
+            }
+        }
+
+        // Process the added/updated route entries
+        for (BgpRouteEntry bgpRouteEntry : addedBgpRouteEntries) {
+            routeUpdate = processAddedRoute(bgpSession, bgpRouteEntry);
+            if (routeUpdate != null) {
+                routeUpdates.add(routeUpdate);
+            }
+        }
+        bgpSessionManager.getRouteListener().update(routeUpdates);
+    }
+
+    /**
+     * Processes an added/updated route entry.
+     *
+     * @param bgpSession the BGP session the route entry update was received on
+     * @param bgpRouteEntry the added/updated route entry
+     * @return the result route update that should be forwarded to the
+     * Route Listener, or null if no route update should be forwarded
+     */
+    private RouteUpdate processAddedRoute(BgpSession bgpSession,
+                                          BgpRouteEntry bgpRouteEntry) {
+        RouteUpdate routeUpdate;
+        BgpRouteEntry bestBgpRouteEntry =
+            bgpSessionManager.findBgpRoute(bgpRouteEntry.prefix());
+
+        //
+        // Install the new route entry if it is better than the
+        // current best route.
+        //
+        if ((bestBgpRouteEntry == null) ||
+            bgpRouteEntry.isBetterThan(bestBgpRouteEntry)) {
+            bgpSessionManager.addBgpRoute(bgpRouteEntry);
+            routeUpdate =
+                new RouteUpdate(RouteUpdate.Type.UPDATE, bgpRouteEntry);
+            return routeUpdate;
+        }
+
+        //
+        // If the route entry arrived on the same BGP Session as
+        // the current best route, then elect the next best route
+        // and install it.
+        //
+        if (bestBgpRouteEntry.getBgpSession() !=
+            bgpRouteEntry.getBgpSession()) {
+            return null;            // Nothing to do
+        }
+
+        // Find the next best route
+        bestBgpRouteEntry = findBestBgpRoute(bgpRouteEntry.prefix());
+        if (bestBgpRouteEntry == null) {
+            //
+            // TODO: Shouldn't happen. Install the new route as a
+            // pre-caution.
+            //
+            log.debug("BGP next best route for prefix {} is missing. " +
+                      "Adding the route that is currently processed.",
+                      bgpRouteEntry.prefix());
+            bestBgpRouteEntry = bgpRouteEntry;
+        }
+
+        // Install the next best route
+        bgpSessionManager.addBgpRoute(bestBgpRouteEntry);
+        routeUpdate = new RouteUpdate(RouteUpdate.Type.UPDATE,
+                                      bestBgpRouteEntry);
+        return routeUpdate;
+    }
+
+    /**
+     * Processes a deleted route entry.
+     *
+     * @param bgpSession the BGP session the route entry update was received on
+     * @param bgpRouteEntry the deleted route entry
+     * @return the result route update that should be forwarded to the
+     * Route Listener, or null if no route update should be forwarded
+     */
+    private RouteUpdate processDeletedRoute(BgpSession bgpSession,
+                                            BgpRouteEntry bgpRouteEntry) {
+        RouteUpdate routeUpdate;
+        BgpRouteEntry bestBgpRouteEntry =
+            bgpSessionManager.findBgpRoute(bgpRouteEntry.prefix());
+
+        //
+        // Remove the route entry only if it was the best one.
+        // Install the the next best route if it exists.
+        //
+        // NOTE: We intentionally use "==" instead of method equals(),
+        // because we need to check whether this is same object.
+        //
+        if (bgpRouteEntry != bestBgpRouteEntry) {
+            return null;            // Nothing to do
+        }
+
+        //
+        // Find the next best route
+        //
+        bestBgpRouteEntry = findBestBgpRoute(bgpRouteEntry.prefix());
+        if (bestBgpRouteEntry != null) {
+            // Install the next best route
+            bgpSessionManager.addBgpRoute(bestBgpRouteEntry);
+            routeUpdate = new RouteUpdate(RouteUpdate.Type.UPDATE,
+                                          bestBgpRouteEntry);
+            return routeUpdate;
+        }
+
+        //
+        // No route found. Remove the route entry
+        //
+        bgpSessionManager.removeBgpRoute(bgpRouteEntry.prefix());
+        routeUpdate = new RouteUpdate(RouteUpdate.Type.DELETE, bgpRouteEntry);
+        return routeUpdate;
+    }
+
+    /**
+     * Finds the best route entry among all BGP Sessions.
+     *
+     * @param prefix the prefix of the route
+     * @return the best route if found, otherwise null
+     */
+    private BgpRouteEntry findBestBgpRoute(IpPrefix prefix) {
+        BgpRouteEntry bestRoute = null;
+
+        // Iterate across all BGP Sessions and select the best route
+        for (BgpSession bgpSession : bgpSessionManager.getBgpSessions()) {
+            BgpRouteEntry route = bgpSession.findBgpRoute(prefix);
+            if (route == null) {
+                continue;
+            }
+            if ((bestRoute == null) || route.isBetterThan(bestRoute)) {
+                bestRoute = route;
+            }
+        }
+        return bestRoute;
+    }
+}
diff --git a/apps/sdnip/src/main/java/org/onosproject/sdnip/bgp/BgpSession.java b/apps/sdnip/src/main/java/org/onosproject/sdnip/bgp/BgpSession.java
index 15c4e3a..a99503e 100644
--- a/apps/sdnip/src/main/java/org/onosproject/sdnip/bgp/BgpSession.java
+++ b/apps/sdnip/src/main/java/org/onosproject/sdnip/bgp/BgpSession.java
@@ -20,7 +20,6 @@
 import java.net.SocketAddress;
 import java.util.Collection;
 import java.util.Collections;
-import java.util.Map;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
 import java.util.concurrent.TimeUnit;
@@ -34,6 +33,7 @@
 import org.jboss.netty.util.Timeout;
 import org.jboss.netty.util.Timer;
 import org.jboss.netty.util.TimerTask;
+import org.onlab.packet.IpPrefix;
 import org.onlab.packet.Ip4Address;
 import org.onlab.packet.Ip4Prefix;
 import org.onlab.packet.Ip6Prefix;
@@ -120,44 +120,113 @@
     }
 
     /**
-     * Gets the BGP RIB-IN IPv4 routing entries.
+     * Gets the IPv4 BGP RIB-IN routing entries.
      *
-     * @return the BGP RIB-IN IPv4 routing entries
+     * @return the IPv4 BGP RIB-IN routing entries
      */
-    public Map<Ip4Prefix, BgpRouteEntry> bgpRibIn4() {
-        return bgpRibIn4;
+    public Collection<BgpRouteEntry> getBgpRibIn4() {
+        return bgpRibIn4.values();
     }
 
     /**
-     * Gets the BGP RIB-IN IPv6 routing entries.
+     * Gets the IPv6 BGP RIB-IN routing entries.
      *
-     * @return the BGP RIB-IN IPv6 routing entries
+     * @return the IPv6 BGP RIB-IN routing entries
      */
-    public Map<Ip6Prefix, BgpRouteEntry> bgpRibIn6() {
-        return bgpRibIn6;
+    public Collection<BgpRouteEntry> getBgpRibIn6() {
+        return bgpRibIn6.values();
     }
 
     /**
-     * Finds a BGP IPv4 routing entry in the BGP RIB-IN.
+     * Finds an IPv4 BGP routing entry for a prefix in the IPv4 BGP RIB-IN.
      *
      * @param prefix the IPv4 prefix of the route to search for
      * @return the IPv4 BGP routing entry if found, otherwise null
      */
-    public BgpRouteEntry findBgpRouteEntry(Ip4Prefix prefix) {
+    public BgpRouteEntry findBgpRoute(Ip4Prefix prefix) {
         return bgpRibIn4.get(prefix);
     }
 
     /**
-     * Finds a BGP IPv6 routing entry in the BGP RIB-IN.
+     * Finds an IPv6 BGP routing entry for a prefix in the IPv6 BGP RIB-IN.
      *
      * @param prefix the IPv6 prefix of the route to search for
      * @return the IPv6 BGP routing entry if found, otherwise null
      */
-    public BgpRouteEntry findBgpRouteEntry(Ip6Prefix prefix) {
+    public BgpRouteEntry findBgpRoute(Ip6Prefix prefix) {
         return bgpRibIn6.get(prefix);
     }
 
     /**
+     * Finds a BGP routing entry for a prefix in the BGP RIB-IN. The prefix
+     * can be either IPv4 or IPv6.
+     *
+     * @param prefix the IP prefix of the route to search for
+     * @return the BGP routing entry if found, otherwise null
+     */
+    public BgpRouteEntry findBgpRoute(IpPrefix prefix) {
+        if (prefix.version() == Ip4Address.VERSION) {
+            // IPv4 prefix
+            Ip4Prefix ip4Prefix = prefix.getIp4Prefix();
+            return bgpRibIn4.get(ip4Prefix);
+        }
+
+        // IPv6 prefix
+        Ip6Prefix ip6Prefix = prefix.getIp6Prefix();
+        return bgpRibIn6.get(ip6Prefix);
+    }
+
+    /**
+     * Adds a BGP route. The route can be either IPv4 or IPv6.
+     *
+     * @param bgpRouteEntry the BGP route entry to use
+     */
+    void addBgpRoute(BgpRouteEntry bgpRouteEntry) {
+        if (bgpRouteEntry.version() == Ip4Address.VERSION) {
+            // IPv4 route
+            Ip4Prefix ip4Prefix = bgpRouteEntry.prefix().getIp4Prefix();
+            bgpRibIn4.put(ip4Prefix, bgpRouteEntry);
+        } else {
+            // IPv6 route
+            Ip6Prefix ip6Prefix = bgpRouteEntry.prefix().getIp6Prefix();
+            bgpRibIn6.put(ip6Prefix, bgpRouteEntry);
+        }
+    }
+
+    /**
+     * Removes an IPv4 BGP route for a prefix.
+     *
+     * @param prefix the prefix to use
+     * @return true if the route was found and removed, otherwise false
+     */
+    boolean removeBgpRoute(Ip4Prefix prefix) {
+        return (bgpRibIn4.remove(prefix) != null);
+    }
+
+    /**
+     * Removes an IPv6 BGP route for a prefix.
+     *
+     * @param prefix the prefix to use
+     * @return true if the route was found and removed, otherwise false
+     */
+    boolean removeBgpRoute(Ip6Prefix prefix) {
+        return (bgpRibIn6.remove(prefix) != null);
+    }
+
+    /**
+     * Removes a BGP route for a prefix. The prefix can be either IPv4 or IPv6.
+     *
+     * @param prefix the prefix to use
+     * @return true if the route was found and removed, otherwise false
+     */
+    boolean removeBgpRoute(IpPrefix prefix) {
+        if (prefix.version() == Ip4Address.VERSION) {
+            return (bgpRibIn4.remove(prefix.getIp4Prefix()) != null);   // IPv4
+        }
+        return (bgpRibIn6.remove(prefix.getIp6Prefix()) != null);       // IPv6
+    }
+
+    /**
      * Gets the BGP session remote address.
      *
      * @return the BGP session remote address
@@ -551,7 +620,7 @@
 
     @Override
     public void channelClosed(ChannelHandlerContext ctx,
-                            ChannelStateEvent channelEvent) {
+                              ChannelStateEvent channelEvent) {
         bgpSessionManager.removeSessionChannel(channelEvent.getChannel());
     }
 
@@ -622,7 +691,7 @@
         bgpRibIn6 = new ConcurrentHashMap<>();
 
         // Push the updates to the BGP Merged RIB
-        BgpSessionManager.BgpRouteSelector bgpRouteSelector =
+        BgpRouteSelector bgpRouteSelector =
             bgpSessionManager.getBgpRouteSelector();
         Collection<BgpRouteEntry> addedRoutes = Collections.emptyList();
         bgpRouteSelector.routeUpdates(this, addedRoutes, deletedRoutes4);
diff --git a/apps/sdnip/src/main/java/org/onosproject/sdnip/bgp/BgpSessionManager.java b/apps/sdnip/src/main/java/org/onosproject/sdnip/bgp/BgpSessionManager.java
index fab1587..6f25bc8 100644
--- a/apps/sdnip/src/main/java/org/onosproject/sdnip/bgp/BgpSessionManager.java
+++ b/apps/sdnip/src/main/java/org/onosproject/sdnip/bgp/BgpSessionManager.java
@@ -22,7 +22,6 @@
 import java.net.InetSocketAddress;
 import java.net.SocketAddress;
 import java.util.Collection;
-import java.util.LinkedList;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
 import java.util.concurrent.Executors;
@@ -37,10 +36,11 @@
 import org.jboss.netty.channel.group.ChannelGroup;
 import org.jboss.netty.channel.group.DefaultChannelGroup;
 import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory;
-import org.onosproject.sdnip.RouteListener;
-import org.onosproject.sdnip.RouteUpdate;
 import org.onlab.packet.Ip4Address;
+import org.onlab.packet.IpPrefix;
 import org.onlab.packet.Ip4Prefix;
+import org.onlab.packet.Ip6Prefix;
+import org.onosproject.sdnip.RouteListener;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -50,6 +50,7 @@
 public class BgpSessionManager {
     private static final Logger log =
         LoggerFactory.getLogger(BgpSessionManager.class);
+
     boolean isShutdown = true;
     private Channel serverChannel;     // Listener for incoming BGP connections
     private ServerBootstrap serverBootstrap;
@@ -58,8 +59,10 @@
         new ConcurrentHashMap<>();
     private Ip4Address myBgpId;        // Same BGP ID for all peers
 
-    private BgpRouteSelector bgpRouteSelector = new BgpRouteSelector();
-    private ConcurrentMap<Ip4Prefix, BgpRouteEntry> bgpRoutes =
+    private BgpRouteSelector bgpRouteSelector = new BgpRouteSelector(this);
+    private ConcurrentMap<Ip4Prefix, BgpRouteEntry> bgpRoutes4 =
+        new ConcurrentHashMap<>();
+    private ConcurrentMap<Ip6Prefix, BgpRouteEntry> bgpRoutes6 =
         new ConcurrentHashMap<>();
 
     private final RouteListener routeListener;
@@ -74,6 +77,24 @@
     }
 
     /**
+     * Checks whether the BGP Session Manager is shutdown.
+     *
+     * @return true if the BGP Session Manager is shutdown, otherwise false
+     */
+    boolean isShutdown() {
+        return this.isShutdown;
+    }
+
+    /**
+     * Gets the route listener.
+     *
+     * @return the route listener to use
+     */
+    RouteListener getRouteListener() {
+        return routeListener;
+    }
+
+    /**
      * Gets the BGP sessions.
      *
      * @return the BGP sessions
@@ -83,12 +104,62 @@
     }
 
     /**
-     * Gets the BGP routes.
+     * Gets the selected IPv4 BGP routes among all BGP sessions.
      *
-     * @return the BGP routes
+     * @return the selected IPv4 BGP routes among all BGP sessions
      */
-    public Collection<BgpRouteEntry> getBgpRoutes() {
-        return bgpRoutes.values();
+    public Collection<BgpRouteEntry> getBgpRoutes4() {
+        return bgpRoutes4.values();
+    }
+
+    /**
+     * Gets the selected IPv6 BGP routes among all BGP sessions.
+     *
+     * @return the selected IPv6 BGP routes among all BGP sessions
+     */
+    public Collection<BgpRouteEntry> getBgpRoutes6() {
+        return bgpRoutes6.values();
+    }
+
+    /**
+     * Finds a BGP route for a prefix. The prefix can be either IPv4 or IPv6.
+     *
+     * @param prefix the prefix to use
+     * @return the BGP route if found, otherwise null
+     */
+    BgpRouteEntry findBgpRoute(IpPrefix prefix) {
+        if (prefix.version() == Ip4Address.VERSION) {
+            return bgpRoutes4.get(prefix.getIp4Prefix());               // IPv4
+        }
+        return bgpRoutes6.get(prefix.getIp6Prefix());                   // IPv6
+    }
+
+    /**
+     * Adds a BGP route. The route can be either IPv4 or IPv6.
+     *
+     * @param bgpRouteEntry the BGP route entry to use
+     */
+    void addBgpRoute(BgpRouteEntry bgpRouteEntry) {
+        if (bgpRouteEntry.version() == Ip4Address.VERSION) {
+            bgpRoutes4.put(bgpRouteEntry.prefix().getIp4Prefix(),       // IPv4
+                           bgpRouteEntry);
+        } else {
+            bgpRoutes6.put(bgpRouteEntry.prefix().getIp6Prefix(),       // IPv6
+                           bgpRouteEntry);
+        }
+    }
+
+    /**
+     * Removes a BGP route for a prefix. The prefix can be either IPv4 or IPv6.
+     *
+     * @param prefix the prefix to use
+     * @return true if the route was found and removed, otherwise false
+     */
+    boolean removeBgpRoute(IpPrefix prefix) {
+        if (prefix.version() == Ip4Address.VERSION) {
+            return (bgpRoutes4.remove(prefix.getIp4Prefix()) != null);  // IPv4
+        }
+        return (bgpRoutes6.remove(prefix.getIp6Prefix()) != null);      // IPv6
     }
 
     /**
@@ -187,9 +258,9 @@
         log.debug("BGP Session Manager start.");
         isShutdown = false;
 
-        ChannelFactory channelFactory =
-            new NioServerSocketChannelFactory(Executors.newCachedThreadPool(namedThreads("BGP-SM-boss-%d")),
-                                              Executors.newCachedThreadPool(namedThreads("BGP-SM-worker-%d")));
+        ChannelFactory channelFactory = new NioServerSocketChannelFactory(
+                Executors.newCachedThreadPool(namedThreads("BGP-SM-boss-%d")),
+                Executors.newCachedThreadPool(namedThreads("BGP-SM-worker-%d")));
         ChannelPipelineFactory pipelineFactory = new ChannelPipelineFactory() {
                 @Override
                 public ChannelPipeline getPipeline() throws Exception {
@@ -231,172 +302,4 @@
         allChannels.close().awaitUninterruptibly();
         serverBootstrap.releaseExternalResources();
     }
-
-    /**
-     * Class to receive and process the BGP routes from each BGP Session/Peer.
-     */
-    class BgpRouteSelector {
-        /**
-         * Processes route entry updates: added/updated and deleted route
-         * entries.
-         *
-         * @param bgpSession the BGP session the route entry updates were
-         * received on
-         * @param addedBgpRouteEntries the added/updated route entries to
-         * process
-         * @param deletedBgpRouteEntries the deleted route entries to process
-         */
-        synchronized void routeUpdates(BgpSession bgpSession,
-                        Collection<BgpRouteEntry> addedBgpRouteEntries,
-                        Collection<BgpRouteEntry> deletedBgpRouteEntries) {
-            Collection<RouteUpdate> routeUpdates = new LinkedList<>();
-            RouteUpdate routeUpdate;
-
-            if (isShutdown) {
-                return;         // Ignore any leftover updates if shutdown
-            }
-            // Process the deleted route entries
-            for (BgpRouteEntry bgpRouteEntry : deletedBgpRouteEntries) {
-                routeUpdate = processDeletedRoute(bgpSession, bgpRouteEntry);
-                if (routeUpdate != null) {
-                    routeUpdates.add(routeUpdate);
-                }
-            }
-
-            // Process the added/updated route entries
-            for (BgpRouteEntry bgpRouteEntry : addedBgpRouteEntries) {
-                routeUpdate = processAddedRoute(bgpSession, bgpRouteEntry);
-                if (routeUpdate != null) {
-                    routeUpdates.add(routeUpdate);
-                }
-            }
-            routeListener.update(routeUpdates);
-        }
-
-        /**
-         * Processes an added/updated route entry.
-         *
-         * @param bgpSession the BGP session the route entry update was
-         * received on
-         * @param bgpRouteEntry the added/updated route entry
-         * @return the result route update that should be forwarded to the
-         * Route Listener, or null if no route update should be forwarded
-         */
-        private RouteUpdate processAddedRoute(BgpSession bgpSession,
-                                              BgpRouteEntry bgpRouteEntry) {
-            RouteUpdate routeUpdate;
-            BgpRouteEntry bestBgpRouteEntry =
-                bgpRoutes.get(bgpRouteEntry.prefix());
-
-            //
-            // Install the new route entry if it is better than the
-            // current best route.
-            //
-            if ((bestBgpRouteEntry == null) ||
-                bgpRouteEntry.isBetterThan(bestBgpRouteEntry)) {
-                bgpRoutes.put(bgpRouteEntry.prefix(), bgpRouteEntry);
-                routeUpdate =
-                    new RouteUpdate(RouteUpdate.Type.UPDATE, bgpRouteEntry);
-                return routeUpdate;
-            }
-
-            //
-            // If the route entry arrived on the same BGP Session as
-            // the current best route, then elect the next best route
-            // and install it.
-            //
-            if (bestBgpRouteEntry.getBgpSession() !=
-                bgpRouteEntry.getBgpSession()) {
-                return null;            // Nothing to do
-            }
-
-            // Find the next best route
-            bestBgpRouteEntry = findBestBgpRoute(bgpRouteEntry.prefix());
-            if (bestBgpRouteEntry == null) {
-                //
-                // TODO: Shouldn't happen. Install the new route as a
-                // pre-caution.
-                //
-                log.debug("BGP next best route for prefix {} is missing. " +
-                          "Adding the route that is currently processed.",
-                          bgpRouteEntry.prefix());
-                bestBgpRouteEntry = bgpRouteEntry;
-            }
-            // Install the next best route
-            bgpRoutes.put(bestBgpRouteEntry.prefix(), bestBgpRouteEntry);
-            routeUpdate = new RouteUpdate(RouteUpdate.Type.UPDATE,
-                                          bestBgpRouteEntry);
-            return routeUpdate;
-        }
-
-        /**
-         * Processes a deleted route entry.
-         *
-         * @param bgpSession the BGP session the route entry update was
-         * received on
-         * @param bgpRouteEntry the deleted route entry
-         * @return the result route update that should be forwarded to the
-         * Route Listener, or null if no route update should be forwarded
-         */
-        private RouteUpdate processDeletedRoute(BgpSession bgpSession,
-                                                BgpRouteEntry bgpRouteEntry) {
-            RouteUpdate routeUpdate;
-            BgpRouteEntry bestBgpRouteEntry =
-                bgpRoutes.get(bgpRouteEntry.prefix());
-
-            //
-            // Remove the route entry only if it was the best one.
-            // Install the the next best route if it exists.
-            //
-            // NOTE: We intentionally use "==" instead of method equals(),
-            // because we need to check whether this is same object.
-            //
-            if (bgpRouteEntry != bestBgpRouteEntry) {
-                return null;            // Nothing to do
-            }
-
-            //
-            // Find the next best route
-            //
-            bestBgpRouteEntry = findBestBgpRoute(bgpRouteEntry.prefix());
-            if (bestBgpRouteEntry != null) {
-                // Install the next best route
-                bgpRoutes.put(bestBgpRouteEntry.prefix(),
-                              bestBgpRouteEntry);
-                routeUpdate = new RouteUpdate(RouteUpdate.Type.UPDATE,
-                                              bestBgpRouteEntry);
-                return routeUpdate;
-            }
-
-            //
-            // No route found. Remove the route entry
-            //
-            bgpRoutes.remove(bgpRouteEntry.prefix());
-            routeUpdate = new RouteUpdate(RouteUpdate.Type.DELETE,
-                                          bgpRouteEntry);
-            return routeUpdate;
-        }
-
-        /**
-         * Finds the best route entry among all BGP Sessions.
-         *
-         * @param prefix the prefix of the route
-         * @return the best route if found, otherwise null
-         */
-        private BgpRouteEntry findBestBgpRoute(Ip4Prefix prefix) {
-            BgpRouteEntry bestRoute = null;
-
-            // Iterate across all BGP Sessions and select the best route
-            for (BgpSession bgpSession : bgpSessions.values()) {
-                BgpRouteEntry route = bgpSession.findBgpRouteEntry(prefix);
-                if (route == null) {
-                    continue;
-                }
-                if ((bestRoute == null) || route.isBetterThan(bestRoute)) {
-                    bestRoute = route;
-                }
-            }
-            return bestRoute;
-        }
-    }
 }
diff --git a/apps/sdnip/src/main/java/org/onosproject/sdnip/bgp/BgpUpdate.java b/apps/sdnip/src/main/java/org/onosproject/sdnip/bgp/BgpUpdate.java
index 2dabfd9..ddce3a7 100644
--- a/apps/sdnip/src/main/java/org/onosproject/sdnip/bgp/BgpUpdate.java
+++ b/apps/sdnip/src/main/java/org/onosproject/sdnip/bgp/BgpUpdate.java
@@ -25,8 +25,8 @@
 import org.jboss.netty.buffer.ChannelBuffers;
 import org.jboss.netty.channel.ChannelHandlerContext;
 import org.onlab.packet.Ip4Address;
-import org.onlab.packet.Ip6Address;
 import org.onlab.packet.Ip4Prefix;
+import org.onlab.packet.Ip6Address;
 import org.onlab.packet.Ip6Prefix;
 import org.onosproject.sdnip.bgp.BgpConstants.Notifications.UpdateMessageError;
 import org.onosproject.sdnip.bgp.BgpConstants.Open.Capabilities.MultiprotocolExtensions;
@@ -112,7 +112,7 @@
         for (Ip4Prefix prefix : withdrawnPrefixes) {
             log.debug("BGP RX UPDATE message WITHDRAWN from {}: {}",
                       bgpSession.getRemoteAddress(), prefix);
-            BgpRouteEntry bgpRouteEntry = bgpSession.bgpRibIn4().get(prefix);
+            BgpRouteEntry bgpRouteEntry = bgpSession.findBgpRoute(prefix);
             if (bgpRouteEntry != null) {
                 decodedBgpRoutes.deletedUnicastRoutes4.put(prefix,
                                                            bgpRouteEntry);
@@ -134,35 +134,30 @@
         //
         // Update the BGP RIB-IN
         //
-        Collection<BgpRouteEntry> bgpRoutes;
-        //
-        bgpRoutes = decodedBgpRoutes.deletedUnicastRoutes4.values();
-        for (BgpRouteEntry bgpRouteEntry : bgpRoutes) {
-            bgpSession.bgpRibIn4().remove(bgpRouteEntry.prefix());
+        for (Ip4Prefix ip4Prefix :
+                 decodedBgpRoutes.deletedUnicastRoutes4.keySet()) {
+            bgpSession.removeBgpRoute(ip4Prefix);
         }
         //
-        bgpRoutes = decodedBgpRoutes.addedUnicastRoutes4.values();
-        for (BgpRouteEntry bgpRouteEntry : bgpRoutes) {
-            bgpSession.bgpRibIn4().put(bgpRouteEntry.prefix(), bgpRouteEntry);
+        for (BgpRouteEntry bgpRouteEntry :
+                 decodedBgpRoutes.addedUnicastRoutes4.values()) {
+            bgpSession.addBgpRoute(bgpRouteEntry);
         }
         //
-        bgpRoutes = decodedBgpRoutes.deletedUnicastRoutes6.values();
-        for (BgpRouteEntry bgpRouteEntry : bgpRoutes) {
-            bgpSession.bgpRibIn6().remove(bgpRouteEntry.prefix());
+        for (Ip6Prefix ip6Prefix :
+                 decodedBgpRoutes.deletedUnicastRoutes6.keySet()) {
+            bgpSession.removeBgpRoute(ip6Prefix);
         }
         //
-        bgpRoutes = decodedBgpRoutes.addedUnicastRoutes6.values();
-        // TODO: fix/enable for IPv6
-        /*
-        for (BgpRouteEntry bgpRouteEntry : bgpRoutes) {
-            bgpSession.bgpRibIn6().put(bgpRouteEntry.prefix(), bgpRouteEntry);
+        for (BgpRouteEntry bgpRouteEntry :
+                 decodedBgpRoutes.addedUnicastRoutes6.values()) {
+            bgpSession.addBgpRoute(bgpRouteEntry);
         }
-        */
 
         //
         // Push the updates to the BGP Merged RIB
         //
-        BgpSessionManager.BgpRouteSelector bgpRouteSelector =
+        BgpRouteSelector bgpRouteSelector =
             bgpSession.getBgpSessionManager().getBgpRouteSelector();
         bgpRouteSelector.routeUpdates(bgpSession,
                                 decodedBgpRoutes.addedUnicastRoutes4.values(),
@@ -408,7 +403,7 @@
 
             // The deleted IPv4 routes
             for (Ip4Prefix prefix : mpNlri.nlri4) {
-                bgpRouteEntry = bgpSession.bgpRibIn4().get(prefix);
+                bgpRouteEntry = bgpSession.findBgpRoute(prefix);
                 if (bgpRouteEntry != null) {
                     decodedBgpRoutes.deletedUnicastRoutes4.put(prefix,
                                                                bgpRouteEntry);
@@ -417,7 +412,7 @@
 
             // The deleted IPv6 routes
             for (Ip6Prefix prefix : mpNlri.nlri6) {
-                bgpRouteEntry = bgpSession.bgpRibIn6().get(prefix);
+                bgpRouteEntry = bgpSession.findBgpRoute(prefix);
                 if (bgpRouteEntry != null) {
                     decodedBgpRoutes.deletedUnicastRoutes6.put(prefix,
                                                                bgpRouteEntry);
@@ -456,8 +451,6 @@
             }
 
             // The added IPv6 routes
-            // TODO: fix/enable for IPv6
-            /*
             for (Ip6Prefix prefix : mpNlri.nlri6) {
                 bgpRouteEntry =
                     new BgpRouteEntry(bgpSession, prefix, mpNlri.nextHop6,
@@ -479,7 +472,6 @@
                 decodedBgpRoutes.addedUnicastRoutes6.put(prefix,
                                                          bgpRouteEntry);
             }
-            */
         }
     }
 
diff --git a/apps/sdnip/src/main/java/org/onosproject/sdnip/cli/BgpRoutesListCommand.java b/apps/sdnip/src/main/java/org/onosproject/sdnip/cli/BgpRoutesListCommand.java
index 56ad480..037c985 100644
--- a/apps/sdnip/src/main/java/org/onosproject/sdnip/cli/BgpRoutesListCommand.java
+++ b/apps/sdnip/src/main/java/org/onosproject/sdnip/cli/BgpRoutesListCommand.java
@@ -46,7 +46,10 @@
             required = false, multiValued = false)
     private String bgpNeighbor;
 
-    private static final String FORMAT_SUMMARY = "Total BGP routes = %d";
+    private static final String FORMAT_SUMMARY_V4 =
+        "Total BGP IPv4 routes = %d";
+    private static final String FORMAT_SUMMARY_V6 =
+        "Total BGP IPv6 routes = %d";
     private static final String FORMAT_HEADER =
         "   Network            Next Hop        Origin LocalPref       MED BGP-ID";
     private static final String FORMAT_ROUTE_LINE1 =
@@ -60,7 +63,7 @@
 
         // Print summary of the routes
         if (routesSummary) {
-            printSummary(service.getBgpRoutes());
+            printSummary(service.getBgpRoutes4(), service.getBgpRoutes6());
             return;
         }
 
@@ -81,43 +84,61 @@
 
         // Print the routes
         if (foundBgpSession != null) {
-            printRoutes(foundBgpSession.bgpRibIn4().values());
-            printRoutes(foundBgpSession.bgpRibIn6().values());
+            printRoutes(foundBgpSession.getBgpRibIn4(),
+                        foundBgpSession.getBgpRibIn6());
         } else {
-            printRoutes(service.getBgpRoutes());
+            printRoutes(service.getBgpRoutes4(), service.getBgpRoutes6());
         }
     }
 
     /**
      * Prints summary of the routes.
      *
-     * @param routes the routes
+     * @param routes4 the IPv4 routes
+     * @param routes6 the IPv6 routes
      */
-    private void printSummary(Collection<BgpRouteEntry> routes) {
+    private void printSummary(Collection<BgpRouteEntry> routes4,
+                              Collection<BgpRouteEntry> routes6) {
         if (outputJson()) {
             ObjectMapper mapper = new ObjectMapper();
             ObjectNode result = mapper.createObjectNode();
-            result.put("totalRoutes", routes.size());
+            result.put("totalRoutes4", routes4.size());
+            result.put("totalRoutes6", routes6.size());
             print("%s", result);
         } else {
-            print(FORMAT_SUMMARY, routes.size());
+            print(FORMAT_SUMMARY_V4, routes4.size());
+            print(FORMAT_SUMMARY_V6, routes6.size());
         }
     }
 
     /**
      * Prints all routes.
      *
-     * @param routes the routes to print
+     * @param routes4 the IPv4 routes to print
+     * @param routes6 the IPv6 routes to print
      */
-    private void printRoutes(Collection<BgpRouteEntry> routes) {
+    private void printRoutes(Collection<BgpRouteEntry> routes4,
+                             Collection<BgpRouteEntry> routes6) {
         if (outputJson()) {
-            print("%s", json(routes));
+            ObjectMapper mapper = new ObjectMapper();
+            ObjectNode result = mapper.createObjectNode();
+            result.put("routes4", json(routes4));
+            result.put("routes6", json(routes6));
+            print("%s", result);
         } else {
+            // The IPv4 routes
             print(FORMAT_HEADER);
-            for (BgpRouteEntry route : routes) {
+            for (BgpRouteEntry route : routes4) {
                 printRoute(route);
             }
-            print(FORMAT_SUMMARY, routes.size());
+            print(FORMAT_SUMMARY_V4, routes4.size());
+            print("");                  // Empty separator line
+            // The IPv6 routes
+            print(FORMAT_HEADER);
+            for (BgpRouteEntry route : routes6) {
+                printRoute(route);
+            }
+            print(FORMAT_SUMMARY_V6, routes6.size());
         }
     }
 
diff --git a/apps/sdnip/src/main/java/org/onosproject/sdnip/cli/RoutesListCommand.java b/apps/sdnip/src/main/java/org/onosproject/sdnip/cli/RoutesListCommand.java
index 19c5fc1..cebbc43 100644
--- a/apps/sdnip/src/main/java/org/onosproject/sdnip/cli/RoutesListCommand.java
+++ b/apps/sdnip/src/main/java/org/onosproject/sdnip/cli/RoutesListCommand.java
@@ -38,7 +38,10 @@
             required = false, multiValued = false)
     private boolean routesSummary = false;
 
-    private static final String FORMAT_SUMMARY = "Total SDN-IP routes = %d";
+    private static final String FORMAT_SUMMARY_V4 =
+        "Total SDN-IP IPv4 routes = %d";
+    private static final String FORMAT_SUMMARY_V6 =
+        "Total SDN-IP IPv6 routes = %d";
     private static final String FORMAT_HEADER =
         "   Network            Next Hop";
     private static final String FORMAT_ROUTE =
@@ -50,44 +53,62 @@
 
         // Print summary of the routes
         if (routesSummary) {
-            printSummary(service.getRoutes());
+            printSummary(service.getRoutes4(), service.getRoutes6());
             return;
         }
 
         // Print all routes
-        printRoutes(service.getRoutes());
+        printRoutes(service.getRoutes4(), service.getRoutes6());
     }
 
     /**
      * Prints summary of the routes.
      *
-     * @param routes the routes
+     * @param routes4 the IPv4 routes
+     * @param routes6 the IPv6 routes
      */
-    private void printSummary(Collection<RouteEntry> routes) {
+    private void printSummary(Collection<RouteEntry> routes4,
+                              Collection<RouteEntry> routes6) {
         if (outputJson()) {
             ObjectMapper mapper = new ObjectMapper();
             ObjectNode result = mapper.createObjectNode();
-            result.put("totalRoutes", routes.size());
+            result.put("totalRoutes4", routes4.size());
+            result.put("totalRoutes6", routes6.size());
             print("%s", result);
         } else {
-            print(FORMAT_SUMMARY, routes.size());
+            print(FORMAT_SUMMARY_V4, routes4.size());
+            print(FORMAT_SUMMARY_V6, routes6.size());
         }
     }
 
     /**
      * Prints all routes.
      *
-     * @param routes the routes to print
+     * @param routes4 the IPv4 routes to print
+     * @param routes6 the IPv6 routes to print
      */
-    private void printRoutes(Collection<RouteEntry> routes) {
+    private void printRoutes(Collection<RouteEntry> routes4,
+                             Collection<RouteEntry> routes6) {
         if (outputJson()) {
-            print("%s", json(routes));
+            ObjectMapper mapper = new ObjectMapper();
+            ObjectNode result = mapper.createObjectNode();
+            result.put("routes4", json(routes4));
+            result.put("routes6", json(routes6));
+            print("%s", result);
         } else {
+            // The IPv4 routes
             print(FORMAT_HEADER);
-            for (RouteEntry route : routes) {
+            for (RouteEntry route : routes4) {
                 printRoute(route);
             }
-            print(FORMAT_SUMMARY, routes.size());
+            print(FORMAT_SUMMARY_V4, routes4.size());
+            print("");                  // Empty separator line
+            // The IPv6 routes
+            print(FORMAT_HEADER);
+            for (RouteEntry route : routes6) {
+                printRoute(route);
+            }
+            print(FORMAT_SUMMARY_V6, routes6.size());
         }
     }
 
diff --git a/apps/sdnip/src/test/java/org/onosproject/sdnip/IntentSyncTest.java b/apps/sdnip/src/test/java/org/onosproject/sdnip/IntentSyncTest.java
index 5edab7c..0868582 100644
--- a/apps/sdnip/src/test/java/org/onosproject/sdnip/IntentSyncTest.java
+++ b/apps/sdnip/src/test/java/org/onosproject/sdnip/IntentSyncTest.java
@@ -61,8 +61,8 @@
 import org.onosproject.sdnip.config.Interface;
 import org.onlab.packet.Ethernet;
 import org.onlab.packet.IpAddress;
-import org.onlab.packet.Ip4Address;
 import org.onlab.packet.IpPrefix;
+import org.onlab.packet.Ip4Address;
 import org.onlab.packet.Ip4Prefix;
 import org.onlab.packet.MacAddress;
 import org.onlab.packet.VlanId;
@@ -308,10 +308,10 @@
                      routeEntry6);
         ribTable.put(RouteEntry.createBinaryString(routeEntry7.prefix()),
                      routeEntry7);
-        TestUtils.setField(router, "ribTable", ribTable);
+        TestUtils.setField(router, "ribTable4", ribTable);
 
-        ConcurrentHashMap<Ip4Prefix, MultiPointToSinglePointIntent>
-        routeIntents =  new ConcurrentHashMap<>();
+        ConcurrentHashMap<IpPrefix, MultiPointToSinglePointIntent>
+            routeIntents =  new ConcurrentHashMap<>();
         routeIntents.put(routeEntry1.prefix(), intent1);
         routeIntents.put(routeEntry3.prefix(), intent3);
         routeIntents.put(routeEntry4Update.prefix(), intent4Update);
@@ -364,12 +364,12 @@
         intentSynchronizer.synchronizeIntents();
 
         // Verify
-        assertEquals(router.getRoutes().size(), 6);
-        assertTrue(router.getRoutes().contains(routeEntry1));
-        assertTrue(router.getRoutes().contains(routeEntry3));
-        assertTrue(router.getRoutes().contains(routeEntry4Update));
-        assertTrue(router.getRoutes().contains(routeEntry5));
-        assertTrue(router.getRoutes().contains(routeEntry6));
+        assertEquals(router.getRoutes4().size(), 6);
+        assertTrue(router.getRoutes4().contains(routeEntry1));
+        assertTrue(router.getRoutes4().contains(routeEntry3));
+        assertTrue(router.getRoutes4().contains(routeEntry4Update));
+        assertTrue(router.getRoutes4().contains(routeEntry5));
+        assertTrue(router.getRoutes4().contains(routeEntry6));
 
         assertEquals(intentSynchronizer.getRouteIntents().size(), 6);
         assertTrue(intentSynchronizer.getRouteIntents().contains(intent1));
@@ -390,12 +390,17 @@
      * @param egressPoint to which packets should be sent
      * @return the constructed MultiPointToSinglePointIntent
      */
-    private MultiPointToSinglePointIntent intentBuilder(Ip4Prefix ipPrefix,
+    private MultiPointToSinglePointIntent intentBuilder(IpPrefix ipPrefix,
             String nextHopMacAddress, ConnectPoint egressPoint) {
 
         TrafficSelector.Builder selectorBuilder =
                 DefaultTrafficSelector.builder();
-        selectorBuilder.matchEthType(Ethernet.TYPE_IPV4).matchIPDst(ipPrefix);
+        if (ipPrefix.version() == Ip4Address.VERSION) {
+            selectorBuilder.matchEthType(Ethernet.TYPE_IPV4);   // IPv4
+        } else {
+            selectorBuilder.matchEthType(Ethernet.TYPE_IPV6);   // IPv6
+        }
+        selectorBuilder.matchIPDst(ipPrefix);
 
         TrafficTreatment.Builder treatmentBuilder =
                 DefaultTrafficTreatment.builder();
diff --git a/apps/sdnip/src/test/java/org/onosproject/sdnip/RouterAsyncArpTest.java b/apps/sdnip/src/test/java/org/onosproject/sdnip/RouterAsyncArpTest.java
index 4d3ad90..f8db770 100644
--- a/apps/sdnip/src/test/java/org/onosproject/sdnip/RouterAsyncArpTest.java
+++ b/apps/sdnip/src/test/java/org/onosproject/sdnip/RouterAsyncArpTest.java
@@ -63,8 +63,8 @@
 import org.onosproject.sdnip.config.SdnIpConfigurationService;
 import org.onlab.packet.Ethernet;
 import org.onlab.packet.IpAddress;
-import org.onlab.packet.Ip4Address;
 import org.onlab.packet.IpPrefix;
+import org.onlab.packet.Ip4Address;
 import org.onlab.packet.Ip4Prefix;
 import org.onlab.packet.MacAddress;
 import org.onlab.packet.VlanId;
@@ -239,8 +239,8 @@
                 new HostEvent(HostEvent.Type.HOST_ADDED, host));
 
         // Verify
-        assertEquals(router.getRoutes().size(), 1);
-        assertTrue(router.getRoutes().contains(routeEntry));
+        assertEquals(router.getRoutes4().size(), 1);
+        assertTrue(router.getRoutes4().contains(routeEntry));
         assertEquals(intentSynchronizer.getRouteIntents().size(), 1);
         Intent firstIntent =
             intentSynchronizer.getRouteIntents().iterator().next();
@@ -333,8 +333,8 @@
                 new HostEvent(HostEvent.Type.HOST_ADDED, host));
 
         // Verify
-        assertEquals(router.getRoutes().size(), 1);
-        assertTrue(router.getRoutes().contains(routeEntryUpdate));
+        assertEquals(router.getRoutes4().size(), 1);
+        assertTrue(router.getRoutes4().contains(routeEntryUpdate));
         assertEquals(intentSynchronizer.getRouteIntents().size(), 1);
         Intent firstIntent =
             intentSynchronizer.getRouteIntents().iterator().next();
@@ -380,7 +380,7 @@
         router.processRouteUpdates(Collections.<RouteUpdate>singletonList(routeUpdate));
 
         // Verify
-        assertEquals(router.getRoutes().size(), 0);
+        assertEquals(router.getRoutes4().size(), 0);
         assertEquals(intentSynchronizer.getRouteIntents().size(), 0);
         verify(intentService);
     }
@@ -424,7 +424,7 @@
                 new DefaultByteArrayNodeFactory());
         ribTable.put(RouteEntry.createBinaryString(routeEntry.prefix()),
                      routeEntry);
-        TestUtils.setField(router, "ribTable", ribTable);
+        TestUtils.setField(router, "ribTable4", ribTable);
     }
 
     /**
@@ -436,7 +436,7 @@
             MultiPointToSinglePointIntent intent)
             throws TestUtilsException {
 
-        ConcurrentHashMap<Ip4Prefix, MultiPointToSinglePointIntent>
+        ConcurrentHashMap<IpPrefix, MultiPointToSinglePointIntent>
             routeIntents =  new ConcurrentHashMap<>();
         routeIntents.put(routeEntry.prefix(), intent);
         TestUtils.setField(intentSynchronizer, "routeIntents", routeIntents);
diff --git a/apps/sdnip/src/test/java/org/onosproject/sdnip/RouterTest.java b/apps/sdnip/src/test/java/org/onosproject/sdnip/RouterTest.java
index 6ef4a20..9ef1f69 100644
--- a/apps/sdnip/src/test/java/org/onosproject/sdnip/RouterTest.java
+++ b/apps/sdnip/src/test/java/org/onosproject/sdnip/RouterTest.java
@@ -36,10 +36,10 @@
 import org.onlab.junit.TestUtils;
 import org.onlab.junit.TestUtils.TestUtilsException;
 import org.onlab.packet.Ethernet;
-import org.onlab.packet.Ip4Address;
-import org.onlab.packet.Ip4Prefix;
 import org.onlab.packet.IpAddress;
 import org.onlab.packet.IpPrefix;
+import org.onlab.packet.Ip4Address;
+import org.onlab.packet.Ip4Prefix;
 import org.onlab.packet.MacAddress;
 import org.onlab.packet.VlanId;
 import org.onosproject.core.ApplicationId;
@@ -278,8 +278,8 @@
         router.processRouteUpdates(Collections.<RouteUpdate>singletonList(routeUpdate));
 
         // Verify
-        assertEquals(router.getRoutes().size(), 1);
-        assertTrue(router.getRoutes().contains(routeEntry));
+        assertEquals(router.getRoutes4().size(), 1);
+        assertTrue(router.getRoutes4().contains(routeEntry));
         assertEquals(intentSynchronizer.getRouteIntents().size(), 1);
         Intent firstIntent =
             intentSynchronizer.getRouteIntents().iterator().next();
@@ -349,8 +349,8 @@
         router.processRouteUpdates(Collections.<RouteUpdate>singletonList(routeUpdate));
 
         // Verify
-        assertEquals(router.getRoutes().size(), 1);
-        assertTrue(router.getRoutes().contains(routeEntryUpdate));
+        assertEquals(router.getRoutes4().size(), 1);
+        assertTrue(router.getRoutes4().contains(routeEntryUpdate));
         assertEquals(intentSynchronizer.getRouteIntents().size(), 1);
         Intent firstIntent =
             intentSynchronizer.getRouteIntents().iterator().next();
@@ -393,7 +393,7 @@
         router.processRouteUpdates(Collections.<RouteUpdate>singletonList(routeUpdate));
 
         // Verify
-        assertEquals(router.getRoutes().size(), 0);
+        assertEquals(router.getRoutes4().size(), 0);
         assertEquals(intentSynchronizer.getRouteIntents().size(), 0);
         verify(intentService);
     }
@@ -422,8 +422,8 @@
         router.processRouteUpdates(Collections.<RouteUpdate>singletonList(routeUpdate));
 
         // Verify
-        assertEquals(router.getRoutes().size(), 1);
-        assertTrue(router.getRoutes().contains(routeEntry));
+        assertEquals(router.getRoutes4().size(), 1);
+        assertTrue(router.getRoutes4().contains(routeEntry));
         assertEquals(intentSynchronizer.getRouteIntents().size(), 0);
         verify(intentService);
     }
diff --git a/apps/sdnip/src/test/java/org/onosproject/sdnip/SdnIpTest.java b/apps/sdnip/src/test/java/org/onosproject/sdnip/SdnIpTest.java
index 06e77dc..0100c39 100644
--- a/apps/sdnip/src/test/java/org/onosproject/sdnip/SdnIpTest.java
+++ b/apps/sdnip/src/test/java/org/onosproject/sdnip/SdnIpTest.java
@@ -59,10 +59,10 @@
 import org.onosproject.sdnip.config.Interface;
 import org.onosproject.sdnip.config.SdnIpConfigurationService;
 import org.onlab.packet.Ethernet;
-import org.onlab.packet.Ip4Address;
-import org.onlab.packet.Ip4Prefix;
 import org.onlab.packet.IpAddress;
 import org.onlab.packet.IpPrefix;
+import org.onlab.packet.Ip4Address;
+import org.onlab.packet.Ip4Prefix;
 import org.onlab.packet.MacAddress;
 
 import com.google.common.collect.Sets;
@@ -226,7 +226,7 @@
         reset(intentService);
 
         for (RouteUpdate update : routeUpdates) {
-            Ip4Address nextHopAddress = update.routeEntry().nextHop();
+            IpAddress nextHopAddress = update.routeEntry().nextHop();
 
             // Find out the egress ConnectPoint
             ConnectPoint egressConnectPoint = getConnectPoint(nextHopAddress);
@@ -255,7 +255,7 @@
 
         latch.await(5000, TimeUnit.MILLISECONDS);
 
-        assertEquals(router.getRoutes().size(), numRoutes);
+        assertEquals(router.getRoutes4().size(), numRoutes);
         assertEquals(intentSynchronizer.getRouteIntents().size(),
                      numRoutes);
 
@@ -286,7 +286,7 @@
         reset(intentService);
 
         for (RouteUpdate update : routeUpdates) {
-            Ip4Address nextHopAddress = update.routeEntry().nextHop();
+            IpAddress nextHopAddress = update.routeEntry().nextHop();
 
             // Find out the egress ConnectPoint
             ConnectPoint egressConnectPoint = getConnectPoint(nextHopAddress);
@@ -333,7 +333,7 @@
 
         deleteCount.await(5000, TimeUnit.MILLISECONDS);
 
-        assertEquals(0, router.getRoutes().size());
+        assertEquals(0, router.getRoutes4().size());
         assertEquals(0, intentSynchronizer.getRouteIntents().size());
         verify(intentService);
     }
diff --git a/apps/sdnip/src/test/java/org/onosproject/sdnip/bgp/BgpSessionManagerTest.java b/apps/sdnip/src/test/java/org/onosproject/sdnip/bgp/BgpSessionManagerTest.java
index a4e0bab..096605a 100644
--- a/apps/sdnip/src/test/java/org/onosproject/sdnip/bgp/BgpSessionManagerTest.java
+++ b/apps/sdnip/src/test/java/org/onosproject/sdnip/bgp/BgpSessionManagerTest.java
@@ -303,7 +303,7 @@
     private Collection<BgpRouteEntry> waitForBgpRibIn(BgpSession bgpSession,
                                                       long expectedRoutes)
         throws InterruptedException {
-        Collection<BgpRouteEntry> bgpRibIn = bgpSession.bgpRibIn4().values();
+        Collection<BgpRouteEntry> bgpRibIn = bgpSession.getBgpRibIn4();
 
         final int maxChecks = 500;              // Max wait of 5 seconds
         for (int i = 0; i < maxChecks; i++) {
@@ -311,7 +311,7 @@
                 break;
             }
             Thread.sleep(10);
-            bgpRibIn = bgpSession.bgpRibIn4().values();
+            bgpRibIn = bgpSession.getBgpRibIn4();
         }
 
         return bgpRibIn;
@@ -329,7 +329,8 @@
      */
     private Collection<BgpRouteEntry> waitForBgpRoutes(long expectedRoutes)
         throws InterruptedException {
-        Collection<BgpRouteEntry> bgpRoutes = bgpSessionManager.getBgpRoutes();
+        Collection<BgpRouteEntry> bgpRoutes =
+            bgpSessionManager.getBgpRoutes4();
 
         final int maxChecks = 500;              // Max wait of 5 seconds
         for (int i = 0; i < maxChecks; i++) {
@@ -337,7 +338,7 @@
                 break;
             }
             Thread.sleep(10);
-            bgpRoutes = bgpSessionManager.getBgpRoutes();
+            bgpRoutes = bgpSessionManager.getBgpRoutes4();
         }
 
         return bgpRoutes;