diff options
| author | Yuxiang Zhu <vfreex@gmail.com> | 2023-01-09 05:48:50 +0000 | 
|---|---|---|
| committer | Yuxiang Zhu <vfreex@gmail.com> | 2023-02-08 21:48:15 +0800 | 
| commit | 4bfe801e7e6c59bdb70500b629fd31bb03598d4b (patch) | |
| tree | fd51141d8fd1c6c112c7742259f5b97cf88abd17 /src | |
| parent | 2622902ac76bc1c3356bb722f63e931119f3eb04 (diff) | |
| download | vyos-1x-4bfe801e7e6c59bdb70500b629fd31bb03598d4b.tar.gz vyos-1x-4bfe801e7e6c59bdb70500b629fd31bb03598d4b.zip | |
T4977: Add Babel routing protocol support
This PR adds basic Babel routing protocol support using the implementation in
FRR.
Signed-off-by: Yuxiang Zhu <vfreex@gmail.com>
Diffstat (limited to 'src')
| -rwxr-xr-x | src/conf_mode/protocols_babel.py | 163 | ||||
| -rwxr-xr-x | src/op_mode/restart_frr.py | 2 | 
2 files changed, 164 insertions, 1 deletions
| diff --git a/src/conf_mode/protocols_babel.py b/src/conf_mode/protocols_babel.py new file mode 100755 index 000000000..20821c7f2 --- /dev/null +++ b/src/conf_mode/protocols_babel.py @@ -0,0 +1,163 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2021-2023 VyOS maintainers and contributors +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 or later as +# published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program.  If not, see <http://www.gnu.org/licenses/>. + +import os + +from sys import exit + +from vyos.config import Config +from vyos.configdict import dict_merge +from vyos.configdict import node_changed +from vyos.configverify import verify_common_route_maps +from vyos.configverify import verify_access_list +from vyos.configverify import verify_prefix_list +from vyos.util import dict_search +from vyos.xml import defaults +from vyos.template import render_to_string +from vyos import ConfigError +from vyos import frr +from vyos import airbag +airbag.enable() + +def get_config(config=None): +    if config: +        conf = config +    else: +        conf = Config() +    base = ['protocols', 'babel'] +    babel = conf.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True) + +    # FRR has VRF support for different routing daemons. As interfaces belong +    # to VRFs - or the global VRF, we need to check for changed interfaces so +    # that they will be properly rendered for the FRR config. Also this eases +    # removal of interfaces from the running configuration. +    interfaces_removed = node_changed(conf, base + ['interface']) +    if interfaces_removed: +        babel['interface_removed'] = list(interfaces_removed) + +    # Bail out early if configuration tree does not exist +    if not conf.exists(base): +        babel.update({'deleted' : ''}) +        return babel + +    # We have gathered the dict representation of the CLI, but there are default +    # options which we need to update into the dictionary retrived. +    default_values = defaults(base) + +    # XXX: T2665: we currently have no nice way for defaults under tag nodes, +    # clean them out and add them manually :( +    del default_values['interface'] + +    # merge in remaining default values +    babel = dict_merge(default_values, babel) + +    # We also need some additional information from the config, prefix-lists +    # and route-maps for instance. They will be used in verify(). +    # +    # XXX: one MUST always call this without the key_mangling() option! See +    # vyos.configverify.verify_common_route_maps() for more information. +    tmp = conf.get_config_dict(['policy']) +    # Merge policy dict into "regular" config dict +    babel = dict_merge(tmp, babel) +    return babel + +def verify(babel): +    if not babel: +        return None + +    # verify distribute_list +    if "distribute_list" in babel: +        acl_keys = { +            "ipv4": [ +                "distribute_list.ipv4.access_list.in", +                "distribute_list.ipv4.access_list.out", +            ], +            "ipv6": [ +                "distribute_list.ipv6.access_list.in", +                "distribute_list.ipv6.access_list.out", +            ] +        } +        prefix_list_keys = { +            "ipv4": [ +                "distribute_list.ipv4.prefix_list.in", +                "distribute_list.ipv4.prefix_list.out", +            ], +            "ipv6":[ +                "distribute_list.ipv6.prefix_list.in", +                "distribute_list.ipv6.prefix_list.out", +            ] +        } +        for address_family in ["ipv4", "ipv6"]: +            for iface_key in babel["distribute_list"].get(address_family, {}).get("interface", {}).keys(): +                acl_keys[address_family].extend([ +                    f"distribute_list.{address_family}.interface.{iface_key}.access_list.in", +                    f"distribute_list.{address_family}.interface.{iface_key}.access_list.out" +                ]) +                prefix_list_keys[address_family].extend([ +                    f"distribute_list.{address_family}.interface.{iface_key}.prefix_list.in", +                    f"distribute_list.{address_family}.interface.{iface_key}.prefix_list.out" +                ]) + +        for address_family, keys in acl_keys.items(): +            for key in keys: +                acl = dict_search(key, babel) +                if acl: +                    verify_access_list(acl, babel, version='6' if address_family == 'ipv6' else '') + +        for address_family, keys in prefix_list_keys.items(): +            for key in keys: +                prefix_list = dict_search(key, babel) +                if prefix_list: +                    verify_prefix_list(prefix_list, babel, version='6' if address_family == 'ipv6' else '') + + +def generate(babel): +    if not babel or 'deleted' in babel: +        return None + +    babel['new_frr_config'] = render_to_string('frr/babeld.frr.j2', babel) +    return None + +def apply(babel): +    babel_daemon = 'babeld' + +    # Save original configuration prior to starting any commit actions +    frr_cfg = frr.FRRConfig() + +    frr_cfg.load_configuration(babel_daemon) +    frr_cfg.modify_section('^router babel', stop_pattern='^exit', remove_stop_mark=True) + +    for key in ['interface', 'interface_removed']: +        if key not in babel: +            continue +        for interface in babel[key]: +            frr_cfg.modify_section(f'^interface {interface}', stop_pattern='^exit', remove_stop_mark=True) + +    if 'new_frr_config' in babel: +        frr_cfg.add_before(frr.default_add_before, babel['new_frr_config']) +    frr_cfg.commit_configuration(babel_daemon) + +    return None + +if __name__ == '__main__': +    try: +        c = get_config() +        verify(c) +        generate(c) +        apply(c) +    except ConfigError as e: +        print(e) +        exit(1) diff --git a/src/op_mode/restart_frr.py b/src/op_mode/restart_frr.py index 91b25567a..680d9f8cc 100755 --- a/src/op_mode/restart_frr.py +++ b/src/op_mode/restart_frr.py @@ -139,7 +139,7 @@ def _reload_config(daemon):  # define program arguments  cmd_args_parser = argparse.ArgumentParser(description='restart frr daemons')  cmd_args_parser.add_argument('--action', choices=['restart'], required=True, help='action to frr daemons') -cmd_args_parser.add_argument('--daemon', choices=['bfdd', 'bgpd', 'ldpd', 'ospfd', 'ospf6d', 'isisd', 'ripd', 'ripngd', 'staticd', 'zebra'], required=False,  nargs='*', help='select single or multiple daemons') +cmd_args_parser.add_argument('--daemon', choices=['bfdd', 'bgpd', 'ldpd', 'ospfd', 'ospf6d', 'isisd', 'ripd', 'ripngd', 'staticd', 'zebra', 'babeld'], required=False,  nargs='*', help='select single or multiple daemons')  # parse arguments  cmd_args = cmd_args_parser.parse_args() | 
