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/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: