def allocate_subnets(network, address_block=IPNetwork("10.0.0.0/8")): """Allocates subnets and IP addresses to links in the network. Args: address_block (IPNetwork): The address block to use. Returns: ip_as_allocs Example usage: >>> network = ank.example_multi_as() >>> allocate_subnets(network) >>> print ank.debug_nodes(network.graph, "lo_ip") {'1a.AS1': IPNetwork('10.0.0.32/32'), '1b.AS1': IPNetwork('10.0.0.33/32'), '1c.AS1': IPNetwork('10.0.0.34/32'), '2a.AS2': IPNetwork('10.1.0.64/32'), '2b.AS2': IPNetwork('10.1.0.65/32'), '2c.AS2': IPNetwork('10.1.0.66/32'), '2d.AS2': IPNetwork('10.1.0.67/32'), '3a.AS3': IPNetwork('10.2.0.0/32')} >>> print ank.debug_edges(network.graph, "ip") {('1a.AS1', '1b.AS1'): IPAddress('10.0.0.10'), ('1a.AS1', '1c.AS1'): IPAddress('10.0.0.22'), ('1b.AS1', '1a.AS1'): IPAddress('10.0.0.9'), ('1b.AS1', '1c.AS1'): IPAddress('10.0.0.26'), ('1b.AS1', '3a.AS3'): IPAddress('10.0.0.17'), ('1c.AS1', '1a.AS1'): IPAddress('10.0.0.21'), ('1c.AS1', '1b.AS1'): IPAddress('10.0.0.25'), ('1c.AS1', '2a.AS2'): IPAddress('10.0.0.29'), ('2a.AS2', '1c.AS1'): IPAddress('10.0.0.30'), ('2a.AS2', '2b.AS2'): IPAddress('10.1.0.10'), ('2a.AS2', '2d.AS2'): IPAddress('10.1.0.26'), ('2b.AS2', '2a.AS2'): IPAddress('10.1.0.9'), ('2b.AS2', '2c.AS2'): IPAddress('10.1.0.18'), ('2c.AS2', '2b.AS2'): IPAddress('10.1.0.17'), ('2c.AS2', '2d.AS2'): IPAddress('10.1.0.30'), ('2d.AS2', '2a.AS2'): IPAddress('10.1.0.25'), ('2d.AS2', '2c.AS2'): IPAddress('10.1.0.29'), ('2d.AS2', '3a.AS3'): IPAddress('10.1.0.33'), ('3a.AS3', '1b.AS1'): IPAddress('10.0.0.18'), ('3a.AS3', '2d.AS2'): IPAddress('10.1.0.34')} >>> print ank.debug_edges(network.graph, "sn") {('1a.AS1', '1b.AS1'): IPNetwork('10.0.0.8/30'), ('1a.AS1', '1c.AS1'): IPNetwork('10.0.0.20/30'), ('1b.AS1', '1a.AS1'): IPNetwork('10.0.0.8/30'), ('1b.AS1', '1c.AS1'): IPNetwork('10.0.0.24/30'), ('1b.AS1', '3a.AS3'): IPNetwork('10.0.0.16/30'), ('1c.AS1', '1a.AS1'): IPNetwork('10.0.0.20/30'), ('1c.AS1', '1b.AS1'): IPNetwork('10.0.0.24/30'), ('1c.AS1', '2a.AS2'): IPNetwork('10.0.0.28/30'), ('2a.AS2', '1c.AS1'): IPNetwork('10.0.0.28/30'), ('2a.AS2', '2b.AS2'): IPNetwork('10.1.0.8/30'), ('2a.AS2', '2d.AS2'): IPNetwork('10.1.0.24/30'), ('2b.AS2', '2a.AS2'): IPNetwork('10.1.0.8/30'), ('2b.AS2', '2c.AS2'): IPNetwork('10.1.0.16/30'), ('2c.AS2', '2b.AS2'): IPNetwork('10.1.0.16/30'), ('2c.AS2', '2d.AS2'): IPNetwork('10.1.0.28/30'), ('2d.AS2', '2a.AS2'): IPNetwork('10.1.0.24/30'), ('2d.AS2', '2c.AS2'): IPNetwork('10.1.0.28/30'), ('2d.AS2', '3a.AS3'): IPNetwork('10.1.0.32/30'), ('3a.AS3', '1b.AS1'): IPNetwork('10.0.0.16/30'), ('3a.AS3', '2d.AS2'): IPNetwork('10.1.0.32/30')} """ LOG.debug("Allocating subnets") # Initialise IP list to be graph edge format ip_as_allocs = {} # allocates subnets to the edges and loopback in network graph # Put into dictionary, indexed by ASN (the name attribute of each as graph) # for easy appending of eBGP links asgraphs = dict((my_as.asn, my_as) for my_as in ank.get_as_graphs(network)) # Simple method: break address_block into a /16 for each network #TODO: check this is feasible - ie against required host count subnet_list = address_block.subnet(16) ebgp_edges = ank.ebgp_edges(network) visited_ebgp_edges = set() for src, dst in sorted(ebgp_edges): # Add the dst (external peer) to AS of src node so they are allocated # a subnet. (The AS choice is arbitrary) if (dst, src) in visited_ebgp_edges: continue src_as = asgraphs[src.asn] src_as.add_edge(src, dst) # record for DNS purposes ank.dns_advertise_link(src, dst) visited_ebgp_edges.add( (src, dst)) for my_as in sorted(asgraphs.values(), key = lambda x: x.asn): asn = my_as.asn as_subnet = subnet_list.next() as_internal_nodes = [n for n in sorted(my_as.nodes()) if network.asn(n) == asn] host_count = my_as.number_of_nodes() # record this subnet ip_as_allocs[my_as.asn] = as_subnet # split into subnets for loopback and ptp ptp_count = my_as.number_of_edges() # Now subnet network into subnets of the larger of these two # TODO tidy up this comment # Note ptp subnets required a /30 ie 4 ips req_sn_count = max(host_count, 4*ptp_count) if req_sn_count == 0: # Nothing to allocate for this AS continue req_pref_len = int(32 - math.ceil(math.log(req_sn_count, 2)) ) # Subnet as subnet into subnets of this size sn_iter = as_subnet.subnet(req_pref_len) # And allocate a subnet for each ptp and loopback if ptp_count > 0: # Don't allocate a ptp subnet if there are no ptp links ptp_subnet = sn_iter.next() loopback_subnet = sn_iter.next() if ptp_count > 0: link_subnet = ptp_subnet.subnet(30) # Now iterate over edges in this as and apply for src, dst in sorted(my_as.edges()): # Note we apply this back to the main graph # not to the as graph! subnet = link_subnet.next() #TODO: fix the technique for accessing edges # as it breaks with multigraphs, as it creates a new edge if network.asn(dst) != asn: # eBGP link where dst has IP allocated from subnet of this AS network.graph[dst][src]['remote_as_sn_block'] = True network.graph[src][dst]['sn'] = subnet network.graph[dst][src]['sn'] = subnet # allocate an ip to each end network.graph[src][dst]['ip'] = subnet[1] network.graph[dst][src]['ip'] = subnet[2] # Allocate an loopback interface to each router #TODO: check if next step is necessary loopback_ips = loopback_subnet.subnet(32) for rtr in sorted(as_internal_nodes): lo_ip = loopback_ips.next() network.graph.node[rtr]['lo_ip'] = lo_ip network.ip_as_allocs = ip_as_allocs