def testHostsCompileSmall(self): topo, combined = linear_hosts((0, 1, 2, 3), (0, 1, 2, 3)) slices = [s for s, _ in combined] policy = ((nc.inport(3, 3) |then| nc.forward(3, 1)) + (nc.inport(3, 3) |then| nc.forward(3, 2)) + (nc.inport(2, 3) |then| nc.forward(2, 2))) combined = [(slices[0], policy), (slices[1], nc.BottomPolicy())] compiled, _ = ec.compile_slices(topo, combined) self.assertIsNone(sat.one_per_edge(topo, compiled)) self.assertTrue(sat.compiled_correctly(topo, policy, compiled))
def test_forwards(self): o = Header({'switch': 2}) |then| Action(2, [1]) r = Header({'switch': 2}) |then| Action(2, [1]) self.assertIsNone(sat.simulates_forwards(topo, o, r)) self.assertIsNone(sat.simulates_forwards(topo, r, o)) o = Header({'switch': 2, 'port': 2}) |then| Action(2, [1]) r = Header({'switch': 2, 'port': 2}) |then| Action(2, [1]) self.assertIsNone(sat.simulates_forwards(topo, o, r)) self.assertIsNone(sat.simulates_forwards(topo, r, o)) o = Header({'switch': 2, 'port': 2}) |then| Action(2, [1]) r = Header({'switch': 2, 'port': 2, 'vlan': 2}) |then| Action(2, [1]) self.assertIsNone(sat.simulates_forwards(topo, o, r)) self.assertIsNone(sat.simulates_forwards(topo, r, o)) o = Header({'switch': 2, 'port': 2}) |then| forward(2, 1) r = Header({'switch': 2, 'port': 2, 'vlan': 2}) |then| Action(2, [1], {'vlan': 2}) self.assertIsNone(sat.simulates_forwards(topo, o, r)) self.assertIsNone(sat.simulates_forwards(topo, r, o)) o = Header({'switch': 0, 'port': 1}) |then| Action(0, [1]) r = Header({'switch': 0, 'port': 1, 'vlan': 1}) |then| Action(0, [1], {'vlan': 1}) self.assertIsNone(sat.simulates_forwards(topo, o, r)) self.assertIsNone(sat.simulates_forwards(topo, r, o)) o = Header({'switch': 0, 'port': 1, 'srcmac': 32432, 'dstmac': 324322}) |then| Action(0, [1]) r = Header({'switch': 0, 'port': 1, 'srcmac': 32432, 'dstmac': 324322, 'vlan': 1}) |then| Action(0, [1], {'vlan': 1}) self.assertIsNone(sat.simulates_forwards(topo, o, r)) self.assertIsNone(sat.simulates_forwards(topo, r, o))
def all_pairs_shortest_path(topo, hosts_only=False): """Construct all-pairs-shortest-path routing policy. Constructs an all-pairs shortest path routing policy for topo, using each switch or host id number as the source and destination mac address. Only make paths to hosts if hosts_only RETURNS: a policy that implements all-pairs shortest path """ forwarding_trees = [] for source in (topo.hosts() if hosts_only else topo.nodes()): # For each node, build the shortest paths to that node # We 'start' at the node because networkx forces us to. paths = nx.shortest_path(topo, source=source) # Build next-hop table next_hops = {} for dest, path in paths.items(): # path is a list, starting at source and ending at dest. if dest is not source and topo.node[dest]['isSwitch']: next_hops[dest] = path[-2] policies = [] for node, next_node in next_hops.items(): out_port = topo.node[node]['ports'][next_node] policies.append(Header({'switch': node}) |then| forward(node, out_port)) forwarding_trees.append(nary_policy_union(policies) % Header({'dstmac': source})) return nary_policy_union(forwarding_trees)
def test_one_per_edge(self): topo = nxtopo.NXTopo() topo.add_switch(1) topo.add_switch(2) topo.add_switch(3) topo.add_switch(4) topo.add_switch(5) topo.add_link(1, 2) topo.add_link(2, 3) topo.add_link(3, 4) topo.add_link(4, 5) topo.finalize() r = (Header({'switch': 2, 'port': 1, 'vlan': 2}) |then| forward(2, 2))+\ (Header({'switch': 3, 'port': 1, 'vlan': 2}) |then| forward(3, 2)) self.assertIsNone(sat.one_per_edge(topo, r)) self.assertIsNotNone(sat.one_per_edge(topo, r, field='srcmac')) r = (Header({'switch': 2, 'port': 1, 'vlan': 1}) |then| forward(2, 2))+\ (Header({'switch': 3, 'port': 1, 'vlan': 1}) |then| forward(3, 2))+\ (Header({'switch': 3, 'port': 1, 'vlan': 2}) |then| forward(3, 2))+\ (Header({'switch': 4, 'port': 1, 'vlan': 2}) |then| forward(4, 2)) self.assertIsNotNone(sat.one_per_edge(topo, r)) self.assertIsNotNone(sat.one_per_edge(topo, r, field='srcmac'))
def flood(topo, all_ports=False): """Construct a policy that floods packets out each port on each switch. if all_ports is set, even forward back out the port it came in. """ switches = topo.switches() policies = [] for switch in switches: ports = set(topo.node[switch]['port'].keys()) for port in ports: # Make a copy of ports without this one if all_ports: other_ports = ports else: other_ports = ports.difference([port]) for other_port in other_ports: pol = inport(switch, port) |then| forward(switch, other_port) policies.append(pol) return nary_policy_union(policies).reduce()
def test_compiled_correctly(self): o = Header({'switch': 2, 'port': 2, 'vlan': 2}) |then| Action(2, [1]) r = Header({'switch': 2, 'port': 2, 'vlan': 2}) |then| Action(2, [1]) self.assertTrue(sat.compiled_correctly(topo, o, r)) o = Header({'switch': 2, 'port': 2}) |then| Action(2, [1]) r = Header({'switch': 2, 'port': 2, 'vlan': 2}) |then| Action(2, [1]) self.assertTrue(sat.compiled_correctly(topo, o, r)) o = Header({'switch': 2, 'port': 2}) |then| forward(2, 1) r = Header({'switch': 2, 'port': 2, 'vlan': 2})\ |then| Action(2, [1], {'vlan': 2}) self.assertTrue(sat.compiled_correctly(topo, o, r)) o = Header({'switch': 1, 'port': 1}) |then| Action(1, [1]) r = Header({'switch': 1, 'port': 1, 'vlan': 1}) |then|\ Action(1, [1], {'vlan': 1}) self.assertTrue(sat.compiled_correctly(topo, o, r)) o = Header({'switch': 1, 'port': 1, 'srcmac': 33, 'dstmac': 32})\ |then| Action(1, [1]) r = Header({'switch': 1, 'port': 1, 'srcmac': 33, 'dstmac': 32, 'vlan': 1})\ |then| Action(1, [1], {'vlan': 1}) self.assertTrue(sat.compiled_correctly(topo, o, r))
def test_simulates_forwards2(self): o = (Header({'switch': 2, 'port': 1}) |then| forward(2, 2))+\ (Header({'switch': 3, 'port': 1}) |then| forward(3, 2)) r = (Header({'switch': 2, 'port': 1, 'vlan': 2}) |then| forward(2, 2))+\ (Header({'switch': 3, 'port': 1, 'vlan': 2}) |then| forward(3, 2)) self.assertIsNone(sat.simulates_forwards2(topo, o, r)) self.assertIsNotNone(sat.simulates_forwards2(topo, o, r, field='srcmac')) o = (Header({'switch': 2, 'port': 1, 'vlan': 1}) |then| forward(2, 2))+\ (Header({'switch': 3, 'port': 1, 'vlan': 1}) |then| forward(3, 2)) r = (Header({'switch': 2, 'port': 1, 'vlan': 2}) |then| forward(2, 2))+\ (Header({'switch': 3, 'port': 1, 'vlan': 2}) |then| forward(3, 2)) self.assertIsNone(sat.simulates_forwards2(topo, o, r)) self.assertIsNotNone(sat.simulates_forwards2(topo, o, r, field='srcmac')) # This is the corner case that demonstrates that we need to restrict # compiled policies to only one vlan per slice. o = (Header({'switch': 2, 'port': 1}) |then| forward(2, 2))+\ (Header({'switch': 3, 'port': 1}) |then| forward(3, 2))+\ (Header({'switch': 4, 'port': 1}) |then| forward(4, 2)) r = (Header({'switch': 2, 'port': 1, 'vlan': 1}) |then| forward(2, 2))+\ (Header({'switch': 3, 'port': 1, 'vlan': 1}) |then| forward(3, 2))+\ (Header({'switch': 3, 'port': 1, 'vlan': 2}) |then| forward(3, 2))+\ (Header({'switch': 4, 'port': 1, 'vlan': 2}) |then| forward(4, 2)) self.assertIsNone(sat.simulates_forwards(topo, o, r)) # NOTE: We would really like this to be a failure, but it isn't. # Therefore, for compiler correctness, we also need one vlan per edge. self.assertIsNone(sat.simulates_forwards2(topo, o, r)) self.assertIsNotNone(sat.simulates_forwards2(topo, o, r, field='srcmac')) # And verify that the compilation test finds this failure self.assertFalse(sat.compiled_correctly(topo, o, r))