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_nary_union(self): loc1 = nc.inport(1, 1) loc2 = nc.inport(2, 2) loc3 = nc.inport(3, 3) union = nc.nary_union([loc1, loc2, loc3]) self.assertTrue(union.match(blank_packet, (1,1))) self.assertTrue(union.match(blank_packet, (2,2))) self.assertTrue(union.match(blank_packet, (3,3))) self.assertFalse(union.match(blank_packet, (-1,-1)))
def test_prim_phys(self): switch_map = {1: 100} port_map = {(1,2): (100, 200), (1,3): (100, 300)} pred = nc.inport(1, 2) expected_pred = nc.inport(100, 200) action = nc.Action(1, ports=[2]) expected_action = nc.Action(100, ports=[200]) policy = pred |then| action expected_policy = expected_pred |then| expected_action phys_policy = policy.get_physical_rep(switch_map, port_map) self.assertEqual(expected_policy, phys_policy)
def test_difference_match(self): loc = nc.inport(1,1) field = nc.Header({'srcmac': 1}) diff = loc - field self.assertTrue(diff.match(fields_neg, (1, 1))) self.assertFalse(diff.match(fields, (-1, -1))) self.assertFalse(diff.match(fields, (1, 1))) self.assertFalse(diff.match(fields_neg, (-1, -1)))
def test_intersection_match(self): loc = nc.inport(1,1) field = nc.Header({'srcmac': 1}) inter = loc & field self.assertFalse(inter.match(fields_neg, (1, 1))) self.assertFalse(inter.match(fields, (-1, -1))) self.assertTrue(inter.match(fields, (1, 1))) self.assertFalse(inter.match(fields_neg, (-1, -1)))
def test_union_match(self): loc = nc.inport(1,1) field = nc.Header({'srcmac': 1}) union = loc + field self.assertTrue(union.match(blank_packet, (1, 1))) self.assertTrue(union.match(fields_neg, (1, 1))) self.assertTrue(union.match(fields, (-1, -1))) self.assertTrue(union.match(fields, (1, 1))) self.assertFalse(union.match(fields_neg, (-1, -1)))
def test_intersect_headers(self): hd = nc.Header bot = nc.Bottom() pairs = [ (bot, exact_header, nega_header), (bot, exact_header, hd({'srcmac': -1})), (bot, exact_header, zero_header), (exact_header, exact_header, empty_header), (bot, hd({'port': 1}), hd({'port': 2})), (bot, inport(1,1), inport(2,2)), (bot, hd({'switch': 1}), hd({'switch':2})), (hd({}), hd({}), hd({})), (hd({'switch': 1}), hd({'switch': 1}), hd({})), (hd({'switch': 1}), hd({'switch': 1}), hd({'switch': 1})), (hd({'port': 1}), hd({'port': 1}), hd({})), (hd({'port': 1}), hd({'port': 1}), hd({'port': 1})), (inport(1, 1), hd({'switch': 1}), hd({'port': 1})), ] for expected, h1, h2 in pairs: self.assertEqual(expected, nc.intersect_headers(h1, h2)) self.assertEqual(expected, nc.intersect_headers(h2, h1))
def test_header_loc(self): switch = 3 port = 7 header = nc.inport(switch, port) self.assertTrue(header.match(blank_packet, (switch, port))) self.assertFalse(header.match(blank_packet, (switch+1, port))) self.assertFalse(header.match(blank_packet, (switch, port+1))) self.assertFalse(header.match(blank_packet, (switch+1, port+1))) switch_only = nc.Header({'switch': switch}) self.assertTrue(switch_only.match(blank_packet, (switch, 3234))) self.assertFalse(switch_only.match(blank_packet, (switch+1, 3234))) port_only = nc.Header({'port': port}) self.assertTrue(port_only.match(blank_packet, (2345, port))) self.assertFalse(port_only.match(blank_packet, (2345, port+1)))
def internal_policy(topo, policy, symm_vlan): """Produce a list of policies for slic restricted to its vlan. This policy must also carry the vlan tags through the network as they change. These policies handle all and only packets incident to switches over an internal edge, and includes stripping vlan tags off them as they leave the network. ARGS: policy: Policy to restrict vlan: {edge -> vlan}, symmetric RETURNS: a new policy object that is policy but restricted to its vlan. """ # print '=========================' # print '=========================' # print policy # print '----' policies = [] # incoming edge to s1. Note that we only get internal edges for ((s1, p1), (s2, p2)), tag in symm_vlan.items(): if s1 in topo.node: pred = (nc.inport(s1, p1) & nc.Header({'vlan': tag})).reduce() # For each outgoing edge from s1, set vlan to what's appropriate for (p_out, dst) in topo.node[s1]['port'].items(): if ((s1, p_out), dst) in symm_vlan: target_vlan = symm_vlan[((s1, p_out), dst)] else: target_vlan = 0 # print '%s:%s on %s -> %s on %s' % (s1, p1, tag, p_out, target_vlan) # print 'restrict with %s' % pred new_policy = (policy % pred).reduce() # print new_policy new_policy = modify_vlan_local(new_policy, (s1, p_out), target_vlan, this_port_only=True) new_policy = new_policy.reduce() # print 'vvv' # print new_policy # print '----' # modify_vlan_local does not create any new reduceables policies.append(new_policy) return policies
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_header_phys(self): port_map = {(1,2):(5,10)} switch_map = {1:5} loc = nc.inport(1, 2) phys = loc.get_physical_predicate(switch_map, port_map) self.assertEquals(5, phys.fields['switch']) self.assertEquals(10, phys.fields['port']) loc = nc.Header({'switch': 1}) phys = loc.get_physical_predicate(switch_map, port_map) self.assertEquals(5, phys.fields['switch']) self.assertNotIn('port', phys.fields) loc = nc.Header({'port': 2}) self.assertRaises(nc.PhysicalException, loc.get_physical_predicate, switch_map, port_map) fields = nc.Header({'srcmac': 3}) phys = fields.get_physical_predicate(switch_map, port_map) self.assertEquals(fields, phys)
def multicast(topo, multicast_field='dstmac', multicast_value=0): """Construct a policy that multicasts packets to all nodes along the MST. Uses multicast_field:multicast_value to recognize multicast packets to send along the minimum spanning tree. """ mst = nx.minimum_spanning_tree(topo) edges = set(mst.edges()) for (n1, n2) in list(edges): edges.add((n2, n1)) policies = [] for node in topo.switches(): ports = set() for switch, port in topo.node[node]['ports'].items(): # If this link is in the MST if (node, switch) in edges: ports.add(port) for port in ports: others = ports.difference([port]) policies.append(inport(node, port) |then| Action(node, others)) return (nary_policy_union(policies) % Header({multicast_field: multicast_value}))
def flood_observe(topo, label=None, all_ports=False): """Construct a policy that floods packets and observes at the leaves. Sequentially assigns labels if none is set. Not thread-safe. """ if label is None: global next_label label = next_label next_label += 1 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|\ Action(switch, ports=[other_port], obs=[label]) policies.append(pol) return nary_policy_union(policies).reduce()
a new policy object that is policy but restricted to its vlan. """ vlan_predicate = nc.Header({'vlan': vlan}) return policy % vlan_predicate def external_predicate((switch, port), predicate): """Produce a predicate that matches predicate incoming on (switch, port). ARGS: (switch, port): precise network location of incoming port predicate: isolation predicate to satisfy RETURNS: Predicate object matching predicate on switch and port. """ return nc.inport(switch, port) & predicate def modify_vlan(policy, vlan): """Re-write all actions of policy to set vlan to vlan.""" if isinstance(policy, nc.BottomPolicy): # Bottom policy, contains no actions return policy elif isinstance(policy, nc.PrimitivePolicy): policy = copy.deepcopy(policy) for action in policy.actions: action.modify['vlan'] = vlan return policy elif isinstance(policy, nc.PolicyUnion): left = modify_vlan(policy.left, vlan) right = modify_vlan(policy.right, vlan) return left + right
def testOutputOverlap(self): p1 = nc.inport(0, 1) |then| nc.Action(0, [2], {'vlan': 0, 'srcmac': 1}) p2 = nc.inport(0, 1) |then| nc.Action(0, [2], {'vlan': 0, 'srcmac': 1}) self.assertIsNotNone(sat.shared_outputs(p1, p2))
# Tests for compile.py, which implements the slice compiler # ################################################################################ import compile as cp import copy import netcore as nc from netcore import Action, inport, Header, then import slicing import unittest a1 = Action(1, [1, 2, 3], {'srcmac':1}) a2 = Action(2, [2, 3], {'dstmac':2}, set([1, 2, 3])) a3 = Action(3, [3], {'ethtype':3}) a4 = Action(3, [4, 5, 6], {'srcip':4}) a5 = Action(3, [3, 5, 6, 7], {'vlan':5}) p1 = inport(1, 0) p2 = inport(2, 1) + inport(2, 3) p3 = inport(3, 3) & Header({'srcmac': 1}) p4 = p3 - Header({'dstmac': 2}) l1 = p1 |then| a1 l2 = p2 |then| a2 l3 = p3 |then| a3 l4 = p4 |then| a4 l5 = (p1 & p4) |then| a5 big_policy = ((l3 + l4 + l5) % p2) + l2 + l1 def actions_of_policy(policy): if isinstance(policy, nc.PrimitivePolicy): return policy.actions