Search in sources :

Example 1 with HeaderQuestion

use of org.batfish.datamodel.questions.smt.HeaderQuestion in project batfish by batfish.

the class Optimizations method computeCanMergeExportVars.

/*
   * Determines when we can merge export variables into a single copy.
   * This will be safe when there is no peer-specific export filter.
   */
private void computeCanMergeExportVars() {
    Graph g = _encoderSlice.getGraph();
    HeaderQuestion q = _encoderSlice.getEncoder().getQuestion();
    boolean noFailures = q.getFailures() == 0;
    _encoderSlice.getGraph().getConfigurations().forEach((router, conf) -> {
        HashMap<Protocol, Boolean> map = new HashMap<>();
        _sliceCanKeepSingleExportVar.put(router, map);
        // the neighbor already being the root of the tree.
        for (Protocol proto : getProtocols().get(router)) {
            if (proto.isConnected() || proto.isStatic()) {
                map.put(proto, noFailures && Optimizations.ENABLE_EXPORT_MERGE_OPTIMIZATION);
            } else if (proto.isOspf()) {
                // Ensure all interfaces are active
                boolean allIfacesActive = true;
                for (GraphEdge edge : g.getEdgeMap().get(router)) {
                    if (g.isEdgeUsed(conf, proto, edge)) {
                        Interface iface = edge.getStart();
                        allIfacesActive = allIfacesActive && g.isInterfaceActive(proto, iface);
                    }
                }
                // Ensure single area for this router
                Set<Long> areas = _encoderSlice.getGraph().getAreaIds().get(router);
                boolean singleArea = areas.size() <= 1;
                map.put(proto, noFailures && allIfacesActive && singleArea && ENABLE_EXPORT_MERGE_OPTIMIZATION);
            } else if (proto.isBgp()) {
                boolean acc = true;
                BgpProcess p = conf.getDefaultVrf().getBgpProcess();
                for (Map.Entry<Prefix, BgpNeighbor> e : p.getNeighbors().entrySet()) {
                    BgpNeighbor n = e.getValue();
                    // If iBGP used, then don't merge
                    if (n.getLocalAs().equals(n.getRemoteAs())) {
                        acc = false;
                        break;
                    }
                    // If not the default export policy, then don't merge
                    if (!isDefaultBgpExport(conf, n)) {
                        acc = false;
                        break;
                    }
                }
                map.put(proto, noFailures && acc && ENABLE_EXPORT_MERGE_OPTIMIZATION);
            } else {
                throw new BatfishException("Error: unkown protocol: " + proto.name());
            }
        }
    });
}
Also used : BatfishException(org.batfish.common.BatfishException) HashSet(java.util.HashSet) Set(java.util.Set) HashMap(java.util.HashMap) BgpProcess(org.batfish.datamodel.BgpProcess) Prefix(org.batfish.datamodel.Prefix) BgpNeighbor(org.batfish.datamodel.BgpNeighbor) Graph(org.batfish.symbolic.Graph) HeaderQuestion(org.batfish.datamodel.questions.smt.HeaderQuestion) Protocol(org.batfish.symbolic.Protocol) GraphEdge(org.batfish.symbolic.GraphEdge) HashMap(java.util.HashMap) Map(java.util.Map) Interface(org.batfish.datamodel.Interface)

Example 2 with HeaderQuestion

use of org.batfish.datamodel.questions.smt.HeaderQuestion in project batfish by batfish.

the class PropertyChecker method checkForwarding.

/*
   * Compute the forwarding behavior for the network. This adds no
   * additional constraints on top of the base network encoding.
   * Forwarding will be determined only for a particular network
   * environment, failure scenario, and data plane packet.
   */
public AnswerElement checkForwarding(HeaderQuestion question) {
    long totalTime = System.currentTimeMillis();
    HeaderQuestion q = new HeaderQuestion(question);
    q.setFailures(0);
    Tuple<Stream<Supplier<NetworkSlice>>, Long> ecs = findAllNetworkSlices(q, null, true);
    Stream<Supplier<NetworkSlice>> stream = ecs.getFirst();
    Long timeAbstraction = ecs.getSecond();
    Optional<Supplier<NetworkSlice>> opt = stream.findFirst();
    if (!opt.isPresent()) {
        throw new BatfishException("Unexpected Error: checkForwarding");
    }
    long timeEc = System.currentTimeMillis();
    Supplier<NetworkSlice> sup = opt.get();
    NetworkSlice slice = sup.get();
    timeEc = System.currentTimeMillis() - timeEc;
    Graph g = slice.getGraph();
    q = new HeaderQuestion(q);
    q.setHeaderSpace(slice.getHeaderSpace());
    long timeEncoding = System.currentTimeMillis();
    Encoder encoder = new Encoder(_settings, g, q);
    encoder.computeEncoding();
    addEnvironmentConstraints(encoder, q.getBaseEnvironmentType());
    timeEncoding = System.currentTimeMillis() - timeEncoding;
    VerificationResult result = encoder.verify().getFirst();
    totalTime = System.currentTimeMillis() - totalTime;
    VerificationStats stats = result.getStats();
    if (q.getBenchmark()) {
        stats.setTimeCreateBdds((double) timeAbstraction);
        stats.setTotalTime(totalTime);
        stats.setAvgComputeEcTime(timeEc);
        stats.setMaxComputeEcTime(timeEc);
        stats.setMinComputeEcTime(timeEc);
        stats.setAvgEncodingTime(timeEncoding);
        stats.setMaxEncodingTime(timeEncoding);
        stats.setMinEncodingTime(timeEncoding);
        stats.setTimeCreateBdds((double) timeAbstraction);
    }
    return new SmtOneAnswerElement(result);
}
Also used : BatfishException(org.batfish.common.BatfishException) SmtOneAnswerElement(org.batfish.symbolic.answers.SmtOneAnswerElement) NetworkSlice(org.batfish.symbolic.abstraction.NetworkSlice) Graph(org.batfish.symbolic.Graph) HeaderQuestion(org.batfish.datamodel.questions.smt.HeaderQuestion) Stream(java.util.stream.Stream) Supplier(java.util.function.Supplier)

Example 3 with HeaderQuestion

use of org.batfish.datamodel.questions.smt.HeaderQuestion in project batfish by batfish.

the class PropertyChecker method checkLocalEquivalence.

/*
   * Computes whether or not two routers are equivalent.
   * To be equivalent, each router must have identical intefaces.
   *
   * We then relate the environments on each interface for each router
   * so that they are required to be equal.
   *
   * We finally check that their forwarding decisions and exported messages
   * will be equal given their equal inputs.
   */
public AnswerElement checkLocalEquivalence(Pattern n, boolean strict, boolean fullModel) {
    Graph graph = new Graph(_batfish);
    List<String> routers = PatternUtils.findMatchingNodes(graph, n, Pattern.compile(""));
    HeaderQuestion q = new HeaderQuestion();
    q.setFullModel(fullModel);
    q.setFailures(0);
    q.setBaseEnvironmentType(EnvironmentType.ANY);
    Collections.sort(routers);
    SortedMap<String, VerificationResult> result = new TreeMap<>();
    int len = routers.size();
    if (len <= 1) {
        return new SmtManyAnswerElement(new TreeMap<>());
    }
    for (int i = 0; i < len - 1; i++) {
        String r1 = routers.get(i);
        String r2 = routers.get(i + 1);
        // TODO: reorder to encode after checking if we can compare them
        // Create transfer function for router 1
        Set<String> toModel1 = new TreeSet<>();
        toModel1.add(r1);
        Graph g1 = new Graph(_batfish, null, toModel1);
        Encoder e1 = new Encoder(_settings, g1, q);
        e1.computeEncoding();
        Context ctx = e1.getCtx();
        // Create transfer function for router 2
        Set<String> toModel2 = new TreeSet<>();
        toModel2.add(r2);
        Graph g2 = new Graph(_batfish, null, toModel2);
        Encoder e2 = new Encoder(e1, g2);
        e2.computeEncoding();
        EncoderSlice slice1 = e1.getMainSlice();
        EncoderSlice slice2 = e2.getMainSlice();
        // Ensure that the two routers have the same interfaces for comparison
        Pattern p = Pattern.compile(".*");
        Pattern neg = Pattern.compile("");
        List<GraphEdge> edges1 = PatternUtils.findMatchingEdges(g1, p, neg, p, neg);
        List<GraphEdge> edges2 = PatternUtils.findMatchingEdges(g2, p, neg, p, neg);
        Set<String> ifaces1 = interfaces(edges1);
        Set<String> ifaces2 = interfaces(edges2);
        if (!(ifaces1.containsAll(ifaces2) && ifaces2.containsAll(ifaces1))) {
            String msg = String.format("Routers %s and %s have different interfaces", r1, r2);
            System.out.println(msg);
            return new SmtManyAnswerElement(new TreeMap<>());
        }
        // TODO: check running same protocols?
        Map<String, Map<Protocol, Map<String, EnumMap<EdgeType, LogicalEdge>>>> lgeMap2 = logicalEdgeMap(slice2);
        BoolExpr equalEnvs = ctx.mkBool(true);
        BoolExpr equalOutputs = ctx.mkBool(true);
        BoolExpr equalIncomingAcls = ctx.mkBool(true);
        Configuration conf1 = g1.getConfigurations().get(r1);
        Configuration conf2 = g2.getConfigurations().get(r2);
        // Set environments equal
        Set<String> communities = new HashSet<>();
        Set<SymbolicRoute> envRecords = new HashSet<>();
        for (Protocol proto1 : slice1.getProtocols().get(r1)) {
            for (ArrayList<LogicalEdge> es : slice1.getLogicalGraph().getLogicalEdges().get(r1).get(proto1)) {
                for (LogicalEdge lge1 : es) {
                    String ifaceName = lge1.getEdge().getStart().getName();
                    LogicalEdge lge2 = lgeMap2.get(r2).get(proto1).get(ifaceName).get(lge1.getEdgeType());
                    if (lge1.getEdgeType() == EdgeType.IMPORT) {
                        SymbolicRoute vars1 = slice1.getLogicalGraph().getEnvironmentVars().get(lge1);
                        SymbolicRoute vars2 = slice2.getLogicalGraph().getEnvironmentVars().get(lge2);
                        BoolExpr aclIn1 = slice1.getIncomingAcls().get(lge1.getEdge());
                        BoolExpr aclIn2 = slice2.getIncomingAcls().get(lge2.getEdge());
                        if (aclIn1 == null) {
                            aclIn1 = ctx.mkBool(true);
                        }
                        if (aclIn2 == null) {
                            aclIn2 = ctx.mkBool(true);
                        }
                        equalIncomingAcls = ctx.mkAnd(equalIncomingAcls, ctx.mkEq(aclIn1, aclIn2));
                        boolean hasEnv1 = (vars1 != null);
                        boolean hasEnv2 = (vars2 != null);
                        if (hasEnv1 && hasEnv2) {
                            BoolExpr samePermitted = ctx.mkEq(vars1.getPermitted(), vars2.getPermitted());
                            // Set communities equal
                            BoolExpr equalComms = e1.mkTrue();
                            for (Map.Entry<CommunityVar, BoolExpr> entry : vars1.getCommunities().entrySet()) {
                                CommunityVar cvar = entry.getKey();
                                BoolExpr ce1 = entry.getValue();
                                BoolExpr ce2 = vars2.getCommunities().get(cvar);
                                if (ce2 != null) {
                                    equalComms = e1.mkAnd(equalComms, e1.mkEq(ce1, ce2));
                                }
                            }
                            // Set communities belonging to one but not the other
                            // off, but give a warning of the difference
                            BoolExpr unsetComms = e1.mkTrue();
                            for (Map.Entry<CommunityVar, BoolExpr> entry : vars1.getCommunities().entrySet()) {
                                CommunityVar cvar = entry.getKey();
                                BoolExpr ce1 = entry.getValue();
                                BoolExpr ce2 = vars2.getCommunities().get(cvar);
                                if (ce2 == null) {
                                    if (!communities.contains(cvar.getValue())) {
                                        communities.add(cvar.getValue());
                                    /* String msg =
                       String.format(
                           "Warning: community %s found for router %s but not %s.",
                           cvar.getValue(), conf1.getEnvName(), conf2.getEnvName());
                      System.out.println(msg); */
                                    }
                                    unsetComms = e1.mkAnd(unsetComms, e1.mkNot(ce1));
                                }
                            }
                            // Do the same thing for communities missing from the other side
                            for (Map.Entry<CommunityVar, BoolExpr> entry : vars2.getCommunities().entrySet()) {
                                CommunityVar cvar = entry.getKey();
                                BoolExpr ce2 = entry.getValue();
                                BoolExpr ce1 = vars1.getCommunities().get(cvar);
                                if (ce1 == null) {
                                    if (!communities.contains(cvar.getValue())) {
                                        communities.add(cvar.getValue());
                                    /* String msg =
                       String.format(
                           "Warning: community %s found for router %s but not %s.",
                           cvar.getValue(), conf2.getEnvName(), conf1.getEnvName());
                      System.out.println(msg); */
                                    }
                                    unsetComms = e1.mkAnd(unsetComms, e1.mkNot(ce2));
                                }
                            }
                            envRecords.add(vars1);
                            BoolExpr equalVars = slice1.equal(conf1, proto1, vars1, vars2, lge1, true);
                            equalEnvs = ctx.mkAnd(equalEnvs, unsetComms, samePermitted, equalVars, equalComms);
                        } else if (hasEnv1 || hasEnv2) {
                            System.out.println("Edge1: " + lge1);
                            System.out.println("Edge2: " + lge2);
                            throw new BatfishException("one had environment");
                        }
                    } else {
                        SymbolicRoute out1 = lge1.getSymbolicRecord();
                        SymbolicRoute out2 = lge2.getSymbolicRecord();
                        equalOutputs = ctx.mkAnd(equalOutputs, slice1.equal(conf1, proto1, out1, out2, lge1, false));
                    }
                }
            }
        }
        // check the stronger version of local equivalence
        if (strict) {
            for (SymbolicRoute env1 : envRecords) {
                for (SymbolicRoute env2 : envRecords) {
                    if (!env1.equals(env2)) {
                        BoolExpr c = e2.mkImplies(env1.getPermitted(), e2.mkNot(env2.getPermitted()));
                        e2.add(c);
                    }
                }
            }
        }
        // TODO: check both have same environment vars (e.g., screw up configuring peer connection)
        // Create assumptions
        BoolExpr validDest;
        validDest = ignoredDestinations(ctx, slice1, r1, conf1);
        validDest = ctx.mkAnd(validDest, ignoredDestinations(ctx, slice2, r2, conf2));
        SymbolicPacket p1 = slice1.getSymbolicPacket();
        SymbolicPacket p2 = slice2.getSymbolicPacket();
        BoolExpr equalPackets = p1.mkEqual(p2);
        BoolExpr assumptions = ctx.mkAnd(equalEnvs, equalPackets, validDest);
        // Create the requirements
        // Best choices should be the same
        BoolExpr required;
        if (strict) {
            SymbolicRoute best1 = e1.getMainSlice().getSymbolicDecisions().getBestNeighbor().get(conf1.getName());
            SymbolicRoute best2 = e2.getMainSlice().getSymbolicDecisions().getBestNeighbor().get(conf2.getName());
            // Just pick some protocol for defaults, shouldn't matter for best choice
            required = equal(e2, conf2, best1, best2);
        } else {
            // Forwarding decisions should be the sames
            Map<String, GraphEdge> geMap2 = interfaceMap(edges2);
            BoolExpr sameForwarding = ctx.mkBool(true);
            for (GraphEdge ge1 : edges1) {
                GraphEdge ge2 = geMap2.get(ge1.getStart().getName());
                BoolExpr dataFwd1 = slice1.getSymbolicDecisions().getDataForwarding().get(r1, ge1);
                BoolExpr dataFwd2 = slice2.getSymbolicDecisions().getDataForwarding().get(r2, ge2);
                assert (dataFwd1 != null);
                assert (dataFwd2 != null);
                sameForwarding = ctx.mkAnd(sameForwarding, ctx.mkEq(dataFwd1, dataFwd2));
            }
            // equalOutputs, equalIncomingAcls);
            required = ctx.mkAnd(sameForwarding);
        }
        e2.add(assumptions);
        e2.add(ctx.mkNot(required));
        VerificationResult res = e2.verify().getFirst();
        String name = r1 + "<-->" + r2;
        result.put(name, res);
    }
    return new SmtManyAnswerElement(result);
}
Also used : BoolExpr(com.microsoft.z3.BoolExpr) Configuration(org.batfish.datamodel.Configuration) TreeSet(java.util.TreeSet) SmtManyAnswerElement(org.batfish.symbolic.answers.SmtManyAnswerElement) Protocol(org.batfish.symbolic.Protocol) EnumMap(java.util.EnumMap) HashSet(java.util.HashSet) Context(com.microsoft.z3.Context) Pattern(java.util.regex.Pattern) BatfishException(org.batfish.common.BatfishException) TreeMap(java.util.TreeMap) CommunityVar(org.batfish.symbolic.CommunityVar) Graph(org.batfish.symbolic.Graph) HeaderQuestion(org.batfish.datamodel.questions.smt.HeaderQuestion) GraphEdge(org.batfish.symbolic.GraphEdge) Map(java.util.Map) EnumMap(java.util.EnumMap) SortedMap(java.util.SortedMap) HashMap(java.util.HashMap) TreeMap(java.util.TreeMap)

Aggregations

BatfishException (org.batfish.common.BatfishException)3 HeaderQuestion (org.batfish.datamodel.questions.smt.HeaderQuestion)3 Graph (org.batfish.symbolic.Graph)3 HashMap (java.util.HashMap)2 HashSet (java.util.HashSet)2 Map (java.util.Map)2 GraphEdge (org.batfish.symbolic.GraphEdge)2 Protocol (org.batfish.symbolic.Protocol)2 BoolExpr (com.microsoft.z3.BoolExpr)1 Context (com.microsoft.z3.Context)1 EnumMap (java.util.EnumMap)1 Set (java.util.Set)1 SortedMap (java.util.SortedMap)1 TreeMap (java.util.TreeMap)1 TreeSet (java.util.TreeSet)1 Supplier (java.util.function.Supplier)1 Pattern (java.util.regex.Pattern)1 Stream (java.util.stream.Stream)1 BgpNeighbor (org.batfish.datamodel.BgpNeighbor)1 BgpProcess (org.batfish.datamodel.BgpProcess)1