use of org.batfish.datamodel.AbstractRoute in project batfish by batfish.
the class AbstractRib method nextHopIpsByPrefix.
@Override
public final Map<Prefix, Set<Ip>> nextHopIpsByPrefix() {
Map<Prefix, Set<Ip>> map = new TreeMap<>();
for (AbstractRoute route : getRoutes()) {
Prefix prefix = route.getNetwork();
Ip nextHopIp = route.getNextHopIp();
Set<Ip> nextHopIps = map.computeIfAbsent(prefix, k -> new TreeSet<>());
nextHopIps.add(nextHopIp);
}
return map;
}
use of org.batfish.datamodel.AbstractRoute in project batfish by batfish.
the class BdpEngine method computeOutputAbstractRoutes.
private SortedMap<String, SortedMap<String, SortedSet<AbstractRoute>>> computeOutputAbstractRoutes(Map<String, Node> nodes, Map<Ip, String> ipOwners) {
SortedMap<String, SortedMap<String, SortedSet<AbstractRoute>>> outputRoutes = new TreeMap<>();
nodes.forEach((hostname, node) -> {
SortedMap<String, SortedSet<AbstractRoute>> routesByVrf = new TreeMap<>();
outputRoutes.put(hostname, routesByVrf);
node._virtualRouters.forEach((vrName, vr) -> {
SortedSet<AbstractRoute> routes = new TreeSet<>();
routes.addAll(vr._mainRib.getRoutes());
for (AbstractRoute route : routes) {
route.setNode(hostname);
route.setVrf(vrName);
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())) {
route.setNextHop(Configuration.NODE_NONE_NAME);
}
if (!nextHopIp.equals(Route.UNSET_ROUTE_NEXT_HOP_IP)) {
String nextHop = ipOwners.get(nextHopIp);
if (nextHop != null) {
route.setNextHop(nextHop);
}
}
}
routesByVrf.put(vrName, routes);
});
});
return outputRoutes;
}
use of org.batfish.datamodel.AbstractRoute 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.AbstractRoute in project batfish by batfish.
the class BdpEngine method collectFlowTraces.
private void collectFlowTraces(BdpDataPlane dp, String currentNodeName, Set<Edge> visitedEdges, List<FlowTraceHop> hopsSoFar, Set<FlowTrace> flowTraces, Flow originalFlow, Flow transformedFlow) {
Ip dstIp = transformedFlow.getDstIp();
Set<String> dstIpOwners = dp._ipOwners.get(dstIp);
if (dstIpOwners != null && dstIpOwners.contains(currentNodeName)) {
FlowTrace trace = new FlowTrace(FlowDisposition.ACCEPTED, hopsSoFar, FlowDisposition.ACCEPTED.toString());
flowTraces.add(trace);
} else {
Node currentNode = dp._nodes.get(currentNodeName);
String vrfName;
if (hopsSoFar.isEmpty()) {
vrfName = transformedFlow.getIngressVrf();
} else {
FlowTraceHop lastHop = hopsSoFar.get(hopsSoFar.size() - 1);
String receivingInterface = lastHop.getEdge().getInt2();
vrfName = currentNode._c.getInterfaces().get(receivingInterface).getVrf().getName();
}
VirtualRouter currentVirtualRouter = currentNode._virtualRouters.get(vrfName);
Map<AbstractRoute, Map<String, Map<Ip, Set<AbstractRoute>>>> nextHopInterfacesByRoute = currentVirtualRouter._fib.getNextHopInterfacesByRoute(dstIp);
Map<String, Map<Ip, Set<AbstractRoute>>> nextHopInterfacesWithRoutes = currentVirtualRouter._fib.getNextHopInterfaces(dstIp);
if (!nextHopInterfacesWithRoutes.isEmpty()) {
for (String nextHopInterfaceName : nextHopInterfacesWithRoutes.keySet()) {
// SortedSet<String> routesForThisNextHopInterface = new
// TreeSet<>(
// nextHopInterfacesWithRoutes.get(nextHopInterfaceName)
// .stream().map(ar -> ar.toString())
// .collect(Collectors.toSet()));
SortedSet<String> routesForThisNextHopInterface = new TreeSet<>();
Ip finalNextHopIp = null;
for (Entry<AbstractRoute, Map<String, Map<Ip, Set<AbstractRoute>>>> e : nextHopInterfacesByRoute.entrySet()) {
AbstractRoute routeCandidate = e.getKey();
Map<String, Map<Ip, Set<AbstractRoute>>> routeCandidateNextHopInterfaces = e.getValue();
if (routeCandidateNextHopInterfaces.containsKey(nextHopInterfaceName)) {
Ip nextHopIp = routeCandidate.getNextHopIp();
if (!nextHopIp.equals(Route.UNSET_ROUTE_NEXT_HOP_IP)) {
Set<Ip> finalNextHopIps = routeCandidateNextHopInterfaces.get(nextHopInterfaceName).keySet();
if (finalNextHopIps.size() > 1) {
throw new BatfishException("Can not currently handle multiple final next hop ips across multiple " + "routes leading to one next hop interface");
}
Ip newFinalNextHopIp = finalNextHopIps.iterator().next();
if (finalNextHopIp != null && !newFinalNextHopIp.equals(finalNextHopIp)) {
throw new BatfishException("Can not currently handle multiple final next hop ips for same next hop " + "interface");
}
finalNextHopIp = newFinalNextHopIp;
}
routesForThisNextHopInterface.add(routeCandidate + "_fnhip:" + finalNextHopIp);
}
}
NodeInterfacePair nextHopInterface = new NodeInterfacePair(currentNodeName, nextHopInterfaceName);
if (nextHopInterfaceName.equals(Interface.NULL_INTERFACE_NAME)) {
List<FlowTraceHop> newHops = new ArrayList<>(hopsSoFar);
Edge newEdge = new Edge(nextHopInterface, new NodeInterfacePair(Configuration.NODE_NONE_NAME, Interface.NULL_INTERFACE_NAME));
FlowTraceHop newHop = new FlowTraceHop(newEdge, routesForThisNextHopInterface, hopFlow(originalFlow, transformedFlow));
newHops.add(newHop);
FlowTrace nullRouteTrace = new FlowTrace(FlowDisposition.NULL_ROUTED, newHops, FlowDisposition.NULL_ROUTED.toString());
flowTraces.add(nullRouteTrace);
} else {
Interface outgoingInterface = dp._nodes.get(nextHopInterface.getHostname())._c.getInterfaces().get(nextHopInterface.getInterface());
// Apply any relevant source NAT rules.
transformedFlow = applySourceNat(transformedFlow, outgoingInterface.getSourceNats());
SortedSet<Edge> edges = dp._topology.getInterfaceEdges().get(nextHopInterface);
if (edges != null) {
boolean continueToNextNextHopInterface = false;
continueToNextNextHopInterface = processCurrentNextHopInterfaceEdges(dp, currentNodeName, visitedEdges, hopsSoFar, flowTraces, originalFlow, transformedFlow, dstIp, dstIpOwners, nextHopInterfaceName, routesForThisNextHopInterface, finalNextHopIp, nextHopInterface, edges, true);
if (continueToNextNextHopInterface) {
continue;
}
} else {
/*
* Should only get here for delta environment where
* non-flow-sink interface from base has no edges in delta
*/
Edge neighborUnreachbleEdge = new Edge(nextHopInterface, new NodeInterfacePair(Configuration.NODE_NONE_NAME, Interface.NULL_INTERFACE_NAME));
FlowTraceHop neighborUnreachableHop = new FlowTraceHop(neighborUnreachbleEdge, routesForThisNextHopInterface, hopFlow(originalFlow, transformedFlow));
List<FlowTraceHop> newHops = new ArrayList<>(hopsSoFar);
newHops.add(neighborUnreachableHop);
/**
* Check if denied out. If not, make standard neighbor-unreachable trace.
*/
IpAccessList outFilter = outgoingInterface.getOutgoingFilter();
boolean denied = false;
if (outFilter != null) {
FlowDisposition disposition = FlowDisposition.DENIED_OUT;
denied = flowTraceDeniedHelper(flowTraces, originalFlow, transformedFlow, newHops, outFilter, disposition);
}
if (!denied) {
FlowTrace trace = new FlowTrace(FlowDisposition.NEIGHBOR_UNREACHABLE_OR_EXITS_NETWORK, newHops, FlowDisposition.NEIGHBOR_UNREACHABLE_OR_EXITS_NETWORK.toString());
flowTraces.add(trace);
}
}
}
}
} else {
FlowTrace trace = new FlowTrace(FlowDisposition.NO_ROUTE, hopsSoFar, FlowDisposition.NO_ROUTE.toString());
flowTraces.add(trace);
}
}
}
use of org.batfish.datamodel.AbstractRoute 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;
}
}
Aggregations