| /* |
| * Copyright 2015-present 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.routing.bgp; |
| |
| import org.onlab.packet.IpPrefix; |
| import org.onosproject.incubator.net.routing.Route; |
| import org.onosproject.routing.RouteUpdate; |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| |
| import java.util.Collection; |
| import java.util.LinkedList; |
| |
| /** |
| * 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 addedBgpRouteEntries the added/updated route entries to process |
| * @param deletedBgpRouteEntries the deleted route entries to process |
| */ |
| synchronized void routeUpdates( |
| Collection<BgpRouteEntry> addedBgpRouteEntries, |
| Collection<BgpRouteEntry> deletedBgpRouteEntries) { |
| |
| Collection<Route> updates = new LinkedList<>(); |
| Collection<Route> withdraws = 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(bgpRouteEntry); |
| convertRouteUpdateToRoute(routeUpdate, updates, withdraws); |
| } |
| |
| // Process the added/updated route entries |
| for (BgpRouteEntry bgpRouteEntry : addedBgpRouteEntries) { |
| routeUpdate = processAddedRoute(bgpRouteEntry); |
| convertRouteUpdateToRoute(routeUpdate, updates, withdraws); |
| } |
| |
| bgpSessionManager.withdraw(withdraws); |
| bgpSessionManager.update(updates); |
| } |
| |
| private void convertRouteUpdateToRoute(RouteUpdate routeUpdate, |
| Collection<Route> updates, |
| Collection<Route> withdraws) { |
| if (routeUpdate != null) { |
| Route route = new Route(Route.Source.BGP, routeUpdate.routeEntry().prefix(), |
| routeUpdate.routeEntry().nextHop()); |
| if (routeUpdate.type().equals(RouteUpdate.Type.UPDATE)) { |
| updates.add(route); |
| } else if (routeUpdate.type().equals(RouteUpdate.Type.DELETE)) { |
| withdraws.add(route); |
| } |
| } |
| } |
| |
| /** |
| * Processes an added/updated route entry. |
| * |
| * @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(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 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(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; |
| } |
| } |