use of org.batfish.symbolic.Protocol 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);
}
use of org.batfish.symbolic.Protocol in project batfish by batfish.
the class TransferSSA method compute.
/*
* Convert a Batfish AST boolean expression to a symbolic Z3 boolean expression
* by performing inlining of stateful side effects.
*/
private TransferResult<BoolExpr, BoolExpr> compute(BooleanExpr expr, TransferParam<SymbolicRoute> p) {
// TODO: right now everything is IPV4
if (expr instanceof MatchIpv4) {
p.debug("MatchIpv4");
return fromExpr(_enc.mkTrue());
}
if (expr instanceof MatchIpv6) {
p.debug("MatchIpv6");
return fromExpr(_enc.mkFalse());
}
if (expr instanceof Conjunction) {
p.debug("Conjunction");
Conjunction c = (Conjunction) expr;
BoolExpr acc = _enc.mkTrue();
TransferResult<BoolExpr, BoolExpr> result = new TransferResult<>();
for (BooleanExpr be : c.getConjuncts()) {
TransferResult<BoolExpr, BoolExpr> r = compute(be, p.indent());
result = result.addChangedVariables(r);
acc = _enc.mkAnd(acc, r.getReturnValue());
}
p.debug("has changed variable");
return result.setReturnValue(acc);
}
if (expr instanceof Disjunction) {
p.debug("Disjunction");
Disjunction d = (Disjunction) expr;
BoolExpr acc = _enc.mkFalse();
TransferResult<BoolExpr, BoolExpr> result = new TransferResult<>();
for (BooleanExpr be : d.getDisjuncts()) {
TransferResult<BoolExpr, BoolExpr> r = compute(be, p.indent());
result = result.addChangedVariables(r);
acc = _enc.mkOr(acc, r.getReturnValue());
}
p.debug("has changed variable");
return result.setReturnValue(acc);
}
if (expr instanceof ConjunctionChain) {
p.debug("ConjunctionChain");
ConjunctionChain d = (ConjunctionChain) expr;
List<BooleanExpr> conjuncts = new ArrayList<>(d.getSubroutines());
if (p.getDefaultPolicy() != null) {
BooleanExpr be = new CallExpr(p.getDefaultPolicy().getDefaultPolicy());
conjuncts.add(be);
}
if (conjuncts.size() == 0) {
return fromExpr(_enc.mkTrue());
} else {
TransferResult<BoolExpr, BoolExpr> result = new TransferResult<>();
BoolExpr acc = _enc.mkFalse();
for (int i = conjuncts.size() - 1; i >= 0; i--) {
BooleanExpr conjunct = conjuncts.get(i);
TransferParam<SymbolicRoute> param = p.setDefaultPolicy(null).setChainContext(TransferParam.ChainContext.CONJUNCTION);
TransferResult<BoolExpr, BoolExpr> r = compute(conjunct, param);
result = result.addChangedVariables(r);
acc = _enc.mkIf(r.getFallthroughValue(), acc, r.getReturnValue());
}
p.debug("ConjunctionChain Result: " + acc);
return result.setReturnValue(acc);
}
}
if (expr instanceof DisjunctionChain) {
p.debug("DisjunctionChain");
DisjunctionChain d = (DisjunctionChain) expr;
List<BooleanExpr> disjuncts = new ArrayList<>(d.getSubroutines());
if (p.getDefaultPolicy() != null) {
BooleanExpr be = new CallExpr(p.getDefaultPolicy().getDefaultPolicy());
disjuncts.add(be);
}
if (disjuncts.size() == 0) {
return fromExpr(_enc.mkTrue());
} else {
TransferResult<BoolExpr, BoolExpr> result = new TransferResult<>();
BoolExpr acc = _enc.mkFalse();
for (int i = disjuncts.size() - 1; i >= 0; i--) {
BooleanExpr disjunct = disjuncts.get(i);
TransferParam<SymbolicRoute> param = p.setDefaultPolicy(null).setChainContext(TransferParam.ChainContext.CONJUNCTION);
TransferResult<BoolExpr, BoolExpr> r = compute(disjunct, param);
result.addChangedVariables(r);
acc = _enc.mkIf(r.getFallthroughValue(), acc, r.getReturnValue());
}
p.debug("DisjunctionChain Result: " + acc);
return result.setReturnValue(acc);
}
}
if (expr instanceof Not) {
p.debug("mkNot");
Not n = (Not) expr;
TransferResult<BoolExpr, BoolExpr> result = compute(n.getExpr(), p);
return result.setReturnValue(_enc.mkNot(result.getReturnValue()));
}
if (expr instanceof MatchProtocol) {
MatchProtocol mp = (MatchProtocol) expr;
Protocol proto = Protocol.fromRoutingProtocol(mp.getProtocol());
if (proto == null) {
p.debug("MatchProtocol(" + mp.getProtocol().protocolName() + "): false");
return fromExpr(_enc.mkFalse());
}
if (_other.getProtocolHistory() == null) {
BoolExpr protoMatch = _enc.mkBool(proto.equals(_proto));
p.debug("MatchProtocol(" + mp.getProtocol().protocolName() + "): " + protoMatch);
return fromExpr(protoMatch);
}
BoolExpr protoMatch = _other.getProtocolHistory().checkIfValue(proto);
p.debug("MatchProtocol(" + mp.getProtocol().protocolName() + "): " + protoMatch);
return fromExpr(protoMatch);
}
if (expr instanceof MatchPrefixSet) {
p.debug("MatchPrefixSet");
MatchPrefixSet m = (MatchPrefixSet) expr;
// For BGP, may change prefix length
TransferResult<BoolExpr, BoolExpr> result = matchPrefixSet(_conf, m.getPrefixSet(), p.getData());
return result.setReturnAssignedValue(_enc.mkTrue());
// TODO: implement me
} else if (expr instanceof MatchPrefix6Set) {
p.debug("MatchPrefix6Set");
return fromExpr(_enc.mkFalse());
} else if (expr instanceof CallExpr) {
p.debug("CallExpr");
// TODO: the call can modify certain fields, need to keep track of these variables
CallExpr c = (CallExpr) expr;
String name = c.getCalledPolicyName();
RoutingPolicy pol = _conf.getRoutingPolicies().get(name);
p = p.setCallContext(TransferParam.CallContext.EXPR_CALL);
TransferResult<BoolExpr, BoolExpr> r = compute(pol.getStatements(), p.indent().enterScope(name), initialResult());
p.debug("CallExpr (return): " + r.getReturnValue());
p.debug("CallExpr (fallthrough): " + r.getFallthroughValue());
return r;
} else if (expr instanceof WithEnvironmentExpr) {
p.debug("WithEnvironmentExpr");
// TODO: this is not correct
WithEnvironmentExpr we = (WithEnvironmentExpr) expr;
// TODO: postStatements() and preStatements()
return compute(we.getExpr(), p);
} else if (expr instanceof MatchCommunitySet) {
p.debug("MatchCommunitySet");
MatchCommunitySet mcs = (MatchCommunitySet) expr;
return fromExpr(matchCommunitySet(_conf, mcs.getExpr(), p.getData()));
} else if (expr instanceof BooleanExprs.StaticBooleanExpr) {
BooleanExprs.StaticBooleanExpr b = (BooleanExprs.StaticBooleanExpr) expr;
switch(b.getType()) {
case CallExprContext:
p.debug("CallExprContext");
return fromExpr(_enc.mkBool(p.getCallContext() == TransferParam.CallContext.EXPR_CALL));
case CallStatementContext:
p.debug("CallStmtContext");
return fromExpr(_enc.mkBool(p.getCallContext() == TransferParam.CallContext.STMT_CALL));
case True:
p.debug("True");
return fromExpr(_enc.mkTrue());
case False:
p.debug("False");
return fromExpr(_enc.mkFalse());
default:
throw new BatfishException("Unhandled " + BooleanExprs.class.getCanonicalName() + ": " + b.getType());
}
} else if (expr instanceof MatchAsPath) {
p.debug("MatchAsPath");
System.out.println("Warning: use of unimplemented feature MatchAsPath");
return fromExpr(_enc.mkFalse());
}
String s = (_isExport ? "export" : "import");
String msg = String.format("Unimplemented feature %s for %s transfer function on interface %s", expr.toString(), s, _graphEdge.toString());
throw new BatfishException(msg);
}
Aggregations