From f7a7a936d4b1b2b0028fcfc1a1fcebfd069b239b Mon Sep 17 00:00:00 2001 From: zsdc Date: Fri, 9 Aug 2019 18:19:46 +0300 Subject: [bfd] T1183: Added some new functionality and fixed bugs in BFD: * added option "echo-mode" and "echo-interval" for BFD peers * added configuration check for usage "multihop" and "echo-mode" * added configuration check for denying deletion BFD peers, which are used in BGP configuration * fixed deleting/changing BFD peers with custom parameters (for example multihop, local-address, etc.) * deleted wrong skipping of configuration check for "shutdown" BFD peers --- interface-definitions/protocols-bfd.xml | 18 ++++ src/conf_mode/protocols_bfd.py | 179 +++++++++++++++++++++----------- 2 files changed, 138 insertions(+), 59 deletions(-) diff --git a/interface-definitions/protocols-bfd.xml b/interface-definitions/protocols-bfd.xml index 47d5bf97d..f2d7d7d2f 100644 --- a/interface-definitions/protocols-bfd.xml +++ b/interface-definitions/protocols-bfd.xml @@ -91,6 +91,18 @@ + + + Echo receive transmission interval + + 10-60000 + The minimal echo receive transmission interval that this system is capable of handling + + + + + + @@ -105,6 +117,12 @@ + + + Enables the echo transmission mode + + + diff --git a/src/conf_mode/protocols_bfd.py b/src/conf_mode/protocols_bfd.py index 04549f4b4..98f38035a 100755 --- a/src/conf_mode/protocols_bfd.py +++ b/src/conf_mode/protocols_bfd.py @@ -31,7 +31,7 @@ config_tmpl = """ ! bfd {% for peer in old_peers -%} - no peer {{ peer }} + no peer {{ peer.remote }}{% if peer.multihop %} multihop{% endif %}{% if peer.src_addr %} local-address {{ peer.src_addr }}{% endif %}{% if peer.src_if %} interface {{ peer.src_if }}{% endif %} {% endfor -%} ! {% for peer in new_peers -%} @@ -39,6 +39,8 @@ bfd detect-multiplier {{ peer.multiplier }} receive-interval {{ peer.rx_interval }} transmit-interval {{ peer.tx_interval }} + {% if peer.echo_mode %}echo-mode{% endif %} + {% if peer.echo_interval != '' %}echo-interval {{ peer.echo_interval }}{% endif %} {% if not peer.shutdown %}no {% endif %}shutdown {% endfor -%} ! @@ -49,6 +51,86 @@ default_config_data = { 'old_peers' : [] } +# get configuration for BFD peer from proposed or effective configuration +def get_bfd_peer_config(peer, conf_mode="proposed"): + conf = Config() + conf.set_level('protocols bfd peer {0}'.format(peer)) + + bfd_peer = { + 'remote': peer, + 'shutdown': False, + 'src_if': '', + 'src_addr': '', + 'multiplier': '3', + 'rx_interval': '300', + 'tx_interval': '300', + 'multihop': False, + 'echo_interval': '', + 'echo_mode': False, + } + + # Check if individual peer is disabled + if conf_mode == "effective" and conf.exists_effective('shutdown'): + bfd_peer['shutdown'] = True + if conf_mode == "proposed" and conf.exists('shutdown'): + bfd_peer['shutdown'] = True + + # Check if peer has a local source interface configured + if conf_mode == "effective" and conf.exists_effective('source interface'): + bfd_peer['src_if'] = conf.return_effective_value('source interface') + if conf_mode == "proposed" and conf.exists('source interface'): + bfd_peer['src_if'] = conf.return_value('source interface') + + # Check if peer has a local source address configured - this is mandatory for IPv6 + if conf_mode == "effective" and conf.exists_effective('source address'): + bfd_peer['src_addr'] = conf.return_effective_value('source address') + if conf_mode == "proposed" and conf.exists('source address'): + bfd_peer['src_addr'] = conf.return_value('source address') + + # Tell BFD daemon that we should expect packets with TTL less than 254 + # (because it will take more than one hop) and to listen on the multihop + # port (4784) + if conf_mode == "effective" and conf.exists_effective('multihop'): + bfd_peer['multihop'] = True + if conf_mode == "proposed" and conf.exists('multihop'): + bfd_peer['multihop'] = True + + # Configures the minimum interval that this system is capable of receiving + # control packets. The default value is 300 milliseconds. + if conf_mode == "effective" and conf.exists_effective('interval receive'): + bfd_peer['rx_interval'] = conf.return_effective_value('interval receive') + if conf_mode == "proposed" and conf.exists('interval receive'): + bfd_peer['rx_interval'] = conf.return_value('interval receive') + + # The minimum transmission interval (less jitter) that this system wants + # to use to send BFD control packets. + if conf_mode == "effective" and conf.exists_effective('interval transmit'): + bfd_peer['tx_interval'] = conf.return_effective_value('interval transmit') + if conf_mode == "proposed" and conf.exists('interval transmit'): + bfd_peer['tx_interval'] = conf.return_value('interval transmit') + + # Configures the detection multiplier to determine packet loss. The remote + # transmission interval will be multiplied by this value to determine the + # connection loss detection timer. The default value is 3. + if conf_mode == "effective" and conf.exists_effective('interval multiplier'): + bfd_peer['multiplier'] = conf.return_effective_value('interval multiplier') + if conf_mode == "proposed" and conf.exists('interval multiplier'): + bfd_peer['multiplier'] = conf.return_value('interval multiplier') + + # Configures the minimal echo receive transmission interval that this system is capable of handling + if conf_mode == "effective" and conf.exists_effective('interval echo-interval'): + bfd_peer['echo_interval'] = conf.return_effective_value('interval echo-interval') + if conf_mode == "proposed" and conf.exists('interval echo-interval'): + bfd_peer['echo_interval'] = conf.return_value('interval echo-interval') + + # Enables or disables the echo transmission mode + if conf_mode == "effective" and conf.exists_effective('echo-mode'): + bfd_peer['echo_mode'] = True + if conf_mode == "proposed" and conf.exists('echo-mode'): + bfd_peer['echo_mode'] = True + + return bfd_peer + def get_config(): bfd = copy.deepcopy(default_config_data) conf = Config() @@ -60,56 +142,16 @@ def get_config(): # as we have to use vtysh to talk to FRR we also need to know # which peers are gone due to a config removal - thus we read in # all peers (active or to delete) - bfd['old_peers'] = conf.list_effective_nodes('peer') + for peer in conf.list_effective_nodes('peer'): + bfd['old_peers'].append(get_bfd_peer_config(peer, "effective")) for peer in conf.list_nodes('peer'): - conf.set_level('protocols bfd peer {0}'.format(peer)) - bfd_peer = { - 'remote': peer, - 'shutdown': False, - 'src_if': '', - 'src_addr': '', - 'multiplier': '3', - 'rx_interval': '300', - 'tx_interval': '300', - 'multihop': False - } - - # Check if individual peer is disabled - if conf.exists('shutdown'): - bfd_peer['shutdown'] = True - - # Check if peer has a local source interface configured - if conf.exists('source interface'): - bfd_peer['src_if'] = conf.return_value('source interface') - - # Check if peer has a local source address configured - this is mandatory for IPv6 - if conf.exists('source address'): - bfd_peer['src_addr'] = conf.return_value('source address') - - # Tell BFD daemon that we should expect packets with TTL less than 254 - # (because it will take more than one hop) and to listen on the multihop - # port (4784) - if conf.exists('multihop'): - bfd_peer['multihop'] = True - - # Configures the minimum interval that this system is capable of receiving - # control packets. The default value is 300 milliseconds. - if conf.exists('interval receive'): - bfd_peer['rx_interval'] = conf.return_value('interval receive') - - # The minimum transmission interval (less jitter) that this system wants - # to use to send BFD control packets. - if conf.exists('interval transmit'): - bfd_peer['tx_interval'] = conf.return_value('interval transmit') - - # Configures the detection multiplier to determine packet loss. The remote - # transmission interval will be multiplied by this value to determine the - # connection loss detection timer. The default value is 3. - if conf.exists('interval multiplier'): - bfd_peer['multiplier'] = conf.return_value('interval multiplier') - - bfd['new_peers'].append(bfd_peer) + bfd['new_peers'].append(get_bfd_peer_config(peer)) + + # find deleted peers + set_new_peers = set(conf.list_nodes('peer')) + set_old_peers = set(conf.list_effective_nodes('peer')) + bfd['deleted_peers'] = set_old_peers - set_new_peers return bfd @@ -117,20 +159,39 @@ def verify(bfd): if bfd is None: return None - for peer in bfd['new_peers']: - # Bail out early if peer is shutdown - if peer['shutdown']: - continue + # some variables to use later + conf = Config() + for peer in bfd['new_peers']: # IPv6 peers require an explicit local address/interface combination if vyos.validate.is_ipv6(peer['remote']): if not (peer['src_if'] and peer['src_addr']): - raise ConfigError('BFD IPv6 peers require explicit local address/interface setting') - - # multihop doesn't accept interface names - if peer['multihop'] and peer['src_if']: - raise ConfigError('multihop does not accept interface names') - + raise ConfigError('BFD IPv6 peers require explicit local address and interface setting') + + # multihop require source address + if peer['multihop'] and not peer['src_addr']: + raise ConfigError('Multihop require source address') + + # multihop and echo-mode cannot be used together + if peer['multihop'] and peer['echo_mode']: + raise ConfigError('Multihop and echo-mode cannot be used together') + + # echo interval can be configured only with enabled echo-mode + if peer['echo_interval'] != '' and not peer['echo_mode']: + raise ConfigError('echo-interval can be configured only with enabled echo-mode') + + # check if we deleted peers are not used in configuration + if conf.exists('protocols bgp'): + bgp_as = conf.list_nodes('protocols bgp')[0] + + # check BGP neighbors + for peer in bfd['deleted_peers']: + if conf.exists('protocols bgp {0} neighbor {1} bfd'.format(bgp_as, peer)): + raise ConfigError('Cannot delete BFD peer {0}: it is used in BGP configuration'.format(peer)) + if conf.exists('protocols bgp {0} neighbor {1} peer-group'.format(bgp_as, peer)): + peer_group = conf.return_value('protocols bgp {0} neighbor {1} peer-group'.format(bgp_as, peer)) + if conf.exists('protocols bgp {0} peer-group {1} bfd'.format(bgp_as, peer_group)): + raise ConfigError('Cannot delete BFD peer {0}: it belongs to BGP peer-group {1} with enabled BFD'.format(peer, peer_group)) return None -- cgit v1.2.3