""" bird.py: defines routing services provided by the BIRD Internet Routing Daemon. """ from core.nodes import ipaddress from core.services.coreservices import CoreService class Bird(CoreService): """ Bird router support """ name = "bird" executables = ("bird",) group = "BIRD" dirs = ("/etc/bird",) configs = ("/etc/bird/bird.conf",) startup = ("bird -c %s" % (configs[0]),) shutdown = ("killall bird",) validate = ("pidof bird",) @classmethod def generate_config(cls, node, filename): """ Return the bird.conf file contents. """ if filename == cls.configs[0]: return cls.generateBirdConf(node) else: raise ValueError @staticmethod def routerid(node): """ Helper to return the first IPv4 address of a node as its router ID. """ for ifc in node.netifs(): if hasattr(ifc, "control") and ifc.control is True: continue for a in ifc.addrlist: a = a.split("/")[0] if ipaddress.is_ipv4_address(a): return a # raise ValueError, "no IPv4 address found for router ID" return "0.0.0.0" @classmethod def generateBirdConf(cls, node): """ Returns configuration file text. Other services that depend on bird will have generatebirdifcconfig() and generatebirdconfig() hooks that are invoked here. """ cfg = """\ /* Main configuration file for BIRD. This is ony a template, * you will *need* to customize it according to your needs * Beware that only double quotes \'"\' are valid. No singles. */ log "/var/log/%s.log" all; #debug protocols all; #debug commands 2; router id %s; # Mandatory for IPv6, may be automatic for IPv4 protocol kernel { persist; # Don\'t remove routes on BIRD shutdown scan time 200; # Scan kernel routing table every 200 seconds export all; import all; } protocol device { scan time 10; # Scan interfaces every 10 seconds } """ % ( cls.name, cls.routerid(node), ) # Generate protocol specific configurations for s in node.services: if cls.name not in s.dependencies: continue cfg += s.generatebirdconfig(node) return cfg class BirdService(CoreService): """ Parent class for Bird services. Defines properties and methods common to Bird's routing daemons. """ name = None executables = ("bird",) group = "BIRD" dependencies = ("bird",) dirs = () configs = () startup = () shutdown = () meta = "The config file for this service can be found in the bird service." @classmethod def generatebirdconfig(cls, node): return "" @classmethod def generatebirdifcconfig(cls, node): """ Use only bare interfaces descriptions in generated protocol configurations. This has the slight advantage of being the same everywhere. """ cfg = "" for ifc in node.netifs(): if hasattr(ifc, "control") and ifc.control is True: continue cfg += ' interface "%s";\n' % ifc.name return cfg class BirdBgp(BirdService): """ BGP BIRD Service (configuration generation) """ name = "BIRD_BGP" custom_needed = True @classmethod def generatebirdconfig(cls, node): return """ /* This is a sample config that should be customized with appropriate AS numbers * and peers; add one section like this for each neighbor */ protocol bgp { local as 65000; # Customize your AS number neighbor 198.51.100.130 as 64496; # Customize neighbor AS number && IP export filter { # We use non-trivial export rules # This is an example. You should advertise only *your routes* if (source = RTS_DEVICE) || (source = RTS_OSPF) then { # bgp_community.add((65000,64501)); # Assign our community accept; } reject; }; import all; } """ class BirdOspf(BirdService): """ OSPF BIRD Service (configuration generation) """ name = "BIRD_OSPFv2" @classmethod def generatebirdconfig(cls, node): cfg = "protocol ospf {\n" cfg += " export filter {\n" cfg += " if source = RTS_BGP then {\n" cfg += " ospf_metric1 = 100;\n" cfg += " accept;\n" cfg += " }\n" cfg += " accept;\n" cfg += " };\n" cfg += " area 0.0.0.0 {\n" cfg += cls.generatebirdifcconfig(node) cfg += " };\n" cfg += "}\n\n" return cfg class BirdRadv(BirdService): """ RADV BIRD Service (configuration generation) """ name = "BIRD_RADV" @classmethod def generatebirdconfig(cls, node): cfg = "/* This is a sample config that must be customized */\n" cfg += "protocol radv {\n" cfg += " # auto configuration on all interfaces\n" cfg += cls.generatebirdifcconfig(node) cfg += " # Advertise DNS\n" cfg += " rdnss {\n" cfg += "# lifetime mult 10;\n" cfg += "# lifetime mult 10;\n" cfg += "# ns 2001:0DB8:1234::11;\n" cfg += "# ns 2001:0DB8:1234::11;\n" cfg += "# ns 2001:0DB8:1234::12;\n" cfg += "# ns 2001:0DB8:1234::12;\n" cfg += " };\n" cfg += "}\n\n" return cfg class BirdRip(BirdService): """ RIP BIRD Service (configuration generation) """ name = "BIRD_RIP" @classmethod def generatebirdconfig(cls, node): cfg = "protocol rip {\n" cfg += " period 10;\n" cfg += " garbage time 60;\n" cfg += cls.generatebirdifcconfig(node) cfg += " honor neighbor;\n" cfg += " authentication none;\n" cfg += " import all;\n" cfg += " export all;\n" cfg += "}\n\n" return cfg class BirdStatic(BirdService): """ Static Bird Service (configuration generation) """ name = "BIRD_static" custom_needed = True @classmethod def generatebirdconfig(cls, node): cfg = "/* This is a sample config that must be customized */\n" cfg += "protocol static {\n" cfg += "# route 0.0.0.0/0 via 198.51.100.130; # Default route. Do NOT advertise on BGP !\n" cfg += "# route 203.0.113.0/24 reject; # Sink route\n" cfg += '# route 10.2.0.0/24 via "arc0"; # Secondary network\n' cfg += "}\n\n" return cfg