summaryrefslogtreecommitdiff
path: root/src/conf_mode/protocols_bgp.py
diff options
context:
space:
mode:
Diffstat (limited to 'src/conf_mode/protocols_bgp.py')
-rwxr-xr-xsrc/conf_mode/protocols_bgp.py100
1 files changed, 80 insertions, 20 deletions
diff --git a/src/conf_mode/protocols_bgp.py b/src/conf_mode/protocols_bgp.py
index 54352460c..43ca37f9d 100755
--- a/src/conf_mode/protocols_bgp.py
+++ b/src/conf_mode/protocols_bgp.py
@@ -17,12 +17,15 @@
import os
from sys import exit
+from sys import argv
from vyos.config import Config
from vyos.configdict import dict_merge
+from vyos.template import is_ip
from vyos.template import render_to_string
from vyos.util import call
from vyos.util import dict_search
+from vyos.validate import is_addr_assigned
from vyos import ConfigError
from vyos import frr
from vyos import airbag
@@ -35,11 +38,24 @@ def get_config(config=None):
conf = config
else:
conf = Config()
- base = ['protocols', 'bgp']
+
+ vrf = None
+ if len(argv) > 1:
+ vrf = argv[1]
+
+ base_path = ['protocols', 'bgp']
+
+ # eqivalent of the C foo ? 'a' : 'b' statement
+ base = vrf and ['vrf', 'name', vrf, 'protocols', 'bgp'] or base_path
bgp = conf.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True)
- # Bail out early if configuration tree does not exist
+ # Assign the name of our VRF context. This MUST be done before the return
+ # statement below, else on deletion we will delete the default instance
+ # instead of the VRF instance.
+ if vrf: bgp.update({'vrf' : vrf})
+
if not conf.exists(base):
+ bgp.update({'deleted' : ''})
return bgp
# We also need some additional information from the config,
@@ -54,15 +70,44 @@ def get_config(config=None):
return bgp
+def verify_remote_as(peer_config, asn_config):
+ if 'remote_as' in peer_config:
+ return peer_config['remote_as']
+
+ if 'peer_group' in peer_config:
+ peer_group_name = peer_config['peer_group']
+ tmp = dict_search(f'peer_group.{peer_group_name}.remote_as', asn_config)
+ if tmp: return tmp
+
+ if 'interface' in peer_config:
+ if 'remote_as' in peer_config['interface']:
+ return peer_config['interface']['remote_as']
+
+ if 'peer_group' in peer_config['interface']:
+ peer_group_name = peer_config['interface']['peer_group']
+ tmp = dict_search(f'peer_group.{peer_group_name}.remote_as', asn_config)
+ if tmp: return tmp
+
+ return None
+
def verify(bgp):
if not bgp:
return None
- # Check if declared more than one ASN
- if len(bgp) > 1:
- raise ConfigError('Only one BGP AS number can be defined!')
+ # FRR bgpd only supports one Autonomous System Number, verify this!
+ asn = 0
+ for key in bgp:
+ if key.isnumeric():
+ asn +=1
+ if asn > 1:
+ raise ConfigError('Only one BGP AS number can be defined!')
for asn, asn_config in bgp.items():
+ # Workaround for https://phabricator.vyos.net/T1711
+ # We also have a vrf, and deleted key now - so we can only veriy "numbers"
+ if not asn.isnumeric():
+ continue
+
# Common verification for both peer-group and neighbor statements
for neighbor in ['neighbor', 'peer_group']:
# bail out early if there is no neighbor or peer-group statement
@@ -75,22 +120,29 @@ def verify(bgp):
# Check if the configure peer-group exists
if 'peer_group' in peer_config:
peer_group = peer_config['peer_group']
- if peer_group not in asn_config['peer_group']:
+ if 'peer_group' not in asn_config or peer_group not in asn_config['peer_group']:
raise ConfigError(f'Specified peer-group "{peer_group}" for '\
f'neighbor "{neighbor}" does not exist!')
- # Some checks can/must only be done on a neighbor and nor a peer-group
+ # ttl-security and ebgp-multihop can't be used in the same configration
+ if 'ebgp_multihop' in peer_config and 'ttl_security' in peer_config:
+ raise ConfigError('You can\'t set both ebgp-multihop and ttl-security hops')
+
+ # Check spaces in the password
+ if 'password' in peer_config and ' ' in peer_config['password']:
+ raise ConfigError('You can\'t use spaces in the password')
+
+ # Some checks can/must only be done on a neighbor and not a peer-group
if neighbor == 'neighbor':
# remote-as must be either set explicitly for the neighbor
# or for the entire peer-group
- if 'interface' in peer_config:
- if 'remote_as' not in peer_config['interface']:
- if 'peer_group' not in peer_config['interface'] or 'remote_as' not in asn_config['peer_group'][ peer_config['interface']['peer_group'] ]:
- raise ConfigError('Remote AS must be set for neighbor or peer-group!')
+ if not verify_remote_as(peer_config, asn_config):
+ raise ConfigError(f'Neighbor "{peer}" remote-as must be set!')
- elif 'remote_as' not in peer_config:
- if 'peer_group' not in peer_config or 'remote_as' not in asn_config['peer_group'][ peer_config['peer_group'] ]:
- raise ConfigError('Remote AS must be set for neighbor or peer-group!')
+ # Only checks for ipv4 and ipv6 neighbors
+ # Check if neighbor address is assigned as system interface address
+ if is_ip(peer) and is_addr_assigned(peer):
+ raise ConfigError(f'Can\'t configure local address as neighbor "{peer}"')
for afi in ['ipv4_unicast', 'ipv6_unicast', 'l2vpn_evpn']:
# Bail out early if address family is not configured
@@ -127,10 +179,10 @@ def verify(bgp):
if 'remote_as' in peer_config and asn != peer_config['remote_as']:
raise ConfigError('route-reflector-client only supported for iBGP peers')
else:
- peer_group_as = dict_search(f'peer_group.{peer_group}.remote_as', asn_config)
- if 'peer_group' in peer_config and peer_group_as != None and peer_group_as != asn:
- raise ConfigError('route-reflector-client only supported for iBGP peers')
-
+ if 'peer_group' in peer_config:
+ peer_group_as = dict_search(f'peer_group.{peer_group}.remote_as', asn_config)
+ if peer_group_as != None and peer_group_as != asn:
+ raise ConfigError('route-reflector-client only supported for iBGP peers')
# Throw an error if a peer group is not configured for allow range
for prefix in dict_search('listen.range', asn_config) or []:
@@ -146,7 +198,7 @@ def verify(bgp):
return None
def generate(bgp):
- if not bgp:
+ if not bgp or 'deleted' in bgp:
bgp['new_frr_config'] = ''
return None
@@ -154,6 +206,8 @@ def generate(bgp):
# of the config dict
asn = list(bgp.keys())[0]
bgp[asn]['asn'] = asn
+ if 'vrf' in bgp:
+ bgp[asn]['vrf'] = bgp['vrf']
bgp['new_frr_config'] = render_to_string('frr/bgp.frr.tmpl', bgp[asn])
return None
@@ -162,7 +216,13 @@ def apply(bgp):
# Save original configuration prior to starting any commit actions
frr_cfg = frr.FRRConfig()
frr_cfg.load_configuration(frr_daemon)
- frr_cfg.modify_section(f'^router bgp \d+$', '')
+
+ if 'vrf' in bgp:
+ vrf = bgp['vrf']
+ frr_cfg.modify_section(f'^router bgp \d+ vrf {vrf}$', '')
+ else:
+ frr_cfg.modify_section('^router bgp \d+$', '')
+
frr_cfg.add_before(r'(ip prefix-list .*|route-map .*|line vty)', bgp['new_frr_config'])
frr_cfg.commit_configuration(frr_daemon)