use of org.batfish.datamodel.FibForward in project batfish by batfish.
the class FlowTracerTest method testBuildRoutingStepFibForward.
@Test
public void testBuildRoutingStepFibForward() {
Prefix prefix = Prefix.parse("12.12.12.12/30");
FibForward fibForward = FibForward.of(Ip.parse("1.1.1.1"), "iface1");
Set<FibEntry> fibEntries = ImmutableSet.of(new FibEntry(fibForward, ImmutableList.of(StaticRoute.testBuilder().setNextHopIp(Ip.parse("2.2.2.2")).setNetwork(prefix).setAdministrativeCost(1).build())));
RoutingStep routingStep = buildRoutingStep("myvrf", fibForward, fibEntries);
assertThat(routingStep.getAction(), equalTo(StepAction.FORWARDED));
assertThat(routingStep.getDetail().getVrf(), equalTo("myvrf"));
assertThat(routingStep.getDetail().getRoutes(), equalTo(ImmutableList.of(new RouteInfo(RoutingProtocol.STATIC, prefix, NextHopIp.of(Ip.parse("2.2.2.2")), 1, 0))));
assertThat(routingStep.getDetail().getArpIp(), equalTo(Ip.parse("1.1.1.1")));
assertThat(routingStep.getDetail().getOutputInterface(), equalTo("iface1"));
}
use of org.batfish.datamodel.FibForward in project batfish by batfish.
the class FlowTracer method fibLookup.
/**
* Perform a FIB lookup of {@code dstIp} on {@code fib} of {@code currentNodeName} and take
* corresponding actions given {@code intraHopBreadcrumbs} already produced at this node. Use
* {@code forwardOutInterfaceHandler} to handle forwarding action.
*/
@VisibleForTesting
void fibLookup(Ip dstIp, String currentNodeName, String lookupVrf, Fib fib, BiConsumer<FlowTracer, FibForward> forwardOutInterfaceHandler, Stack<Breadcrumb> intraHopBreadcrumbs) {
// Loop detection
Breadcrumb breadcrumb = newBreadcrumb(currentNodeName, _vrfName, _ingressInterface, _currentFlow);
if (_breadcrumbs.contains(breadcrumb)) {
buildLoopTrace(breadcrumb);
return;
}
if (intraHopBreadcrumbs.isEmpty()) {
_breadcrumbs.push(breadcrumb);
}
try {
Set<FibEntry> fibEntries = fib.get(dstIp);
if (fibEntries.isEmpty()) {
buildNoRouteTrace(lookupVrf);
return;
}
// Group traces by action (we do not want extra branching if there is branching
// in FIB resolution)
TreeMap<FibAction, Set<FibEntry>> groupedByFibAction = new TreeMap<>(FibActionComparator.INSTANCE);
for (FibEntry e : fibEntries) {
groupedByFibAction.computeIfAbsent(e.getAction(), k -> new HashSet<>()).add(e);
}
// For every action corresponding to ECMP LPM FibEntry
groupedByFibAction.forEach(((fibAction, fibEntriesForFibAction) -> {
forkTracerSameNode().forward(fibAction, fibEntriesForFibAction, dstIp, currentNodeName, lookupVrf, forwardOutInterfaceHandler, intraHopBreadcrumbs, breadcrumb);
}));
} finally {
if (intraHopBreadcrumbs.isEmpty()) {
_breadcrumbs.pop();
}
}
}
use of org.batfish.datamodel.FibForward in project batfish by batfish.
the class FlowTracer method processSessions.
/**
* Check this {@param flow} matches a session on this device. If so, process the flow. Returns
* true if the flow is matched/processed.
*/
private boolean processSessions() {
String inputIfaceName = _ingressInterface;
String currentNodeName = _currentNode.getName();
Collection<FirewallSessionTraceInfo> sessions = _ingressInterface != null ? _tracerouteContext.getSessionsForIncomingInterface(currentNodeName, inputIfaceName) : // Flow originated here; check for sessions to match flows originating in current VRF
_tracerouteContext.getSessionsForOriginatingVrf(currentNodeName, _vrfName);
if (sessions.isEmpty()) {
return false;
}
// session match expr cannot use MatchSrcInterface or ACL/IpSpace references.
Evaluator aclEval = new Evaluator(_currentFlow, null, ImmutableMap.of(), ImmutableMap.of());
List<FirewallSessionTraceInfo> matchingSessions = sessions.stream().filter(session -> aclEval.visit(session.getSessionFlows())).collect(Collectors.toList());
checkState(matchingSessions.size() < 2, "Flow cannot match more than 1 session");
if (matchingSessions.isEmpty()) {
return false;
}
FirewallSessionTraceInfo session = matchingSessions.get(0);
MatchSessionStepDetail.Builder matchDetail = MatchSessionStepDetail.builder().setSessionScope(session.getSessionScope()).setSessionAction(session.getAction()).setMatchCriteria(session.getMatchCriteria());
Configuration config = _tracerouteContext.getConfigurations().get(currentNodeName);
Map<String, IpAccessList> ipAccessLists = config.getIpAccessLists();
Map<String, IpSpace> ipSpaces = config.getIpSpaces();
// compute transformation. it will be applied after applying incoming ACL
Transformation transformation = session.getTransformation();
TransformationResult transformationResult = Optional.ofNullable(transformation).map(t -> TransformationEvaluator.eval(t, _currentFlow, inputIfaceName, ipAccessLists, ipSpaces)).orElse(null);
if (transformation != null) {
matchDetail.setTransformation(flowDiffs(_currentFlow, transformationResult.getOutputFlow()));
}
_steps.add(new MatchSessionStep(matchDetail.build()));
// apply incoming ACL if any
if (inputIfaceName != null) {
FirewallSessionInterfaceInfo incomingIfaceSessionInfo = config.getAllInterfaces().get(inputIfaceName).getFirewallSessionInterfaceInfo();
checkState(incomingIfaceSessionInfo != null, "Session matched, but interface %s does not have FirewallSessionInterfaceInfo.", inputIfaceName);
String incomingAclName = incomingIfaceSessionInfo.getIncomingAclName();
if (incomingAclName != null && applyFilter(ipAccessLists.get(incomingAclName), FilterType.INGRESS_FILTER) == DENIED) {
return true;
}
}
session.getAction().accept(new SessionActionVisitor<Void>() {
@Override
public Void visitAcceptVrf(Accept acceptVrf) {
// Apply transformation to flow if present, then accept
if (transformationResult != null) {
_currentFlow = transformationResult.getOutputFlow();
_steps.addAll(transformationResult.getTraceSteps());
}
buildAcceptTrace();
return null;
}
@Override
public Void visitPostNatFibLookup(PostNatFibLookup postNatFibLookup) {
// Apply transformation first, if any
if (transformationResult != null) {
_currentFlow = transformationResult.getOutputFlow();
_steps.addAll(transformationResult.getTraceSteps());
}
// Accept if the flow is destined for this vrf on this host.
if (isAcceptedAtCurrentVrf(_currentFlow.getDstIp())) {
buildAcceptTrace();
return null;
}
fibLookup(_currentFlow.getDstIp(), currentNodeName, _vrfName, _tracerouteContext.getFib(currentNodeName, _vrfName).get(), (flowTracer, fibForward) -> {
String outgoingIfaceName = fibForward.getInterfaceName();
// TODO: handle ACLs
// add ExitOutputIfaceStep
flowTracer._steps.add(buildExitOutputIfaceStep(outgoingIfaceName));
SortedSet<NodeInterfacePair> neighborIfaces = _tracerouteContext.getInterfaceNeighbors(currentNodeName, outgoingIfaceName);
if (neighborIfaces.isEmpty()) {
FlowDisposition disposition = _tracerouteContext.computeDisposition(currentNodeName, outgoingIfaceName, _currentFlow.getDstIp());
flowTracer.buildArpFailureTrace(outgoingIfaceName, _currentFlow.getDstIp(), disposition);
} else {
flowTracer.processOutgoingInterfaceEdges(outgoingIfaceName, fibForward.getArpIp().orElse(_currentFlow.getDstIp()), neighborIfaces);
}
});
return null;
}
@Override
public Void visitPreNatFibLookup(PreNatFibLookup preNatFibLookup) {
// TODO Confirm it is possible to accept at this point in pipeline.
if (isAcceptedAtCurrentVrf(_currentFlow.getDstIp())) {
buildAcceptTrace();
return null;
}
fibLookup(_currentFlow.getDstIp(), currentNodeName, _vrfName, _tracerouteContext.getFib(currentNodeName, _vrfName).get(), (flowTracer, fibForward) -> {
// Routing happened, so it's finally time to apply transformation
if (transformationResult != null) {
flowTracer._currentFlow = transformationResult.getOutputFlow();
flowTracer._steps.addAll(transformationResult.getTraceSteps());
}
String outgoingIfaceName = fibForward.getInterfaceName();
// TODO: handle ACLs
// add ExitOutputIfaceStep
flowTracer._steps.add(buildExitOutputIfaceStep(outgoingIfaceName));
SortedSet<NodeInterfacePair> neighborIfaces = _tracerouteContext.getInterfaceNeighbors(currentNodeName, outgoingIfaceName);
Ip arpIp = fibForward.getArpIp().orElse(flowTracer._currentFlow.getDstIp());
if (neighborIfaces.isEmpty()) {
FlowDisposition disposition = _tracerouteContext.computeDisposition(currentNodeName, outgoingIfaceName, flowTracer._currentFlow.getDstIp());
flowTracer.buildArpFailureTrace(outgoingIfaceName, arpIp, disposition);
} else {
flowTracer.processOutgoingInterfaceEdges(outgoingIfaceName, arpIp, neighborIfaces);
}
});
return null;
}
@Override
public Void visitForwardOutInterface(ForwardOutInterface forwardOutInterface) {
// Apply transformation first, if any
Flow originalFlow = _currentFlow;
if (transformationResult != null) {
_currentFlow = transformationResult.getOutputFlow();
_steps.addAll(transformationResult.getTraceSteps());
}
// cycle detection
Breadcrumb breadcrumb = newBreadcrumb(currentNodeName, _vrfName, _ingressInterface, originalFlow);
if (_breadcrumbs.contains(breadcrumb)) {
buildLoopTrace(breadcrumb);
return null;
}
_breadcrumbs.push(breadcrumb);
try {
NodeInterfacePair nextHop = forwardOutInterface.getNextHop();
String outgoingInterfaceName = forwardOutInterface.getOutgoingInterface();
Interface outgoingInterface = config.getAllInterfaces().get(outgoingInterfaceName);
// apply outgoing ACL from firewall info, if any
StepAction filterResult = Optional.ofNullable(outgoingInterface.getFirewallSessionInterfaceInfo()).map(FirewallSessionInterfaceInfo::getOutgoingAclName).map(outgoingAclName -> applyFilter(ipAccessLists.get(outgoingAclName), FilterType.EGRESS_FILTER)).orElse(null);
if (filterResult == DENIED) {
return null;
}
// add ExitOutIfaceStep
_steps.add(buildExitOutputIfaceStep(outgoingInterfaceName));
if (nextHop == null) {
/* ARP error. Currently we can't use buildArpFailureTrace for sessions, because forwarding
* analysis disposition maps currently include routing conditions, which do not apply to
* sessions.
*
* ARP failure is only possible for sessions that have an outgoing interface but no next
* hop. This only happens when the user started the trace entering the outgoing interface.
* For now, just always call this EXITS_NETWORK. In the future we may want to apply the
* normal ARP error disposition logic, which would require factoring out routing-independent
* disposition maps in forwarding analysis.
*/
buildArpFailureTrace(outgoingInterfaceName, originalFlow.getDstIp(), FlowDisposition.EXITS_NETWORK);
return null;
}
Hop hop = new Hop(new Node(currentNodeName), _steps);
_hops.add(forwardedHop(hop, _originalFlow, checkNotNull(getVisitedBreadcrumb(), "Must push a breadcrumb before forwarding to next hop"), getHopSessionInfo()));
if (_traceRecorder.tryRecordPartialTrace(_hops)) {
return null;
}
// Forward to neighbor.
forkTracerFollowEdge(NodeInterfacePair.of(currentNodeName, outgoingInterfaceName), nextHop).processHop();
return null;
} finally {
_breadcrumbs.pop();
}
}
});
return true;
}
use of org.batfish.datamodel.FibForward in project batfish by batfish.
the class FlowTracer method buildRoutingStep.
@VisibleForTesting
static RoutingStep buildRoutingStep(String vrf, FibAction fibAction, Set<FibEntry> fibEntries) {
RoutingStep.Builder routingStepBuilder = RoutingStep.builder();
List<RouteInfo> routeInfos = fibEntriesToRouteInfos(fibEntries);
RoutingStepDetail.Builder routingStepDetailBuilder = RoutingStepDetail.builder().setVrf(vrf).setRoutes(routeInfos);
fibAction.accept(new FibActionVisitor<Void>() {
@Override
public Void visitFibForward(FibForward fibForward) {
assert !routeInfos.isEmpty();
RouteInfo primaryRouteInfo = routeInfos.get(0);
if (primaryRouteInfo.getNextHop() instanceof NextHopVtep) {
NextHopVtep nhVtep = (NextHopVtep) primaryRouteInfo.getNextHop();
routingStepDetailBuilder.setForwardingDetail(ForwardedIntoVxlanTunnel.of(nhVtep.getVni(), nhVtep.getVtepIp()));
} else {
Optional<Ip> maybeResolvedNextHopIp = fibForward.getArpIp();
if (!maybeResolvedNextHopIp.isPresent()) {
routingStepDetailBuilder.setForwardingDetail(ForwardedOutInterface.of(fibForward.getInterfaceName()));
} else {
Ip resolvedNextHopIp = maybeResolvedNextHopIp.get();
routingStepDetailBuilder.setForwardingDetail(ForwardedOutInterface.of(fibForward.getInterfaceName(), resolvedNextHopIp)).setArpIp(resolvedNextHopIp);
}
}
routingStepDetailBuilder.setOutputInterface(fibForward.getInterfaceName());
routingStepBuilder.setAction(FORWARDED);
return null;
}
@Override
public Void visitFibNextVrf(FibNextVrf fibNextVrf) {
routingStepDetailBuilder.setForwardingDetail(DelegatedToNextVrf.of(fibNextVrf.getNextVrf()));
routingStepBuilder.setAction(FORWARDED_TO_NEXT_VRF);
return null;
}
@Override
public Void visitFibNullRoute(FibNullRoute fibNullRoute) {
routingStepDetailBuilder.setForwardingDetail(Discarded.instance());
routingStepBuilder.setAction(NULL_ROUTED);
return null;
}
});
return routingStepBuilder.setDetail(routingStepDetailBuilder.build()).build();
}
Aggregations