use of org.batfish.datamodel.Route in project batfish by batfish.
the class BdpEngine method debugIterations.
private String debugIterations(String msg, Map<Integer, SortedSet<Route>> iterationRoutes, int first, int last) {
StringBuilder sb = new StringBuilder();
sb.append(msg);
sb.append("\n");
SortedSet<Route> initialRoutes = iterationRoutes.get(first);
sb.append("Initial routes (iteration " + first + "):\n");
for (Route route : initialRoutes) {
String routeStr = route.prettyPrint(null);
sb.append(routeStr);
}
for (int i = first + 1; i <= last; i++) {
SortedSet<Route> baseRoutes = iterationRoutes.get(i - 1);
SortedSet<Route> deltaRoutes = iterationRoutes.get(i);
SortedSet<Route> added = CommonUtil.difference(deltaRoutes, baseRoutes, TreeSet<Route>::new);
SortedSet<Route> removed = CommonUtil.difference(baseRoutes, deltaRoutes, TreeSet<Route>::new);
SortedSet<Route> changed = CommonUtil.union(added, removed, TreeSet<Route>::new);
sb.append("Changed routes (iteration " + (i - 1) + " ==> " + i + "):\n");
for (Route route : changed) {
String diffSymbol = added.contains(route) ? "+" : "-";
String routeStr = route.prettyPrint(diffSymbol);
sb.append(routeStr);
}
}
String errorMessage = sb.toString();
return errorMessage;
}
use of org.batfish.datamodel.Route in project batfish by batfish.
the class BdpEngine method computeOutputRoutes.
private SortedSet<Route> computeOutputRoutes(Map<String, Node> nodes, Map<Ip, String> ipOwners) {
SortedSet<Route> outputRoutes = new TreeSet<>();
nodes.forEach((hostname, node) -> {
node._virtualRouters.forEach((vrName, vr) -> {
for (AbstractRoute route : vr._mainRib.getRoutes()) {
RouteBuilder rb = new RouteBuilder();
rb.setNode(hostname);
rb.setNetwork(route.getNetwork());
Ip nextHopIp = route.getNextHopIp();
if (route.getProtocol() == RoutingProtocol.CONNECTED || (route.getProtocol() == RoutingProtocol.STATIC && nextHopIp.equals(Route.UNSET_ROUTE_NEXT_HOP_IP)) || Interface.NULL_INTERFACE_NAME.equals(route.getNextHopInterface())) {
rb.setNextHop(Configuration.NODE_NONE_NAME);
}
if (!nextHopIp.equals(Route.UNSET_ROUTE_NEXT_HOP_IP)) {
rb.setNextHopIp(nextHopIp);
String nextHop = ipOwners.get(nextHopIp);
if (nextHop != null) {
rb.setNextHop(nextHop);
}
}
rb.setNextHopInterface(route.getNextHopInterface());
rb.setAdministrativeCost(route.getAdministrativeCost());
rb.setCost(route.getMetric());
rb.setProtocol(route.getProtocol());
rb.setTag(route.getTag());
rb.setVrf(vrName);
Route outputRoute = rb.build();
outputRoutes.add(outputRoute);
}
});
});
return outputRoutes;
}
use of org.batfish.datamodel.Route in project batfish by batfish.
the class BdpEngine method computeFixedPoint.
/**
* Attempt to compute the fixed point of the data plane.
*
* @param nodes A dictionary of configuration-wrapping Bdp nodes keyed by name
* @param topology The topology representing physical adjacencies between interface of the nodes
* @param dp The output data plane
* @param externalAdverts Optional external BGP advertisements fed into the data plane computation
* @param ae The output answer element in which to store a report of the computation. Also
* contains the current recovery iteration.
* @param recoveryIterationHashCodes Dependent-route computation iteration hash-code dictionaries,
* themselves keyed by outer recovery iteration.
* @return true iff the computation is oscillating
*/
private boolean computeFixedPoint(SortedMap<String, Node> nodes, Topology topology, BdpDataPlane dp, Set<BgpAdvertisement> externalAdverts, BdpAnswerElement ae, SortedMap<Integer, SortedMap<Integer, Integer>> recoveryIterationHashCodes, SortedMap<Integer, SortedSet<Prefix>> iterationOscillatingPrefixes) {
try (ActiveSpan computeFixedPointSpan = GlobalTracer.get().buildSpan("Computing fixed point").startActive()) {
// avoid unused warning
assert computeFixedPointSpan != null;
SortedSet<Prefix> oscillatingPrefixes = ae.getOscillatingPrefixes();
// BEGIN DONE ONCE (except main rib)
// For each virtual router, setup the initial easy-to-do routes and init protocol-based RIBs:
AtomicInteger initialCompleted = _newBatch.apply("Compute initial connected and static routes, ospf setup, bgp setup", nodes.size());
try (ActiveSpan initRibsBdpSpan = GlobalTracer.get().buildSpan("Initializing easy routes for BDP").startActive()) {
// avoid unused warning
assert initRibsBdpSpan != null;
nodes.values().parallelStream().forEach(n -> {
for (VirtualRouter vr : n._virtualRouters.values()) {
vr.initRibsForBdp(dp.getIpOwners(), externalAdverts);
}
initialCompleted.incrementAndGet();
});
}
// OSPF internal routes
int numOspfInternalIterations;
try (ActiveSpan ospfInternalRoutesSpan = GlobalTracer.get().buildSpan("Initializing OSPF internal routes").startActive()) {
assert ospfInternalRoutesSpan != null;
numOspfInternalIterations = initOspfInternalRoutes(nodes, topology);
}
// RIP internal routes
try (ActiveSpan ripInternalRoutesSpan = GlobalTracer.get().buildSpan("Initializing RIP internal routes").startActive()) {
assert ripInternalRoutesSpan != null;
initRipInternalRoutes(nodes, topology);
}
// Prep for traceroutes
nodes.values().parallelStream().forEach(n -> n._virtualRouters.values().forEach(vr -> {
vr.importRib(vr._mainRib, vr._independentRib);
// Needed for activateStaticRoutes
vr._prevMainRib = vr._mainRib;
vr.activateStaticRoutes();
}));
// Update bgp neighbors with reachability
dp.setNodes(nodes);
computeFibs(nodes);
dp.setTopology(topology);
initRemoteBgpNeighbors(nodes.entrySet().stream().collect(ImmutableMap.toImmutableMap(Entry::getKey, e -> e.getValue().getConfiguration())), dp.getIpOwners(), true, this, dp);
// END DONE ONCE
/*
* Setup maps to track iterations. We need this for oscillation detection.
* Specifically, if we detect that an iteration hashcode (a hash of all the nodes' RIBs)
* has been previously encountered, we go into recovery mode.
* Recovery mode means enabling "lockstep route propagation" for oscillating prefixes.
*
* Lockstep route propagation only allows one of the neighbors to propagate routes for
* oscillating prefixes in a given iteration.
* E.g., lexicographically lower neighbor propagates routes during
* odd iterations, and lex-higher neighbor during even iterations.
*/
Map<Integer, SortedSet<Integer>> iterationsByHashCode = new HashMap<>();
SortedMap<Integer, Integer> iterationHashCodes = new TreeMap<>();
Map<Integer, SortedSet<Route>> iterationRoutes = null;
Map<Integer, SortedMap<String, SortedMap<String, SortedSet<AbstractRoute>>>> iterationAbstractRoutes = null;
if (_settings.getBdpRecordAllIterations()) {
if (_settings.getBdpDetail()) {
iterationAbstractRoutes = new TreeMap<>();
} else {
iterationRoutes = new TreeMap<>();
}
} else if (_maxRecordedIterations > 0) {
if (_settings.getBdpDetail()) {
iterationAbstractRoutes = new LRUMap<>(_maxRecordedIterations);
} else {
iterationRoutes = new LRUMap<>(_maxRecordedIterations);
}
}
AtomicBoolean dependentRoutesChanged = new AtomicBoolean(false);
AtomicBoolean evenDependentRoutesChanged = new AtomicBoolean(false);
AtomicBoolean oddDependentRoutesChanged = new AtomicBoolean(false);
int numDependentRoutesIterations = 0;
// Go into iteration mode, until the routes converge (or oscillation is detected)
do {
numDependentRoutesIterations++;
AtomicBoolean currentChangedMonitor;
if (oscillatingPrefixes.isEmpty()) {
currentChangedMonitor = dependentRoutesChanged;
} else if (numDependentRoutesIterations % 2 == 0) {
currentChangedMonitor = evenDependentRoutesChanged;
} else {
currentChangedMonitor = oddDependentRoutesChanged;
}
currentChangedMonitor.set(false);
computeDependentRoutesIteration(nodes, topology, dp, numDependentRoutesIterations, oscillatingPrefixes);
/* Collect sizes of certain RIBs this iteration */
computeIterationStatistics(nodes, ae, numDependentRoutesIterations);
recordIterationDebugInfo(nodes, dp, iterationRoutes, iterationAbstractRoutes, numDependentRoutesIterations);
// Check to see if hash has changed
AtomicInteger checkFixedPointCompleted = _newBatch.apply("Iteration " + numDependentRoutesIterations + ": Check if fixed-point reached", nodes.size());
// This hashcode uniquely identifies the iteration (i.e., network state)
int iterationHashCode = computeIterationHashCode(nodes);
SortedSet<Integer> iterationsWithThisHashCode = iterationsByHashCode.computeIfAbsent(iterationHashCode, h -> new TreeSet<>());
iterationHashCodes.put(numDependentRoutesIterations, iterationHashCode);
int minNumberOfUnchangedIterationsForConvergence = oscillatingPrefixes.isEmpty() ? 1 : 2;
if (iterationsWithThisHashCode.isEmpty() || (!oscillatingPrefixes.isEmpty() && iterationsWithThisHashCode.equals(Collections.singleton(numDependentRoutesIterations - 1)))) {
iterationsWithThisHashCode.add(numDependentRoutesIterations);
} else if (!iterationsWithThisHashCode.contains(numDependentRoutesIterations - minNumberOfUnchangedIterationsForConvergence)) {
int lowestIterationWithThisHashCode = iterationsWithThisHashCode.first();
int completedOscillationRecoveryAttempts = ae.getCompletedOscillationRecoveryAttempts();
if (!oscillatingPrefixes.isEmpty()) {
completedOscillationRecoveryAttempts++;
ae.setCompletedOscillationRecoveryAttempts(completedOscillationRecoveryAttempts);
}
recoveryIterationHashCodes.put(completedOscillationRecoveryAttempts, iterationHashCodes);
handleOscillation(recoveryIterationHashCodes, iterationRoutes, iterationAbstractRoutes, lowestIterationWithThisHashCode, numDependentRoutesIterations, iterationOscillatingPrefixes, ae);
return true;
}
compareToPreviousIteration(nodes, currentChangedMonitor, checkFixedPointCompleted);
computeFibs(nodes);
initRemoteBgpNeighbors(nodes.entrySet().stream().collect(ImmutableMap.toImmutableMap(Entry::getKey, e -> e.getValue().getConfiguration())), dp.getIpOwners(), true, this, dp);
} while (checkDependentRoutesChanged(dependentRoutesChanged, evenDependentRoutesChanged, oddDependentRoutesChanged, oscillatingPrefixes, numDependentRoutesIterations));
AtomicInteger computeBgpAdvertisementsToOutsideCompleted = _newBatch.apply("Compute BGP advertisements sent to outside", nodes.size());
nodes.values().parallelStream().forEach(n -> {
for (VirtualRouter vr : n._virtualRouters.values()) {
vr.computeBgpAdvertisementsToOutside(dp.getIpOwners());
}
computeBgpAdvertisementsToOutsideCompleted.incrementAndGet();
});
// Set iteration stats in the answer
ae.setOspfInternalIterations(numOspfInternalIterations);
ae.setDependentRoutesIterations(numDependentRoutesIterations);
return false;
}
}
use of org.batfish.datamodel.Route in project batfish by batfish.
the class NxosRoutingTableExtractor method exitRoute.
@Override
public void exitRoute(RouteContext ctx) {
if (ctx.protocol().LOCAL() != null) {
return;
}
if (_currentVrfRoutes == null) {
initVrf(Configuration.DEFAULT_VRF_NAME);
}
RoutingProtocol protocol = toProtocol(ctx.protocol());
String nextHopInterface = Route.UNSET_NEXT_HOP_INTERFACE;
if (ctx.nexthopint != null) {
nextHopInterface = ctx.nexthopint.getText();
}
int admin = toInteger(ctx.admin);
int cost = toInteger(ctx.cost);
Ip nextHopIp = Route.UNSET_ROUTE_NEXT_HOP_IP;
if (protocol != RoutingProtocol.CONNECTED && ctx.nexthop != null) {
nextHopIp = new Ip(ctx.nexthop.getText());
}
RouteBuilder rb = new RouteBuilder();
rb.setNode(_hostname);
rb.setNetwork(_currentPrefix);
if (protocol == RoutingProtocol.CONNECTED || (protocol == RoutingProtocol.STATIC && nextHopIp.equals(Route.UNSET_ROUTE_NEXT_HOP_IP)) || Interface.NULL_INTERFACE_NAME.equals(nextHopInterface)) {
rb.setNextHop(Configuration.NODE_NONE_NAME);
}
if (!nextHopIp.equals(Route.UNSET_ROUTE_NEXT_HOP_IP)) {
rb.setNextHopIp(nextHopIp);
String nextHop = _ipOwners.get(nextHopIp);
if (nextHop != null) {
rb.setNextHop(nextHop);
}
}
if (nextHopInterface != null) {
rb.setNextHopInterface(nextHopInterface);
}
rb.setAdministrativeCost(admin);
rb.setCost(cost);
rb.setProtocol(protocol);
rb.setTag(Route.UNSET_ROUTE_TAG);
rb.setVrf(_currentVrfName);
Route route = rb.build();
_currentVrfRoutes.add(route);
}
use of org.batfish.datamodel.Route in project batfish by batfish.
the class BdpDataPlanePluginTest method testIosRtStaticMatchesBdp.
@Test
public void testIosRtStaticMatchesBdp() throws IOException {
String testrigResourcePrefix = TESTRIGS_PREFIX + "ios-rt-static-ad";
List<String> configurationNames = ImmutableList.of("r1");
List<String> routingTableNames = ImmutableList.of("r1");
Batfish batfish = BatfishTestUtils.getBatfishFromTestrigText(TestrigText.builder().setConfigurationText(testrigResourcePrefix, configurationNames).setRoutingTablesText(testrigResourcePrefix, routingTableNames).build(), _folder);
BdpDataPlanePlugin dataPlanePlugin = new BdpDataPlanePlugin();
dataPlanePlugin.initialize(batfish);
batfish.computeDataPlane(false);
SortedMap<String, RoutesByVrf> environmentRoutes = batfish.loadEnvironmentRoutingTables();
SortedMap<String, SortedMap<String, SortedSet<AbstractRoute>>> routes = dataPlanePlugin.getRoutes(batfish.loadDataPlane());
Prefix staticRoutePrefix = Prefix.parse("10.0.0.0/8");
SortedSet<AbstractRoute> r1BdpRoutes = routes.get("r1").get(DEFAULT_VRF_NAME);
AbstractRoute r1BdpRoute = r1BdpRoutes.stream().filter(r -> r.getNetwork().equals(staticRoutePrefix)).findFirst().get();
SortedSet<Route> r1EnvironmentRoutes = environmentRoutes.get("r1").get(DEFAULT_VRF_NAME);
Route r1EnvironmentRoute = r1EnvironmentRoutes.stream().filter(r -> r.getNetwork().equals(staticRoutePrefix)).findFirst().get();
assertThat(r1BdpRoute.getAdministrativeCost(), equalTo(r1EnvironmentRoute.getAdministrativeCost()));
assertThat(r1BdpRoute.getMetric(), equalTo(r1EnvironmentRoute.getMetric()));
assertThat(r1BdpRoute.getProtocol(), equalTo(r1EnvironmentRoute.getProtocol()));
}
Aggregations