core-extra/daemon/core/services/xorp.py

477 lines
14 KiB
Python

"""
xorp.py: defines routing services provided by the XORP routing suite.
"""
import logging
import netaddr
from core.services.coreservices 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"
dirs = ("/etc/xorp",)
configs = ("/etc/xorp/config.boot",)
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 generate_config(cls, node, filename):
"""
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 node.services:
try:
s.dependencies.index(cls.name)
cfg += s.generatexorpconfig(node)
except ValueError:
logging.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"
dependencies = ("xorp_rtrmgr",)
dirs = ()
configs = ()
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:
a = a.split("/")[0]
if netaddr.valid_ipv4(a):
return a
# raise ValueError, "no IPv4 address found for router ID"
return "0.0.0.0"
@classmethod
def generate_config(cls, node, filename):
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:
addr = a.split("/")[0]
if not netaddr.valid_ipv4(addr):
continue
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:
addr = a.split("/")[0]
if not netaddr.valid_ipv4(addr):
continue
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
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:
addr = a.split("/")[0]
if not netaddr.valid_ipv4(addr):
continue
cfg += "\t\taddress %s {\n" % addr
cfg += "\t\t}\n"
cfg += "\t }\n"
cfg += "\t}\n"
cfg += " }\n"
cfg += "}\n"
return cfg