From b9cbbf5709e6324a6ffc42f5a83cb1fc9caaf0e3 Mon Sep 17 00:00:00 2001 From: Blake Harnden <32446120+bharnden@users.noreply.github.com> Date: Thu, 23 Jan 2020 21:08:40 -0800 Subject: [PATCH] finished converting quagga services to config services --- .../configservices/quaggaservices/services.py | 245 ++++++++++++++++++ .../quaggaservices/templates/Quagga.conf | 6 +- 2 files changed, 249 insertions(+), 2 deletions(-) diff --git a/daemon/core/configservices/quaggaservices/services.py b/daemon/core/configservices/quaggaservices/services.py index 604c98a6..bc937038 100644 --- a/daemon/core/configservices/quaggaservices/services.py +++ b/daemon/core/configservices/quaggaservices/services.py @@ -1,12 +1,15 @@ import abc +import logging from typing import Any, Dict import netaddr from core import constants from core.configservice.base import ConfigService, ConfigServiceMode +from core.emane.nodes import EmaneNet from core.nodes.base import CoreNodeBase from core.nodes.interface import CoreInterface +from core.nodes.network import WlanNode GROUP = "Quagga" @@ -27,6 +30,20 @@ def has_mtu_mismatch(ifc: CoreInterface) -> bool: return False +def get_min_mtu(ifc): + """ + Helper to discover the minimum MTU of interfaces linked with the + given interface. + """ + mtu = ifc.mtu + if not ifc.net: + return mtu + for i in ifc.net.netifs(): + if i.mtu < mtu: + mtu = i.mtu + return mtu + + def get_router_id(node: CoreNodeBase) -> str: """ Helper to return the first IPv4 address of a node as its router ID. @@ -168,3 +185,231 @@ class Ospfv2(QuaggaService, ConfigService): ! """ return self.render_text(text, data) + + +class Ospfv3(QuaggaService, ConfigService): + """ + The OSPFv3 service provides IPv6 routing for wired networks. It does + not build its own configuration file but has hooks for adding to the + unified Quagga.conf file. + """ + + name = "OSPFv3" + shutdown = ("killall ospf6d",) + validate = ("pidof ospf6d",) + ipv4_routing = True + ipv6_routing = True + + def quagga_interface_config(self, ifc: CoreInterface) -> str: + mtu = get_min_mtu(ifc) + if mtu < ifc.mtu: + return f"ipv6 ospf6 ifmtu {mtu}" + else: + return "" + + def quagga_config(self) -> str: + router_id = get_router_id(self.node) + ifnames = [] + for ifc in self.node.netifs(): + if getattr(ifc, "control", False): + continue + ifnames.append(ifc.name) + data = dict(router_id=router_id, ifnames=ifnames) + text = """ + router ospf6 + instance-id 65 + router-id ${router_id} + % for ifname in ifnames: + interface ${ifname} area 0.0.0.0 + % endfor + ! + """ + return self.render_text(text, data) + + +class Ospfv3mdr(Ospfv3): + """ + The OSPFv3 MANET Designated Router (MDR) service provides IPv6 + routing for wireless networks. It does not build its own + configuration file but has hooks for adding to the + unified Quagga.conf file. + """ + + name = "OSPFv3MDR" + + def data(self) -> Dict[str, Any]: + for ifc in self.node.netifs(): + is_wireless = isinstance(ifc.net, (WlanNode, EmaneNet)) + logging.info("MDR wireless: %s", is_wireless) + return dict() + + def quagga_interface_config(self, ifc: CoreInterface) -> str: + config = super().quagga_interface_config(ifc) + if isinstance(ifc.net, (WlanNode, EmaneNet)): + config = self.clean_text( + f""" + {config} + ipv6 ospf6 hello-interval 2 + ipv6 ospf6 dead-interval 6 + ipv6 ospf6 retransmit-interval 5 + ipv6 ospf6 network manet-designated-router + ipv6 ospf6 twohoprefresh 3 + ipv6 ospf6 adjacencyconnectivity uniconnected + ipv6 ospf6 lsafullness mincostlsa + """ + ) + return config + + +class Bgp(QuaggaService, ConfigService): + """ + The BGP service provides interdomain routing. + Peers must be manually configured, with a full mesh for those + having the same AS number. + """ + + name = "BGP" + shutdown = ["killall bgpd"] + validate = ["pidof bgpd"] + ipv4_routing = True + ipv6_routing = True + + def quagga_interface_config(self, ifc: CoreInterface) -> str: + router_id = get_router_id(self.node) + text = f""" + ! BGP configuration + ! You should configure the AS number below + ! along with this router's peers. + router bgp {self.node.id} + bgp router-id {router_id} + redistribute connected + !neighbor 1.2.3.4 remote-as 555 + ! + """ + return self.clean_text(text) + + +class Rip(QuaggaService, ConfigService): + """ + The RIP service provides IPv4 routing for wired networks. + """ + + name = "RIP" + shutdown = ["killall ripd"] + validate = ["pidof ripd"] + ipv4_routing = True + + def quagga_config(self) -> str: + text = """ + router rip + redistribute static + redistribute connected + redistribute ospf + network 0.0.0.0/0 + ! + """ + return self.clean_text(text) + + +class Ripng(QuaggaService, ConfigService): + """ + The RIP NG service provides IPv6 routing for wired networks. + """ + + name = "RIPNG" + shutdown = ["killall ripngd"] + validate = ["pidof ripngd"] + ipv6_routing = True + + def quagga_config(self) -> str: + text = """ + router ripng + redistribute static + redistribute connected + redistribute ospf6 + network ::/0 + ! + """ + return self.clean_text(text) + + +class Babel(QuaggaService, ConfigService): + """ + The Babel service provides a loop-avoiding distance-vector routing + protocol for IPv6 and IPv4 with fast convergence properties. + """ + + name = "Babel" + shutdown = ["killall babeld"] + validate = ["pidof babeld"] + ipv6_routing = True + + def quagga_config(self) -> str: + ifnames = [] + for ifc in self.node.netifs(): + if getattr(ifc, "control", False): + continue + ifnames.append(ifc.name) + text = """ + router babel + % for ifname in ifnames: + network ${ifname} + % endfor + redistribute static + redistribute connected + ! + """ + data = dict(ifnames=ifnames) + return self.render_text(text, data) + + def quagga_interface_config(self, ifc: CoreInterface) -> str: + if isinstance(ifc.net, (WlanNode, EmaneNet)): + text = """ + babel wireless + no babel split-horizon + """ + else: + text = """ + babel wired + babel split-horizon + """ + return self.clean_text(text) + + +class Xpimd(QuaggaService, ConfigService): + """ + PIM multicast routing based on XORP. + """ + + name = "Xpimd" + shutdown = ["killall xpimd"] + validate = ["pidof xpimd"] + ipv4_routing = True + + def quagga_config(self) -> str: + ifname = "eth0" + for ifc in self.node.netifs(): + if ifc.name != "lo": + ifname = ifc.name + break + + text = f""" + router mfea + ! + router igmp + ! + router pim + !ip pim rp-address 10.0.0.1 + ip pim bsr-candidate {ifname} + ip pim rp-candidate {ifname} + !ip pim spt-threshold interval 10 bytes 80000 + ! + """ + return self.clean_text(text) + + def quagga_interface_config(self, ifc: CoreInterface) -> str: + text = """ + ip mfea + ip pim + """ + return self.clean_text(text) diff --git a/daemon/core/configservices/quaggaservices/templates/Quagga.conf b/daemon/core/configservices/quaggaservices/templates/Quagga.conf index 282fc63d..853b1707 100644 --- a/daemon/core/configservices/quaggaservices/templates/Quagga.conf +++ b/daemon/core/configservices/quaggaservices/templates/Quagga.conf @@ -10,9 +10,11 @@ interface ${ifc.name} ipv6 address ${addr} % endfor % endif - % if is_control: + % if not is_control: % for service in services: - ${service.quagga_interface_config(ifc)} + % for line in service.quagga_interface_config(ifc).split("\n"): + ${line} + % endfor % endfor % endif !