blob: 259ae3cff896340b0dfe659dafbbd95073427c16 [file] [log] [blame]
/*
* Copyright 2017-present Open Networking Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onosproject.routing.bgp;
import org.onlab.packet.IpPrefix;
import org.onosproject.cluster.ClusterService;
import org.onosproject.routeservice.Route;
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;
private ClusterService clusterService;
/**
* Constructor.
*
* @param bgpSessionManager the BGP Session Manager to use
* @param clusterService the cluster service
*/
BgpRouteSelector(BgpSessionManager bgpSessionManager, ClusterService clusterService) {
this.bgpSessionManager = bgpSessionManager;
this.clusterService = clusterService;
}
/**
* 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(), clusterService.getLocalNode().id());
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;
}
}