# # CORE # Copyright (c)2012 Jean-Tiare Le Bigot. # See the LICENSE file included in this distribution. # # authors: Jean-Tiare Le Bigot # Jeff Ahrenholz # ''' bird.py: defines routing services provided by the BIRD Internet Routing Daemon. ''' import os from core.service import CoreService, addservice from core.misc.ipaddr import IPv4Prefix from core.constants import * class Bird(CoreService): ''' Bird router support ''' _name = "bird" _group = "BIRD" _depends = () _dirs = ("/etc/bird",) _configs = ("/etc/bird/bird.conf",) _startindex = 35 _startup = ("bird -c %s" % (_configs[0]),) _shutdown = ("killall bird", ) _validate = ("pidof bird", ) @classmethod def generateconfig(cls, node, filename, services): ''' Return the bird.conf file contents. ''' if filename == cls._configs[0]: return cls.generateBirdConf(node, services) 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 == True: continue for a in ifc.addrlist: if a.find(".") >= 0: return a .split('/') [0] #raise ValueError, "no IPv4 address found for router ID" return "0.0.0.0" @classmethod def generateBirdConf(cls, node, services): ''' 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 services: if cls._name not in s._depends: 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 = "BirdDaemon" _group = "BIRD" _depends = ("bird", ) _dirs = () _configs = () _startindex = 40 _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 == 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 # Register all protocols addservice(Bird) addservice(BirdOspf) addservice(BirdBgp) #addservice(BirdRadv) # untested addservice(BirdRip) addservice(BirdStatic)