Pavlin Radoslavov

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
......@@ -29,7 +29,7 @@ import java.util.concurrent.Executors;
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 @@ public class IntentSynchronizer {
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 class IntentSynchronizer {
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 @@ public class IntentSynchronizer {
* 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 @@ public class IntentSynchronizer {
//
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 @@ public class IntentSynchronizer {
//
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 @@ public class IntentSynchronizer {
// 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;
......
......@@ -19,8 +19,8 @@ import static com.google.common.base.Preconditions.checkNotNull;
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 @@ import com.google.common.base.MoreObjects;
* 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 @@ public class RouteEntry {
* @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 @@ public class RouteEntry {
*
* @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();
}
......
......@@ -30,9 +30,9 @@ import java.util.concurrent.LinkedBlockingQueue;
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 @@ import com.googlecode.concurrenttrees.radixinverted.InvertedRadixTree;
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 @@ public class Router implements RouteListener {
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 @@ public class Router implements RouteListener {
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 @@ public class Router implements RouteListener {
}
/**
* 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 @@ public class Router implements RouteListener {
*/
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 @@ public class Router implements RouteListener {
* @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 @@ public class Router implements RouteListener {
}
// 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 @@ public class Router implements RouteListener {
* 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 @@ public class Router implements RouteListener {
* @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 @@ public class Router implements RouteListener {
// 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 @@ public class Router implements RouteListener {
}
if (!submitIntents.isEmpty()) {
Collection<Ip4Prefix> withdrawPrefixes = new LinkedList<>();
Collection<IpPrefix> withdrawPrefixes = new LinkedList<>();
intentSynchronizer.updateRouteIntents(submitIntents,
withdrawPrefixes);
}
......@@ -464,25 +566,6 @@ public class Router implements RouteListener {
}
/**
* 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 @@ public class Router implements RouteListener {
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:
......
......@@ -164,13 +164,23 @@ public class SdnIp implements SdnIpService {
}
@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
......
......@@ -32,18 +32,32 @@ public interface SdnIpService {
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
......
......@@ -20,8 +20,9 @@ import static com.google.common.base.Preconditions.checkNotNull;
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 @@ public class BgpRouteEntry extends RouteEntry {
* @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);
......
/*
* 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;
}
}
......@@ -20,7 +20,6 @@ import java.net.InetSocketAddress;
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.HashedWheelTimer;
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 @@ public class BgpSession extends SimpleChannelHandler {
}
/**
* 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 @@ public class BgpSession extends SimpleChannelHandler {
@Override
public void channelClosed(ChannelHandlerContext ctx,
ChannelStateEvent channelEvent) {
ChannelStateEvent channelEvent) {
bgpSessionManager.removeSessionChannel(channelEvent.getChannel());
}
......@@ -622,7 +691,7 @@ public class BgpSession extends SimpleChannelHandler {
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);
......
......@@ -22,7 +22,6 @@ import java.net.InetAddress;
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.Channels;
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 @@ import org.slf4j.LoggerFactory;
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 @@ public class BgpSessionManager {
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 @@ public class BgpSessionManager {
}
/**
* 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 @@ public class BgpSessionManager {
}
/**
* 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 @@ public class BgpSessionManager {
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 @@ public class BgpSessionManager {
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;
}
}
}
......
......@@ -25,8 +25,8 @@ import org.jboss.netty.buffer.ChannelBuffer;
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 @@ final class BgpUpdate {
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 @@ final class BgpUpdate {
//
// 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 @@ final class BgpUpdate {
// 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 @@ final class BgpUpdate {
// 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 @@ final class BgpUpdate {
}
// 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 @@ final class BgpUpdate {
decodedBgpRoutes.addedUnicastRoutes6.put(prefix,
bgpRouteEntry);
}
*/
}
}
......
......@@ -46,7 +46,10 @@ public class BgpRoutesListCommand extends AbstractShellCommand {
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 @@ public class BgpRoutesListCommand extends AbstractShellCommand {
// Print summary of the routes
if (routesSummary) {
printSummary(service.getBgpRoutes());
printSummary(service.getBgpRoutes4(), service.getBgpRoutes6());
return;
}
......@@ -81,43 +84,61 @@ public class BgpRoutesListCommand extends AbstractShellCommand {
// 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 : routes4) {
printRoute(route);
}
print(FORMAT_SUMMARY_V4, routes4.size());
print(""); // Empty separator line
// The IPv6 routes
print(FORMAT_HEADER);
for (BgpRouteEntry route : routes) {
for (BgpRouteEntry route : routes6) {
printRoute(route);
}
print(FORMAT_SUMMARY, routes.size());
print(FORMAT_SUMMARY_V6, routes6.size());
}
}
......
......@@ -38,7 +38,10 @@ public class RoutesListCommand extends AbstractShellCommand {
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 @@ public class RoutesListCommand extends AbstractShellCommand {
// 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 : routes4) {
printRoute(route);
}
print(FORMAT_SUMMARY_V4, routes4.size());
print(""); // Empty separator line
// The IPv6 routes
print(FORMAT_HEADER);
for (RouteEntry route : routes) {
for (RouteEntry route : routes6) {
printRoute(route);
}
print(FORMAT_SUMMARY, routes.size());
print(FORMAT_SUMMARY_V6, routes6.size());
}
}
......
......@@ -61,8 +61,8 @@ import org.onosproject.net.provider.ProviderId;
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 @@ public class IntentSyncTest extends AbstractIntentTest {
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 @@ public class IntentSyncTest extends AbstractIntentTest {
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 @@ public class IntentSyncTest extends AbstractIntentTest {
* @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();
......
......@@ -63,8 +63,8 @@ import org.onosproject.sdnip.config.Interface;
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 @@ public class RouterAsyncArpTest extends AbstractIntentTest {
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 @@ public class RouterAsyncArpTest extends AbstractIntentTest {
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 @@ public class RouterAsyncArpTest extends AbstractIntentTest {
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 @@ public class RouterAsyncArpTest extends AbstractIntentTest {
new DefaultByteArrayNodeFactory());
ribTable.put(RouteEntry.createBinaryString(routeEntry.prefix()),
routeEntry);
TestUtils.setField(router, "ribTable", ribTable);
TestUtils.setField(router, "ribTable4", ribTable);
}
/**
......@@ -436,7 +436,7 @@ public class RouterAsyncArpTest extends AbstractIntentTest {
MultiPointToSinglePointIntent intent)
throws TestUtilsException {
ConcurrentHashMap<Ip4Prefix, MultiPointToSinglePointIntent>
ConcurrentHashMap<IpPrefix, MultiPointToSinglePointIntent>
routeIntents = new ConcurrentHashMap<>();
routeIntents.put(routeEntry.prefix(), intent);
TestUtils.setField(intentSynchronizer, "routeIntents", routeIntents);
......
......@@ -36,10 +36,10 @@ import org.junit.Test;
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 @@ public class RouterTest extends AbstractIntentTest {
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 @@ public class RouterTest extends AbstractIntentTest {
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 @@ public class RouterTest extends AbstractIntentTest {
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 @@ public class RouterTest extends AbstractIntentTest {
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);
}
......
......@@ -59,10 +59,10 @@ import org.onosproject.sdnip.config.BgpPeer;
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 @@ public class SdnIpTest extends AbstractIntentTest {
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 @@ public class SdnIpTest extends AbstractIntentTest {
latch.await(5000, TimeUnit.MILLISECONDS);
assertEquals(router.getRoutes().size(), numRoutes);
assertEquals(router.getRoutes4().size(), numRoutes);
assertEquals(intentSynchronizer.getRouteIntents().size(),
numRoutes);
......@@ -286,7 +286,7 @@ public class SdnIpTest extends AbstractIntentTest {
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 @@ public class SdnIpTest extends AbstractIntentTest {
deleteCount.await(5000, TimeUnit.MILLISECONDS);
assertEquals(0, router.getRoutes().size());
assertEquals(0, router.getRoutes4().size());
assertEquals(0, intentSynchronizer.getRouteIntents().size());
verify(intentService);
}
......
......@@ -303,7 +303,7 @@ public class BgpSessionManagerTest {
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 @@ public class BgpSessionManagerTest {
break;
}
Thread.sleep(10);
bgpRibIn = bgpSession.bgpRibIn4().values();
bgpRibIn = bgpSession.getBgpRibIn4();
}
return bgpRibIn;
......@@ -329,7 +329,8 @@ public class BgpSessionManagerTest {
*/
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 @@ public class BgpSessionManagerTest {
break;
}
Thread.sleep(10);
bgpRoutes = bgpSessionManager.getBgpRoutes();
bgpRoutes = bgpSessionManager.getBgpRoutes4();
}
return bgpRoutes;
......