use of org.batfish.datamodel.FlowTrace in project batfish by batfish.
the class CommonUtil method initRemoteBgpNeighbors.
/**
* Initialize BGP neighbors for all nodes.
*
* @param configurations map of all configurations, keyed by hostname
* @param ipOwners mapping of Ips to a set of nodes (hostnames) that owns those IPs
* @param checkReachability whether bgp neighbor reachability should be checked
* @param flowProcessor dataplane plugin to use to check reachability. Must not be {@code null} if
* {@code checkReachability = true}
* @param dp dataplane to use to check reachability. Must not be {@code null} if {@code
* checkReachability = true}
*/
public static void initRemoteBgpNeighbors(Map<String, Configuration> configurations, Map<Ip, Set<String>> ipOwners, boolean checkReachability, @Nullable FlowProcessor flowProcessor, @Nullable DataPlane dp) {
// TODO: handle duplicate ips on different vrfs
Map<BgpNeighbor, Ip> remoteAddresses = new IdentityHashMap<>();
Map<Ip, Set<BgpNeighbor>> localAddresses = new HashMap<>();
/*
* Construct maps indicating which neighbor owns which Ip Address
*/
for (Configuration node : configurations.values()) {
String hostname = node.getHostname();
for (Vrf vrf : node.getVrfs().values()) {
BgpProcess proc = vrf.getBgpProcess();
if (proc == null) {
// nothing to do if no bgp process on this VRF
continue;
}
for (BgpNeighbor bgpNeighbor : proc.getNeighbors().values()) {
/*
* Begin by initializing candidate neighbors to an empty set
*/
bgpNeighbor.initCandidateRemoteBgpNeighbors();
// Skip things we don't handle
if (bgpNeighbor.getPrefix().getPrefixLength() < Prefix.MAX_PREFIX_LENGTH) {
throw new BatfishException(hostname + ": Do not support dynamic bgp sessions at this time: " + bgpNeighbor.getPrefix());
}
Ip remoteAddress = bgpNeighbor.getAddress();
if (remoteAddress == null) {
throw new BatfishException(hostname + ": Could not determine remote address of bgp neighbor: " + bgpNeighbor);
}
Ip localAddress = bgpNeighbor.getLocalIp();
if (localAddress == null || !ipOwners.containsKey(localAddress) || !ipOwners.get(localAddress).contains(hostname)) {
// Local address is not owned by anybody
continue;
}
remoteAddresses.put(bgpNeighbor, remoteAddress);
// Add this neighbor as owner of its local address
localAddresses.computeIfAbsent(localAddress, k -> Collections.newSetFromMap(new IdentityHashMap<>())).add(bgpNeighbor);
}
}
}
/*
* For each neighbor, construct the set of candidate neighbors, then filter out impossible
* sessions.
*/
for (Entry<BgpNeighbor, Ip> e : remoteAddresses.entrySet()) {
BgpNeighbor bgpNeighbor = e.getKey();
Ip remoteAddress = e.getValue();
Ip localAddress = bgpNeighbor.getLocalIp();
int localLocalAs = bgpNeighbor.getLocalAs();
int localRemoteAs = bgpNeighbor.getRemoteAs();
/*
* Let the set of candidate neighbors be set of neighbors that own the remoteAddress
*/
Set<BgpNeighbor> remoteBgpNeighborCandidates = localAddresses.get(remoteAddress);
if (remoteBgpNeighborCandidates == null) {
// No possible remote neighbors
continue;
}
/*
* Filter the set of candidate neighbors based on these checks:
* - Remote neighbor's remote address is the same as our local address
* - Remote neighbor's remote AS is the same as our local AS (and vice-versa)
*/
for (BgpNeighbor remoteBgpNeighborCandidate : remoteBgpNeighborCandidates) {
int remoteLocalAs = remoteBgpNeighborCandidate.getLocalAs();
int remoteRemoteAs = remoteBgpNeighborCandidate.getRemoteAs();
Ip reciprocalRemoteIp = remoteBgpNeighborCandidate.getAddress();
if (localAddress.equals(reciprocalRemoteIp) && localLocalAs == remoteRemoteAs && localRemoteAs == remoteLocalAs) {
/*
* Fairly confident establishing the session is possible here, but still check
* reachability if needed.
* We should check reachability only for eBgp multihop or iBgp
*/
if (checkReachability && (bgpNeighbor.getEbgpMultihop() || localLocalAs == remoteLocalAs)) {
/*
* Ensure that the session can be established by running traceroute in both directions
*/
if (flowProcessor == null || dp == null) {
throw new BatfishException("Cannot compute neighbor reachability without a dataplane");
}
Flow.Builder fb = new Flow.Builder();
fb.setIpProtocol(IpProtocol.TCP);
fb.setTag("neighbor-resolution");
fb.setIngressNode(bgpNeighbor.getOwner().getHostname());
fb.setSrcIp(localAddress);
fb.setDstIp(remoteAddress);
fb.setSrcPort(NamedPort.EPHEMERAL_LOWEST.number());
fb.setDstPort(NamedPort.BGP.number());
Flow forwardFlow = fb.build();
fb.setIngressNode(remoteBgpNeighborCandidate.getOwner().getHostname());
fb.setSrcIp(forwardFlow.getDstIp());
fb.setDstIp(forwardFlow.getSrcIp());
fb.setSrcPort(forwardFlow.getDstPort());
fb.setDstPort(forwardFlow.getSrcPort());
Flow backwardFlow = fb.build();
SortedMap<Flow, Set<FlowTrace>> traces = flowProcessor.processFlows(dp, ImmutableSet.of(forwardFlow, backwardFlow));
if (traces.values().stream().map(fts -> fts.stream().allMatch(ft -> ft.getDisposition() != FlowDisposition.ACCEPTED)).anyMatch(Predicate.isEqual(true))) {
/*
* If either flow has all traceroutes fail, do not consider the neighbor valid
*/
continue;
}
bgpNeighbor.getCandidateRemoteBgpNeighbors().add(remoteBgpNeighborCandidate);
} else {
bgpNeighbor.getCandidateRemoteBgpNeighbors().add(remoteBgpNeighborCandidate);
}
}
}
Set<BgpNeighbor> finalCandidates = bgpNeighbor.getCandidateRemoteBgpNeighbors();
if (finalCandidates.size() > 1) {
/* If we still have not narrowed it down to a single neighbor,
* pick based on sorted hostnames
*/
SortedMap<String, BgpNeighbor> hostnameToNeighbor = finalCandidates.stream().collect(ImmutableSortedMap.toImmutableSortedMap(String::compareTo, k -> k.getOwner().getHostname(), Function.identity()));
bgpNeighbor.setRemoteBgpNeighbor(hostnameToNeighbor.get(hostnameToNeighbor.firstKey()));
} else if (finalCandidates.size() == 1) {
bgpNeighbor.setRemoteBgpNeighbor(finalCandidates.iterator().next());
} else {
bgpNeighbor.setRemoteBgpNeighbor(null);
}
}
}
use of org.batfish.datamodel.FlowTrace in project batfish by batfish.
the class BdpEngine method processFlows.
@Override
public SortedMap<Flow, Set<FlowTrace>> processFlows(DataPlane dataPlane, Set<Flow> flows) {
Map<Flow, Set<FlowTrace>> flowTraces = new ConcurrentHashMap<>();
BdpDataPlane dp = (BdpDataPlane) dataPlane;
flows.parallelStream().forEach(flow -> {
Set<FlowTrace> currentFlowTraces = new TreeSet<>();
flowTraces.put(flow, currentFlowTraces);
String ingressNodeName = flow.getIngressNode();
if (ingressNodeName == null) {
throw new BatfishException("Cannot construct flow trace since ingressNode is not specified");
}
Ip dstIp = flow.getDstIp();
if (dstIp == null) {
throw new BatfishException("Cannot construct flow trace since dstIp is not specified");
}
Set<Edge> visitedEdges = Collections.emptySet();
List<FlowTraceHop> hops = new ArrayList<>();
Set<String> dstIpOwners = dp._ipOwners.get(dstIp);
SortedSet<Edge> edges = new TreeSet<>();
String ingressInterfaceName = flow.getIngressInterface();
if (ingressInterfaceName != null) {
edges.add(new Edge(TRACEROUTE_INGRESS_NODE_NAME, TRACEROUTE_INGRESS_NODE_INTERFACE_NAME, ingressNodeName, ingressInterfaceName));
processCurrentNextHopInterfaceEdges(dp, TRACEROUTE_INGRESS_NODE_NAME, visitedEdges, hops, currentFlowTraces, flow, flow, dstIp, dstIpOwners, null, new TreeSet<>(), null, null, edges, false);
} else {
collectFlowTraces(dp, ingressNodeName, visitedEdges, hops, currentFlowTraces, flow, flow);
}
});
return new TreeMap<>(flowTraces);
}
use of org.batfish.datamodel.FlowTrace 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.FlowTrace in project batfish by batfish.
the class BdpEngine method flowTraceDeniedHelper.
private boolean flowTraceDeniedHelper(Set<FlowTrace> flowTraces, Flow originalFlow, Flow transformedFlow, List<FlowTraceHop> newHops, IpAccessList filter, FlowDisposition disposition) {
boolean out = disposition == FlowDisposition.DENIED_OUT;
FilterResult outResult = filter.filter(transformedFlow);
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;
}
} else {
lineDesc = "no-match";
}
String notes = disposition + "{" + 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(), hopFlow(originalFlow, transformedFlow));
newHops.add(deniedOutHop);
}
FlowTrace trace = new FlowTrace(disposition, newHops, notes);
flowTraces.add(trace);
}
return denied;
}
use of org.batfish.datamodel.FlowTrace in project batfish by batfish.
the class CounterExample method buildFlowTrace.
/*
* Build flow information for a given hop along a path
*/
Tuple<Flow, FlowTrace> buildFlowTrace(Encoder enc, String router) {
EncoderSlice slice = enc.getMainSlice();
SymbolicPacket pkt = slice.getSymbolicPacket();
SymbolicDecisions decisions = slice.getSymbolicDecisions();
Flow f = buildFlow(pkt, router);
SortedSet<String> visited = new TreeSet<>();
List<FlowTraceHop> hops = new ArrayList<>();
String current = router;
while (true) {
visited.add(current);
// Get the forwarding variables
Map<GraphEdge, BoolExpr> dfwd = decisions.getDataForwarding().get(current);
Map<GraphEdge, BoolExpr> cfwd = decisions.getControlForwarding().get(current);
Map<GraphEdge, BoolExpr> across = enc.getMainSlice().getForwardsAcross().get(current);
// Find the route used
SymbolicRoute r = decisions.getBestNeighbor().get(current);
Protocol proto = buildProcotol(r, slice, current);
Prefix pfx = buildPrefix(r, f);
// pick the next router
boolean found = false;
for (Entry<GraphEdge, BoolExpr> entry : dfwd.entrySet()) {
GraphEdge ge = entry.getKey();
BoolExpr dexpr = entry.getValue();
BoolExpr cexpr = cfwd.get(ge);
BoolExpr aexpr = across.get(ge);
String route = buildRoute(pfx, proto, ge);
if (isTrue(dexpr)) {
hops.add(buildFlowTraceHop(ge, route));
if (ge.getPeer() != null && visited.contains(ge.getPeer())) {
FlowTrace ft = new FlowTrace(FlowDisposition.LOOP, hops, "LOOP");
return new Tuple<>(f, ft);
}
if (isFalse(aexpr)) {
Interface i = ge.getEnd();
IpAccessList acl = i.getIncomingFilter();
FilterResult fr = acl.filter(f);
String line = "default deny";
if (fr.getMatchLine() != null) {
line = acl.getLines().get(fr.getMatchLine()).getName();
}
String note = String.format("DENIED_IN{%s}{%s}", acl.getName(), line);
FlowTrace ft = new FlowTrace(FlowDisposition.DENIED_IN, hops, note);
return new Tuple<>(f, ft);
}
boolean isLoopback = slice.getGraph().isLoopback(ge);
if (isLoopback) {
FlowTrace ft = new FlowTrace(FlowDisposition.ACCEPTED, hops, "ACCEPTED");
return new Tuple<>(f, ft);
}
if (ge.getPeer() == null) {
boolean isBgpPeering = slice.getGraph().getEbgpNeighbors().get(ge) != null;
if (isBgpPeering) {
FlowTrace ft = new FlowTrace(FlowDisposition.ACCEPTED, hops, "ACCEPTED");
return new Tuple<>(f, ft);
} else {
FlowTrace ft = new FlowTrace(FlowDisposition.NEIGHBOR_UNREACHABLE_OR_EXITS_NETWORK, hops, "NEIGHBOR_UNREACHABLE_OR_EXITS_NETWORK");
return new Tuple<>(f, ft);
}
}
if (slice.getGraph().isHost(ge.getPeer())) {
FlowTrace ft = new FlowTrace(FlowDisposition.ACCEPTED, hops, "ACCEPTED");
return new Tuple<>(f, ft);
}
current = ge.getPeer();
found = true;
break;
} else if (isTrue(cexpr)) {
hops.add(buildFlowTraceHop(ge, route));
Interface i = ge.getStart();
IpAccessList acl = i.getOutgoingFilter();
FilterResult fr = acl.filter(f);
IpAccessListLine line = acl.getLines().get(fr.getMatchLine());
String note = String.format("DENIED_OUT{%s}{%s}", acl.getName(), line.getName());
FlowTrace ft = new FlowTrace(FlowDisposition.DENIED_OUT, hops, note);
return new Tuple<>(f, ft);
}
}
if (!found) {
BoolExpr permitted = r.getPermitted();
if (boolVal(permitted)) {
// Check if there is an accepting interface
for (GraphEdge ge : slice.getGraph().getEdgeMap().get(current)) {
Interface i = ge.getStart();
Ip ip = i.getAddress().getIp();
if (ip.equals(f.getDstIp())) {
FlowTrace ft = new FlowTrace(FlowDisposition.ACCEPTED, hops, "ACCEPTED");
return new Tuple<>(f, ft);
}
}
FlowTrace ft = new FlowTrace(FlowDisposition.NEIGHBOR_UNREACHABLE_OR_EXITS_NETWORK, hops, "NEIGHBOR_UNREACHABLE_OR_EXITS_NETWORK");
return new Tuple<>(f, ft);
}
FlowTrace ft = new FlowTrace(FlowDisposition.NO_ROUTE, hops, "NO_ROUTE");
return new Tuple<>(f, ft);
}
}
}
Aggregations