Esempio n. 1
0
    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
Esempio n. 2
0
    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,
                   ))
Esempio n. 3
0
    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)
                ))