def test_addr_assignment(self): """Test correct assignment of Docker host addresses to border router interfaces.""" net = HostNetwork("host_network", ipaddress.ip_network("10.0.0.0/24")) hosts = [LocalHost(), LocalHost()] asys = [AS(host, False) for host in hosts] net.set_host_ip(hosts[0], ipaddress.ip_address("10.0.0.10")) net.set_host_ip(hosts[1], ipaddress.ip_address("10.0.0.11")) with self.assertRaises(errors.NotAvailable): net.assign_br_address(ISD_AS("1-ff00:0:000"), asys[0], IfId(1), pref_ip=ipaddress.ip_address("10.0.0.2")) ip, port = net.assign_br_address(ISD_AS("1-ff00:0:000"), asys[0], IfId(1)) self.assertEqual(ip, ipaddress.ip_address("10.0.0.10")) self.assertEqual(port, L4Port(50000)) ip, port = net.assign_br_address(ISD_AS("1-ff00:0:001"), asys[1], IfId(1)) self.assertEqual(ip, ipaddress.ip_address("10.0.0.11")) self.assertEqual(port, L4Port(50000)) ip, port = net.assign_br_address(ISD_AS("1-ff00:0:001"), asys[1], IfId(2)) self.assertEqual(ip, ipaddress.ip_address("10.0.0.11")) self.assertEqual(port, L4Port(50001))
def test_ifid_assignment(self): self.assertEqual(self.asys.get_unused_ifid(), 1) self.asys.border_routers[1].links[IfId(1)] = Link( LinkEp("1-ff00:0:110-br2#1"), LinkEp("1-ff00:0:112#1"), LinkType.CHILD) self.assertEqual(self.asys.get_unused_ifid(), 3) self.asys.border_routers[1].links[IfId(3)] = Link( LinkEp("1-ff00:0:110-br2#1"), LinkEp("1-ff00:0:113#1"), LinkType.CHILD) self.assertEqual(self.asys.get_unused_ifid(), 6)
def setUp(self): asys = AS(LocalHost(), True) asys.border_routers = [ BorderRouter(1), BorderRouter(2) ] asys.border_routers[0].links[IfId(2)] = Link(LinkEp("1-ff00:0:110-br1#2"), LinkEp("2-ff00:0:210#1"), LinkType.CORE) asys.border_routers[0].links[IfId(4)] = Link(LinkEp("1-ff00:0:110-br1#4"), LinkEp("2-ff00:0:211#1"), LinkType.CORE) asys.border_routers[1].links[IfId(5)] = Link(LinkEp("1-ff00:0:110-br2#5"), LinkEp("1-ff00:0:111#1"), LinkType.CHILD) self.asys = asys
def pick_unused_ifid(ifids: List[IfId]) -> IfId: """Returns the smallest interface ID not in `ifids`. :param ifids: List of ifids in ascending order. """ if len(ifids) == 0: return IfId(FIRST_IFID) else: for i, ifid in enumerate(ifids): if (i + FIRST_IFID) != ifid: return IfId(i + FIRST_IFID) else: return IfId(ifids[-1] + 1)
def __init__(self, topo_file: MutableMapping[str, Any]): """Initializes used interface identifier sets from the given topology file.""" self.used_ifids: DefaultDict[ISD_AS, List[IfId]] = defaultdict(list) for link in topo_file['links']: for ep in [LinkEp(link['a']), LinkEp(link['b'])]: if ep.ifid: self.used_ifids[ep].append(IfId(ep.ifid)) for ifids in self.used_ifids.values(): ifids.sort()
def test_br_addr_assignment(self): """Test assignment of (IP, port) tuples to border router interfaces.""" br = DockerBridge("test", LocalHost(), ipaddress.IPv4Network("10.0.0.0/29")) asys = AS(LocalHost(), False) # Assign BR interface addresses for _ in range(2): # Multiple calls must return the same addresses for ifid in range(1, 3): for as_id in range(1, 6): ip, port = br.assign_br_address(ISD_AS(as_id), asys, IfId(ifid)) self.assertEqual( ip, ipaddress.IPv4Address(0x0A000000 + as_id + 1)) self.assertEqual(port, 50000 + ifid - 1) with self.assertRaises(errors.OutOfResources): br.assign_br_address(ISD_AS(8), asys, IfId(1)) # AS IP assignment for as_id in range(1, 6): isd_as = ISD_AS(as_id) self.assertEqual(br.get_ip_address(isd_as), br.assign_ip_address(isd_as)) # Retrieve BR interface underlay addresses for ifid in range(1, 3): for asys in range(1, 6): ip, port = unwrap(br.get_br_address(ISD_AS(asys), IfId(ifid))) self.assertEqual(ip, ipaddress.IPv4Address(0x0A000000 + asys + 1)) self.assertEqual(port, 50000 + ifid - 1) # Free BR interfaces for asys in range(1, 6): for ifid in range(1, 3): self.assertEqual(br.free_br_address(ISD_AS(asys), IfId(ifid)), 3 - ifid) # Check whether IPs are still bound for asys in range(1, 6): ip = br.get_ip_address(ISD_AS(asys)) self.assertEqual(ip, ipaddress.IPv4Address(0x0A000000 + asys + 1))
def add_link_ep(self, link_ep: LinkEp, link: Link) -> None: """Assign the given link to a `BorderRouter` object identified by `link_ep`. This function assigns IDs to border routers in the same way as the SCION topology generator does: IDs are assigned consecutively starting from 1. Border routers, which have not been given a name in the topology file have a single interface, i.e., a new BR is created with the next available ID for every link added to the AS. Border routers that have a name can have multiple interfaces, and are given an ID the first time they are encountered. Border routers are named in the topology definition by inserting a name between the ISD-AS identifer and the interface ID. For example, "1-ff00:0:110-test#1" names interface "1" of a BR called "test" in ASff00:0:110. """ unnamed_brs, named_brs = self._border_routers[link_ep] nextId = len(unnamed_brs) + len(named_brs) + 1 br_name = link_ep.br_name() if br_name: if br_name not in named_brs: named_brs[br_name] = BorderRouter(nextId) named_brs[br_name].links[IfId(link_ep.ifid)] = link else: unnamed_brs.append(BorderRouter(nextId)) unnamed_brs[-1].links[IfId(link_ep.ifid)] = link
def _update_underlay_addresses(topo_file: Mapping[str, Any], isd_as: ISD_AS, asys: AS): """Update the underlay addresses of the border router interfaces to match the given AS object. :param topo_file: Parsed topology.json file to modify. :param isd_as: AS the topology file belongs to. :param asys: AS the topology file belongs to. """ for br in asys.border_routers: br_name = br.get_name(isd_as) interfaces = topo_file['BorderRouters'][br_name]['Interfaces'] for ifid, iface in interfaces.items(): local, remote = br.links[IfId(int(ifid))].get_underlay_addresses(isd_as) iface['PublicOverlay']['Addr'] = str(local.ip) iface['PublicOverlay']['OverlayPort'] = int(local.port) iface['RemoteOverlay']['Addr'] = str(remote.ip) iface['RemoteOverlay']['OverlayPort'] = int(remote.port)
def test(self): """Test successful address assignment.""" topo = self.topo assign_underlay_addresses(topo) self.assertEqual(len(topo.bridges), 4) # link 1 net = topo.get_bridge_subnet(ipaddress.ip_network("10.0.10.0/29")) self.assertIsInstance(net, DockerBridge) self.assertEqual( net.get_br_address(ISD_AS("1-ff00:0:110"), IfId(1)), UnderlayAddress(ipaddress.ip_address("10.0.10.2"), L4Port(50000))) self.assertEqual( net.get_br_address(ISD_AS("1-ff00:0:111"), IfId(1)), UnderlayAddress(ipaddress.ip_address("10.0.10.3"), L4Port(50000))) # link 2 net = topo.get_bridge_subnet(ipaddress.ip_network("10.0.11.0/29")) self.assertIsInstance(net, DockerBridge) self.assertEqual( net.get_br_address(ISD_AS("1-ff00:0:110"), IfId(2)), UnderlayAddress(ipaddress.ip_address("10.0.11.2"), L4Port(50000))) self.assertEqual( net.get_br_address(ISD_AS("1-ff00:0:112"), IfId(1)), UnderlayAddress(ipaddress.ip_address("10.0.11.3"), L4Port(50000))) # links 3 to 5 net = topo.get_bridge_subnet(ipaddress.ip_network("10.0.20.0/24")) self.assertIsInstance(net, OvsBridge) self.assertEqual( net.get_br_address(ISD_AS("1-ff00:0:111"), IfId(2)), UnderlayAddress(ipaddress.ip_address("10.0.20.2"), L4Port(50000))) self.assertEqual( net.get_br_address(ISD_AS("1-ff00:0:111"), IfId(3)), UnderlayAddress(ipaddress.ip_address("10.0.20.2"), L4Port(50001))) self.assertEqual( net.get_br_address(ISD_AS("1-ff00:0:112"), IfId(2)), UnderlayAddress(ipaddress.ip_address("10.0.20.3"), L4Port(50000))) self.assertEqual( net.get_br_address(ISD_AS("1-ff00:0:112"), IfId(3)), UnderlayAddress(ipaddress.ip_address("10.0.20.3"), L4Port(50001))) self.assertEqual( net.get_br_address(ISD_AS("1-ff00:0:113"), IfId(1)), UnderlayAddress(ipaddress.ip_address("10.0.20.4"), L4Port(50000))) self.assertEqual( net.get_br_address(ISD_AS("1-ff00:0:113"), IfId(2)), UnderlayAddress(ipaddress.ip_address("10.0.20.4"), L4Port(50001))) # link 6 net = topo.get_bridge_subnet(ipaddress.ip_network("10.0.21.0/24")) self.assertEqual( net.get_br_address(ISD_AS("1-ff00:0:112"), IfId(4)), UnderlayAddress(ipaddress.ip_address("10.0.21.9"), L4Port(50000))) self.assertEqual( net.get_br_address(ISD_AS("1-ff00:0:113"), IfId(3)), UnderlayAddress(ipaddress.ip_address("10.0.21.10"), L4Port(50000)))
def test(self): """Test successful parse.""" topo_file = yaml.safe_load(TEST_TOPO) topo = extract_topo_info(topo_file) ASES = [ ISD_AS("1-ff00:0:110"), ISD_AS("1-ff00:0:111"), ISD_AS("1-ff00:0:112") ] self.assertIsNone(topo.coordinator) self.assertEqual(len(topo.additional_services), 0) # IPv6 self.assertFalse(topo.ipv6_enabled) # Default link subnet self.assertEqual(topo.default_link_subnet, ipaddress.ip_network("10.0.10.0/24")) # ASes self.assertEqual(len(topo.ases), 3) for asys in ASES: self.assertIn(asys, topo.ases) # IXPs self.assertIn("ixp1", topo.ixps) ixp = topo.ixps["ixp1"] for isd_as in [ISD_AS("1-ff00:0:111"), ISD_AS("1-ff00:0:112")]: self.assertIn(isd_as, ixp.ases) self.assertIs(ixp.ases[isd_as], topo.ases[isd_as]) # Networks self.assertEqual(len(topo.bridges), 2) ip_net = ipaddress.ip_network("10.0.11.0/29") self.assertIsInstance(topo.get_bridge_subnet(ip_net), DockerBridge) ip_net = ipaddress.ip_network("10.0.20.0/24") self.assertIsInstance(topo.get_bridge_subnet(ip_net), OvsBridge) # Links self.assertEqual(len(topo.links), 3) self.assertEqual(topo.links[0].ep_a, LinkEp("1-ff00:0:110#1")) self.assertEqual(topo.links[0].ep_b, LinkEp("1-ff00:0:111#1")) self.assertIsNone(topo.links[0].bridge) self.assertEqual(topo.links[1].ep_a, LinkEp("1-ff00:0:110#2")) self.assertEqual(topo.links[1].ep_b, LinkEp("1-ff00:0:112#1")) self.assertEqual( topo.links[1].bridge, topo.get_bridge_subnet(ipaddress.ip_network("10.0.11.0/29"))) self.assertEqual(topo.links[2].ep_a, LinkEp("1-ff00:0:111#2")) self.assertEqual(topo.links[2].ep_b, LinkEp("1-ff00:0:112#2")) self.assertEqual( topo.links[2].bridge, topo.get_bridge_subnet(ipaddress.ip_network("10.0.20.0/24"))) # BRs self.assertEqual(len(topo.ases[ASES[0]].border_routers), 2) self.assertEqual(len(topo.ases[ASES[1]].border_routers), 2) self.assertEqual(len(topo.ases[ASES[2]].border_routers), 1) # br1-ff00_0_110-1 br = topo.ases[ASES[0]].border_routers[0] self.assertEqual(br.id, 1) self.assertEqual(br.get_name(ASES[0]), "br1-ff00_0_110-1") self.assertEqual(len(br.links), 1) self.assertIs(br.links[IfId(1)], topo.links[0]) # br1-ff00_0_110-2 br = topo.ases[ASES[0]].border_routers[1] self.assertEqual(br.id, 2) self.assertEqual(br.get_name(ASES[0]), "br1-ff00_0_110-2") self.assertEqual(len(br.links), 1) self.assertIs(br.links[IfId(2)], topo.links[1]) # br1-ff00_0_111-1 br = topo.ases[ASES[1]].border_routers[0] self.assertEqual(br.id, 1) self.assertEqual(br.get_name(ASES[1]), "br1-ff00_0_111-1") self.assertEqual(len(br.links), 1) self.assertIs(br.links[IfId(1)], topo.links[0]) # br1-ff00_0_111-2 br = topo.ases[ASES[1]].border_routers[1] self.assertEqual(br.id, 2) self.assertEqual(br.get_name(ASES[1]), "br1-ff00_0_111-2") self.assertEqual(len(br.links), 1) self.assertIs(br.links[IfId(2)], topo.links[2]) # br1-ff00_0_112-1 br = topo.ases[ASES[2]].border_routers[0] self.assertEqual(br.id, 1) self.assertEqual(br.get_name(ASES[2]), "br1-ff00_0_112-1") self.assertEqual(len(br.links), 2) self.assertIs(br.links[IfId(1)], topo.links[1]) self.assertIs(br.links[IfId(2)], topo.links[2]) # topo_file self.assertNotIn("link_subnet", topo_file['defaults']) self.assertNotIn("IXPs", topo_file) for link in topo_file['links']: self.assertNotIn('network', link)
def test(self): """Test successful parse of a topology with multiple hosts and a coordinator.""" topo_file = yaml.safe_load(TEST_TOPO) topo = extract_topo_info(topo_file) assign_underlay_addresses(topo) # IPv6 self.assertFalse(topo.ipv6_enabled) # Default link subnet self.assertEqual(topo.default_link_subnet, ipaddress.ip_network("10.0.10.0/24")) # Hosts self.assertEqual(len(topo.hosts), 2) self.assertIsInstance(topo.hosts['localhost'], LocalHost) self.assertIsInstance(topo.hosts['host1'], RemoteHost) host = cast(RemoteHost, topo.hosts['host1']) self.assertEqual(host.name, 'host1') self.assertEqual(host.ssh_host, ipaddress.ip_address("192.168.244.3")) self.assertEqual(host._ssh_port, 22) self.assertEqual(host._username, "scion") self.assertEqual(host._identity_file, ".ssh/id_rsa") # Networks self.assertEqual(len(topo.bridges), 8) bridge = topo.get_bridge_subnet(ipaddress.ip_network("10.0.20.0/24")) self.assertIsInstance(bridge, DockerBridge) self.assertEqual(bridge.name, "bridge1") self.assertEqual( cast(DockerBridge, bridge)._host, topo.hosts['localhost']) bridge = topo.get_bridge_subnet(ipaddress.ip_network("10.0.21.0/24")) self.assertIsInstance(bridge, OvsBridge) self.assertEqual(bridge.name, "ovs_bridge1") self.assertEqual( cast(OvsBridge, bridge)._host, topo.hosts['localhost']) bridge = topo.get_bridge_subnet(ipaddress.ip_network("10.0.22.0/24")) self.assertIsInstance(bridge, OverlayNetwork) self.assertEqual(bridge.name, "overlay_bridge1") self.assertEqual( cast(OverlayNetwork, bridge)._host, topo.hosts['host1']) self.assertEqual(cast(OverlayNetwork, bridge).encrypted, True) bridge = topo.get_bridge_subnet(ipaddress.ip_network("10.0.23.0/24")) self.assertIsInstance(bridge, HostNetwork) self.assertEqual(bridge.name, "physical_network1") # Coordinator self.assertIs(topo.coordinator.bridge, topo.get_bridge_name("overlay_bridge1")) self.assertIs(topo.coordinator.host, topo.hosts['localhost']) self.assertFalse(topo.coordinator.cpu_affinity.is_unrestricted()) self.assertEqual(str(topo.coordinator.cpu_affinity), "0") expected = UnderlayAddress(ipaddress.ip_address("192.168.244.2"), L4Port(8000)) self.assertEqual(topo.coordinator.exposed_at, expected) self.assertEqual(len(topo.coordinator.users), 3) user = topo.coordinator.users['admin'] self.assertEqual(user.email, "*****@*****.**") self.assertEqual(user.password, "admin") self.assertTrue(user.is_admin) user = topo.coordinator.users['user1'] self.assertEqual(user.email, "*****@*****.**") self.assertEqual(user.password, "user1") self.assertFalse(user.is_admin) user = topo.coordinator.users['user2'] self.assertEqual(user.email, "*****@*****.**") self.assertEqual(user.password, "user2") self.assertFalse(user.is_admin) # Prometheus self.assertEqual(len(topo.additional_services), 1) prom = cast(Prometheus, topo.additional_services[0]) self.assertIs(prom.bridge, topo.get_bridge_name("overlay_bridge1")) self.assertEqual(prom.host, topo.hosts['localhost']) self.assertFalse(prom.cpu_affinity.is_unrestricted()) self.assertEqual(str(prom.cpu_affinity), "1") expected = UnderlayAddress(ipaddress.ip_address("192.168.244.2"), L4Port(9090)) self.assertEqual(prom.exposed_at, expected) self.assertEqual(prom.scrape_interval, "5s") self.assertIn(ISD_AS("1-ff00:0:110"), prom.targets) self.assertIn(ISD_AS("1-ff00:0:111"), prom.targets) self.assertIn(ISD_AS("1-ff00:0:112"), prom.targets) # ASes self.assertEqual(len(topo.ases), 8) asys = topo.ases[ISD_AS("1-ff00:0:110")] self.assertTrue(asys.is_core) self.assertFalse(asys.is_attachment_point) self.assertIs(asys.host, topo.hosts['localhost']) self.assertTrue(asys.cpu_affinity.is_unrestricted()) self.assertIsNone(asys.owner) asys = topo.ases[ISD_AS("1-ff00:0:111")] self.assertFalse(asys.is_core) self.assertTrue(asys.is_attachment_point) self.assertIs(asys.host, topo.hosts['localhost']) self.assertTrue(asys.cpu_affinity.is_unrestricted()) self.assertIsNone(asys.owner) asys = topo.ases[ISD_AS("1-ff00:0:112")] self.assertFalse(asys.is_core) self.assertFalse(asys.is_attachment_point) self.assertIs(asys.host, topo.hosts['localhost']) self.assertFalse(asys.cpu_affinity.is_unrestricted()) self.assertEqual(str(asys.cpu_affinity), "0,1,2,3") self.assertEqual(asys.owner, "user1") asys = topo.ases[ISD_AS("1-ff00:0:113")] self.assertFalse(asys.is_core) self.assertFalse(asys.is_attachment_point) self.assertIs(asys.host, topo.hosts['localhost']) self.assertFalse(asys.cpu_affinity.is_unrestricted()) self.assertEqual(str(asys.cpu_affinity), "2,3,5") self.assertEqual(asys.owner, "user1") asys = topo.ases[ISD_AS("2-ff00:0:210")] self.assertTrue(asys.is_core) self.assertFalse(asys.is_attachment_point) self.assertIs(asys.host, topo.hosts['localhost']) self.assertIsNone(asys.owner) asys = topo.ases[ISD_AS("2-ff00:0:211")] self.assertFalse(asys.is_core) self.assertTrue(asys.is_attachment_point) self.assertIs(asys.host, topo.hosts['localhost']) self.assertIsNone(asys.owner) asys = topo.ases[ISD_AS("2-ff00:0:212")] self.assertFalse(asys.is_core) self.assertFalse(asys.is_attachment_point) self.assertIs(asys.host, topo.hosts['host1']) self.assertEqual(asys.owner, "user2") asys = topo.ases[ISD_AS("2-ff00:0:213")] self.assertFalse(asys.is_core) self.assertFalse(asys.is_attachment_point) self.assertIs(asys.host, topo.hosts['host1']) self.assertEqual(asys.owner, "user2") # IXPs self.assertIn("ixp1", topo.ixps) ixp = topo.ixps["ixp1"] expected_ases = [ISD_AS("1-ff00:0:112"), ISD_AS("1-ff00:0:113")] for isd_as in expected_ases: self.assertIn(isd_as, ixp.ases) self.assertIs(ixp.ases[isd_as], topo.ases[isd_as]) self.assertIsInstance(ixp.bridge, DockerBridge) self.assertEqual(ixp.bridge.ip_network, ipaddress.ip_network("10.0.12.0/24")) self.assertIn("ixp2", topo.ixps) ixp = topo.ixps["ixp2"] expected_ases = [ ISD_AS("1-ff00:0:112"), ISD_AS("1-ff00:0:113"), ISD_AS("2-ff00:0:212"), ISD_AS("2-ff00:0:213") ] for isd_as in expected_ases: self.assertIn(isd_as, ixp.ases) self.assertIs(ixp.ases[isd_as], topo.ases[isd_as]) self.assertIsInstance(ixp.bridge, HostNetwork) self.assertIs(ixp.bridge, topo.get_bridge_name("physical_network1")) # Links self.assertEqual(len(topo.links), 13) interfaces = dict(topo.ases[ISD_AS("1-ff00:0:110")].links()) link = interfaces[IfId(1)] subnet = ipaddress.ip_network("10.0.13.0/24") self.assertIs(link.bridge, topo.get_bridge_subnet(subnet)) interfaces = dict(topo.ases[ISD_AS("1-ff00:0:111")].links()) link = interfaces[IfId(1)] self.assertTrue( link.bridge.ip_network.overlaps(topo.default_link_subnet)) link = interfaces[IfId(2)] self.assertTrue( link.bridge.ip_network.overlaps(topo.default_link_subnet)) interfaces = dict(topo.ases[ISD_AS("2-ff00:0:211")].links()) link = interfaces[IfId(1)] self.assertIs(link.bridge, topo.get_bridge_name("overlay_bridge1")) link = interfaces[IfId(2)] self.assertIs(link.bridge, topo.get_bridge_name("overlay_bridge1")) null = LinkEp() dummy_link_count = 0 for link in topo.links: if link.type == LinkType.UNSET: dummy_link_count += 1 self.assertEqual(link.ep_b, null) elif link.ep_b == null: self.assertEqual(link.type, LinkType.UNSET) self.assertNotEqual(link.ep_a, null) self.assertEqual(dummy_link_count, 6) # topo_file self.assertNotIn("link_subnet", topo_file['defaults']) self.assertNotIn("hosts", topo_file) self.assertNotIn("networks", topo_file) self.assertNotIn("coordinator", topo_file) self.assertNotIn("IXPs", topo_file) for link in topo_file['links']: self.assertNotIn('network', link)