""" xorp.py: defines routing services provided by the XORP routing suite. """ from core import logger from core.service import CoreService class XorpRtrmgr(CoreService): """ XORP router manager service builds a config.boot file based on other enabled XORP services, and launches necessary daemons upon startup. """ name = "xorp_rtrmgr" executables = ("xorp_rtrmgr",) group = "XORP" depends = () dirs = ("/etc/xorp",) configs = ("/etc/xorp/config.boot",) startindex = 35 startup = ("xorp_rtrmgr -d -b %s -l /var/log/%s.log -P /var/run/%s.pid" % (configs[0], name, name),) shutdown = ("killall xorp_rtrmgr",) validate = ("pidof xorp_rtrmgr",) @classmethod def generateconfig(cls, node, filename, services): """ Returns config.boot configuration file text. Other services that depend on this will have generatexorpconfig() hooks that are invoked here. Filename currently ignored. """ cfg = "interfaces {\n" for ifc in node.netifs(): cfg += " interface %s {\n" % ifc.name cfg += "\tvif %s {\n" % ifc.name cfg += "".join(map(cls.addrstr, ifc.addrlist)) cfg += cls.lladdrstr(ifc) cfg += "\t}\n" cfg += " }\n" cfg += "}\n\n" for s in services: try: s.depends.index(cls.name) cfg += s.generatexorpconfig(node) except ValueError: logger.exception("error getting value from service: %s", cls.name) return cfg @staticmethod def addrstr(x): """ helper for mapping IP addresses to XORP config statements """ addr, plen = x.split("/") cfg = "\t address %s {\n" % addr cfg += "\t\tprefix-length: %s\n" % plen cfg += "\t }\n" return cfg @staticmethod def lladdrstr(ifc): """ helper for adding link-local address entries (required by OSPFv3) """ cfg = "\t address %s {\n" % ifc.hwaddr.tolinklocal() cfg += "\t\tprefix-length: 64\n" cfg += "\t }\n" return cfg class XorpService(CoreService): """ Parent class for XORP services. Defines properties and methods common to XORP's routing daemons. """ name = None executables = ("xorp_rtrmgr",) group = "XORP" depends = ("xorp_rtrmgr",) dirs = () configs = () startindex = 40 startup = () shutdown = () meta = "The config file for this service can be found in the xorp_rtrmgr service." @staticmethod def fea(forwarding): """ Helper to add a forwarding engine entry to the config file. """ cfg = "fea {\n" cfg += " %s {\n" % forwarding cfg += "\tdisable:false\n" cfg += " }\n" cfg += "}\n" return cfg @staticmethod def mfea(forwarding, ifcs): """ Helper to add a multicast forwarding engine entry to the config file. """ names = [] for ifc in ifcs: if hasattr(ifc, 'control') and ifc.control is True: continue names.append(ifc.name) names.append("register_vif") cfg = "plumbing {\n" cfg += " %s {\n" % forwarding for name in names: cfg += "\tinterface %s {\n" % name cfg += "\t vif %s {\n" % name cfg += "\t\tdisable: false\n" cfg += "\t }\n" cfg += "\t}\n" cfg += " }\n" cfg += "}\n" return cfg @staticmethod def policyexportconnected(): """ Helper to add a policy statement for exporting connected routes. """ cfg = "policy {\n" cfg += " policy-statement export-connected {\n" cfg += "\tterm 100 {\n" cfg += "\t from {\n" cfg += "\t\tprotocol: \"connected\"\n" cfg += "\t }\n" cfg += "\t}\n" cfg += " }\n" cfg += "}\n" return cfg @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: if a.find(".") >= 0: return a.split('/')[0] # raise ValueError, "no IPv4 address found for router ID" return "0.0.0.0" @classmethod def generateconfig(cls, node, filename, services): return "" @classmethod def generatexorpconfig(cls, node): return "" class XorpOspfv2(XorpService): """ The OSPFv2 service provides IPv4 routing for wired networks. It does not build its own configuration file but has hooks for adding to the unified XORP configuration file. """ name = "XORP_OSPFv2" @classmethod def generatexorpconfig(cls, node): cfg = cls.fea("unicast-forwarding4") rtrid = cls.routerid(node) cfg += "\nprotocols {\n" cfg += " ospf4 {\n" cfg += "\trouter-id: %s\n" % rtrid cfg += "\tarea 0.0.0.0 {\n" for ifc in node.netifs(): if hasattr(ifc, 'control') and ifc.control is True: continue cfg += "\t interface %s {\n" % ifc.name cfg += "\t\tvif %s {\n" % ifc.name for a in ifc.addrlist: if a.find(".") < 0: continue addr = a.split("/")[0] cfg += "\t\t address %s {\n" % addr cfg += "\t\t }\n" cfg += "\t\t}\n" cfg += "\t }\n" cfg += "\t}\n" cfg += " }\n" cfg += "}\n" return cfg class XorpOspfv3(XorpService): """ The OSPFv3 service provides IPv6 routing. It does not build its own configuration file but has hooks for adding to the unified XORP configuration file. """ name = "XORP_OSPFv3" @classmethod def generatexorpconfig(cls, node): cfg = cls.fea("unicast-forwarding6") rtrid = cls.routerid(node) cfg += "\nprotocols {\n" cfg += " ospf6 0 { /* Instance ID 0 */\n" cfg += "\trouter-id: %s\n" % rtrid cfg += "\tarea 0.0.0.0 {\n" for ifc in node.netifs(): if hasattr(ifc, 'control') and ifc.control is True: continue cfg += "\t interface %s {\n" % ifc.name cfg += "\t\tvif %s {\n" % ifc.name cfg += "\t\t}\n" cfg += "\t }\n" cfg += "\t}\n" cfg += " }\n" cfg += "}\n" return cfg class XorpBgp(XorpService): """ IPv4 inter-domain routing. AS numbers and peers must be customized. """ name = "XORP_BGP" custom_needed = True @classmethod def generatexorpconfig(cls, node): cfg = "/* This is a sample config that should be customized with\n" cfg += " appropriate AS numbers and peers */\n" cfg += cls.fea("unicast-forwarding4") cfg += cls.policyexportconnected() rtrid = cls.routerid(node) cfg += "\nprotocols {\n" cfg += " bgp {\n" cfg += "\tbgp-id: %s\n" % rtrid cfg += "\tlocal-as: 65001 /* change this */\n" cfg += "\texport: \"export-connected\"\n" cfg += "\tpeer 10.0.1.1 { /* change this */\n" cfg += "\t local-ip: 10.0.1.1\n" cfg += "\t as: 65002\n" cfg += "\t next-hop: 10.0.0.2\n" cfg += "\t}\n" cfg += " }\n" cfg += "}\n" return cfg class XorpRip(XorpService): """ RIP IPv4 unicast routing. """ name = "XORP_RIP" @classmethod def generatexorpconfig(cls, node): cfg = cls.fea("unicast-forwarding4") cfg += cls.policyexportconnected() cfg += "\nprotocols {\n" cfg += " rip {\n" cfg += "\texport: \"export-connected\"\n" for ifc in node.netifs(): if hasattr(ifc, 'control') and ifc.control is True: continue cfg += "\tinterface %s {\n" % ifc.name cfg += "\t vif %s {\n" % ifc.name for a in ifc.addrlist: if a.find(".") < 0: continue addr = a.split("/")[0] cfg += "\t\taddress %s {\n" % addr cfg += "\t\t disable: false\n" cfg += "\t\t}\n" cfg += "\t }\n" cfg += "\t}\n" cfg += " }\n" cfg += "}\n" return cfg class XorpRipng(XorpService): """ RIP NG IPv6 unicast routing. """ name = "XORP_RIPNG" @classmethod def generatexorpconfig(cls, node): cfg = cls.fea("unicast-forwarding6") cfg += cls.policyexportconnected() cfg += "\nprotocols {\n" cfg += " ripng {\n" cfg += "\texport: \"export-connected\"\n" for ifc in node.netifs(): if hasattr(ifc, 'control') and ifc.control is True: continue cfg += "\tinterface %s {\n" % ifc.name cfg += "\t vif %s {\n" % ifc.name # for a in ifc.addrlist: # if a.find(":") < 0: # continue # addr = a.split("/")[0] # cfg += "\t\taddress %s {\n" % addr # cfg += "\t\t disable: false\n" # cfg += "\t\t}\n" cfg += "\t\taddress %s {\n" % ifc.hwaddr.tolinklocal() cfg += "\t\t disable: false\n" cfg += "\t\t}\n" cfg += "\t }\n" cfg += "\t}\n" cfg += " }\n" cfg += "}\n" return cfg class XorpPimSm4(XorpService): """ PIM Sparse Mode IPv4 multicast routing. """ name = "XORP_PIMSM4" @classmethod def generatexorpconfig(cls, node): cfg = cls.mfea("mfea4", node.netifs()) cfg += "\nprotocols {\n" cfg += " igmp {\n" names = [] for ifc in node.netifs(): if hasattr(ifc, 'control') and ifc.control is True: continue names.append(ifc.name) cfg += "\tinterface %s {\n" % ifc.name cfg += "\t vif %s {\n" % ifc.name cfg += "\t\tdisable: false\n" cfg += "\t }\n" cfg += "\t}\n" cfg += " }\n" cfg += "}\n" cfg += "\nprotocols {\n" cfg += " pimsm4 {\n" names.append("register_vif") for name in names: cfg += "\tinterface %s {\n" % name cfg += "\t vif %s {\n" % name cfg += "\t\tdr-priority: 1\n" cfg += "\t }\n" cfg += "\t}\n" cfg += "\tbootstrap {\n" cfg += "\t cand-bsr {\n" cfg += "\t\tscope-zone 224.0.0.0/4 {\n" cfg += "\t\t cand-bsr-by-vif-name: \"%s\"\n" % names[0] cfg += "\t\t}\n" cfg += "\t }\n" cfg += "\t cand-rp {\n" cfg += "\t\tgroup-prefix 224.0.0.0/4 {\n" cfg += "\t\t cand-rp-by-vif-name: \"%s\"\n" % names[0] cfg += "\t\t}\n" cfg += "\t }\n" cfg += "\t}\n" cfg += " }\n" cfg += "}\n" cfg += "\nprotocols {\n" cfg += " fib2mrib {\n" cfg += "\tdisable: false\n" cfg += " }\n" cfg += "}\n" return cfg class XorpPimSm6(XorpService): """ PIM Sparse Mode IPv6 multicast routing. """ name = "XORP_PIMSM6" @classmethod def generatexorpconfig(cls, node): cfg = cls.mfea("mfea6", node.netifs()) cfg += "\nprotocols {\n" cfg += " mld {\n" names = [] for ifc in node.netifs(): if hasattr(ifc, 'control') and ifc.control is True: continue names.append(ifc.name) cfg += "\tinterface %s {\n" % ifc.name cfg += "\t vif %s {\n" % ifc.name cfg += "\t\tdisable: false\n" cfg += "\t }\n" cfg += "\t}\n" cfg += " }\n" cfg += "}\n" cfg += "\nprotocols {\n" cfg += " pimsm6 {\n" names.append("register_vif") for name in names: cfg += "\tinterface %s {\n" % name cfg += "\t vif %s {\n" % name cfg += "\t\tdr-priority: 1\n" cfg += "\t }\n" cfg += "\t}\n" cfg += "\tbootstrap {\n" cfg += "\t cand-bsr {\n" cfg += "\t\tscope-zone ff00::/8 {\n" cfg += "\t\t cand-bsr-by-vif-name: \"%s\"\n" % names[0] cfg += "\t\t}\n" cfg += "\t }\n" cfg += "\t cand-rp {\n" cfg += "\t\tgroup-prefix ff00::/8 {\n" cfg += "\t\t cand-rp-by-vif-name: \"%s\"\n" % names[0] cfg += "\t\t}\n" cfg += "\t }\n" cfg += "\t}\n" cfg += " }\n" cfg += "}\n" cfg += "\nprotocols {\n" cfg += " fib2mrib {\n" cfg += "\tdisable: false\n" cfg += " }\n" cfg += "}\n" return cfg class XorpOlsr(XorpService): """ OLSR IPv4 unicast MANET routing. """ name = "XORP_OLSR" @classmethod def generatexorpconfig(cls, node): cfg = cls.fea("unicast-forwarding4") rtrid = cls.routerid(node) cfg += "\nprotocols {\n" cfg += " olsr4 {\n" cfg += "\tmain-address: %s\n" % rtrid for ifc in node.netifs(): if hasattr(ifc, 'control') and ifc.control is True: continue cfg += "\tinterface %s {\n" % ifc.name cfg += "\t vif %s {\n" % ifc.name for a in ifc.addrlist: if a.find(".") < 0: continue addr = a.split("/")[0] cfg += "\t\taddress %s {\n" % addr cfg += "\t\t}\n" cfg += "\t }\n" cfg += "\t}\n" cfg += " }\n" cfg += "}\n" return cfg