Prioritize certain type of routes among others
FPM, STATIC and configured subnets go to high priority batch, while others (e.g. DHCP) go to low priority batch
Change-Id: I8dcef67945c31bd0eab869510bea0f1f278b2925
diff --git a/app/src/main/java/org/onosproject/segmentrouting/DefaultRoutingHandler.java b/app/src/main/java/org/onosproject/segmentrouting/DefaultRoutingHandler.java
index 149e837..f456e66 100644
--- a/app/src/main/java/org/onosproject/segmentrouting/DefaultRoutingHandler.java
+++ b/app/src/main/java/org/onosproject/segmentrouting/DefaultRoutingHandler.java
@@ -23,6 +23,7 @@
import com.google.common.collect.Sets;
import org.onlab.packet.EthType;
+import com.google.common.collect.Streams;
import org.onlab.packet.Ip4Address;
import org.onlab.packet.Ip6Address;
import org.onlab.packet.IpPrefix;
@@ -59,6 +60,7 @@
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
+import java.util.stream.Collectors;
import java.util.stream.Stream;
import static com.google.common.base.Preconditions.checkNotNull;
@@ -694,56 +696,82 @@
route.get(0), route.get(1), nhops);
perDstNextHops.put(route.get(1), nhops);
});
- Set<IpPrefix> ipDev1 = (subnets == null) ? config.getSubnets(ep.dev1)
- : subnets;
- Set<IpPrefix> ipDev2 = (subnets == null) ? config.getSubnets(ep.dev2)
- : subnets;
- ipDev1 = (ipDev1 == null) ? Sets.newHashSet() : ipDev1;
- ipDev2 = (ipDev2 == null) ? Sets.newHashSet() : ipDev2;
+
+ List<Set<IpPrefix>> batchedSubnetDev1, batchedSubnetDev2;
+ if (subnets != null) {
+ batchedSubnetDev1 = Lists.<Set<IpPrefix>>newArrayList(Sets.newHashSet(subnets));
+ batchedSubnetDev2 = Lists.<Set<IpPrefix>>newArrayList(Sets.newHashSet(subnets));
+ } else {
+ batchedSubnetDev1 = config.getBatchedSubnets(ep.dev1);
+ batchedSubnetDev2 = config.getBatchedSubnets(ep.dev2);
+ }
+ List<Set<IpPrefix>> batchedSubnetBoth = Streams
+ .zip(batchedSubnetDev1.stream(), batchedSubnetDev2.stream(), (a, b) -> Sets.intersection(a, b))
+ .filter(set -> !set.isEmpty())
+ .collect(Collectors.toList());
+ List<Set<IpPrefix>> batchedSubnetDev1Only = Streams
+ .zip(batchedSubnetDev1.stream(), batchedSubnetDev2.stream(), (a, b) -> Sets.difference(a, b))
+ .filter(set -> !set.isEmpty())
+ .collect(Collectors.toList());
+ List<Set<IpPrefix>> batchedSubnetDev2Only = Streams
+ .zip(batchedSubnetDev1.stream(), batchedSubnetDev2.stream(), (a, b) -> Sets.difference(b, a))
+ .filter(set -> !set.isEmpty())
+ .collect(Collectors.toList());
+
Set<DeviceId> nhDev1 = perDstNextHops.get(ep.dev1);
Set<DeviceId> nhDev2 = perDstNextHops.get(ep.dev2);
+
// handle routing to subnets common to edge-pair
// only if the targetSw is not part of the edge-pair and there
// exists a next hop to at least one of the devices in the edge-pair
if (!ep.includes(targetSw)
- && ((nhDev1 != null && !nhDev1.isEmpty())
- || (nhDev2 != null && !nhDev2.isEmpty()))) {
- if (!populateEcmpRoutingRulePartial(
- targetSw,
- ep.dev1, ep.dev2,
- perDstNextHops,
- Sets.intersection(ipDev1, ipDev2))) {
- return false; // abort everything and fail fast
+ && ((nhDev1 != null && !nhDev1.isEmpty()) || (nhDev2 != null && !nhDev2.isEmpty()))) {
+ log.trace("getSubnets on both {} and {}: {}", ep.dev1, ep.dev2, batchedSubnetBoth);
+ for (Set<IpPrefix> prefixes : batchedSubnetBoth) {
+ if (!populateEcmpRoutingRulePartial(
+ targetSw,
+ ep.dev1, ep.dev2,
+ perDstNextHops,
+ prefixes)) {
+ return false; // abort everything and fail fast
+ }
}
+
}
// handle routing to subnets that only belong to dev1 only if
// a next-hop exists from the target to dev1
- Set<IpPrefix> onlyDev1Subnets = Sets.difference(ipDev1, ipDev2);
- if (!onlyDev1Subnets.isEmpty()
- && nhDev1 != null && !nhDev1.isEmpty()) {
+ if (!batchedSubnetDev1Only.isEmpty() &&
+ batchedSubnetDev1Only.stream().anyMatch(subnet -> !subnet.isEmpty()) &&
+ nhDev1 != null && !nhDev1.isEmpty()) {
Map<DeviceId, Set<DeviceId>> onlyDev1NextHops = new HashMap<>();
onlyDev1NextHops.put(ep.dev1, nhDev1);
- if (!populateEcmpRoutingRulePartial(
- targetSw,
- ep.dev1, null,
- onlyDev1NextHops,
- onlyDev1Subnets)) {
- return false; // abort everything and fail fast
+ log.trace("getSubnets on {} only: {}", ep.dev1, batchedSubnetDev1Only);
+ for (Set<IpPrefix> prefixes : batchedSubnetDev1Only) {
+ if (!populateEcmpRoutingRulePartial(
+ targetSw,
+ ep.dev1, null,
+ onlyDev1NextHops,
+ prefixes)) {
+ return false; // abort everything and fail fast
+ }
}
}
// handle routing to subnets that only belong to dev2 only if
// a next-hop exists from the target to dev2
- Set<IpPrefix> onlyDev2Subnets = Sets.difference(ipDev2, ipDev1);
- if (!onlyDev2Subnets.isEmpty()
- && nhDev2 != null && !nhDev2.isEmpty()) {
+ if (!batchedSubnetDev2Only.isEmpty() &&
+ batchedSubnetDev2Only.stream().anyMatch(subnet -> !subnet.isEmpty()) &&
+ nhDev2 != null && !nhDev2.isEmpty()) {
Map<DeviceId, Set<DeviceId>> onlyDev2NextHops = new HashMap<>();
onlyDev2NextHops.put(ep.dev2, nhDev2);
- if (!populateEcmpRoutingRulePartial(
- targetSw,
- ep.dev2, null,
- onlyDev2NextHops,
- onlyDev2Subnets)) {
- return false; // abort everything and fail fast
+ log.trace("getSubnets on {} only: {}", ep.dev2, batchedSubnetDev2Only);
+ for (Set<IpPrefix> prefixes : batchedSubnetDev2Only) {
+ if (!populateEcmpRoutingRulePartial(
+ targetSw,
+ ep.dev2, null,
+ onlyDev2NextHops,
+ prefixes)) {
+ return false; // abort everything and fail fast
+ }
}
}
}
@@ -860,9 +888,12 @@
}
if (targetIsEdge && dest1IsEdge) {
- subnets = (subnets != null && !subnets.isEmpty())
- ? Sets.newHashSet(subnets)
- : Sets.newHashSet(config.getSubnets(destSw1));
+ List<Set<IpPrefix>> batchedSubnets;
+ if (subnets != null && !subnets.isEmpty()) {
+ batchedSubnets = Lists.<Set<IpPrefix>>newArrayList(Sets.newHashSet(subnets));
+ } else {
+ batchedSubnets = config.getBatchedSubnets(destSw1);
+ }
// XXX - Rethink this - ignoring routerIPs in all other switches
// even edge to edge switches
/*subnets.add(dest1RouterIpv4.toIpPrefix());
@@ -875,15 +906,15 @@
subnets.add(dest2RouterIpv6.toIpPrefix());
}
}*/
- log.debug(". populateEcmpRoutingRulePartial in device {} towards {} {} "
- + "for subnets {}", targetSw, destSw1,
- (destSw2 != null) ? ("& " + destSw2) : "",
- subnets);
- result = rulePopulator.populateIpRuleForSubnet(targetSw, subnets,
- destSw1, destSw2,
- nextHops);
- if (!result) {
- return false;
+ log.trace("getSubnets on {}: {}", destSw1, batchedSubnets);
+ for (Set<IpPrefix> prefixes : batchedSubnets) {
+ log.debug(". populateEcmpRoutingRulePartial in device {} towards {} {} "
+ + "for subnets {}", targetSw, destSw1,
+ (destSw2 != null) ? ("& " + destSw2) : "",
+ prefixes);
+ if (!rulePopulator.populateIpRuleForSubnet(targetSw, prefixes, destSw1, destSw2, nextHops)) {
+ return false;
+ }
}
}
diff --git a/app/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java b/app/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java
index eebb12a..7319e56 100644
--- a/app/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java
+++ b/app/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java
@@ -242,7 +242,7 @@
public TopologyService topologyService;
@Reference(cardinality = ReferenceCardinality.MANDATORY)
- RouteService routeService;
+ public RouteService routeService;
@Reference(cardinality = ReferenceCardinality.MANDATORY)
public NetworkConfigRegistry cfgService;
@@ -1223,7 +1223,7 @@
* @param resolvedRoute resolved route
* @return locations of nexthop. Might be empty if next hop is not found
*/
- Set<ConnectPoint> nextHopLocations(ResolvedRoute resolvedRoute) {
+ public Set<ConnectPoint> nextHopLocations(ResolvedRoute resolvedRoute) {
HostId hostId = HostId.hostId(resolvedRoute.nextHopMac(), resolvedRoute.nextHopVlan());
return Optional.ofNullable(hostService.getHost(hostId))
.map(Host::locations).orElse(Sets.newHashSet())
diff --git a/app/src/main/java/org/onosproject/segmentrouting/config/DeviceConfiguration.java b/app/src/main/java/org/onosproject/segmentrouting/config/DeviceConfiguration.java
index 93e76ad..f2a971b 100644
--- a/app/src/main/java/org/onosproject/segmentrouting/config/DeviceConfiguration.java
+++ b/app/src/main/java/org/onosproject/segmentrouting/config/DeviceConfiguration.java
@@ -20,8 +20,10 @@
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Lists;
import com.google.common.collect.Multimaps;
import com.google.common.collect.SetMultimap;
+import com.google.common.collect.Sets;
import org.onlab.packet.Ip4Address;
import org.onlab.packet.Ip6Address;
import org.onlab.packet.IpAddress;
@@ -35,6 +37,7 @@
import org.onosproject.net.config.basics.InterfaceConfig;
import org.onosproject.net.host.InterfaceIpAddress;
import org.onosproject.net.intf.Interface;
+import org.onosproject.routeservice.Route;
import org.onosproject.segmentrouting.SegmentRoutingManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -492,8 +495,10 @@
* Returns all subnets for a segment router, including subnets learnt from route service.
*
* @param deviceId device identifier
- * @return list of ip prefixes or null if not found
+ * @return set of ip prefixes or null if not found
+ * @deprecated use getBatchedSubnets(DeviceId deviceId) instead
*/
+ @Deprecated
public Set<IpPrefix> getSubnets(DeviceId deviceId) {
SegmentRouterInfo srinfo = deviceConfigMap.get(deviceId);
if (srinfo != null && srinfo.subnets != null) {
@@ -506,6 +511,40 @@
return null;
}
+ /**
+ * Returns batches of all subnets reachable on the given device.
+ * <p>
+ * First batch includes configured subnets, FPM and STATIC routes
+ * Second batch includes all other type of routes obtained from routeService, including DHCP routes.
+ *
+ * @param deviceId device identifier
+ * @return list of subnet batches, each batch includes a set of prefixes.
+ */
+ // TODO Querying routeService directly may be expensive. Some kind of reverse lookup cache should be developed.
+ public List<Set<IpPrefix>> getBatchedSubnets(DeviceId deviceId) {
+ Set<IpPrefix> high = Sets.newHashSet();
+ Set<IpPrefix> low = Sets.newHashSet();
+
+ high.addAll(getConfiguredSubnets(deviceId));
+ srManager.routeService.getRouteTables().stream()
+ .map(tableId -> srManager.routeService.getResolvedRoutes(tableId))
+ .flatMap(Collection::stream)
+ .forEach(resolvedRoute -> {
+ // Continue to next resolved route if none of the next hop attaches to given device
+ if (srManager.nextHopLocations(resolvedRoute).stream()
+ .noneMatch(cp -> Objects.equals(deviceId, cp.deviceId()))) {
+ return;
+ }
+ // Prioritize STATIC and FPM among others
+ if (resolvedRoute.route().source() == Route.Source.STATIC ||
+ resolvedRoute.route().source() == Route.Source.FPM) {
+ high.add(resolvedRoute.prefix());
+ } else {
+ low.add(resolvedRoute.prefix());
+ }
+ });
+ return Lists.newArrayList(high, low);
+ }
/**
* Returns the subnet configuration of given device and port.