package org.batfish.bdp; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.SortedSet; import java.util.TreeMap; import java.util.TreeSet; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicInteger; import org.apache.commons.collections4.map.LRUMap; import org.batfish.common.BatfishException; import org.batfish.common.Version; import org.batfish.common.plugin.DataPlanePlugin; import org.batfish.common.util.CommonUtil; import org.batfish.datamodel.AbstractRoute; import org.batfish.datamodel.Configuration; import org.batfish.datamodel.Edge; import org.batfish.datamodel.FilterResult; import org.batfish.datamodel.Flow; import org.batfish.datamodel.FlowDisposition; import org.batfish.datamodel.FlowTrace; import org.batfish.datamodel.FlowTraceHop; import org.batfish.datamodel.Interface; import org.batfish.datamodel.Ip; import org.batfish.datamodel.IpAccessList; import org.batfish.datamodel.LineAction; import org.batfish.datamodel.Prefix; import org.batfish.datamodel.Route; import org.batfish.datamodel.RouteBuilder; import org.batfish.datamodel.RoutingProtocol; import org.batfish.datamodel.Topology; import org.batfish.datamodel.answers.Answer; import org.batfish.datamodel.answers.BdpAnswerElement; import org.batfish.datamodel.collections.AdvertisementSet; import org.batfish.datamodel.collections.EdgeSet; import org.batfish.datamodel.collections.IbgpTopology; import org.batfish.datamodel.collections.NodeInterfacePair; import org.batfish.datamodel.collections.RouteSet; public class BdpDataPlanePlugin extends DataPlanePlugin { /** * Set to true to debug all iterations, including during oscillation. Ignores * max recorded iterations value. */ private static boolean DEBUG_ALL_ITERATIONS = false; private static final int DEBUG_MAX_RECORDED_ITERATIONS = 12; /** * Set to true to debug oscillation. Make sure to set max recorded iterations * to minimum necessary value. */ private static boolean DEBUG_REPEAT_ITERATIONS = false; private final Map<BdpDataPlane, Map<Flow, Set<FlowTrace>>> _flowTraces; public BdpDataPlanePlugin() { _flowTraces = new HashMap<>(); } private void collectFlowTraces(BdpDataPlane dp, String currentNodeName, Set<Edge> visitedEdges, List<FlowTraceHop> hopsSoFar, Set<FlowTrace> flowTraces, Flow flow) { Ip dstIp = flow.getDstIp(); Set<String> ipOwners = dp._ipOwners.get(dstIp); if (ipOwners != null && ipOwners.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 = flow.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, Set<String>> nextHopInterfacesByRoute = currentVirtualRouter._fib .getNextHopInterfacesByRoute(dstIp); Map<String, 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<>(); boolean nextHopIpRoute = false; for (Entry<AbstractRoute, Set<String>> e : nextHopInterfacesByRoute .entrySet()) { AbstractRoute routeCandidate = e.getKey(); Set<String> routeCandidateNextHopInterfaces = e.getValue(); if (routeCandidateNextHopInterfaces .contains(nextHopInterfaceName)) { Ip nextHopIp = routeCandidate.getNextHopIp(); if (nextHopIp != null && !nextHopIp .equals(Route.UNSET_ROUTE_NEXT_HOP_IP)) { nextHopIpRoute = true; } routesForThisNextHopInterface .add(routeCandidate.toString()); } } 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); newHops.add(newHop); FlowTrace nullRouteTrace = new FlowTrace( FlowDisposition.NULL_ROUTED, newHops, FlowDisposition.NULL_ROUTED.toString()); flowTraces.add(nullRouteTrace); } else if (dp._flowSinks.contains(nextHopInterface)) { List<FlowTraceHop> newHops = new ArrayList<>(hopsSoFar); Edge newEdge = new Edge(nextHopInterface, new NodeInterfacePair(Configuration.NODE_NONE_NAME, Interface.FLOW_SINK_TERMINATION_NAME)); FlowTraceHop newHop = new FlowTraceHop(newEdge, routesForThisNextHopInterface); newHops.add(newHop); FlowTrace flowSinkTrace = new FlowTrace( FlowDisposition.ACCEPTED, newHops, FlowDisposition.ACCEPTED.toString()); flowTraces.add(flowSinkTrace); } else { EdgeSet edges = dp._topology.getInterfaceEdges() .get(nextHopInterface); if (edges != null) { int unreachableNeighbors = 0; int potentialNeighbors = 0; for (Edge edge : edges) { if (!edge.getNode1().equals(currentNodeName)) { continue; } potentialNeighbors++; List<FlowTraceHop> newHops = new ArrayList<>(hopsSoFar); Set<Edge> newVisitedEdges = new LinkedHashSet<>( visitedEdges); FlowTraceHop newHop = new FlowTraceHop(edge, routesForThisNextHopInterface); newVisitedEdges.add(edge); newHops.add(newHop); /* * Check to see whether neighbor would refrain from * sending ARP reply (NEIGHBOR_UNREACHABLE) * * This occurs if: * * - Using interface-only route * * AND * * - Neighbor does not own dstIp * * AND EITHER * * -- Neighbor not using proxy-arp * * - OR * * -- Subnet of neighbor's receiving-interface contains * dstIp */ if (!nextHopIpRoute) { // using interface-only route String node2 = edge.getNode2(); if (ipOwners == null || !ipOwners.contains(node2)) { // neighbor does not own dstIp String int2Name = edge.getInt2(); Interface int2 = dp._nodes.get(node2)._c .getInterfaces().get(int2Name); boolean neighborUnreachable = false; Boolean proxyArp = int2.getProxyArp(); if (proxyArp == null || !proxyArp) { // TODO: proxyArp probably shouldn't be null neighborUnreachable = true; } else { for (Prefix prefix : int2.getAllPrefixes()) { if (prefix.getNetworkPrefix() .contains(dstIp)) { neighborUnreachable = true; break; } } } if (neighborUnreachable) { unreachableNeighbors++; continue; } } } if (visitedEdges.contains(edge)) { FlowTrace trace = new FlowTrace(FlowDisposition.LOOP, newHops, FlowDisposition.LOOP.toString()); flowTraces.add(trace); potentialNeighbors--; continue; } String nextNodeName = edge.getNode2(); // now check output filter and input filter IpAccessList outFilter = dp._nodes .get(currentNodeName)._c.getInterfaces() .get(nextHopInterfaceName) .getOutgoingFilter(); if (outFilter != null) { FlowDisposition disposition = FlowDisposition.DENIED_OUT; boolean denied = flowTraceDeniedHelper(flowTraces, flow, newHops, outFilter, disposition); if (denied) { potentialNeighbors--; continue; } } IpAccessList inFilter = dp._nodes.get(nextNodeName)._c .getInterfaces().get(edge.getInt2()) .getIncomingFilter(); if (inFilter != null) { FlowDisposition disposition = FlowDisposition.DENIED_IN; boolean denied = flowTraceDeniedHelper(flowTraces, flow, newHops, inFilter, disposition); if (denied) { potentialNeighbors--; continue; } } // recurse collectFlowTraces(dp, nextNodeName, newVisitedEdges, newHops, flowTraces, flow); } if (unreachableNeighbors > 0 && unreachableNeighbors == potentialNeighbors) { FlowTrace trace = neighborUnreachableTrace(hopsSoFar, nextHopInterface, routesForThisNextHopInterface); flowTraces.add(trace); continue; } } else { // Should only get here for delta environment where // non-flow-sink interface from base has no edges in delta FlowTrace trace = neighborUnreachableTrace(hopsSoFar, nextHopInterface, routesForThisNextHopInterface); flowTraces.add(trace); } } } } else { FlowTrace trace = new FlowTrace(FlowDisposition.NO_ROUTE, hopsSoFar, FlowDisposition.NO_ROUTE.toString()); flowTraces.add(trace); } } } @Override public Answer computeDataPlane(boolean differentialContext) { Answer answer = new Answer(); BdpDataPlane dp = new BdpDataPlane(); BdpAnswerElement ae = new BdpAnswerElement(); Map<String, Configuration> configurations = _batfish.loadConfigurations(); Topology topology = _batfish.computeTopology(configurations); _batfish.resetTimer(); _logger.info("\n*** COMPUTING DATA PLANE ***\n"); Map<Ip, Set<String>> ipOwners = _batfish.computeIpOwners(configurations, true); dp.initIpOwners(configurations, ipOwners); _batfish.initRemoteBgpNeighbors(configurations, dp.getIpOwners()); Map<String, Node> nodes = new TreeMap<>(); configurations.values() .forEach(c -> nodes.put(c.getHostname(), new Node(c, nodes))); AdvertisementSet externalAdverts = _batfish .processExternalBgpAnnouncements(configurations); computeFixedPoint(nodes, topology, dp, externalAdverts, ae); computeFibs(nodes); dp.setNodes(nodes); dp.setTopology(topology); dp.setFlowSinks(_batfish.computeFlowSinks(configurations, differentialContext, topology)); ae.setVersion(Version.getVersion()); _batfish.newBatch("Writing data plane to disk", 0); _batfish.writeDataPlane(dp, ae); _batfish.printElapsedTime(); answer.addAnswerElement(ae); return answer; } private void computeFibs(Map<String, Node> nodes) { AtomicInteger completed = _batfish.newBatch("Computing FIBs", nodes.size()); nodes.values().parallelStream().forEach(n -> { for (VirtualRouter vr : n._virtualRouters.values()) { vr.computeFib(); } completed.incrementAndGet(); }); } private void computeFixedPoint(Map<String, Node> nodes, Topology topology, BdpDataPlane dp, AdvertisementSet externalAdverts, BdpAnswerElement ae) { // BEGIN DONE ONCE (except main rib) // connected, initial static routes, ospf setup, bgp setup AtomicInteger initialCompleted = _batfish.newBatch( "Compute initial connected and static routes, ospf setup, bgp setup", nodes.size()); nodes.values().parallelStream().forEach(n -> { for (VirtualRouter vr : n._virtualRouters.values()) { vr.initConnectedRib(); vr.importRib(vr._independentRib, vr._connectedRib); vr.importRib(vr._mainRib, vr._connectedRib); vr.initStaticRib(); vr.importRib(vr._independentRib, vr._staticInterfaceRib); vr.importRib(vr._mainRib, vr._staticInterfaceRib); vr.initBaseOspfRoutes(); vr.initEbgpTopology(dp); vr.initBaseBgpRibs(externalAdverts, dp.getIpOwners()); } initialCompleted.incrementAndGet(); }); final Object routesChangedMonitor = new Object(); // OSPF internal routes final boolean[] ospfInternalChanged = new boolean[] { true }; int ospfInternalIterations = 0; while (ospfInternalChanged[0]) { ospfInternalIterations++; ospfInternalChanged[0] = false; AtomicInteger ospfInterAreaSummaryCompleted = _batfish .newBatch("Compute OSPF Inter-area summaries: iteration " + ospfInternalIterations, nodes.size()); nodes.values().parallelStream().forEach(n -> { for (VirtualRouter vr : n._virtualRouters.values()) { if (vr.computeInterAreaSummaries()) { synchronized (routesChangedMonitor) { ospfInternalChanged[0] = true; } } } ospfInterAreaSummaryCompleted.incrementAndGet(); }); AtomicInteger ospfInternalCompleted = _batfish .newBatch("Compute OSPF Internal routes: iteration " + ospfInternalIterations, nodes.size()); nodes.values().parallelStream().forEach(n -> { for (VirtualRouter vr : n._virtualRouters.values()) { if (vr.propagateOspfInternalRoutes(nodes, topology)) { synchronized (routesChangedMonitor) { ospfInternalChanged[0] = true; } } } ospfInternalCompleted.incrementAndGet(); }); AtomicInteger ospfInternalUnstageCompleted = _batfish .newBatch("Unstage OSPF Internal routes: iteration " + ospfInternalIterations, nodes.size()); nodes.values().parallelStream().forEach(n -> { for (VirtualRouter vr : n._virtualRouters.values()) { vr.unstageOspfInternalRoutes(); } ospfInternalUnstageCompleted.incrementAndGet(); }); } AtomicInteger ospfInternalImportCompleted = _batfish .newBatch("Import OSPF Internal routes", nodes.size()); nodes.values().parallelStream().forEach(n -> { for (VirtualRouter vr : n._virtualRouters.values()) { vr.importRib(vr._ospfRib, vr._ospfIntraAreaRib); vr.importRib(vr._ospfRib, vr._ospfInterAreaRib); vr.importRib(vr._independentRib, vr._ospfRib); } ospfInternalImportCompleted.incrementAndGet(); }); // END DONE ONCE Map<Integer, Integer> iterationByHashCode = new HashMap<>(); Map<Integer, RouteSet> iterationRoutes = null; if (DEBUG_ALL_ITERATIONS) { iterationRoutes = new TreeMap<>(); } else if (DEBUG_REPEAT_ITERATIONS && DEBUG_MAX_RECORDED_ITERATIONS > 1) { iterationRoutes = new LRUMap<>(DEBUG_MAX_RECORDED_ITERATIONS); } boolean[] dependentRoutesChanged = new boolean[] { true }; int dependentRoutesIterations = 0; while (dependentRoutesChanged[0]) { dependentRoutesIterations++; dependentRoutesChanged[0] = false; // (Re)initialization of dependent route calculation AtomicInteger reinitializeDependentCompleted = _batfish .newBatch("Iteration " + dependentRoutesIterations + ": Reinitialize dependent routes", nodes.size()); nodes.values().parallelStream().forEach(n -> { for (VirtualRouter vr : n._virtualRouters.values()) { /* * RIBs that are read from */ vr._prevMainRib = vr._mainRib; vr._mainRib = new Rib(vr); vr._prevOspfExternalType1Rib = vr._ospfExternalType1Rib; vr._ospfExternalType1Rib = new OspfExternalType1Rib(vr); vr._prevOspfExternalType2Rib = vr._ospfExternalType2Rib; vr._ospfExternalType2Rib = new OspfExternalType2Rib(vr); vr._prevBgpRib = vr._bgpRib; vr._bgpRib = new BgpRib(vr); vr._prevEbgpRib = vr._ebgpRib; vr._ebgpRib = new BgpRib(vr); vr.importRib(vr._ebgpRib, vr._baseEbgpRib); vr._prevIbgpRib = vr._ibgpRib; vr._ibgpRib = new BgpRib(vr); vr.importRib(vr._ibgpRib, vr._baseIbgpRib); /* * RIBs not read from */ vr._ospfRib = new OspfRib(vr); /* * Staging RIBs */ vr._ebgpStagingRib = new BgpRib(vr); vr._ibgpStagingRib = new BgpRib(vr); vr._ospfExternalType1StagingRib = new OspfExternalType1Rib(vr); vr._ospfExternalType2StagingRib = new OspfExternalType2Rib(vr); /* * Add routes that cannot change (does not affect below * computation) */ vr.importRib(vr._mainRib, vr._independentRib); /* * Re-add independent OSPF routes to ospfRib for tie-breaking */ vr.importRib(vr._ospfRib, vr._ospfIntraAreaRib); vr.importRib(vr._ospfRib, vr._ospfInterAreaRib); } reinitializeDependentCompleted.incrementAndGet(); }); // Static nextHopIp routes AtomicInteger recomputeStaticCompleted = _batfish.newBatch( "Iteration " + dependentRoutesIterations + ": Recompute static routes with next-hop IP", nodes.size()); nodes.values().parallelStream().forEach(n -> { boolean staticChanged = true; while (staticChanged) { for (VirtualRouter vr : n._virtualRouters.values()) { staticChanged = false; if (vr.activateStaticRoutes()) { staticChanged = true; } } } recomputeStaticCompleted.incrementAndGet(); }); // Generated/aggregate routes AtomicInteger recomputeAggregateCompleted = _batfish .newBatch( "Iteration " + dependentRoutesIterations + ": Recompute aggregate/generated routes", nodes.size()); nodes.values().parallelStream().forEach(n -> { for (VirtualRouter vr : n._virtualRouters.values()) { boolean generatedChanged = true; vr._generatedRib = new Rib(vr); while (generatedChanged) { generatedChanged = false; if (vr.activateGeneratedRoutes()) { generatedChanged = true; } } vr.importRib(vr._mainRib, vr._generatedRib); } recomputeAggregateCompleted.incrementAndGet(); }); // OSPF external routes // recompute exports nodes.values().parallelStream().forEach(n -> { for (VirtualRouter vr : n._virtualRouters.values()) { vr.initOspfExports(); } }); // repropagate exports final boolean[] ospfExternalChanged = new boolean[] { true }; int ospfExternalSubIterations = 0; while (ospfExternalChanged[0]) { ospfExternalSubIterations++; AtomicInteger propagateOspfExternalCompleted = _batfish .newBatch("Iteration " + dependentRoutesIterations + ": Propagate OSPF external routes: subIteration: " + ospfExternalSubIterations, nodes.size()); ospfExternalChanged[0] = false; nodes.values().parallelStream().forEach(n -> { for (VirtualRouter vr : n._virtualRouters.values()) { if (vr.propagateOspfExternalRoutes(nodes, topology)) { synchronized (routesChangedMonitor) { ospfExternalChanged[0] = true; } } } propagateOspfExternalCompleted.incrementAndGet(); }); AtomicInteger unstageOspfExternalCompleted = _batfish .newBatch("Iteration " + dependentRoutesIterations + ": Unstage OSPF external routes: subIteration: " + ospfExternalSubIterations, nodes.size()); nodes.values().parallelStream().forEach(n -> { for (VirtualRouter vr : n._virtualRouters.values()) { vr.unstageOspfExternalRoutes(); } unstageOspfExternalCompleted.incrementAndGet(); }); } AtomicInteger importOspfExternalCompleted = _batfish .newBatch("Iteration " + dependentRoutesIterations + ": Unstage OSPF external routes", nodes.size()); nodes.values().parallelStream().forEach(n -> { for (VirtualRouter vr : n._virtualRouters.values()) { vr.importRib(vr._ospfRib, vr._ospfExternalType1Rib); vr.importRib(vr._ospfRib, vr._ospfExternalType2Rib); vr.importRib(vr._mainRib, vr._ospfRib); } importOspfExternalCompleted.incrementAndGet(); }); // BGP routes // first let's initialize nodes-level generated/aggregate routes nodes.values().parallelStream().forEach(n -> { for (VirtualRouter vr : n._virtualRouters.values()) { vr.initBgpAggregateRoutes(); } }); AtomicInteger propagateBgpCompleted = _batfish.newBatch("Iteration " + dependentRoutesIterations + ": Propagate BGP routes", nodes.size()); AtomicInteger currentBgpRoutes = new AtomicInteger(); nodes.values().parallelStream().forEach(n -> { for (VirtualRouter vr : n._virtualRouters.values()) { int numBgpRoutes = vr.propagateBgpRoutes(nodes, dp.getIpOwners()); currentBgpRoutes.addAndGet(numBgpRoutes); } propagateBgpCompleted.incrementAndGet(); }); ae.getBgpRoutesByIteration().put(dependentRoutesIterations, currentBgpRoutes.get()); AtomicInteger importBgpCompleted = _batfish.newBatch( "Iteration " + dependentRoutesIterations + ": Import BGP routes into respective RIBs", nodes.size()); nodes.values().parallelStream().forEach(n -> { for (VirtualRouter vr : n._virtualRouters.values()) { vr.unstageBgpRoutes(); vr.importRib(vr._bgpRib, vr._ebgpRib); vr.importRib(vr._bgpRib, vr._ibgpRib); vr.importRib(vr._mainRib, vr._bgpRib); } importBgpCompleted.incrementAndGet(); }); if (DEBUG_REPEAT_ITERATIONS) { Map<Ip, String> ipOwners = dp.getIpOwnersSimple(); RouteSet routes = computeOutputRoutes(nodes, ipOwners); iterationRoutes.put(dependentRoutesIterations, routes); } // Check to see if routes have changed AtomicInteger checkFixedPointCompleted = _batfish.newBatch("Iteration " + dependentRoutesIterations + ": Check if fixed-point reached", nodes.size()); int iterationHashCode = computeIterationHashCode(nodes); Integer iterationWithThisHashCode = iterationByHashCode .get(iterationHashCode); if (iterationWithThisHashCode == null) { iterationByHashCode.put(iterationHashCode, dependentRoutesIterations); } else if (dependentRoutesIterations - iterationWithThisHashCode > 1) { String msg = "Iteration " + dependentRoutesIterations + " has same hash as iteration: " + iterationWithThisHashCode + "\n" + iterationByHashCode.toString(); if (!DEBUG_REPEAT_ITERATIONS) { throw new BatfishException(msg); } else if (!DEBUG_ALL_ITERATIONS) { String errorMessage = debugIterations(msg, iterationRoutes, iterationWithThisHashCode, dependentRoutesIterations); throw new BatfishException(errorMessage); } else { String errorMessage = debugIterations(msg, iterationRoutes, 1, dependentRoutesIterations); throw new BatfishException(errorMessage); } } nodes.values().parallelStream().forEach(n -> { for (VirtualRouter vr : n._virtualRouters.values()) { boolean changed = false; if (!vr._mainRib.getRoutes() .equals(vr._prevMainRib.getRoutes())) { changed = true; } if (!vr._ospfExternalType1Rib.getRoutes() .equals(vr._prevOspfExternalType1Rib.getRoutes())) { changed = true; } if (!vr._ospfExternalType2Rib.getRoutes() .equals(vr._prevOspfExternalType2Rib.getRoutes())) { changed = true; } if (changed) { synchronized (routesChangedMonitor) { dependentRoutesChanged[0] = true; } } } checkFixedPointCompleted.incrementAndGet(); }); } int totalRoutes = nodes.values().stream() .flatMap(n -> n._virtualRouters.values().stream()) .mapToInt(vr -> vr._mainRib.getRoutes().size()).sum(); ae.setOspfInternalIterations(ospfInternalIterations); ae.setDependentRoutesIterations(dependentRoutesIterations); ae.setTotalRoutes(totalRoutes); } private int computeIterationHashCode(Map<String, Node> nodes) { int mainHash = nodes.values().parallelStream() .mapToInt(n -> n._virtualRouters.values().stream() .mapToInt(vr -> vr._mainRib.getRoutes().hashCode()).sum()) .sum(); int ospfExternalType1Hash = nodes.values().parallelStream() .mapToInt(n -> n._virtualRouters.values().stream() .mapToInt( vr -> vr._ospfExternalType1Rib.getRoutes().hashCode()) .sum()) .sum(); int ospfExternalType2Hash = nodes.values().parallelStream() .mapToInt(n -> n._virtualRouters.values().stream() .mapToInt( vr -> vr._ospfExternalType2Rib.getRoutes().hashCode()) .sum()) .sum(); int hash = mainHash + ospfExternalType1Hash + ospfExternalType2Hash; return hash; } private RouteSet computeOutputRoutes(Map<String, Node> nodes, Map<Ip, String> ipOwners) { RouteSet outputRoutes = new RouteSet(); 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()); if (route.getProtocol() == RoutingProtocol.CONNECTED || (route.getProtocol() == RoutingProtocol.STATIC && route.getNextHopIp() == null) || Interface.NULL_INTERFACE_NAME .equals(route.getNextHopInterface())) { rb.setNextHop(Configuration.NODE_NONE_NAME); } Ip nextHopIp = route.getNextHopIp(); if (nextHopIp != null) { rb.setNextHopIp(nextHopIp); String nextHop = ipOwners.get(nextHopIp); if (nextHop != null) { rb.setNextHop(nextHop); } } String nextHopInterface = route.getNextHopInterface(); if (nextHopInterface != null) { rb.setNextHopInterface(nextHopInterface); } 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; } private String debugIterations(String msg, Map<Integer, RouteSet> iterationRoutes, int first, int last) { StringBuilder sb = new StringBuilder(); sb.append(msg); sb.append("\n"); RouteSet initialRoutes = iterationRoutes.get(first); sb.append("Initial routes (iteration " + first + "):\n"); for (Route route : initialRoutes) { String routeStr = route.prettyPrint(false); sb.append(routeStr); } for (int i = first + 1; i <= last; i++) { RouteSet baseRoutes = iterationRoutes.get(i - 1); RouteSet deltaRoutes = iterationRoutes.get(i); RouteSet added = CommonUtil.difference(deltaRoutes, baseRoutes, RouteSet::new); for (Route route : added) { route.setDiffSymbol("+"); } RouteSet removed = CommonUtil.difference(baseRoutes, deltaRoutes, RouteSet::new); for (Route route : removed) { route.setDiffSymbol("-"); } RouteSet changed = CommonUtil.union(added, removed, RouteSet::new); sb.append( "Changed routes (iteration " + (i - 1) + " ==> " + i + "):\n"); for (Route route : changed) { String routeStr = route.prettyPrint(true); sb.append(routeStr); } } String errorMessage = sb.toString(); return errorMessage; } private boolean flowTraceDeniedHelper(Set<FlowTrace> flowTraces, Flow flow, List<FlowTraceHop> newHops, IpAccessList filter, FlowDisposition disposition) { boolean out = disposition == FlowDisposition.DENIED_OUT; FilterResult outResult = filter.filter(flow); boolean denied = outResult.getAction() == LineAction.REJECT; if (denied) { String outFilterName = filter.getName(); Integer matchLine = outResult.getMatchLine(); String lineDesc; if (matchLine != null) { lineDesc = filter.getLines().get(matchLine).getName(); if (lineDesc == null) { lineDesc = "line:" + matchLine.toString(); } } else { lineDesc = "no-match"; } String notes = disposition.toString() + "{" + outFilterName + "}{" + lineDesc + "}"; if (out) { FlowTraceHop lastHop = newHops.get(newHops.size() - 1); newHops.remove(newHops.size() - 1); Edge lastEdge = lastHop.getEdge(); Edge deniedOutEdge = new Edge(lastEdge.getFirst(), new NodeInterfacePair(Configuration.NODE_NONE_NAME, Interface.NULL_INTERFACE_NAME)); FlowTraceHop deniedOutHop = new FlowTraceHop(deniedOutEdge, lastHop.getRoutes()); newHops.add(deniedOutHop); } FlowTrace trace = new FlowTrace(disposition, newHops, notes); flowTraces.add(trace); } return denied; } @Override public AdvertisementSet getAdvertisements() { throw new UnsupportedOperationException( "no implementation for generated method"); // TODO Auto-generated // method stub } @Override public List<Flow> getHistoryFlows() { BdpDataPlane dp = loadDataPlane(); List<Flow> flowList = new ArrayList<>(); _flowTraces.get(dp).forEach((flow, flowTraces) -> { for (int i = 0; i < flowTraces.size(); i++) { flowList.add(flow); } }); return flowList; } @Override public List<FlowTrace> getHistoryFlowTraces() { BdpDataPlane dp = loadDataPlane(); List<FlowTrace> flowTraceList = new ArrayList<>(); _flowTraces.get(dp).forEach((flow, flowTraces) -> { for (FlowTrace flowTrace : flowTraces) { flowTraceList.add(flowTrace); } }); return flowTraceList; } @Override public IbgpTopology getIbgpNeighbors() { throw new UnsupportedOperationException( "no implementation for generated method"); // TODO Auto-generated // method stub } @Override public RouteSet getRoutes() { BdpDataPlane dp = loadDataPlane(); RouteSet outputRoutes = computeOutputRoutes(dp._nodes, dp.getIpOwnersSimple()); return outputRoutes; } private BdpDataPlane loadDataPlane() { return (BdpDataPlane) _batfish.loadDataPlane(); } private FlowTrace neighborUnreachableTrace(List<FlowTraceHop> completedHops, NodeInterfacePair srcInterface, SortedSet<String> routes) { Edge neighborUnreachbleEdge = new Edge(srcInterface, new NodeInterfacePair(Configuration.NODE_NONE_NAME, Interface.NULL_INTERFACE_NAME)); FlowTraceHop neighborUnreachableHop = new FlowTraceHop( neighborUnreachbleEdge, routes); List<FlowTraceHop> newHops = new ArrayList<>(completedHops); newHops.add(neighborUnreachableHop); FlowTrace trace = new FlowTrace( FlowDisposition.NEIGHBOR_UNREACHABLE_OR_EXITS_NETWORK, newHops, FlowDisposition.NEIGHBOR_UNREACHABLE_OR_EXITS_NETWORK.toString()); return trace; } @Override public void processFlows(Set<Flow> flows) { BdpDataPlane dp = loadDataPlane(); Map<Flow, Set<FlowTrace>> flowTraces = new ConcurrentHashMap<>(); flows.parallelStream().forEach(flow -> { Set<FlowTrace> currentFlowTraces = new TreeSet<>(); flowTraces.put(flow, currentFlowTraces); String ingressNodeName = flow.getIngressNode(); Set<Edge> visitedEdges = Collections.emptySet(); List<FlowTraceHop> hops = new ArrayList<>(); collectFlowTraces(dp, ingressNodeName, visitedEdges, hops, currentFlowTraces, flow); }); _flowTraces.put(dp, new TreeMap<>(flowTraces)); } }