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/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;
-        }
-    }
 }