def transfer(topo, p_out, p_in): """Build constraint for moving p_out to p_in across an edge.""" options = [] for s1, s2 in topo.edges(): p1 = topo.node[s1]["ports"][s2] p2 = topo.node[s2]["ports"][s1] # Need both directions because topo.edges() only gives one direction for # undirected graphs. constraint1 = And(And(switch(p_out) == s1, port(p_out) == p1), And(switch(p_in) == s2, port(p_in) == p2)) constraint2 = And(And(switch(p_out) == s2, port(p_out) == p2), And(switch(p_in) == s1, port(p_in) == p1)) options.append(constraint1) options.append(constraint2) forward = nary_or(options) # We also need to ensure that the rest of the packet is the same. Without # this, packet properties can change in flight. header_constraints = [] for f in HEADERS: if f is not "switch" and f is not "port": header_constraints.append(HEADER_INDEX[f](p_out) == HEADER_INDEX[f](p_in)) # header_constraints is never empty return And(forward, nary_and(header_constraints))
def one_per_edge(topo, pol, field="vlan"): """Determine if pol only uses one value of field on each internal edge. We don't care about external edges because they never have the problem of preventing tiling of two-node connectivity since this slice never reads packets sent to them. """ p, pp, q, qq = Consts("p pp q qq", Packet) r, rr, s, ss = Consts("r rr s ss", Packet) solv = Solver() solv.add(forwards(pol, p, pp)) solv.add(transfer(topo, pp, r)) solv.add(forwards(pol, r, rr)) solv.add(forwards(pol, q, qq)) solv.add(switch(pp) == switch(qq)) solv.add(port(pp) == port(qq)) solv.add(HEADER_INDEX[field](pp) != HEADER_INDEX[field](qq)) if solv.check() == unsat: return None else: return solv.model(), (p, pp), HEADER_INDEX