use of com.microsoft.z3.ArithExpr in project batfish by batfish.
the class PropertyChecker method checkEqualLength.
/*
* Computes whether a collection of source routers will always have
* equal path length to destination port(s).
*/
public AnswerElement checkEqualLength(HeaderLocationQuestion q) {
return checkProperty(q, (enc, srcRouters, destPorts) -> {
PropertyAdder pa = new PropertyAdder(enc.getMainSlice());
Map<String, ArithExpr> lenVars = pa.instrumentPathLength(destPorts);
Map<String, BoolExpr> eqVars = new HashMap<>();
List<Expr> lens = new ArrayList<>();
for (String router : srcRouters) {
lens.add(lenVars.get(router));
}
BoolExpr allEqual = PropertyAdder.allEqual(enc.getCtx(), lens);
enc.add(enc.mkNot(allEqual));
for (Entry<String, ArithExpr> entry : lenVars.entrySet()) {
String name = entry.getKey();
BoolExpr b = srcRouters.contains(name) ? allEqual : enc.mkTrue();
eqVars.put(name, b);
}
return eqVars;
}, (vp) -> new SmtOneAnswerElement(vp.getResult()));
}
use of com.microsoft.z3.ArithExpr in project batfish by batfish.
the class TransferSSA method compute.
/*
* Convert a list of statements into a Z3 boolean expression for the transfer function.
*/
private TransferResult<BoolExpr, BoolExpr> compute(List<Statement> statements, TransferParam<SymbolicRoute> p, TransferResult<BoolExpr, BoolExpr> result) {
boolean doesReturn = false;
for (Statement stmt : statements) {
if (stmt instanceof StaticStatement) {
StaticStatement ss = (StaticStatement) stmt;
switch(ss.getType()) {
case ExitAccept:
doesReturn = true;
p.debug("ExitAccept");
result = returnValue(p, result, true);
break;
case ReturnTrue:
doesReturn = true;
p.debug("ReturnTrue");
result = returnValue(p, result, true);
break;
case ExitReject:
doesReturn = true;
p.debug("ExitReject");
result = returnValue(p, result, false);
break;
case ReturnFalse:
doesReturn = true;
p.debug("ReturnFalse");
result = returnValue(p, result, false);
break;
case SetDefaultActionAccept:
p.debug("SetDefaulActionAccept");
p = p.setDefaultAccept(true);
break;
case SetDefaultActionReject:
p.debug("SetDefaultActionReject");
p = p.setDefaultAccept(false);
break;
case SetLocalDefaultActionAccept:
p.debug("SetLocalDefaultActionAccept");
p = p.setDefaultAcceptLocal(true);
break;
case SetLocalDefaultActionReject:
p.debug("SetLocalDefaultActionReject");
p = p.setDefaultAcceptLocal(false);
break;
case ReturnLocalDefaultAction:
p.debug("ReturnLocalDefaultAction");
// TODO: need to set local default action in an environment
if (p.getDefaultAcceptLocal()) {
result = returnValue(p, result, true);
} else {
result = returnValue(p, result, false);
}
break;
case FallThrough:
p.debug("Fallthrough");
result = fallthrough(p, result);
break;
case Return:
// TODO: assumming this happens at the end of the function, so it is ignored for now.
p.debug("Return");
break;
case RemovePrivateAs:
p.debug("RemovePrivateAs");
System.out.println("Warning: use of unimplemented feature RemovePrivateAs");
break;
default:
throw new BatfishException("TODO: computeTransferFunction: " + ss.getType());
}
} else if (stmt instanceof If) {
p.debug("If");
If i = (If) stmt;
TransferResult<BoolExpr, BoolExpr> r = compute(i.getGuard(), p);
result = result.addChangedVariables(r);
BoolExpr guard = (BoolExpr) r.getReturnValue().simplify();
String str = guard.toString();
// If there are updates in the guard, add them to the parameter p before entering branches
for (Pair<String, Expr> changed : r.getChangedVariables()) {
p.debug("CHANGED: " + changed.getFirst());
updateSingleValue(p, changed.getFirst(), changed.getSecond());
}
p.debug("guard: " + str);
// If we know the branch ahead of time, then specialize
switch(str) {
case "true":
p.debug("True Branch");
result = compute(i.getTrueStatements(), p.indent(), result);
break;
case "false":
p.debug("False Branch");
compute(i.getFalseStatements(), p.indent(), result);
break;
default:
p.debug("True Branch");
// clear changed variables before proceeding
TransferParam<SymbolicRoute> p1 = p.indent().setData(p.getData().copy());
TransferParam<SymbolicRoute> p2 = p.indent().setData(p.getData().copy());
TransferResult<BoolExpr, BoolExpr> trueBranch = compute(i.getTrueStatements(), p1, initialResult());
p.debug("False Branch");
TransferResult<BoolExpr, BoolExpr> falseBranch = compute(i.getFalseStatements(), p2, initialResult());
p.debug("JOIN");
PList<Pair<String, Pair<Expr, Expr>>> pairs = trueBranch.mergeChangedVariables(falseBranch);
// Extract and deal with the return value first so that other
// variables have this reflected in their value
int idx = pairs.find(pair -> pair.getFirst().equals("RETURN"));
if (idx >= 0) {
Pair<String, Pair<Expr, Expr>> ret = pairs.get(idx);
pairs = pairs.minus(idx);
pairs = pairs.plus(pairs.size(), ret);
}
for (Pair<String, Pair<Expr, Expr>> pair : pairs) {
String s = pair.getFirst();
p.debug("CHANGED: " + s);
Pair<Expr, Expr> x = joinPoint(p, result, guard, pair);
result = result.addChangedVariable(s, x.getFirst());
if (s.equals("RETURN")) {
result = result.setReturnValue((BoolExpr) x.getFirst()).setReturnAssignedValue((BoolExpr) x.getSecond());
}
if (s.equals("FALLTHROUGH")) {
result = result.setFallthroughValue((BoolExpr) x.getFirst()).setReturnAssignedValue((BoolExpr) x.getSecond());
}
}
break;
}
} else if (stmt instanceof SetDefaultPolicy) {
p.debug("SetDefaultPolicy");
p = p.setDefaultPolicy((SetDefaultPolicy) stmt);
} else if (stmt instanceof SetMetric) {
p.debug("SetMetric");
// TODO: what is the semantics for BGP? Is this MED?
if (!_current.getProto().isBgp()) {
SetMetric sm = (SetMetric) stmt;
LongExpr ie = sm.getMetric();
ArithExpr newValue = applyLongExprModification(p.getData().getMetric(), ie);
newValue = _enc.mkIf(result.getReturnAssignedValue(), p.getData().getMetric(), newValue);
ArithExpr x = createArithVariableWith(p, "METRIC", newValue);
p.getData().setMetric(x);
result = result.addChangedVariable("METRIC", x);
}
} else if (stmt instanceof SetOspfMetricType) {
p.debug("SetOspfMetricType");
SetOspfMetricType somt = (SetOspfMetricType) stmt;
OspfMetricType mt = somt.getMetricType();
SymbolicOspfType t;
if (mt == OspfMetricType.E1) {
t = new SymbolicOspfType(_enc, OspfType.E1);
} else {
t = new SymbolicOspfType(_enc, OspfType.E2);
}
BitVecExpr newValue = t.getBitVec();
newValue = _enc.mkIf(result.getReturnAssignedValue(), p.getData().getOspfType().getBitVec(), newValue);
BitVecExpr x = createBitVecVariableWith(p, "OSPF-TYPE", 2, newValue);
p.getData().getOspfType().setBitVec(x);
result = result.addChangedVariable("OSPF-TYPE", x);
} else if (stmt instanceof SetLocalPreference) {
p.debug("SetLocalPreference");
SetLocalPreference slp = (SetLocalPreference) stmt;
IntExpr ie = slp.getLocalPreference();
ArithExpr newValue = applyIntExprModification(p.getData().getLocalPref(), ie);
newValue = _enc.mkIf(result.getReturnAssignedValue(), p.getData().getLocalPref(), newValue);
ArithExpr x = createArithVariableWith(p, "LOCAL-PREF", newValue);
p.getData().setLocalPref(x);
result = result.addChangedVariable("LOCAL-PREF", x);
} else if (stmt instanceof AddCommunity) {
p.debug("AddCommunity");
AddCommunity ac = (AddCommunity) stmt;
Set<CommunityVar> comms = _enc.getGraph().findAllCommunities(_conf, ac.getExpr());
for (CommunityVar cvar : comms) {
BoolExpr newValue = _enc.mkIf(result.getReturnAssignedValue(), p.getData().getCommunities().get(cvar), _enc.mkTrue());
BoolExpr x = createBoolVariableWith(p, cvar.getValue(), newValue);
p.getData().getCommunities().put(cvar, x);
result = result.addChangedVariable(cvar.getValue(), x);
}
} else if (stmt instanceof SetCommunity) {
p.debug("SetCommunity");
SetCommunity sc = (SetCommunity) stmt;
Set<CommunityVar> comms = _enc.getGraph().findAllCommunities(_conf, sc.getExpr());
for (CommunityVar cvar : comms) {
BoolExpr newValue = _enc.mkIf(result.getReturnAssignedValue(), p.getData().getCommunities().get(cvar), _enc.mkTrue());
BoolExpr x = createBoolVariableWith(p, cvar.getValue(), newValue);
p.getData().getCommunities().put(cvar, x);
result = result.addChangedVariable(cvar.getValue(), x);
}
} else if (stmt instanceof DeleteCommunity) {
p.debug("DeleteCommunity");
DeleteCommunity ac = (DeleteCommunity) stmt;
Set<CommunityVar> comms = _enc.getGraph().findAllCommunities(_conf, ac.getExpr());
Set<CommunityVar> toDelete = new HashSet<>();
// Find comms to delete
for (CommunityVar cvar : comms) {
if (cvar.getType() == Type.REGEX) {
toDelete.addAll(_enc.getCommunityDependencies().get(cvar));
} else {
toDelete.add(cvar);
}
}
// Delete each community
for (CommunityVar cvar : toDelete) {
BoolExpr newValue = _enc.mkIf(result.getReturnAssignedValue(), p.getData().getCommunities().get(cvar), _enc.mkFalse());
BoolExpr x = createBoolVariableWith(p, cvar.getValue(), newValue);
p.getData().getCommunities().put(cvar, x);
result = result.addChangedVariable(cvar.getValue(), x);
}
} else if (stmt instanceof RetainCommunity) {
p.debug("RetainCommunity");
// no op
} else if (stmt instanceof PrependAsPath) {
p.debug("PrependAsPath");
PrependAsPath pap = (PrependAsPath) stmt;
Integer prependCost = prependLength(pap.getExpr());
ArithExpr newValue = _enc.mkSum(p.getData().getMetric(), _enc.mkInt(prependCost));
newValue = _enc.mkIf(result.getReturnAssignedValue(), p.getData().getMetric(), newValue);
ArithExpr x = createArithVariableWith(p, "METRIC", newValue);
p.getData().setMetric(x);
result = result.addChangedVariable("METRIC", x);
} else if (stmt instanceof SetOrigin) {
p.debug("SetOrigin");
System.out.println("Warning: use of unimplemented feature SetOrigin");
} else if (stmt instanceof SetNextHop) {
p.debug("SetNextHop");
System.out.println("Warning: use of unimplemented feature SetNextHop");
} else {
String s = (_isExport ? "export" : "import");
String msg = String.format("Unimplemented feature %s for %s transfer function on interface %s", stmt.toString(), s, _graphEdge.toString());
throw new BatfishException(msg);
}
}
// If this is the outermost call, then we relate the variables
if (p.getInitialCall()) {
p.debug("InitialCall finalizing");
// Apply the default action
if (!doesReturn) {
p.debug("Applying default action: " + p.getDefaultAccept());
if (p.getDefaultAccept()) {
result = returnValue(p, result, true);
} else {
result = returnValue(p, result, false);
}
}
BoolExpr related = relateVariables(p, result);
BoolExpr retValue = _enc.mkIf(result.getReturnValue(), related, _enc.mkNot(_current.getPermitted()));
result = result.setReturnValue(retValue);
}
return result;
}
use of com.microsoft.z3.ArithExpr in project batfish by batfish.
the class TransferSSA method matchPrefixSet.
/*
* Converts a prefix set to a boolean expression.
*/
private TransferResult<BoolExpr, BoolExpr> matchPrefixSet(Configuration conf, PrefixSetExpr e, SymbolicRoute other) {
ArithExpr otherLen = other.getPrefixLength();
TransferResult<BoolExpr, BoolExpr> result = new TransferResult<>();
if (e instanceof ExplicitPrefixSet) {
ExplicitPrefixSet x = (ExplicitPrefixSet) e;
Set<PrefixRange> ranges = x.getPrefixSpace().getPrefixRanges();
if (ranges.isEmpty()) {
return result.setReturnValue(_enc.mkTrue());
}
// by checking for static/connected/OSPF routes specifically.
if (ranges.size() == 1) {
for (PrefixRange r : ranges) {
int start = r.getLengthRange().getStart();
int end = r.getLengthRange().getEnd();
Prefix pfx = r.getPrefix();
if (start == end && start == pfx.getPrefixLength()) {
String router = _conf.getName();
Set<Prefix> origin = _enc.getOriginatedNetworks().get(router, Protocol.BGP);
if (origin != null && origin.contains(pfx)) {
// Compute static and connected routes
Set<Prefix> ostatic = _enc.getOriginatedNetworks().get(router, Protocol.STATIC);
Set<Prefix> oconn = _enc.getOriginatedNetworks().get(router, Protocol.CONNECTED);
boolean hasStatic = ostatic != null && ostatic.contains(pfx);
boolean hasConnected = oconn != null && oconn.contains(pfx);
ArithExpr originLength = _enc.mkInt(pfx.getPrefixLength());
if (hasStatic || hasConnected) {
BoolExpr directRoute = _enc.isRelevantFor(originLength, r);
ArithExpr newLength = _enc.mkIf(directRoute, originLength, otherLen);
result = result.addChangedVariable("PREFIX-LEN", newLength);
return result.setReturnValue(directRoute);
} else {
// Also use network statement if OSPF has a route with the correct length
SymbolicRoute rec = _enc.getBestNeighborPerProtocol(router, Protocol.OSPF);
if (rec != null) {
BoolExpr ospfRelevant = _enc.isRelevantFor(rec.getPrefixLength(), r);
ArithExpr newLength = _enc.mkIf(ospfRelevant, originLength, otherLen);
result = result.addChangedVariable("PREFIX-LEN", newLength);
return result.setReturnValue(ospfRelevant);
}
}
}
}
}
}
// Compute if the other best route is relevant for this match statement
BoolExpr acc = _enc.mkFalse();
for (PrefixRange range : ranges) {
acc = _enc.mkOr(acc, _enc.isRelevantFor(otherLen, range));
}
return result.setReturnValue(acc);
} else if (e instanceof NamedPrefixSet) {
NamedPrefixSet x = (NamedPrefixSet) e;
String name = x.getName();
RouteFilterList fl = conf.getRouteFilterLists().get(name);
return result.setReturnValue(matchFilterList(fl, other));
} else {
throw new BatfishException("TODO: match prefix set: " + e);
}
}
use of com.microsoft.z3.ArithExpr in project batfish by batfish.
the class TransferSSA method relateVariables.
/*
* Relate the symbolic control plane route variables
*/
private BoolExpr relateVariables(TransferParam<SymbolicRoute> p, TransferResult<BoolExpr, BoolExpr> result) {
ArithExpr defaultLen = _enc.mkInt(_enc.defaultLength());
ArithExpr defaultAd = _enc.defaultAdminDistance(_conf, _proto, p.getData());
ArithExpr defaultMed = _enc.mkInt(_enc.defaultMed(_proto));
ArithExpr defaultLp = _enc.mkInt(_enc.defaultLocalPref());
ArithExpr defaultId = _enc.mkInt(_enc.defaultId());
ArithExpr defaultMet = _enc.mkInt(_enc.defaultMetric());
// TODO: remove all isChanged calls with actual symbolic values that test for a change
boolean isIbgp = _graphEdge.isAbstract() && _proto.isBgp();
// Update prefix length when aggregation
BoolExpr len = _enc.safeEq(_current.getPrefixLength(), getOrDefault(p.getData().getPrefixLength(), defaultLen));
BoolExpr per = _enc.safeEq(_current.getPermitted(), p.getData().getPermitted());
// Only update the router id for import edges
BoolExpr id = _enc.mkTrue();
if (!_isExport) {
id = _enc.safeEq(_current.getRouterId(), getOrDefault(p.getData().getRouterId(), defaultId));
}
// Update OSPF area id
BoolExpr area;
if (p.getData().getOspfArea() == null || _iface.getOspfAreaName() == null) {
area = _enc.mkTrue();
} else {
area = _enc.safeEqEnum(_current.getOspfArea(), _iface.getOspfAreaName());
}
// Set the IGP metric accordingly
BoolExpr igpMet = _enc.mkTrue();
boolean isNonClient = _graphEdge.isAbstract() && (_enc.getGraph().peerType(_graphEdge) != Graph.BgpSendType.TO_EBGP);
boolean isClient = _graphEdge.isAbstract() && (_enc.getGraph().peerType(_graphEdge) == Graph.BgpSendType.TO_RR);
if (_graphEdge.isAbstract() && _current.getIgpMetric() != null) {
String router = _graphEdge.getRouter();
String peer = _graphEdge.getPeer();
// Case where it is a non client, we lookup the next-hop
if (isNonClient) {
EncoderSlice s = _enc.getEncoder().getSlice(peer);
SymbolicRoute r = s.getSymbolicDecisions().getBestNeighbor().get(router);
igpMet = _enc.mkEq(_current.getIgpMetric(), r.getMetric());
}
// Case where it is a client, next-hop depends on the clientId tag we added
if (isClient) {
BoolExpr acc = _enc.mkTrue();
for (Map.Entry<String, Integer> entry : _enc.getGraph().getOriginatorId().entrySet()) {
String r = entry.getKey();
Integer clientId = entry.getValue();
if (!r.equals(router)) {
EncoderSlice s = _enc.getEncoder().getSlice(r);
SymbolicRoute record = s.getSymbolicDecisions().getBestNeighbor().get(r);
BoolExpr eq = _enc.mkEq(_current.getIgpMetric(), record.getMetric());
acc = _enc.mkAnd(acc, _enc.mkImplies(p.getData().getClientId().checkIfValue(clientId), eq));
}
}
igpMet = acc;
}
}
// Set whether or not is iBGP or not on import
BoolExpr isInternal = // TODO: and !isExport?
_enc.safeEq(_current.getBgpInternal(), _enc.mkBool(isIbgp));
// Update OSPF type
BoolExpr type;
if (result.isChanged("OSPF-TYPE")) {
type = _enc.safeEqEnum(_current.getOspfType(), p.getData().getOspfType());
} else {
boolean hasAreaIface = _iface.getOspfAreaName() != null;
boolean hasArea = p.getData().getOspfArea() != null;
boolean hasType = p.getData().getOspfType() != null;
boolean areaPossiblyChanged = hasType && hasArea && hasAreaIface;
// Check if area changed
if (areaPossiblyChanged) {
BoolExpr internal = p.getData().getOspfType().isInternal();
BoolExpr same = p.getData().getOspfArea().checkIfValue(_iface.getOspfAreaName());
BoolExpr update = _enc.mkAnd(internal, _enc.mkNot(same));
BoolExpr copyOld = _enc.safeEqEnum(_current.getOspfType(), p.getData().getOspfType());
type = _enc.mkIf(update, _current.getOspfType().checkIfValue(OspfType.OIA), copyOld);
} else {
type = _enc.safeEqEnum(_current.getOspfType(), p.getData().getOspfType());
}
}
BoolExpr comms = _enc.mkTrue();
// update all community values
for (Map.Entry<CommunityVar, BoolExpr> entry : _current.getCommunities().entrySet()) {
CommunityVar cvar = entry.getKey();
BoolExpr e = entry.getValue();
BoolExpr eOther = p.getData().getCommunities().get(cvar);
// Update the communities if they should be sent
if (sendCommunity()) {
if (cvar.getType() != CommunityVar.Type.REGEX) {
comms = _enc.mkAnd(comms, _enc.mkEq(e, eOther));
}
} else {
comms = _enc.mkAnd(comms, _enc.mkNot(e));
}
}
ArithExpr otherAd = (p.getData().getAdminDist() == null ? defaultAd : p.getData().getAdminDist());
ArithExpr otherMed = (p.getData().getMed() == null ? defaultMed : p.getData().getMed());
ArithExpr otherLp = getOrDefault(p.getData().getLocalPref(), defaultLp);
ArithExpr otherMet = getOrDefault(p.getData().getMetric(), defaultMet);
// otherMet = applyMetricUpdate(otherMet);
BoolExpr ad = _enc.safeEq(_current.getAdminDist(), otherAd);
BoolExpr history = _enc.equalHistories(_current, p.getData());
BoolExpr med = _enc.safeEq(_current.getMed(), otherMed);
BoolExpr met = _enc.safeEq(_current.getMetric(), otherMet);
BoolExpr lp = _enc.safeEq(_current.getLocalPref(), otherLp);
// If this was an external route, then we need to add the correct next-hop tag
boolean isEbgpEdge = _enc.getGraph().getEbgpNeighbors().get(_graphEdge) != null;
BoolExpr cid = _enc.mkTrue();
if (_isExport && _proto.isBgp() && p.getData().getClientId() != null) {
if (isEbgpEdge) {
cid = _current.getClientId().checkIfValue(0);
} else {
cid = _enc.safeEqEnum(_current.getClientId(), p.getData().getClientId());
}
}
if (!_isExport && _proto.isBgp() && p.getData().getClientId() != null) {
BoolExpr fromExternal = p.getData().getClientId().checkIfValue(0);
BoolExpr edgeIsInternal = _enc.mkBool(!isClient && !isNonClient);
BoolExpr copyOver = _enc.safeEqEnum(_current.getClientId(), p.getData().getClientId());
Integer x = _enc.getGraph().getOriginatorId().get(_graphEdge.getRouter());
SymbolicOriginatorId soid = _current.getClientId();
BoolExpr setNewValue = (x == null ? soid.checkIfValue(0) : soid.checkIfValue(x));
cid = _enc.mkIf(_enc.mkAnd(fromExternal, edgeIsInternal), setNewValue, copyOver);
}
BoolExpr updates = _enc.mkAnd(per, len, ad, med, lp, met, id, cid, type, area, comms, history, isInternal, igpMet);
BoolExpr noOverflow = noOverflow(otherMet, _proto);
return _enc.mkIf(noOverflow, updates, _enc.mkNot(_current.getPermitted()));
}
Aggregations