def configure_dns(self): """Generates BIND configuration files for DNS Can check configs eg: Forward:: bash-3.2$ named-checkzone -d AS3 ank_lab/netkit_lab/AS3_l3_3_dns_1/etc/bind/db.AS3 loading "AS3" from "ank_lab/netkit_lab/AS3_l3_3_dns_1/etc/bind/db.AS3" class "IN" zone AS3/IN: loaded serial 2008080101 OK Reverse:: bash-3.2$ named-checkzone -d 0.10.in-addr.arpa ank_lab/netkit_lab/AS3_l3_3_dns_1/etc/bind/db.0.10.in-addr.arpa. loading "0.10.in-addr.arpa" from "ank_lab/netkit_lab/AS3_l3_3_dns_1/etc/bind/db.0.10.in-addr.arpa." class "IN" zone 0.10.in-addr.arpa/IN: loaded serial 2008080101 OK named:: bash-3.2$ named-checkconf ank_lab/netkit_lab/AS3_l3_3_dns_1/etc/bind/named.conf """ import netaddr ip_localhost = netaddr.IPAddress("127.0.0.1") linux_bind_dir = "/etc/bind" resolve_template = lookup.get_template("linux/resolv.mako") forward_template = lookup.get_template("bind/forward.mako") named_template = lookup.get_template("bind/named.mako") reverse_template = lookup.get_template("bind/reverse.mako") root_template = lookup.get_template("bind/root.mako") root_dns_template = lookup.get_template("bind/root_dns.mako") root_dns_named_template = lookup.get_template("bind/root_dns_named.mako") ip_as_allocs = ank.get_ip_as_allocs(self.network) dns_servers = ank.dns_servers(self.network) root_servers = list(ank.root_dns_servers(self.network)) auth_servers = ank.dns.dns_auth_servers(self.network) caching_servers = ank.dns.dns_cache_servers(self.network) clients = ank.dns.dns_clients(self.network) routers = set(self.network.routers()) #TODO: use with for opening files for server in root_servers: children = ank.dns.dns_hiearchy_children(server) child_servers = [] for child in children: advertise_block = ip_as_allocs[child.asn] reverse_identifier = ank.rev_dns_identifier(advertise_block) child_servers.append( (child.domain, reverse_identifier, ank.server_ip(child))) f_root_db = open(os.path.join(bind_dir(self.network, server), "db.root"), 'wb') f_root_db.write( root_dns_template.render( dns_servers = child_servers, server = server, )) f_named = open( os.path.join(bind_dir(self.network, server), "named.conf"), 'wb') f_named.write(root_dns_named_template.render( logging = False, )) for server in caching_servers: #root_db_hint = ( ("ns.AS%s" % n.asn, ank.server_ip(n)) for n in ank.dns_hiearchy_parents(server)) root_db_hint = ( ("ROOT-SERVER", ank.server_ip(n)) for n in root_servers) root_db_hint = list(root_db_hint) #TODO: make caching use parent rather than global root f_root = open( os.path.join(bind_dir(self.network, server), "db.root"), 'wb') f_root.write( root_template.render( root_servers = root_db_hint)) f_named = open( os.path.join(bind_dir(self.network, server), "named.conf"), 'wb') f_named.write(named_template.render( entry_list = [], bind_dir = linux_bind_dir, logging = False, )) f_named.close() for server in auth_servers: named_list = [] advertise_links = list(ank.advertise_links(server)) advertise_hosts = list(ank.dns_auth_children(server)) LOG.debug("DNS server %s advertises %s" % (server, advertise_links)) #TODO: make reverse dns handle domains other than /8 /16 /24 advertise_block = ip_as_allocs[server.asn] # remove trailing fullstop reverse_identifier = ank.rev_dns_identifier(advertise_block).rstrip(".") #TODO: look at using advertise_block.network.reverse_dns - check what Bind needs named_list.append(reverse_identifier) f_named = open( os.path.join(bind_dir(self.network, server), "named.conf"), 'wb') f_named.write(named_template.render( domain = server.domain, entry_list = named_list, bind_dir = linux_bind_dir, logging = False, )) f_named.close() for_entry_list = list( (self.interface_id(link.id), link.local_host.dns_host_portion_only, link.ip) for link in advertise_links) # Add loopbacks for routers for_entry_list += ( (self.lo_interface(0), host.dns_host_portion_only, host.lo_ip.ip) #TODO: make thise check l3 group rather than asn (generalise) for host in advertise_hosts if host.is_router and host.asn == server.asn) rev_entry_list = list( (ank.reverse_subnet(link.ip, advertise_block.prefixlen), self.interface_id(link.id), link.local_host.dns_hostname) for link in advertise_links) # Add loopbacks for routers rev_entry_list += ( (ank.reverse_subnet(host.lo_ip.ip, advertise_block.prefixlen), self.lo_interface(0), host.dns_host_portion_only) #TODO: make thise check l3 group rather than asn (generalise) for host in advertise_hosts if host.is_router and host.asn == server.asn) #TODO: provide better way to get eg eth0.host than string concat inside the template host_cname_list = [] for host in advertise_hosts: if host.asn != server.asn: # host is from another asn, skip. #TODO: extend this to make sure matches same asn, l3group and l2group continue if host.is_router: # has lo_ip cname = "%s.%s" % (self.lo_interface(), host.dns_host_portion_only) else: # choose an interface - arbitrary choice, choose first host link interface = self.interface_id(ank.server_interface_id(host)) cname = "%s.%s" % (interface, host.dns_host_portion_only) host_cname_list.append( (host.dns_host_portion_only, cname)) #Sort to make format nicer host_cname_list = sorted(host_cname_list, key = lambda x: x[1]) for_entry_list = sorted(for_entry_list) for_entry_list = sorted(for_entry_list, key = lambda x: x[1]) f_forward = open ( os.path.join(bind_dir(self.network, server), "db.%s" % server.domain), 'wb') f_forward.write(forward_template.render( domain = server.domain, entry_list = for_entry_list, host_cname_list = host_cname_list, dns_server = server.dns_hostname, dns_server_ip = ank.server_ip(server), )) f_reverse = open(os.path.join(bind_dir(self.network, server), "db.%s" % reverse_identifier), 'wb') f_reverse.write(reverse_template.render( domain = server.domain, identifier = reverse_identifier, entry_list = rev_entry_list, dns_server= server.dns_hostname, )) #TODO: make l2 use l3 for caching #TODO: ROOT-SERVER can't be part of a domain... - need to correctly handle case of multiple root servers # and also need to handle this for case of single root server (ie no hiearchy) probably ok as /etc/resolv.conf points to server itself, not through dns hints root_db_hint = ( ("ROOT-SERVER", ank.server_ip(n)) for n in ank.dns_hiearchy_parents(server)) f_root = open( os.path.join(bind_dir(self.network, server), "db.root"), 'wb') f_root.write( root_template.render( root_servers = root_db_hint)) for server in dns_servers: f_resolv = open( os.path.join(etc_dir(self.network, server), "resolv.conf"), 'wb') f_resolv.write ( resolve_template.render( nameservers = [ank.server_ip(server)], domain = server.domain)) # Configure clients for client in clients: server_ips = (ank.server_ip(server) for server in ank.dns_hiearchy_parents(client)) server_ips = list(server_ips) f_resolv = open( os.path.join(etc_dir(self.network, client), "resolv.conf"), 'wb') f_resolv.write ( resolve_template.render( nameservers = server_ips, domain = client.domain)) return
def configure(self): """Configure C-BGP""" LOG.info("Configuring C-BGP") self.initialise() default_weight = 1 template = lookup.get_template("cbgp/cbgp.mako") physical_graph = self.network.graph igp_graph = ank.igp_graph(self.network) ibgp_graph = ank.get_ibgp_graph(self.network) ebgp_graph = ank.get_ebgp_graph(self.network) as_graphs = ank.get_as_graphs(self.network) ip_as_allocs = ank.get_ip_as_allocs(self.network) # Allocs for ebgp announcements physical_topology = defaultdict(dict) ibgp_topology = {} igp_topology = {} ebgp_topology = {} ebgp_prefixes = {} bgp_routers = {} # Fast lookup of loopbacks - the unique router ID for cBGP loopback = dict( (n, self.network.lo_ip(n).ip) for n in physical_graph) # Physical topology for as_graph in as_graphs: asn = as_graph.name physical_topology[asn]['nodes'] = [loopback[n] for n in as_graph] physical_topology[asn]['links'] = [ (loopback[s], loopback[t]) for (s,t) in unidirectional(as_graph.edges())] # Interdomain links interdomain_links = [ (loopback[s], loopback[t]) for (s,t) in unidirectional(ebgp_graph.edges())] #IGP configuration for as_graph in as_graphs: asn = as_graph.name igp_topology[asn] = {} # Subgraph of IGP graph for this AS as_igp_graph = igp_graph.subgraph(as_graph.nodes()) igp_topology[asn]['nodes'] = [loopback[n] for n in as_igp_graph] igp_topology[asn]['links'] = [ (loopback[s], loopback[t], data.get('weight', default_weight)) for (s,t,data) in (as_graph.edges(data=True))] # iBGP configuration #TODO: if ibgp graph is a clique then use "bgp domain 1 full-mesh" where 1 is asn # use nx.graph_clique_number(G) and compare to size of G, if same then is a clique # otherwise create ibgp session by session #TODO: add support for non full-mesh (need to find documentation on this) for as_graph in as_graphs: asn = as_graph.name for router in as_graph: if not router.is_router: continue if router not in ibgp_graph: # likely single node AS continue ibgp_topology[router] = [] for peer in ibgp_graph.neighbors(router): ibgp_topology[router].append(peer) bgp_routers[asn] = [n.lo_ip.ip for n in ank.bgp_routers(self.network) if n.asn == asn] # eBGP configuration for node in ebgp_graph.nodes(): node_id = loopback[node] peers = [] for peer in ebgp_graph.neighbors(node): peers.append( (self.network.asn(peer), loopback[peer])) ebgp_topology[node_id] = peers # Prefixes to originate adv_subnet = ip_as_allocs[self.network.asn(node)] ebgp_prefixes[node_id] = adv_subnet #TODO: see if can just do for node in ebgp_graph ie without the .nodes() on end # bgp policy bgp_policy = {} for router in self.network.routers(): for peer in self.network.g_session.neighbors(router): pol_egress = self.network.g_session[router][peer]['egress'] pol_ingress = self.network.g_session[peer][router]['ingress'] if len(pol_ingress) or len(pol_egress): try: bgp_policy[router][peer] = { 'ingress': pol_ingress, 'egress': pol_egress, } except KeyError: bgp_policy[router] = {} bgp_policy[router][peer] = { 'ingress': pol_ingress, 'egress': pol_egress, } # tags dict for mapping from tag to community value, and for prefixes tags = self.network.g_session.graph['tags'] prefixes = self.network.g_session.graph['prefixes'] with open( cbgp_file(), 'w') as f_cbgp: f_cbgp.write( template.render( physical_topology = physical_topology, interdomain_links = interdomain_links, igp_topology = igp_topology, ibgp_topology = ibgp_topology, ebgp_topology = ebgp_topology, ebgp_prefixes = ebgp_prefixes, bgp_routers = bgp_routers, bgp_policy = bgp_policy, tags = tags, prefixes = prefixes, ))
def configure_bgp(self): """Generates BGP specific configuration files""" ip_as_allocs = ank.get_ip_as_allocs(self.network) LOG.debug("Configuring BGP") template = lookup.get_template("quagga/bgp.mako") route_maps = {} ibgp_graph = ank.get_ibgp_graph(self.network) ebgp_graph = ank.get_ebgp_graph(self.network) physical_graph = self.network.graph for my_as in ank.get_as_graphs(self.network): asn = my_as.asn LOG.debug("Configuring IGP for AS %s " % asn) # get nodes ie intersection #H = nx.intersection(my_as, ibgp_graph) # get ibgp graph that contains only nodes from this AS for router in self.network.routers(asn): bgp_groups = {} route_maps = [] ibgp_neighbor_list = [] ibgp_rr_client_list = [] route_map_groups = {} if router in ibgp_graph: for src, neigh, data in ibgp_graph.edges(router, data=True): route_maps_in = self.network.g_session[neigh][router]['ingress'] rm_group_name_in = None if len(route_maps_in): rm_group_name_in = "rm_%s_in" % neigh.folder_name route_map_groups[rm_group_name_in] = [match_tuple for route_map in route_maps_in for match_tuple in route_map.match_tuples] route_maps_out = self.network.g_session[router][neigh]['egress'] rm_group_name_out = None if len(route_maps_out): rm_group_name_in = "rm_%s_out" % neigh.folder_name route_map_groups[rm_group_name_out] = [match_tuple for route_map in route_maps_out for match_tuple in route_map.match_tuples] description = data.get("rr_dir") + " to " + ank.fqdn(self.network, neigh) if data.get('rr_dir') == 'down': ibgp_rr_client_list.append( { 'id': self.network.lo_ip(neigh).ip, 'description': description, 'route_maps_in': rm_group_name_in, 'route_maps_out': rm_group_name_out, }) elif (data.get('rr_dir') in set(['up', 'over', 'peer']) or data.get('rr_dir') is None): ibgp_neighbor_list.append( { 'id': self.network.lo_ip(neigh).ip, 'description': description, 'route_maps_in': rm_group_name_in, 'route_maps_out': rm_group_name_out, }) bgp_groups['internal_peers'] = { 'type': 'internal', 'neighbors': ibgp_neighbor_list } if len(ibgp_rr_client_list): bgp_groups['internal_rr'] = { 'type': 'internal', 'neighbors': ibgp_rr_client_list, 'cluster': self.network.lo_ip(router).ip, } if router in ebgp_graph: external_peers = [] for peer in ebgp_graph.neighbors(router): route_maps_in = self.network.g_session[peer][router]['ingress'] rm_group_name_in = None if len(route_maps_in): rm_group_name_in = "rm_%s_in" % peer.folder_name route_map_groups[rm_group_name_in] = [match_tuple for route_map in route_maps_in for match_tuple in route_map.match_tuples] # Now need to update the sequence numbers for the flattened route maps route_maps_out = self.network.g_session[router][peer]['egress'] rm_group_name_out = None if len(route_maps_out): rm_group_name_out = "rm_%s_out" % peer.folder_name route_map_groups[rm_group_name_out] = [match_tuple for route_map in route_maps_out for match_tuple in route_map.match_tuples] peer_ip = physical_graph[peer][router]['ip'] external_peers.append({ 'id': peer_ip, 'route_maps_in': rm_group_name_in, 'route_maps_out': rm_group_name_out, 'peer_as': self.network.asn(peer)}) bgp_groups['external_peers'] = { 'type': 'external', 'neighbors': external_peers} # Ensure only one copy of each route map, can't use set due to list inside tuples (which won't hash) # Use dict indexed by name, and then extract the dict items, dict hashing ensures only one route map per name community_lists = {} prefix_lists = {} node_bgp_data = self.network.g_session.node.get(router) if node_bgp_data: community_lists = node_bgp_data.get('tags') prefix_lists = node_bgp_data.get('prefixes') policy_options = { 'community_lists': community_lists, 'prefix_lists': prefix_lists, 'route_maps': route_map_groups, } f_handle = open(os.path.join(zebra_dir(self.network, router), "bgpd.conf"),'wb') #TODO: remove community_lists and prefix_lists as they are put into policy_options f_handle.write(template.render( hostname = router.device_hostname, asn = self.network.asn(router), password = self.zebra_password, enable_password = self.zebra_password, router_id = self.network.lo_ip(router).ip, community_lists = community_lists, policy_options = policy_options, prefix_lists = prefix_lists, #TODO: see how this differs to router_id identifying_loopback = self.network.lo_ip(router), bgp_groups = bgp_groups, ibgp_neighbor_list = ibgp_neighbor_list, ibgp_rr_client_list = ibgp_rr_client_list, route_maps = route_maps, logfile = "/var/log/zebra/bgpd.log", debug=True, use_debug=True, dump=False, snmp=False, interfaces = self.configure_interfaces(router) ))