From 36e5f07f8dda51cc5bb0a105077e751f1c851435 Mon Sep 17 00:00:00 2001 From: Lucas Christian Date: Thu, 7 Oct 2021 20:41:02 -0700 Subject: T562: Config syntax for defining DNS forward authoritative zones --- interface-definitions/dns-forwarding.xml.in | 452 ++++++++++++++++++++- .../include/dns/time-to-live.xml.i | 15 + 2 files changed, 466 insertions(+), 1 deletion(-) create mode 100644 interface-definitions/include/dns/time-to-live.xml.i (limited to 'interface-definitions') diff --git a/interface-definitions/dns-forwarding.xml.in b/interface-definitions/dns-forwarding.xml.in index 5b0c87597..ced138bff 100644 --- a/interface-definitions/dns-forwarding.xml.in +++ b/interface-definitions/dns-forwarding.xml.in @@ -105,6 +105,456 @@ + + + Domain to host authoritative records for + + text + An absolute DNS name + + + ^[-_a-zA-Z0-9.]{1,63}$ + + + + + + DNS zone records + + + + + "A" record + + text + A DNS name relative to the root record + + + @ + Root record + + + ^([-_a-zA-Z0-9.]{1,63}|@)(?<!\.)$ + + + + + + IPv4 address [REQUIRED] + + ipv4 + IPv4 address + + + + + + + + #include + #include + + + + + "AAAA" record + + text + A DNS name relative to the root record + + + @ + Root record + + + ^([-_a-zA-Z0-9.]{1,63}|@)(?<!\.)$ + + + + + + IPv6 address [REQUIRED] + + ipv6 + IPv6 address + + + + + + + + #include + #include + + + + + "CNAME" record + + text + A DNS name relative to the root record + + + @ + Root record + + + ^([-_a-zA-Z0-9.]{1,63}|@)(?<!\.)$ + + + + + + Target DNS name [REQUIRED] + + name.example.com + An absolute DNS name + + + ^[-_a-zA-Z0-9.]{1,63}(?<!\.)$ + + + + #include + #include + + + + + "MX" record + + text + A DNS name relative to the root record + + + @ + Root record + + + ^([-_a-zA-Z0-9.]{1,63}|@)(?<!\.)$ + + + + + + Mail server [REQUIRED] + + name.example.com + An absolute DNS name + + + ^[-_a-zA-Z0-9.]{1,63}(?<!\.)$ + + + + + + Server priority + + u32:1-999 + Server priority (lower numbers are higher priority) + + + + + + 10 + + + + #include + #include + + + + + "PTR" record + + text + A DNS name relative to the root record + + + @ + Root record + + + ^([-_a-zA-Z0-9.]{1,63}|@)(?<!\.)$ + + + + + + Target DNS name [REQUIRED] + + name.example.com + An absolute DNS name + + + ^[-_a-zA-Z0-9.]{1,63}(?<!\.)$ + + + + #include + #include + + + + + "TXT" record + + text + A DNS name relative to the root record + + + @ + Root record + + + ^([-_a-zA-Z0-9.]{1,63}|@)(?<!\.)$ + + + + + + Record contents [REQUIRED] + + text + Record contents + + + + + #include + #include + + + + + "SPF" record (type=SPF) + + text + A DNS name relative to the root record + + + @ + Root record + + + ^([-_a-zA-Z0-9.]{1,63}|@)(?<!\.)$ + + + + + + Record contents [REQUIRED] + + text + Record contents + + + + #include + #include + + + + + Host an DNS "SRV" record + + text + A DNS name relative to the root record + + + @ + Root record + + + ^([-_a-zA-Z0-9.]{1,63}|@)(?<!\.)$ + + + + + + Service entry [REQUIRED] + + u32:0-65535 + Entry number + + + + + + + + + Server hostname [REQUIRED] + + name.example.com + An absolute DNS name + + + ^[-_a-zA-Z0-9.]{1,63}(?<!\.)$ + + + + + + Port number [REQUIRED] + + u32:0-65535 + TCP/UDP port number + + + + + + + + + Entry priority + + u32:0-65535 + Entry priority (lower numbers are higher priority) + + + + + + 10 + + + + Entry weight + + u32:0-65535 + Entry weight + + + + + + 0 + + + + #include + #include + + + + + "NAPTR" record + + text + A DNS name relative to the root record + + + @ + Root record + + + ^([-_a-zA-Z0-9.]{1,63}|@)(?<!\.)$ + + + + + + NAPTR rule [REQUIRED] + + u32:0-65535 + Rule number + + + + + + + + + Rule order + + u32:0-65535 + Rule order (lower order is evaluated first) + + + + + + + + + Rule preference + + u32:0-65535 + Rule preference + + + + + + 0 + + + + "S" flag + + + + + + "A" flag + + + + + + "U" flag + + + + + + "P" flag + + + + + + Service type + + ^[a-zA-Z][a-zA-Z0-9]{0,31}(\+[a-zA-Z][a-zA-Z0-9]{0,31})?$ + + + + + + Regular expression + + + + + Replacement DNS name + + name.example.com + An absolute DNS name + + + ^[-_a-zA-Z0-9.]{1,63}(?<!\.)$ + + + + + + #include + #include + + + + + #include + + Do not use local /etc/hosts file in name resolution @@ -114,7 +564,7 @@ Makes the server authoritatively not aware of RFC1918 addresses - + diff --git a/interface-definitions/include/dns/time-to-live.xml.i b/interface-definitions/include/dns/time-to-live.xml.i new file mode 100644 index 000000000..5c1a1472d --- /dev/null +++ b/interface-definitions/include/dns/time-to-live.xml.i @@ -0,0 +1,15 @@ + + + + Time-to-live (TTL) + + u32:0-2147483647 + TTL in seconds + + + + + + 300 + + -- cgit v1.2.3 From d6a79444ff131220529938b0ff849f43f3a9e1c0 Mon Sep 17 00:00:00 2001 From: Lucas Christian Date: Tue, 12 Oct 2021 22:51:29 -0700 Subject: Fix default values --- interface-definitions/dns-forwarding.xml.in | 2 +- src/conf_mode/dns_forwarding.py | 20 ++++++++++---------- 2 files changed, 11 insertions(+), 11 deletions(-) (limited to 'interface-definitions') diff --git a/interface-definitions/dns-forwarding.xml.in b/interface-definitions/dns-forwarding.xml.in index ced138bff..4faf604ad 100644 --- a/interface-definitions/dns-forwarding.xml.in +++ b/interface-definitions/dns-forwarding.xml.in @@ -360,7 +360,7 @@ - Host an DNS "SRV" record + "SRV" record text A DNS name relative to the root record diff --git a/src/conf_mode/dns_forwarding.py b/src/conf_mode/dns_forwarding.py index 278fa7226..5a5ae4d14 100755 --- a/src/conf_mode/dns_forwarding.py +++ b/src/conf_mode/dns_forwarding.py @@ -94,7 +94,7 @@ def get_config(config=None): rdata = recorddata[rtype][subnode] if rtype in [ 'a', 'aaaa' ]: - rdefaults = defaults(base + ['authoritative-domain', rtype]) # T2665 + rdefaults = defaults(base + ['authoritative-domain', 'records', rtype]) # T2665 rdata = dict_merge(rdefaults, rdata) if not 'address' in rdata: @@ -109,7 +109,7 @@ def get_config(config=None): 'value': address }) elif rtype in ['cname', 'ptr']: - rdefaults = defaults(base + ['authoritative-domain', rtype]) # T2665 + rdefaults = defaults(base + ['authoritative-domain', 'records', rtype]) # T2665 rdata = dict_merge(rdefaults, rdata) if not 'target' in rdata: @@ -123,7 +123,7 @@ def get_config(config=None): 'value': '{}.'.format(rdata['target']) }) elif rtype == 'mx': - rdefaults = defaults(base + ['authoritative-domain', rtype]) # T2665 + rdefaults = defaults(base + ['authoritative-domain', 'records', rtype]) # T2665 del rdefaults['server'] rdata = dict_merge(rdefaults, rdata) @@ -133,7 +133,7 @@ def get_config(config=None): for servername in rdata['server']: serverdata = rdata['server'][servername] - serverdefaults = defaults(base + ['authoritative-domain', rtype, 'server']) # T2665 + serverdefaults = defaults(base + ['authoritative-domain', 'records', rtype, 'server']) # T2665 serverdata = dict_merge(serverdefaults, serverdata) zone['records'].append({ 'name': subnode, @@ -142,7 +142,7 @@ def get_config(config=None): 'value': '{} {}.'.format(serverdata['priority'], servername) }) elif rtype == 'txt': - rdefaults = defaults(base + ['authoritative-domain', rtype]) # T2665 + rdefaults = defaults(base + ['authoritative-domain', 'records', rtype]) # T2665 rdata = dict_merge(rdefaults, rdata) if not 'value' in rdata: @@ -157,7 +157,7 @@ def get_config(config=None): 'value': "\"{}\"".format(value.replace("\"", "\\\"")) }) elif rtype == 'spf': - rdefaults = defaults(base + ['authoritative-domain', rtype]) # T2665 + rdefaults = defaults(base + ['authoritative-domain', 'records', rtype]) # T2665 rdata = dict_merge(rdefaults, rdata) if not 'value' in rdata: @@ -171,7 +171,7 @@ def get_config(config=None): 'value': '"{}"'.format(rdata['value'].replace("\"", "\\\"")) }) elif rtype == 'srv': - rdefaults = defaults(base + ['authoritative-domain', rtype]) # T2665 + rdefaults = defaults(base + ['authoritative-domain', 'records', rtype]) # T2665 del rdefaults['entry'] rdata = dict_merge(rdefaults, rdata) @@ -181,7 +181,7 @@ def get_config(config=None): for entryno in rdata['entry']: entrydata = rdata['entry'][entryno] - entrydefaults = defaults(base + ['authoritative-domain', rtype, 'entry']) # T2665 + entrydefaults = defaults(base + ['authoritative-domain', 'records', rtype, 'entry']) # T2665 entrydata = dict_merge(entrydefaults, entrydata) if not 'hostname' in entrydata: @@ -199,7 +199,7 @@ def get_config(config=None): 'value': '{} {} {} {}.'.format(entrydata['priority'], entrydata['weight'], entrydata['port'], entrydata['hostname']) }) elif rtype == 'naptr': - rdefaults = defaults(base + ['authoritative-domain', rtype]) # T2665 + rdefaults = defaults(base + ['authoritative-domain', 'records', rtype]) # T2665 del rdefaults['rule'] rdata = dict_merge(rdefaults, rdata) @@ -210,7 +210,7 @@ def get_config(config=None): for ruleno in rdata['rule']: ruledata = rdata['rule'][ruleno] - ruledefaults = defaults(base + ['authoritative-domain', rtype, 'rule']) # T2665 + ruledefaults = defaults(base + ['authoritative-domain', 'records', rtype, 'rule']) # T2665 ruledata = dict_merge(ruledefaults, ruledata) flags = "" if 'lookup-srv' in ruledata: -- cgit v1.2.3 From 3d413c06621c3c299972380391285b456919deb5 Mon Sep 17 00:00:00 2001 From: Viacheslav Date: Tue, 23 Nov 2021 17:58:58 +0000 Subject: netns: T3829: Allow attach dumX interfaces to netns --- interface-definitions/interfaces-dummy.xml.in | 1 + 1 file changed, 1 insertion(+) (limited to 'interface-definitions') diff --git a/interface-definitions/interfaces-dummy.xml.in b/interface-definitions/interfaces-dummy.xml.in index 2bc88c1a7..4d4c44160 100644 --- a/interface-definitions/interfaces-dummy.xml.in +++ b/interface-definitions/interfaces-dummy.xml.in @@ -27,6 +27,7 @@ #include + #include #include -- cgit v1.2.3 From a2fe5dc40d439a28deb4d09e78fcf93aba4555a4 Mon Sep 17 00:00:00 2001 From: Viacheslav Date: Tue, 23 Nov 2021 18:07:12 +0000 Subject: netns: T3829: Add XML for netns CLI configuration --- .../include/interface/netns.xml.i | 14 +++++++++++++ interface-definitions/netns.xml.in | 23 ++++++++++++++++++++++ 2 files changed, 37 insertions(+) create mode 100644 interface-definitions/include/interface/netns.xml.i create mode 100644 interface-definitions/netns.xml.in (limited to 'interface-definitions') diff --git a/interface-definitions/include/interface/netns.xml.i b/interface-definitions/include/interface/netns.xml.i new file mode 100644 index 000000000..39f9118fa --- /dev/null +++ b/interface-definitions/include/interface/netns.xml.i @@ -0,0 +1,14 @@ + + + + Network namespace name + + text + Network namespace name + + + netns name + + + + diff --git a/interface-definitions/netns.xml.in b/interface-definitions/netns.xml.in new file mode 100644 index 000000000..88451737a --- /dev/null +++ b/interface-definitions/netns.xml.in @@ -0,0 +1,23 @@ + + + + + Network namespace + 299 + + + + + Network namespace name + + + + + Network namespace description + + + + + + + -- cgit v1.2.3 From 47584e030f3341b265e826643951648982cb20d0 Mon Sep 17 00:00:00 2001 From: Viacheslav Date: Tue, 23 Nov 2021 18:11:10 +0000 Subject: netns: T3829: Ability to configure network namespaces --- interface-definitions/netns.xml.in | 10 +-- python/vyos/ifconfig/interface.py | 42 ++++++--- python/vyos/util.py | 18 ++++ smoketest/scripts/cli/test_interfaces_netns.py | 83 +++++++++++++++++ src/conf_mode/netns.py | 118 +++++++++++++++++++++++++ 5 files changed, 254 insertions(+), 17 deletions(-) create mode 100755 smoketest/scripts/cli/test_interfaces_netns.py create mode 100755 src/conf_mode/netns.py (limited to 'interface-definitions') diff --git a/interface-definitions/netns.xml.in b/interface-definitions/netns.xml.in index 88451737a..80de805fb 100644 --- a/interface-definitions/netns.xml.in +++ b/interface-definitions/netns.xml.in @@ -9,13 +9,13 @@ Network namespace name + + ^[a-zA-Z0-9-_]{1,100} + + Netns name must be alphanumeric and can contain hyphens and underscores. - - - Network namespace description - - + #include diff --git a/python/vyos/ifconfig/interface.py b/python/vyos/ifconfig/interface.py index 50da2553a..bcb692697 100755 --- a/python/vyos/ifconfig/interface.py +++ b/python/vyos/ifconfig/interface.py @@ -37,6 +37,7 @@ from vyos.util import mac2eui64 from vyos.util import dict_search from vyos.util import read_file from vyos.util import get_interface_config +from vyos.util import get_interface_namespace from vyos.util import is_systemd_service_active from vyos.template import is_ipv4 from vyos.template import is_ipv6 @@ -515,19 +516,33 @@ class Interface(Control): if prev_state == 'up': self.set_admin_state('up') + def del_netns(self, netns): + """ + Remove interface from given NETNS. + """ + + # If NETNS does not exist then there is nothing to delete + if not os.path.exists(f'/run/netns/{netns}'): + return None + + # As a PoC we only allow 'dummy' interfaces + if 'dum' not in self.ifname: + return None + + # Check if interface realy exists in namespace + if get_interface_namespace(self.ifname) != None: + self._cmd(f'ip netns exec {get_interface_namespace(self.ifname)} ip link del dev {self.ifname}') + return + def set_netns(self, netns): """ - Add/Remove interface from given NETNS. + Add interface from given NETNS. Example: >>> from vyos.ifconfig import Interface >>> Interface('dum0').set_netns('foo') """ - #tmp = self.get_interface('netns') - #if tmp == netns: - # return None - self.set_interface('netns', netns) def set_vrf(self, vrf): @@ -1371,6 +1386,16 @@ class Interface(Control): if mac: self.set_mac(mac) + # If interface is connected to NETNS we don't have to check all other + # settings like MTU/IPv6/sysctl values, etc. + # Since the interface is pushed onto a separate logical stack + # Configure NETNS + if dict_search('netns', config) != None: + self.set_netns(config.get('netns', '')) + return + else: + self.del_netns(config.get('netns', '')) + # Update interface description self.set_alias(config.get('description', '')) @@ -1423,13 +1448,6 @@ class Interface(Control): # checked before self.set_vrf(config.get('vrf', '')) - # If interface attached to NETNS we shouldn't check all other settings - # As interface placed to separate logical stack - # Configure NETNS - if dict_search('netns', config) != None: - self.set_netns(config.get('netns', '')) - return - # Configure MSS value for IPv4 TCP connections tmp = dict_search('ip.adjust_mss', config) value = tmp if (tmp != None) else '0' diff --git a/python/vyos/util.py b/python/vyos/util.py index 9aa1f98d2..6c375e1a6 100644 --- a/python/vyos/util.py +++ b/python/vyos/util.py @@ -794,6 +794,24 @@ def get_interface_address(interface): tmp = loads(cmd(f'ip -d -j addr show {interface}'))[0] return tmp +def get_interface_namespace(iface): + """ + Returns wich netns the interface belongs to + """ + from json import loads + # Check if netns exist + tmp = loads(cmd(f'ip --json netns ls')) + if len(tmp) == 0: + return None + + for ns in tmp: + namespace = f'{ns["name"]}' + # Search interface in each netns + data = loads(cmd(f'ip netns exec {namespace} ip -j link show')) + for compare in data: + if iface == compare["ifname"]: + return namespace + def get_all_vrfs(): """ Return a dictionary of all system wide known VRF instances """ from json import loads diff --git a/smoketest/scripts/cli/test_interfaces_netns.py b/smoketest/scripts/cli/test_interfaces_netns.py new file mode 100755 index 000000000..9975a6b09 --- /dev/null +++ b/smoketest/scripts/cli/test_interfaces_netns.py @@ -0,0 +1,83 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2021 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 . + +import re +import os +import json +import unittest + +from netifaces import interfaces +from base_vyostest_shim import VyOSUnitTestSHIM + +from vyos.configsession import ConfigSession +from vyos.configsession import ConfigSessionError +from vyos.ifconfig import Interface +from vyos.ifconfig import Section +from vyos.util import cmd + +base_path = ['netns'] +namespaces = ['mgmt', 'front', 'back', 'ams-ix'] + +class NETNSTest(VyOSUnitTestSHIM.TestCase): + + def setUp(self): + self._interfaces = ['dum10', 'dum12', 'dum50'] + + def test_create_netns(self): + for netns in namespaces: + base = base_path + ['name', netns] + self.cli_set(base) + + # commit changes + self.cli_commit() + + netns_list = cmd('ip netns ls') + + # Verify NETNS configuration + for netns in namespaces: + self.assertTrue(netns in netns_list) + + + def test_netns_assign_interface(self): + netns = 'foo' + self.cli_set(['netns', 'name', netns]) + + # Set + for iface in self._interfaces: + self.cli_set(['interfaces', 'dummy', iface, 'netns', netns]) + + # commit changes + self.cli_commit() + + netns_iface_list = cmd(f'sudo ip netns exec {netns} ip link show') + + for iface in self._interfaces: + self.assertTrue(iface in netns_iface_list) + + # Delete + for iface in self._interfaces: + self.cli_delete(['interfaces', 'dummy', iface, 'netns', netns]) + + # commit changes + self.cli_commit() + + netns_iface_list = cmd(f'sudo ip netns exec {netns} ip link show') + + for iface in self._interfaces: + self.assertNotIn(iface, netns_iface_list) + +if __name__ == '__main__': + unittest.main(verbosity=2) diff --git a/src/conf_mode/netns.py b/src/conf_mode/netns.py new file mode 100755 index 000000000..0924eb616 --- /dev/null +++ b/src/conf_mode/netns.py @@ -0,0 +1,118 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2021 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 . + +import os + +from sys import exit +from tempfile import NamedTemporaryFile + +from vyos.config import Config +from vyos.configdict import node_changed +from vyos.ifconfig import Interface +from vyos.util import call +from vyos.util import dict_search +from vyos.util import get_interface_config +from vyos import ConfigError +from vyos import airbag +airbag.enable() + + +def netns_interfaces(c, match): + """ + get NETNS bound interfaces + """ + matched = [] + old_level = c.get_level() + c.set_level(['interfaces']) + section = c.get_config_dict([], get_first_key=True) + for type in section: + interfaces = section[type] + for name in interfaces: + interface = interfaces[name] + if 'netns' in interface: + v = interface.get('netns', '') + if v == match: + matched.append(name) + + c.set_level(old_level) + return matched + +def get_config(config=None): + if config: + conf = config + else: + conf = Config() + + base = ['netns'] + netns = conf.get_config_dict(base, get_first_key=True, + no_tag_node_value_mangle=True) + + # determine which NETNS has been removed + for name in node_changed(conf, base + ['name']): + if 'netns_remove' not in netns: + netns.update({'netns_remove' : {}}) + + netns['netns_remove'][name] = {} + # get NETNS bound interfaces + interfaces = netns_interfaces(conf, name) + if interfaces: netns['netns_remove'][name]['interface'] = interfaces + + return netns + +def verify(netns): + # ensure NETNS is not assigned to any interface + if 'netns_remove' in netns: + for name, config in netns['netns_remove'].items(): + if 'interface' in config: + raise ConfigError(f'Can not remove NETNS "{name}", it still has '\ + f'member interfaces!') + + if 'name' in netns: + for name, config in netns['name'].items(): + print(name) + + return None + + +def generate(netns): + if not netns: + return None + + return None + + +def apply(netns): + + for tmp in (dict_search('netns_remove', netns) or []): + if os.path.isfile(f'/run/netns/{tmp}'): + call(f'ip netns del {tmp}') + + if 'name' in netns: + for name, config in netns['name'].items(): + if not os.path.isfile(f'/run/netns/{name}'): + call(f'ip netns add {name}') + + return None + +if __name__ == '__main__': + try: + c = get_config() + verify(c) + generate(c) + apply(c) + except ConfigError as e: + print(e) + exit(1) -- cgit v1.2.3 From 2b8d9131d6c32b52b4dde42e663ac8518d955852 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sat, 4 Dec 2021 07:18:26 +0100 Subject: xml: bfd: hint default values on CLI tab complete --- interface-definitions/include/bfd-common.xml.i | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'interface-definitions') diff --git a/interface-definitions/include/bfd-common.xml.i b/interface-definitions/include/bfd-common.xml.i index 1d6ab5d55..6021576f6 100644 --- a/interface-definitions/include/bfd-common.xml.i +++ b/interface-definitions/include/bfd-common.xml.i @@ -15,7 +15,7 @@ Minimum interval of receiving control packets u32:10-60000 - Interval in milliseconds + Interval in milliseconds (default: 300) @@ -28,7 +28,7 @@ Minimum interval of transmitting control packets u32:10-60000 - Interval in milliseconds + Interval in milliseconds (default: 300) @@ -41,7 +41,7 @@ Multiplier to determine packet loss u32:2-255 - Remote transmission interval will be multiplied by this value + Remote transmission interval will be multiplied by this value (default: 3) -- cgit v1.2.3 From 63cadf52a4b8d8aa31a868c4b19e44a9eff12d37 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sat, 4 Dec 2021 07:23:14 +0100 Subject: bfd: T4044: add VRF support for peers --- interface-definitions/protocols-bfd.xml.in | 1 + smoketest/scripts/cli/test_protocols_bfd.py | 11 ++++++++++- src/conf_mode/protocols_bfd.py | 4 ++++ 3 files changed, 15 insertions(+), 1 deletion(-) (limited to 'interface-definitions') diff --git a/interface-definitions/protocols-bfd.xml.in b/interface-definitions/protocols-bfd.xml.in index 7b22b8125..d5a968001 100644 --- a/interface-definitions/protocols-bfd.xml.in +++ b/interface-definitions/protocols-bfd.xml.in @@ -73,6 +73,7 @@ + #include diff --git a/smoketest/scripts/cli/test_protocols_bfd.py b/smoketest/scripts/cli/test_protocols_bfd.py index 46a019dfc..532814ef3 100755 --- a/smoketest/scripts/cli/test_protocols_bfd.py +++ b/smoketest/scripts/cli/test_protocols_bfd.py @@ -24,6 +24,7 @@ PROCESS_NAME = 'bfdd' base_path = ['protocols', 'bfd'] dum_if = 'dum1001' +vrf_name = 'red' peers = { '192.0.2.10' : { 'intv_rx' : '500', @@ -42,7 +43,7 @@ peers = { }, '2001:db8::a' : { 'source_addr': '2001:db8::1', - 'source_intf': dum_if, + 'vrf' : vrf_name, }, '2001:db8::b' : { 'source_addr': '2001:db8::1', @@ -73,6 +74,8 @@ class TestProtocolsBFD(VyOSUnitTestSHIM.TestCase): self.assertTrue(process_named_running(PROCESS_NAME)) def test_bfd_peer(self): + self.cli_set(['vrf', 'name', vrf_name, 'table', '1000']) + for peer, peer_config in peers.items(): if 'echo_mode' in peer_config: self.cli_set(base_path + ['peer', peer, 'echo-mode']) @@ -92,6 +95,8 @@ class TestProtocolsBFD(VyOSUnitTestSHIM.TestCase): self.cli_set(base_path + ['peer', peer, 'source', 'address', peer_config["source_addr"]]) if 'source_intf' in peer_config: self.cli_set(base_path + ['peer', peer, 'source', 'interface', peer_config["source_intf"]]) + if 'vrf' in peer_config: + self.cli_set(base_path + ['peer', peer, 'vrf', peer_config["vrf"]]) # commit changes self.cli_commit() @@ -106,6 +111,8 @@ class TestProtocolsBFD(VyOSUnitTestSHIM.TestCase): tmp += f' local-address {peer_config["source_addr"]}' if 'source_intf' in peer_config: tmp += f' interface {peer_config["source_intf"]}' + if 'vrf' in peer_config: + tmp += f' vrf {peer_config["vrf"]}' self.assertIn(tmp, frrconfig) peerconfig = self.getFRRconfig(f' peer {peer}', end='') @@ -126,6 +133,8 @@ class TestProtocolsBFD(VyOSUnitTestSHIM.TestCase): else: self.assertNotIn(f'shutdown', peerconfig) + self.cli_delete(['vrf', 'name', vrf_name]) + def test_bfd_profile(self): peer = '192.0.2.10' diff --git a/src/conf_mode/protocols_bfd.py b/src/conf_mode/protocols_bfd.py index 1f8e4ddc6..6981d0db1 100755 --- a/src/conf_mode/protocols_bfd.py +++ b/src/conf_mode/protocols_bfd.py @@ -18,6 +18,7 @@ import os from vyos.config import Config from vyos.configdict import dict_merge +from vyos.configverify import verify_vrf from vyos.template import is_ipv6 from vyos.template import render_to_string from vyos.validate import is_ipv6_link_local @@ -83,6 +84,9 @@ def verify(bfd): if 'source' in peer_config and 'interface' in peer_config['source']: raise ConfigError('Multihop and source interface cannot be used together') + if 'vrf' in peer_config: + verify_vrf(peer_config) + return None def generate(bfd): -- cgit v1.2.3 From 9ad20a63b3f5557ea03e8778f29a173bb5f56cef Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sat, 4 Dec 2021 07:28:26 +0100 Subject: bfd: T4043: add support for passive mode Mark session as passive: a passive session will not attempt to start the connection and will wait for control packets from peer before it begins replying. This feature is useful when you have a router that acts as the central node of a star network and you want to avoid sending BFD control packets you don't need to. The default is active-mode --- data/templates/frr/bfdd.frr.tmpl | 6 ++++++ interface-definitions/include/bfd-common.xml.i | 6 ++++++ smoketest/scripts/cli/test_protocols_bfd.py | 10 ++++++++++ 3 files changed, 22 insertions(+) (limited to 'interface-definitions') diff --git a/data/templates/frr/bfdd.frr.tmpl b/data/templates/frr/bfdd.frr.tmpl index dd26b930a..e0e94c24d 100644 --- a/data/templates/frr/bfdd.frr.tmpl +++ b/data/templates/frr/bfdd.frr.tmpl @@ -13,6 +13,9 @@ bfd {% if profile_config.echo_mode is defined %} echo-mode {% endif %} +{% if profile_config.passive is defined %} + passive-mode +{% endif %} {% if profile_config.shutdown is defined %} shutdown {% else %} @@ -35,6 +38,9 @@ bfd {% if peer_config.echo_mode is defined %} echo-mode {% endif %} +{% if peer_config.passive is defined %} + passive-mode +{% endif %} {% if peer_config.shutdown is defined %} shutdown {% else %} diff --git a/interface-definitions/include/bfd-common.xml.i b/interface-definitions/include/bfd-common.xml.i index 6021576f6..8379784f7 100644 --- a/interface-definitions/include/bfd-common.xml.i +++ b/interface-definitions/include/bfd-common.xml.i @@ -63,6 +63,12 @@ + + + Do not attempt to start sessions + + + Disable this peer diff --git a/smoketest/scripts/cli/test_protocols_bfd.py b/smoketest/scripts/cli/test_protocols_bfd.py index 532814ef3..d33a64301 100755 --- a/smoketest/scripts/cli/test_protocols_bfd.py +++ b/smoketest/scripts/cli/test_protocols_bfd.py @@ -38,6 +38,7 @@ peers = { 'intv_mult' : '100', 'intv_rx' : '222', 'intv_tx' : '333', + 'passive' : '', 'shutdown' : '', 'source_intf': dum_if, }, @@ -63,6 +64,7 @@ profiles = { 'bar' : { 'intv_mult' : '102', 'intv_rx' : '444', + 'passive' : '', }, } @@ -89,6 +91,8 @@ class TestProtocolsBFD(VyOSUnitTestSHIM.TestCase): self.cli_set(base_path + ['peer', peer, 'interval', 'transmit', peer_config["intv_tx"]]) if 'multihop' in peer_config: self.cli_set(base_path + ['peer', peer, 'multihop']) + if 'passive' in peer_config: + self.cli_set(base_path + ['peer', peer, 'passive']) if 'shutdown' in peer_config: self.cli_set(base_path + ['peer', peer, 'shutdown']) if 'source_addr' in peer_config: @@ -128,6 +132,8 @@ class TestProtocolsBFD(VyOSUnitTestSHIM.TestCase): self.assertIn(f'receive-interval {peer_config["intv_rx"]}', peerconfig) if 'intv_tx' in peer_config: self.assertIn(f'transmit-interval {peer_config["intv_tx"]}', peerconfig) + if 'passive' in peer_config: + self.assertIn(f'passive-mode', peerconfig) if 'shutdown' in peer_config: self.assertIn(f'shutdown', peerconfig) else: @@ -149,6 +155,8 @@ class TestProtocolsBFD(VyOSUnitTestSHIM.TestCase): self.cli_set(base_path + ['profile', profile, 'interval', 'receive', profile_config["intv_rx"]]) if 'intv_tx' in profile_config: self.cli_set(base_path + ['profile', profile, 'interval', 'transmit', profile_config["intv_tx"]]) + if 'passive' in profile_config: + self.cli_set(base_path + ['profile', profile, 'passive']) if 'shutdown' in profile_config: self.cli_set(base_path + ['profile', profile, 'shutdown']) @@ -171,6 +179,8 @@ class TestProtocolsBFD(VyOSUnitTestSHIM.TestCase): self.assertIn(f'receive-interval {profile_config["intv_rx"]}', config) if 'intv_tx' in profile_config: self.assertIn(f'transmit-interval {profile_config["intv_tx"]}', config) + if 'passive' in profile_config: + self.assertIn(f'passive-mode', config) if 'shutdown' in profile_config: self.assertIn(f'shutdown', config) else: -- cgit v1.2.3 From 84b29c7c71af6663ef26dc40475cf4b0e5b179a6 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sat, 4 Dec 2021 14:15:37 +0100 Subject: validators: T4042: rename bgp-route-target -> bgp-rd-rt --- .../include/bgp/afi-l2vpn-common.xml.i | 6 +-- .../include/bgp/afi-route-target-vpn.xml.i | 6 +-- src/validators/bgp-rd-rt | 51 ++++++++++++++++++++++ src/validators/bgp-route-target | 51 ---------------------- 4 files changed, 57 insertions(+), 57 deletions(-) create mode 100755 src/validators/bgp-rd-rt delete mode 100755 src/validators/bgp-route-target (limited to 'interface-definitions') diff --git a/interface-definitions/include/bgp/afi-l2vpn-common.xml.i b/interface-definitions/include/bgp/afi-l2vpn-common.xml.i index 8deb189ab..d586635c8 100644 --- a/interface-definitions/include/bgp/afi-l2vpn-common.xml.i +++ b/interface-definitions/include/bgp/afi-l2vpn-common.xml.i @@ -25,7 +25,7 @@ Route target (A.B.C.D:MN|EF:OPQR|GHJK:MN) - + @@ -37,7 +37,7 @@ Route target (A.B.C.D:MN|EF:OPQR|GHJK:MN) - + @@ -49,7 +49,7 @@ Route target (A.B.C.D:MN|EF:OPQR|GHJK:MN) - + diff --git a/interface-definitions/include/bgp/afi-route-target-vpn.xml.i b/interface-definitions/include/bgp/afi-route-target-vpn.xml.i index 0cd0fdd76..5784f9eac 100644 --- a/interface-definitions/include/bgp/afi-route-target-vpn.xml.i +++ b/interface-definitions/include/bgp/afi-route-target-vpn.xml.i @@ -17,7 +17,7 @@ Space separated route target list (A.B.C.D:MN|EF:OPQR|GHJK:MN) - + @@ -29,7 +29,7 @@ Space separated route target list (A.B.C.D:MN|EF:OPQR|GHJK:MN) - + @@ -41,7 +41,7 @@ Space separated route target list (A.B.C.D:MN|EF:OPQR|GHJK:MN) - + diff --git a/src/validators/bgp-rd-rt b/src/validators/bgp-rd-rt new file mode 100755 index 000000000..24b0043f9 --- /dev/null +++ b/src/validators/bgp-rd-rt @@ -0,0 +1,51 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2021 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 . + +from argparse import ArgumentParser +from vyos.template import is_ipv4 + +parser = ArgumentParser() +group = parser.add_mutually_exclusive_group() +group.add_argument('--route-target', action='store', help='Validate one BGP route-target') +group.add_argument('--route-target-multi', action='store', help='Validate multiple, whitespace separated BGP route-targets') +args = parser.parse_args() + +def is_valid_rt(rt): + # every route target needs to have a colon and must consists of two parts + value = rt.split(':') + if len(value) != 2: + return False + # A route target must either be only numbers, or the first part must be an + # IPv4 address + if (is_ipv4(value[0]) or value[0].isdigit()) and value[1].isdigit(): + return True + return False + +if __name__ == '__main__': + if args.route_target: + if not is_valid_rt(args.route_target): + exit(1) + + elif args.route_target_multi: + for rt in args.route_target_multi.split(' '): + if not is_valid_rt(rt): + exit(1) + + else: + parser.print_help() + exit(1) + + exit(0) diff --git a/src/validators/bgp-route-target b/src/validators/bgp-route-target deleted file mode 100755 index e7e4d403f..000000000 --- a/src/validators/bgp-route-target +++ /dev/null @@ -1,51 +0,0 @@ -#!/usr/bin/env python3 -# -# Copyright (C) 2021 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 . - -from argparse import ArgumentParser -from vyos.template import is_ipv4 - -parser = ArgumentParser() -group = parser.add_mutually_exclusive_group() -group.add_argument('--single', action='store', help='Validate and allow only one route-target') -group.add_argument('--multi', action='store', help='Validate multiple, whitespace separated route-targets') -args = parser.parse_args() - -def is_valid_rt(rt): - # every route target needs to have a colon and must consists of two parts - value = rt.split(':') - if len(value) != 2: - return False - # A route target must either be only numbers, or the first part must be an - # IPv4 address - if (is_ipv4(value[0]) or value[0].isdigit()) and value[1].isdigit(): - return True - return False - -if __name__ == '__main__': - if args.single: - if not is_valid_rt(args.single): - exit(1) - - elif args.multi: - for rt in args.multi.split(' '): - if not is_valid_rt(rt): - exit(1) - - else: - parser.print_help() - exit(1) - - exit(0) -- cgit v1.2.3 From f89d51293fb276fe64367b3019004c3e53a79821 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sat, 4 Dec 2021 14:51:19 +0100 Subject: bgp: T4042: bugfix route-distinguisher value range --- .../include/bgp/route-distinguisher.xml.i | 2 +- src/validators/bgp-rd-rt | 22 +++++++++++++++------- 2 files changed, 16 insertions(+), 8 deletions(-) (limited to 'interface-definitions') diff --git a/interface-definitions/include/bgp/route-distinguisher.xml.i b/interface-definitions/include/bgp/route-distinguisher.xml.i index 6d0aa3ef1..8bc5b452e 100644 --- a/interface-definitions/include/bgp/route-distinguisher.xml.i +++ b/interface-definitions/include/bgp/route-distinguisher.xml.i @@ -7,7 +7,7 @@ Route Distinguisher, (x.x.x.x:yyy|xxxx:yyyy) - ^((25[0-5]|2[0-4][0-9]|[1][0-9][0-9]|[1-9][0-9]|[0-9]?)(\.(25[0-5]|2[0-4][0-9]|[1][0-9][0-9]|[1-9][0-9]|[0-9]?)){3}|[0-9]{1,10}):[0-9]{1,5}$ + diff --git a/src/validators/bgp-rd-rt b/src/validators/bgp-rd-rt index 24b0043f9..b2b69c9be 100755 --- a/src/validators/bgp-rd-rt +++ b/src/validators/bgp-rd-rt @@ -19,29 +19,37 @@ from vyos.template import is_ipv4 parser = ArgumentParser() group = parser.add_mutually_exclusive_group() +group.add_argument('--route-distinguisher', action='store', help='Validate BGP route distinguisher') group.add_argument('--route-target', action='store', help='Validate one BGP route-target') group.add_argument('--route-target-multi', action='store', help='Validate multiple, whitespace separated BGP route-targets') args = parser.parse_args() -def is_valid_rt(rt): - # every route target needs to have a colon and must consists of two parts +def is_valid(rt): + """ Verify BGP RD/RT - both can be verified using the same logic """ + # every RD/RT (route distinguisher/route target) needs to have a colon and + # must consists of two parts value = rt.split(':') if len(value) != 2: return False - # A route target must either be only numbers, or the first part must be an - # IPv4 address + + # An RD/RT must either be only numbers, or the first part must be an IPv4 + # address if (is_ipv4(value[0]) or value[0].isdigit()) and value[1].isdigit(): return True return False if __name__ == '__main__': - if args.route_target: - if not is_valid_rt(args.route_target): + if args.route_distinguisher: + if not is_valid(args.route_distinguisher): + exit(1) + + elif args.route_target: + if not is_valid(args.route_target): exit(1) elif args.route_target_multi: for rt in args.route_target_multi.split(' '): - if not is_valid_rt(rt): + if not is_valid(rt): exit(1) else: -- cgit v1.2.3 From 2f31381416adda8f86d3b66d5e83fcf2b551df3f Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sat, 4 Dec 2021 16:59:49 +0100 Subject: webproxy: T563: bugfix append-domain regex (cherry picked from commit e29e5c685809d8c18b5d3560c6cbcb4edce6eb82) --- interface-definitions/service_webproxy.xml.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'interface-definitions') diff --git a/interface-definitions/service_webproxy.xml.in b/interface-definitions/service_webproxy.xml.in index d61a95690..03f504ac7 100644 --- a/interface-definitions/service_webproxy.xml.in +++ b/interface-definitions/service_webproxy.xml.in @@ -16,7 +16,7 @@ Domain to use for urls that do not contain a '.' - ^[\.][a-z0-9-][$]? + [.][A-Za-z0-9][-.A-Za-z0-9]* Must start append-domain with a '.' -- cgit v1.2.3 From bb77dd269bfb9522f5b56ac027598ac20e101f13 Mon Sep 17 00:00:00 2001 From: Viacheslav Date: Mon, 6 Dec 2021 15:09:36 +0000 Subject: sflow: T4046: Add source-address for sflow --- data/templates/netflow/uacctd.conf.tmpl | 3 +++ interface-definitions/flow-accounting-conf.xml.in | 1 + src/conf_mode/flow_accounting_conf.py | 3 ++- 3 files changed, 6 insertions(+), 1 deletion(-) (limited to 'interface-definitions') diff --git a/data/templates/netflow/uacctd.conf.tmpl b/data/templates/netflow/uacctd.conf.tmpl index 1c183bb20..11fc76769 100644 --- a/data/templates/netflow/uacctd.conf.tmpl +++ b/data/templates/netflow/uacctd.conf.tmpl @@ -68,5 +68,8 @@ sfprobe_agentip[sf_{{ server['address'] }}]: {{ templatecfg['sflow']['agent-addr {% if templatecfg['sflow']['sampling-rate'] != none %} sampling_rate[sf_{{ server['address'] }}]: {{ templatecfg['sflow']['sampling-rate'] }} {% endif %} +{% if templatecfg['sflow']['source-address'] != none %} +sfprobe_source_ip[sf_{{ server['address'] }}]: {{ templatecfg['sflow']['source-address'] }} +{% endif %} {% endfor %} {% endif %} diff --git a/interface-definitions/flow-accounting-conf.xml.in b/interface-definitions/flow-accounting-conf.xml.in index 113c1d849..b98794792 100644 --- a/interface-definitions/flow-accounting-conf.xml.in +++ b/interface-definitions/flow-accounting-conf.xml.in @@ -420,6 +420,7 @@ + #include diff --git a/src/conf_mode/flow_accounting_conf.py b/src/conf_mode/flow_accounting_conf.py index 0a4559ade..daad00067 100755 --- a/src/conf_mode/flow_accounting_conf.py +++ b/src/conf_mode/flow_accounting_conf.py @@ -169,7 +169,8 @@ def get_config(): 'configured': vc.exists('system flow-accounting sflow'), 'agent-address': vc.return_value('system flow-accounting sflow agent-address'), 'sampling-rate': vc.return_value('system flow-accounting sflow sampling-rate'), - 'servers': None + 'servers': None, + 'source-address': vc.return_value('system flow-accounting sflow source-address') }, 'netflow': { 'configured': vc.exists('system flow-accounting netflow'), -- cgit v1.2.3 From 955f260ce682d64d27b3b11e618b1ae0176e4b91 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Mon, 6 Dec 2021 20:57:20 +0100 Subject: https: T4055: add vrf support --- data/templates/https/override.conf.tmpl | 15 +++++++++++++++ interface-definitions/https.xml.in | 1 + src/conf_mode/https.py | 8 +++++++- 3 files changed, 23 insertions(+), 1 deletion(-) create mode 100644 data/templates/https/override.conf.tmpl (limited to 'interface-definitions') diff --git a/data/templates/https/override.conf.tmpl b/data/templates/https/override.conf.tmpl new file mode 100644 index 000000000..824b1ba3b --- /dev/null +++ b/data/templates/https/override.conf.tmpl @@ -0,0 +1,15 @@ +{% set vrf_command = 'ip vrf exec ' + vrf + ' ' if vrf is defined else '' %} +[Unit] +StartLimitIntervalSec=0 +After=vyos-router.service + +[Service] +ExecStartPre= +ExecStartPre={{vrf_command}}/usr/sbin/nginx -t -q -g 'daemon on; master_process on;' +ExecStart= +ExecStart={{vrf_command}}/usr/sbin/nginx -g 'daemon on; master_process on;' +ExecReload= +ExecReload={{vrf_command}}/usr/sbin/nginx -g 'daemon on; master_process on;' -s reload +Restart=always +RestartPreventExitStatus= +RestartSec=10 diff --git a/interface-definitions/https.xml.in b/interface-definitions/https.xml.in index f60df7c34..d26cd5e7a 100644 --- a/interface-definitions/https.xml.in +++ b/interface-definitions/https.xml.in @@ -143,6 +143,7 @@ + #include diff --git a/src/conf_mode/https.py b/src/conf_mode/https.py index 86c6cd1b9..cd5073aa2 100755 --- a/src/conf_mode/https.py +++ b/src/conf_mode/https.py @@ -23,6 +23,7 @@ import vyos.defaults import vyos.certbot_util from vyos.config import Config +from vyos.configverify import verify_vrf from vyos import ConfigError from vyos.pki import wrap_certificate from vyos.pki import wrap_private_key @@ -34,6 +35,7 @@ from vyos import airbag airbag.enable() config_file = '/etc/nginx/sites-available/default' +systemd_override = r'/etc/systemd/system/nginx.service.d/override.conf' cert_dir = '/etc/ssl/certs' key_dir = '/etc/ssl/private' certbot_dir = vyos.defaults.directories['certbot'] @@ -103,6 +105,8 @@ def verify(https): if not domains_found: raise ConfigError("At least one 'virtual-host server-name' " "matching the 'certbot domain-name' is required.") + + verify_vrf(https) return None def generate(https): @@ -208,10 +212,12 @@ def generate(https): } render(config_file, 'https/nginx.default.tmpl', data) - + render(systemd_override, 'https/override.conf.tmpl', https) return None def apply(https): + # Reload systemd manager configuration + call('systemctl daemon-reload') if https is not None: call('systemctl restart nginx.service') else: -- cgit v1.2.3 From 30c78e83ecd57e708c88d02e114702876f9ca312 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Mon, 6 Dec 2021 20:57:47 +0100 Subject: xml: vrf: use "txt" in valueHelp --- interface-definitions/include/interface/vrf.xml.i | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'interface-definitions') diff --git a/interface-definitions/include/interface/vrf.xml.i b/interface-definitions/include/interface/vrf.xml.i index 5ad978a27..8605f56e8 100644 --- a/interface-definitions/include/interface/vrf.xml.i +++ b/interface-definitions/include/interface/vrf.xml.i @@ -3,7 +3,7 @@ VRF instance name - text + txt VRF instance name -- cgit v1.2.3 From d731caccf3eef486d483de032d423e979cd762f5 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Mon, 6 Dec 2021 20:57:56 +0100 Subject: xml: ntp: remove indent --- .../include/dhcp/ntp-server.xml.i | 26 +++++++++++----------- 1 file changed, 13 insertions(+), 13 deletions(-) (limited to 'interface-definitions') diff --git a/interface-definitions/include/dhcp/ntp-server.xml.i b/interface-definitions/include/dhcp/ntp-server.xml.i index 32d8207e5..4d7235aa1 100644 --- a/interface-definitions/include/dhcp/ntp-server.xml.i +++ b/interface-definitions/include/dhcp/ntp-server.xml.i @@ -1,15 +1,15 @@ - - - IP address of NTP server - - ipv4 - NTP server IPv4 address - - - - - - - + + + IP address of NTP server + + ipv4 + NTP server IPv4 address + + + + + + + -- cgit v1.2.3 From 49047b88c9bac0b2e007ccce7ac7d42e82ee0a2b Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Mon, 6 Dec 2021 21:09:42 +0100 Subject: bfd: T3753: FRR 8.1 uses a default echo-interval of 50 - reflect this in CLI --- interface-definitions/include/bfd-common.xml.i | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'interface-definitions') diff --git a/interface-definitions/include/bfd-common.xml.i b/interface-definitions/include/bfd-common.xml.i index 8379784f7..f4dc659a5 100644 --- a/interface-definitions/include/bfd-common.xml.i +++ b/interface-definitions/include/bfd-common.xml.i @@ -54,12 +54,13 @@ Echo receive transmission interval u32:10-60000 - The minimal echo receive transmission interval that this system is capable of handling + The minimal echo receive transmission interval that this system is capable of handling (default: 50) + 50 -- cgit v1.2.3 From 8359d95d42a24d0b6bc61b3fe345ea144216d802 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Mon, 6 Dec 2021 21:16:31 +0100 Subject: Revert "bfd: T3753: FRR 8.1 uses a default echo-interval of 50 - reflect this in CLI" This reverts commit 49047b88c9bac0b2e007ccce7ac7d42e82ee0a2b. > Echo mode is only available for single hop sessions --- interface-definitions/include/bfd-common.xml.i | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'interface-definitions') diff --git a/interface-definitions/include/bfd-common.xml.i b/interface-definitions/include/bfd-common.xml.i index f4dc659a5..8379784f7 100644 --- a/interface-definitions/include/bfd-common.xml.i +++ b/interface-definitions/include/bfd-common.xml.i @@ -54,13 +54,12 @@ Echo receive transmission interval u32:10-60000 - The minimal echo receive transmission interval that this system is capable of handling (default: 50) + The minimal echo receive transmission interval that this system is capable of handling - 50 -- cgit v1.2.3 From 3b6504d4bfc8a7f19613ee32fef5242711ca390a Mon Sep 17 00:00:00 2001 From: DmitriyEshenko Date: Mon, 6 Dec 2021 19:41:55 +0000 Subject: pppoe-server: T3006: Add range to regex generator --- data/templates/accel-ppp/pppoe.config.tmpl | 18 ++- interface-definitions/service_pppoe-server.xml.in | 18 ++- python/vyos/range_regex.py | 142 ++++++++++++++++++++++ src/conf_mode/service_pppoe-server.py | 15 +++ 4 files changed, 178 insertions(+), 15 deletions(-) create mode 100644 python/vyos/range_regex.py (limited to 'interface-definitions') diff --git a/data/templates/accel-ppp/pppoe.config.tmpl b/data/templates/accel-ppp/pppoe.config.tmpl index 238e7ee15..0a8e0079b 100644 --- a/data/templates/accel-ppp/pppoe.config.tmpl +++ b/data/templates/accel-ppp/pppoe.config.tmpl @@ -108,19 +108,17 @@ ac-name={{ access_concentrator }} {% if iface_config.vlan_id is not defined and iface_config.vlan_range is not defined %} interface={{ iface }} {% endif %} -{% if iface_config.vlan_id is defined and iface_config.vlan_range is not defined %} -{% for vlan in iface_config.vlan_id %} -interface={{ iface }}.{{ vlan }} -vlan-mon={{ iface }},{{ vlan }} +{% if iface_config.vlan_range is defined %} +{% for regex in iface_config.regex %} +interface=re:^{{ iface | replace('.', '\\.') }}\.({{ regex }})$ {% endfor %} -{% endif %} -{% if iface_config.vlan_range is defined and iface_config.vlan_id is not defined %} vlan-mon={{ iface }},{{ iface_config.vlan_range | join(',') }} -interface=re:{{ iface | replace('.', '\\.') }}\.\d+ {% endif %} -{% if iface_config.vlan_id is defined and iface_config.vlan_range is defined %} -vlan-mon={{ iface }},{{ iface_config.vlan_id | join(',') }},{{ iface_config.vlan_range | join(',') }} -interface=re:{{ iface | replace('.', '\\.') }}\.\d+ +{% if iface_config.vlan_id is defined %} +{% for vlan in iface_config.vlan_id %} +vlan-mon={{ iface }},{{ vlan }} +interface=re:^{{ iface | replace('.', '\\.') }}\.{{ vlan }}$ +{% endfor %} {% endif %} {% endfor %} {% endif %} diff --git a/interface-definitions/service_pppoe-server.xml.in b/interface-definitions/service_pppoe-server.xml.in index 188aed6c4..97952d882 100644 --- a/interface-definitions/service_pppoe-server.xml.in +++ b/interface-definitions/service_pppoe-server.xml.in @@ -70,19 +70,27 @@ - VLAN monitor for the automatic creation of vlans (user per vlan) + VLAN monitor for the automatic creation of single vlan + + u32:1-4094 + VLAN monitor for the automatic creation of single vlan + - + - VLAN ID needs to be between 1 and 4096 + VLAN ID needs to be between 1 and 4094 - VLAN monitor for the automatic creation of vlans (user per vlan) + VLAN monitor for the automatic creation of vlans range + + start-end + VLAN monitor range for the automatic creation of vlans (e.g. 1-4094) + - (409[0-6]|40[0-8][0-9]|[1-3][0-9]{3}|[1-9][0-9]{0,2})-(409[0-6]|40[0-8][0-9]|[1-3][0-9]{3}|[1-9][0-9]{0,2}) + diff --git a/python/vyos/range_regex.py b/python/vyos/range_regex.py new file mode 100644 index 000000000..a8190d140 --- /dev/null +++ b/python/vyos/range_regex.py @@ -0,0 +1,142 @@ +'''Copyright (c) 2013, Dmitry Voronin +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this +list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +''' +import math + +# coding=utf8 + +# Split range to ranges that has its unique pattern. +# Example for 12-345: +# +# 12- 19: 1[2-9] +# 20- 99: [2-9]\d +# 100-299: [1-2]\d{2} +# 300-339: 3[0-3]\d +# 340-345: 34[0-5] + +def range_to_regex(inpt_range): + if isinstance(inpt_range, str): + range_list = inpt_range.split('-') + # Check input arguments + if len(range_list) == 2: + # The first element in range must be higher then the second + if int(range_list[0]) < int(range_list[1]): + return regex_for_range(int(range_list[0]), int(range_list[1])) + + return None + +def bounded_regex_for_range(min_, max_): + return r'\b({})\b'.format(regex_for_range(min_, max_)) + +def regex_for_range(min_, max_): + """ + > regex_for_range(12, 345) + '1[2-9]|[2-9]\d|[1-2]\d{2}|3[0-3]\d|34[0-5]' + """ + positive_subpatterns = [] + negative_subpatterns = [] + + if min_ < 0: + min__ = 1 + if max_ < 0: + min__ = abs(max_) + max__ = abs(min_) + + negative_subpatterns = split_to_patterns(min__, max__) + min_ = 0 + + if max_ >= 0: + positive_subpatterns = split_to_patterns(min_, max_) + + negative_only_subpatterns = ['-' + val for val in negative_subpatterns if val not in positive_subpatterns] + positive_only_subpatterns = [val for val in positive_subpatterns if val not in negative_subpatterns] + intersected_subpatterns = ['-?' + val for val in negative_subpatterns if val in positive_subpatterns] + + subpatterns = negative_only_subpatterns + intersected_subpatterns + positive_only_subpatterns + return '|'.join(subpatterns) + + +def split_to_patterns(min_, max_): + subpatterns = [] + + start = min_ + for stop in split_to_ranges(min_, max_): + subpatterns.append(range_to_pattern(start, stop)) + start = stop + 1 + + return subpatterns + + +def split_to_ranges(min_, max_): + stops = {max_} + + nines_count = 1 + stop = fill_by_nines(min_, nines_count) + while min_ <= stop < max_: + stops.add(stop) + + nines_count += 1 + stop = fill_by_nines(min_, nines_count) + + zeros_count = 1 + stop = fill_by_zeros(max_ + 1, zeros_count) - 1 + while min_ < stop <= max_: + stops.add(stop) + + zeros_count += 1 + stop = fill_by_zeros(max_ + 1, zeros_count) - 1 + + stops = list(stops) + stops.sort() + + return stops + + +def fill_by_nines(integer, nines_count): + return int(str(integer)[:-nines_count] + '9' * nines_count) + + +def fill_by_zeros(integer, zeros_count): + return integer - integer % 10 ** zeros_count + + +def range_to_pattern(start, stop): + pattern = '' + any_digit_count = 0 + + for start_digit, stop_digit in zip(str(start), str(stop)): + if start_digit == stop_digit: + pattern += start_digit + elif start_digit != '0' or stop_digit != '9': + pattern += '[{}-{}]'.format(start_digit, stop_digit) + else: + any_digit_count += 1 + + if any_digit_count: + pattern += r'\d' + + if any_digit_count > 1: + pattern += '{{{}}}'.format(any_digit_count) + + return pattern \ No newline at end of file diff --git a/src/conf_mode/service_pppoe-server.py b/src/conf_mode/service_pppoe-server.py index 9fbd531da..1f31d132d 100755 --- a/src/conf_mode/service_pppoe-server.py +++ b/src/conf_mode/service_pppoe-server.py @@ -24,8 +24,11 @@ from vyos.configverify import verify_accel_ppp_base_service from vyos.template import render from vyos.util import call from vyos.util import dict_search +from vyos.util import get_interface_config from vyos import ConfigError from vyos import airbag +from vyos.range_regex import range_to_regex + airbag.enable() pppoe_conf = r'/run/accel-pppd/pppoe.conf' @@ -56,6 +59,11 @@ def verify(pppoe): if 'interface' not in pppoe: raise ConfigError('At least one listen interface must be defined!') + # Check is interface exists in the system + for iface in pppoe['interface']: + if not get_interface_config(iface): + raise ConfigError(f'Interface {iface} does not exist!') + # local ippool and gateway settings config checks if not (dict_search('client_ip_pool.subnet', pppoe) or (dict_search('client_ip_pool.start', pppoe) and @@ -73,6 +81,13 @@ def generate(pppoe): if not pppoe: return None + # Generate special regex for dynamic interfaces + for iface in pppoe['interface']: + if 'vlan_range' in pppoe['interface'][iface]: + pppoe['interface'][iface]['regex'] = [] + for vlan_range in pppoe['interface'][iface]['vlan_range']: + pppoe['interface'][iface]['regex'].append(range_to_regex(vlan_range)) + render(pppoe_conf, 'accel-ppp/pppoe.config.tmpl', pppoe) if dict_search('authentication.mode', pppoe) == 'local': -- cgit v1.2.3 From 0ec938326ffd63ad5eab5f3be3038e13fe3118ec Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Thu, 9 Dec 2021 23:17:47 +0100 Subject: xml: include: create dedicated bfd subfolder --- interface-definitions/include/bfd-common.xml.i | 78 ---------------------- interface-definitions/include/bfd.xml.i | 8 --- interface-definitions/include/bfd/bfd.xml.i | 10 +++ interface-definitions/include/bfd/common.xml.i | 78 ++++++++++++++++++++++ .../include/isis/protocol-common-config.xml.i | 2 +- .../include/ospf/interface-common.xml.i | 2 +- interface-definitions/protocols-bfd.xml.in | 4 +- 7 files changed, 92 insertions(+), 90 deletions(-) delete mode 100644 interface-definitions/include/bfd-common.xml.i delete mode 100644 interface-definitions/include/bfd.xml.i create mode 100644 interface-definitions/include/bfd/bfd.xml.i create mode 100644 interface-definitions/include/bfd/common.xml.i (limited to 'interface-definitions') diff --git a/interface-definitions/include/bfd-common.xml.i b/interface-definitions/include/bfd-common.xml.i deleted file mode 100644 index 8379784f7..000000000 --- a/interface-definitions/include/bfd-common.xml.i +++ /dev/null @@ -1,78 +0,0 @@ - - - - Enables the echo transmission mode - - - - - - Configure timer intervals - - - - - Minimum interval of receiving control packets - - u32:10-60000 - Interval in milliseconds (default: 300) - - - - - - 300 - - - - Minimum interval of transmitting control packets - - u32:10-60000 - Interval in milliseconds (default: 300) - - - - - - 300 - - - - Multiplier to determine packet loss - - u32:2-255 - Remote transmission interval will be multiplied by this value (default: 3) - - - - - - 3 - - - - Echo receive transmission interval - - u32:10-60000 - The minimal echo receive transmission interval that this system is capable of handling - - - - - - - - - - - Do not attempt to start sessions - - - - - - Disable this peer - - - - diff --git a/interface-definitions/include/bfd.xml.i b/interface-definitions/include/bfd.xml.i deleted file mode 100644 index 2bc3664e1..000000000 --- a/interface-definitions/include/bfd.xml.i +++ /dev/null @@ -1,8 +0,0 @@ - - - - Enable Bidirectional Forwarding Detection (BFD) - - - - diff --git a/interface-definitions/include/bfd/bfd.xml.i b/interface-definitions/include/bfd/bfd.xml.i new file mode 100644 index 000000000..022956d98 --- /dev/null +++ b/interface-definitions/include/bfd/bfd.xml.i @@ -0,0 +1,10 @@ + + + + Enable Bidirectional Forwarding Detection (BFD) + + + #include + + + diff --git a/interface-definitions/include/bfd/common.xml.i b/interface-definitions/include/bfd/common.xml.i new file mode 100644 index 000000000..e52221441 --- /dev/null +++ b/interface-definitions/include/bfd/common.xml.i @@ -0,0 +1,78 @@ + + + + Enables the echo transmission mode + + + + + + Configure timer intervals + + + + + Minimum interval of receiving control packets + + u32:10-60000 + Interval in milliseconds (default: 300) + + + + + + 300 + + + + Minimum interval of transmitting control packets + + u32:10-60000 + Interval in milliseconds (default: 300) + + + + + + 300 + + + + Multiplier to determine packet loss + + u32:2-255 + Remote transmission interval will be multiplied by this value (default: 3) + + + + + + 3 + + + + Echo receive transmission interval + + u32:10-60000 + The minimal echo receive transmission interval that this system is capable of handling + + + + + + + + + + + Do not attempt to start sessions + + + + + + Disable this peer + + + + diff --git a/interface-definitions/include/isis/protocol-common-config.xml.i b/interface-definitions/include/isis/protocol-common-config.xml.i index 84e2f7bb2..8ffa14a19 100644 --- a/interface-definitions/include/isis/protocol-common-config.xml.i +++ b/interface-definitions/include/isis/protocol-common-config.xml.i @@ -648,7 +648,7 @@ - #include + #include Configure circuit type for interface diff --git a/interface-definitions/include/ospf/interface-common.xml.i b/interface-definitions/include/ospf/interface-common.xml.i index 4b0aef380..738651594 100644 --- a/interface-definitions/include/ospf/interface-common.xml.i +++ b/interface-definitions/include/ospf/interface-common.xml.i @@ -1,5 +1,5 @@ -#include +#include Interface cost diff --git a/interface-definitions/protocols-bfd.xml.in b/interface-definitions/protocols-bfd.xml.in index d5a968001..265c9603a 100644 --- a/interface-definitions/protocols-bfd.xml.in +++ b/interface-definitions/protocols-bfd.xml.in @@ -66,7 +66,7 @@ - #include + #include Allow this BFD peer to not be directly connected @@ -88,7 +88,7 @@ - #include + #include -- cgit v1.2.3 From e639b84e56a131e6391aa94adb1c1937f3c8dc60 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Thu, 9 Dec 2021 23:19:19 +0100 Subject: xml: T4058: provide building block for BFD profiles --- interface-definitions/include/bfd/profile.xml.i | 14 ++++++++++++++ interface-definitions/protocols-bfd.xml.in | 13 +------------ 2 files changed, 15 insertions(+), 12 deletions(-) create mode 100644 interface-definitions/include/bfd/profile.xml.i (limited to 'interface-definitions') diff --git a/interface-definitions/include/bfd/profile.xml.i b/interface-definitions/include/bfd/profile.xml.i new file mode 100644 index 000000000..5ff057286 --- /dev/null +++ b/interface-definitions/include/bfd/profile.xml.i @@ -0,0 +1,14 @@ + + + + Use settings from BFD profile + + protocols bfd profile + + + txt + BFD profile name + + + + diff --git a/interface-definitions/protocols-bfd.xml.in b/interface-definitions/protocols-bfd.xml.in index 265c9603a..a9957d884 100644 --- a/interface-definitions/protocols-bfd.xml.in +++ b/interface-definitions/protocols-bfd.xml.in @@ -26,18 +26,7 @@ - - - Use settings from BFD profile - - protocols bfd profile - - - txt - BFD profile name - - - + #include Bind listener to specified interface/address, mandatory for IPv6 -- cgit v1.2.3 From 006b17a319c540a2fb384b90c65294f5a607787c Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Thu, 9 Dec 2021 23:19:43 +0100 Subject: bgp: T4058: add support for BFD profiles --- data/templates/frr/bgpd.frr.tmpl | 6 ++++++ interface-definitions/include/bgp/neighbor-bfd.xml.i | 1 + smoketest/scripts/cli/test_protocols_bgp.py | 20 ++++++++++++++++++++ 3 files changed, 27 insertions(+) (limited to 'interface-definitions') diff --git a/data/templates/frr/bgpd.frr.tmpl b/data/templates/frr/bgpd.frr.tmpl index fbdbafd6e..a3c6431f6 100644 --- a/data/templates/frr/bgpd.frr.tmpl +++ b/data/templates/frr/bgpd.frr.tmpl @@ -17,6 +17,12 @@ {% endif %} {% if config.bfd is defined %} neighbor {{ neighbor }} bfd +{% if config.bfd.check_control_plane_failure is defined %} + neighbor {{ neighbor }} bfd check-control-plane-failure +{% endif %} +{% if config.bfd.profile is defined and config.bfd.profile is not none %} + neighbor {{ neighbor }} bfd profile {{ config.bfd.profile }} +{% endif %} {% endif %} {% if config.capability is defined and config.capability is not none %} {% if config.capability.dynamic is defined %} diff --git a/interface-definitions/include/bgp/neighbor-bfd.xml.i b/interface-definitions/include/bgp/neighbor-bfd.xml.i index d486bdd8a..fac2a1166 100644 --- a/interface-definitions/include/bgp/neighbor-bfd.xml.i +++ b/interface-definitions/include/bgp/neighbor-bfd.xml.i @@ -4,6 +4,7 @@ Enable Bidirectional Forwarding Detection (BFD) support + #include Allow to write CBIT independence in BFD outgoing packets and read both C-BIT value of BFD and lookup BGP peer status diff --git a/smoketest/scripts/cli/test_protocols_bgp.py b/smoketest/scripts/cli/test_protocols_bgp.py index 16284ed01..5fdca4fc2 100755 --- a/smoketest/scripts/cli/test_protocols_bgp.py +++ b/smoketest/scripts/cli/test_protocols_bgp.py @@ -32,9 +32,11 @@ prefix_list_in = 'pfx-foo-in' prefix_list_out = 'pfx-foo-out' prefix_list_in6 = 'pfx-foo-in6' prefix_list_out6 = 'pfx-foo-out6' +bfd_profile = 'foo-bar-baz' neighbor_config = { '192.0.2.1' : { + 'bfd' : '', 'cap_dynamic' : '', 'cap_ext_next' : '', 'remote_as' : '100', @@ -51,6 +53,7 @@ neighbor_config = { 'addpath_all' : '', }, '192.0.2.2' : { + 'bfd_profile' : bfd_profile, 'remote_as' : '200', 'shutdown' : '', 'no_cap_nego' : '', @@ -98,6 +101,7 @@ neighbor_config = { peer_group_config = { 'foo' : { + 'bfd' : '', 'remote_as' : '100', 'passive' : '', 'password' : 'VyOS-Secure123', @@ -116,6 +120,7 @@ peer_group_config = { 'no_send_comm_ext' : '', }, 'baz' : { + 'bfd_profile' : bfd_profile, 'cap_dynamic' : '', 'cap_ext_next' : '', 'remote_as' : '200', @@ -154,6 +159,11 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase): def verify_frr_config(self, peer, peer_config, frrconfig): # recurring patterns to verify for both a simple neighbor and a peer-group + if 'bfd' in peer_config: + self.assertIn(f' neighbor {peer} bfd', frrconfig) + if 'bfd_profile' in peer_config: + self.assertIn(f' neighbor {peer} bfd profile {peer_config["bfd_profile"]}', frrconfig) + self.assertIn(f' neighbor {peer} bfd check-control-plane-failure', frrconfig) if 'cap_dynamic' in peer_config: self.assertIn(f' neighbor {peer} capability dynamic', frrconfig) if 'cap_ext_next' in peer_config: @@ -270,6 +280,11 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase): if 'adv_interv' in peer_config: self.cli_set(base_path + ['neighbor', peer, 'advertisement-interval', peer_config["adv_interv"]]) + if 'bfd' in peer_config: + self.cli_set(base_path + ['neighbor', peer, 'bfd']) + if 'bfd_profile' in peer_config: + self.cli_set(base_path + ['neighbor', peer, 'bfd', 'profile', peer_config["bfd_profile"]]) + self.cli_set(base_path + ['neighbor', peer, 'bfd', 'check-control-plane-failure']) if 'cap_dynamic' in peer_config: self.cli_set(base_path + ['neighbor', peer, 'capability', 'dynamic']) if 'cap_ext_next' in peer_config: @@ -339,6 +354,11 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase): def test_bgp_03_peer_groups(self): # Test out individual peer-group configuration items for peer_group, config in peer_group_config.items(): + if 'bfd' in config: + self.cli_set(base_path + ['peer-group', peer_group, 'bfd']) + if 'bfd_profile' in config: + self.cli_set(base_path + ['peer-group', peer_group, 'bfd', 'profile', config["bfd_profile"]]) + self.cli_set(base_path + ['peer-group', peer_group, 'bfd', 'check-control-plane-failure']) if 'cap_dynamic' in config: self.cli_set(base_path + ['peer-group', peer_group, 'capability', 'dynamic']) if 'cap_ext_next' in config: -- cgit v1.2.3 From eb29d8d5a0bc536364b4024ec6c336451b58ba49 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Fri, 10 Dec 2021 20:54:44 +0100 Subject: vxlan: T3700: add support for external controlled FDB Background information [1]. Specifies whether an external control plane (e.g. ip route encap/EVPN) or the internal FDB should be used. [1]: https://legacy.netdevconf.info/2.2/slides/prabhu-linuxbridge-tutorial.pdf --- interface-definitions/interfaces-vxlan.xml.in | 6 ++++++ python/vyos/ifconfig/vxlan.py | 10 +++++---- smoketest/scripts/cli/test_interfaces_vxlan.py | 29 ++++++++++++++++++++++++++ src/conf_mode/interfaces-vxlan.py | 24 +++++++++++++++++++-- 4 files changed, 63 insertions(+), 6 deletions(-) (limited to 'interface-definitions') diff --git a/interface-definitions/interfaces-vxlan.xml.in b/interface-definitions/interfaces-vxlan.xml.in index 0a8a88596..caeb58116 100644 --- a/interface-definitions/interfaces-vxlan.xml.in +++ b/interface-definitions/interfaces-vxlan.xml.in @@ -19,6 +19,12 @@ #include #include #include + + + Use external control plane + + + Multicast group address for VXLAN interface diff --git a/python/vyos/ifconfig/vxlan.py b/python/vyos/ifconfig/vxlan.py index d73fb47b8..9615f396d 100644 --- a/python/vyos/ifconfig/vxlan.py +++ b/python/vyos/ifconfig/vxlan.py @@ -54,18 +54,20 @@ class VXLANIf(Interface): # arguments used by iproute2. For more information please refer to: # - https://man7.org/linux/man-pages/man8/ip-link.8.html mapping = { - 'source_address' : 'local', - 'source_interface' : 'dev', - 'remote' : 'remote', 'group' : 'group', + 'external' : 'external', 'parameters.ip.dont_fragment': 'df set', 'parameters.ip.tos' : 'tos', 'parameters.ip.ttl' : 'ttl', 'parameters.ipv6.flowlabel' : 'flowlabel', 'parameters.nolearning' : 'nolearning', + 'remote' : 'remote', + 'source_address' : 'local', + 'source_interface' : 'dev', + 'vni' : 'id', } - cmd = 'ip link add {ifname} type {type} id {vni} dstport {port}' + cmd = 'ip link add {ifname} type {type} dstport {port}' for vyos_key, iproute2_key in mapping.items(): # dict_search will return an empty dict "{}" for valueless nodes like # "parameters.nolearning" - thus we need to test the nodes existence diff --git a/smoketest/scripts/cli/test_interfaces_vxlan.py b/smoketest/scripts/cli/test_interfaces_vxlan.py index f63c850d8..17874af89 100755 --- a/smoketest/scripts/cli/test_interfaces_vxlan.py +++ b/smoketest/scripts/cli/test_interfaces_vxlan.py @@ -16,6 +16,7 @@ import unittest +from vyos.configsession import ConfigSessionError from vyos.ifconfig import Interface from vyos.util import get_interface_config @@ -78,6 +79,9 @@ class VXLANInterfaceTest(BasicInterfaceTest.TestCase): label = options['linkinfo']['info_data']['label'] self.assertIn(f'parameters ipv6 flowlabel {label}', self._options[interface]) + if any('external' in s for s in self._options[interface]): + self.assertTrue(options['linkinfo']['info_data']['external']) + self.assertEqual('vxlan', options['linkinfo']['info_kind']) self.assertEqual('set', options['linkinfo']['info_data']['df']) self.assertEqual(f'0x{tos}', options['linkinfo']['info_data']['tos']) @@ -85,5 +89,30 @@ class VXLANInterfaceTest(BasicInterfaceTest.TestCase): self.assertEqual(Interface(interface).get_admin_state(), 'up') ttl += 10 + def test_vxlan_external(self): + interface = 'vxlan0' + source_address = '192.0.2.1' + self.cli_set(self._base_path + [interface, 'external']) + self.cli_set(self._base_path + [interface, 'source-address', source_address]) + + # Now add some more interfaces - this must fail and a CLI error needs + # to be generated as Linux can only handle one VXLAN tunnel when using + # external mode. + for intf in self._interfaces: + for option in self._options.get(intf, []): + self.cli_set(self._base_path + [intf] + option.split()) + with self.assertRaises(ConfigSessionError): + self.cli_commit() + + # Remove those test interfaces again + for intf in self._interfaces: + self.cli_delete(self._base_path + [intf]) + + self.cli_commit() + + options = get_interface_config(interface) + self.assertTrue(options['linkinfo']['info_data']['external']) + self.assertEqual('vxlan', options['linkinfo']['info_kind']) + if __name__ == '__main__': unittest.main(verbosity=2) diff --git a/src/conf_mode/interfaces-vxlan.py b/src/conf_mode/interfaces-vxlan.py index 804f2d14f..b197d08a6 100755 --- a/src/conf_mode/interfaces-vxlan.py +++ b/src/conf_mode/interfaces-vxlan.py @@ -44,6 +44,20 @@ def get_config(config=None): base = ['interfaces', 'vxlan'] vxlan = get_interface_dict(conf, base) + # We need to verify that no other VXLAN tunnel is configured when external + # mode is in use - Linux Kernel limitation + conf.set_level(base) + vxlan['other_tunnels'] = conf.get_config_dict([], key_mangling=('-', '_'), + get_first_key=True, + no_tag_node_value_mangle=True) + + # This if-clause is just to be sure - it will always evaluate to true + ifname = vxlan['ifname'] + if ifname in vxlan['other_tunnels']: + del vxlan['other_tunnels'][ifname] + if len(vxlan['other_tunnels']) == 0: + del vxlan['other_tunnels'] + return vxlan def verify(vxlan): @@ -63,8 +77,14 @@ def verify(vxlan): if not any(tmp in ['group', 'remote', 'source_address'] for tmp in vxlan): raise ConfigError('Group, remote or source-address must be configured') - if 'vni' not in vxlan: - raise ConfigError('Must configure VNI for VXLAN') + if 'vni' not in vxlan and 'external' not in vxlan: + raise ConfigError( + 'Must either configure VXLAN "vni" or use "external" CLI option!') + + if {'external', 'other_tunnels'} <= set(vxlan): + other_tunnels = ', '.join(vxlan['other_tunnels']) + raise ConfigError(f'Only one VXLAN tunnel is supported when "external" '\ + f'CLI option is used. Additional tunnels: {other_tunnels}') if 'source_interface' in vxlan: # VXLAN adds at least an overhead of 50 byte - we need to check the -- cgit v1.2.3 From 6a8026e1f9a18f4c3e34b376ecce58e5225ac2b3 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sun, 12 Dec 2021 10:07:16 +0100 Subject: xml: bgp: fix "shutdown" help string (remove whitespace) --- interface-definitions/include/bgp/neighbor-shutdown.xml.i | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'interface-definitions') diff --git a/interface-definitions/include/bgp/neighbor-shutdown.xml.i b/interface-definitions/include/bgp/neighbor-shutdown.xml.i index 6d15899a6..acc7bc5a9 100644 --- a/interface-definitions/include/bgp/neighbor-shutdown.xml.i +++ b/interface-definitions/include/bgp/neighbor-shutdown.xml.i @@ -1,7 +1,7 @@ - Administratively shut down this neighbor + Administratively shutdown this neighbor -- cgit v1.2.3 From ede5ba7c1e9e5e2862f91d3082b50b71a1ffe920 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sun, 12 Dec 2021 09:56:46 +0100 Subject: bgp: T3967: add "parameters conditional-advertisement timer " option Set the period to rerun the conditional advertisement scanner process. The default is 60 seconds. --- data/templates/frr/bgpd.frr.tmpl | 5 +++++ .../include/bgp/protocol-common-config.xml.i | 20 ++++++++++++++++++++ smoketest/scripts/cli/test_protocols_bgp.py | 4 ++++ 3 files changed, 29 insertions(+) (limited to 'interface-definitions') diff --git a/data/templates/frr/bgpd.frr.tmpl b/data/templates/frr/bgpd.frr.tmpl index a3c6431f6..cae53d09c 100644 --- a/data/templates/frr/bgpd.frr.tmpl +++ b/data/templates/frr/bgpd.frr.tmpl @@ -475,6 +475,11 @@ router bgp {{ local_as }} {{ 'vrf ' ~ vrf if vrf is defined and vrf is not none {% if parameters.cluster_id is defined and parameters.cluster_id is not none %} bgp cluster-id {{ parameters.cluster_id }} {% endif %} +{% if parameters.conditional_advertisement is defined and parameters.conditional_advertisement is not none %} +{% if parameters.conditional_advertisement.timer is defined and parameters.conditional_advertisement.timer is not none %} + bgp conditional-advertisement timer {{ parameters.conditional_advertisement.timer }} +{% endif %} +{% endif %} {% if parameters.confederation is defined and parameters.confederation is not none %} {% if parameters.confederation.identifier is defined and parameters.confederation.identifier is not none %} bgp confederation identifier {{ parameters.confederation.identifier }} diff --git a/interface-definitions/include/bgp/protocol-common-config.xml.i b/interface-definitions/include/bgp/protocol-common-config.xml.i index 2dfae517e..351ee8eda 100644 --- a/interface-definitions/include/bgp/protocol-common-config.xml.i +++ b/interface-definitions/include/bgp/protocol-common-config.xml.i @@ -1181,6 +1181,26 @@ + + + Conditional advertisement settings + + + + + Set period to rescan BGP table to check if condition is met + + u32:5-240 + Period to rerun the conditional advertisement scanner process (default: 60) + + + + + + 60 + + + Enable route-flap dampening diff --git a/smoketest/scripts/cli/test_protocols_bgp.py b/smoketest/scripts/cli/test_protocols_bgp.py index 5fdca4fc2..8282d507a 100755 --- a/smoketest/scripts/cli/test_protocols_bgp.py +++ b/smoketest/scripts/cli/test_protocols_bgp.py @@ -218,6 +218,7 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase): max_path_v4ibgp = '4' max_path_v6 = '8' max_path_v6ibgp = '16' + cond_adv_timer = '30' self.cli_set(base_path + ['parameters', 'router-id', router_id]) self.cli_set(base_path + ['parameters', 'log-neighbor-changes']) @@ -239,6 +240,8 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase): self.cli_set(base_path + ['parameters', 'bestpath', 'bandwidth', 'default-weight-for-missing']) self.cli_set(base_path + ['parameters', 'bestpath', 'compare-routerid']) + self.cli_set(base_path + ['parameters', 'conditional-advertisement', 'timer', cond_adv_timer]) + # AFI maximum path support self.cli_set(base_path + ['address-family', 'ipv4-unicast', 'maximum-paths', 'ebgp', max_path_v4]) self.cli_set(base_path + ['address-family', 'ipv4-unicast', 'maximum-paths', 'ibgp', max_path_v4ibgp]) @@ -254,6 +257,7 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase): self.assertIn(f' bgp router-id {router_id}', frrconfig) self.assertIn(f' bgp log-neighbor-changes', frrconfig) self.assertIn(f' bgp default local-preference {local_pref}', frrconfig) + self.assertIn(f' bgp conditional-advertisement timer {cond_adv_timer}', frrconfig) self.assertIn(f' bgp graceful-restart stalepath-time {stalepath_time}', frrconfig) self.assertIn(f' bgp graceful-shutdown', frrconfig) self.assertIn(f' bgp bestpath as-path multipath-relax', frrconfig) -- cgit v1.2.3 From ee2c84b2f0cc13d71122bd2ee0640bb13aa8040e Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sun, 12 Dec 2021 10:06:17 +0100 Subject: bgp: T4069: add "parameters fast-convergence" CLI option Whenever BGP peer address becomes unreachable we must bring down the BGP session immediately. Currently only single-hop EBGP sessions are brought down immediately. IBGP and multi-hop EBGP sessions wait for hold-timer expiry to bring down the sessions. This new configuration option helps user to teardown BGP sessions immediately whenever peer becomes unreachable. This configuration is available at the bgp level. When enabled, configuration is applied to all the neighbors configured in that bgp instance. --- data/templates/frr/bgpd.frr.tmpl | 3 +++ interface-definitions/include/bgp/protocol-common-config.xml.i | 6 ++++++ smoketest/scripts/cli/test_protocols_bgp.py | 2 ++ 3 files changed, 11 insertions(+) (limited to 'interface-definitions') diff --git a/data/templates/frr/bgpd.frr.tmpl b/data/templates/frr/bgpd.frr.tmpl index cae53d09c..351e4a7ed 100644 --- a/data/templates/frr/bgpd.frr.tmpl +++ b/data/templates/frr/bgpd.frr.tmpl @@ -510,6 +510,9 @@ router bgp {{ local_as }} {{ 'vrf ' ~ vrf if vrf is defined and vrf is not none {% endfor %} {% endif %} {% endif %} +{% if parameters.fast_convergence is defined %} + bgp fast-convergence +{% endif %} {% if parameters.graceful_restart is defined %} bgp graceful-restart {{ 'stalepath-time ' ~ parameters.graceful_restart.stalepath_time if parameters.graceful_restart.stalepath_time is defined }} {% endif %} diff --git a/interface-definitions/include/bgp/protocol-common-config.xml.i b/interface-definitions/include/bgp/protocol-common-config.xml.i index 351ee8eda..5dd4e522b 100644 --- a/interface-definitions/include/bgp/protocol-common-config.xml.i +++ b/interface-definitions/include/bgp/protocol-common-config.xml.i @@ -1363,6 +1363,12 @@ + + + Teardown sessions immediately whenever peer becomes unreachable + + + Graceful restart capability parameters diff --git a/smoketest/scripts/cli/test_protocols_bgp.py b/smoketest/scripts/cli/test_protocols_bgp.py index 8282d507a..2693ca5ba 100755 --- a/smoketest/scripts/cli/test_protocols_bgp.py +++ b/smoketest/scripts/cli/test_protocols_bgp.py @@ -241,6 +241,7 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase): self.cli_set(base_path + ['parameters', 'bestpath', 'compare-routerid']) self.cli_set(base_path + ['parameters', 'conditional-advertisement', 'timer', cond_adv_timer]) + self.cli_set(base_path + ['parameters', 'fast-convergence']) # AFI maximum path support self.cli_set(base_path + ['address-family', 'ipv4-unicast', 'maximum-paths', 'ebgp', max_path_v4]) @@ -258,6 +259,7 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase): self.assertIn(f' bgp log-neighbor-changes', frrconfig) self.assertIn(f' bgp default local-preference {local_pref}', frrconfig) self.assertIn(f' bgp conditional-advertisement timer {cond_adv_timer}', frrconfig) + self.assertIn(f' bgp fast-convergence', frrconfig) self.assertIn(f' bgp graceful-restart stalepath-time {stalepath_time}', frrconfig) self.assertIn(f' bgp graceful-shutdown', frrconfig) self.assertIn(f' bgp bestpath as-path multipath-relax', frrconfig) -- cgit v1.2.3 From 1d4c23ca9b62dfa6db6580440b21cd89c5d8f7e8 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sun, 12 Dec 2021 10:06:57 +0100 Subject: bgp: T4069: add "parameters minimum-holdtime " CLI option This command allows user to prevent session establishment with BGP peers with lower holdtime less than configured minimum holdtime. When this command is not set, minimum holdtime does not work. --- data/templates/frr/bgpd.frr.tmpl | 3 +++ .../include/bgp/protocol-common-config.xml.i | 12 ++++++++++++ smoketest/scripts/cli/test_protocols_bgp.py | 3 +++ 3 files changed, 18 insertions(+) (limited to 'interface-definitions') diff --git a/data/templates/frr/bgpd.frr.tmpl b/data/templates/frr/bgpd.frr.tmpl index 351e4a7ed..c04475070 100644 --- a/data/templates/frr/bgpd.frr.tmpl +++ b/data/templates/frr/bgpd.frr.tmpl @@ -522,6 +522,9 @@ router bgp {{ local_as }} {{ 'vrf ' ~ vrf if vrf is defined and vrf is not none {% if parameters.log_neighbor_changes is defined %} bgp log-neighbor-changes {% endif %} +{% if parameters.minimum_holdtime is defined and parameters.minimum_holdtime is not none %} + bgp minimum-holdtime {{ parameters.minimum_holdtime }} +{% endif %} {% if parameters.network_import_check is defined %} bgp network import-check {% endif %} diff --git a/interface-definitions/include/bgp/protocol-common-config.xml.i b/interface-definitions/include/bgp/protocol-common-config.xml.i index 5dd4e522b..20ac59c5b 100644 --- a/interface-definitions/include/bgp/protocol-common-config.xml.i +++ b/interface-definitions/include/bgp/protocol-common-config.xml.i @@ -1400,6 +1400,18 @@ + + + BGP minimum holdtime + + u32:1-65535 + Minimum holdtime in seconds + + + + + + Enable IGP route check for network statements diff --git a/smoketest/scripts/cli/test_protocols_bgp.py b/smoketest/scripts/cli/test_protocols_bgp.py index 2693ca5ba..983f6ecd3 100755 --- a/smoketest/scripts/cli/test_protocols_bgp.py +++ b/smoketest/scripts/cli/test_protocols_bgp.py @@ -219,6 +219,7 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase): max_path_v6 = '8' max_path_v6ibgp = '16' cond_adv_timer = '30' + min_hold_time = '2' self.cli_set(base_path + ['parameters', 'router-id', router_id]) self.cli_set(base_path + ['parameters', 'log-neighbor-changes']) @@ -242,6 +243,7 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase): self.cli_set(base_path + ['parameters', 'conditional-advertisement', 'timer', cond_adv_timer]) self.cli_set(base_path + ['parameters', 'fast-convergence']) + self.cli_set(base_path + ['parameters', 'minimum-holdtime', min_hold_time]) # AFI maximum path support self.cli_set(base_path + ['address-family', 'ipv4-unicast', 'maximum-paths', 'ebgp', max_path_v4]) @@ -265,6 +267,7 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase): self.assertIn(f' bgp bestpath as-path multipath-relax', frrconfig) self.assertIn(f' bgp bestpath bandwidth default-weight-for-missing', frrconfig) self.assertIn(f' bgp bestpath compare-routerid', frrconfig) + self.assertIn(f' bgp minimum-holdtime {min_hold_time}', frrconfig) self.assertNotIn(f'bgp ebgp-requires-policy', frrconfig) afiv4_config = self.getFRRconfig(' address-family ipv4 unicast') -- cgit v1.2.3 From bddabbf696ec8ff638b93c5260aa4823dcc43df3 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sun, 12 Dec 2021 10:09:24 +0100 Subject: bgp: T4069: add "parameters reject-as-sets" CLI option This command enables rejection of incoming and outgoing routes having AS_SET or AS_CONFED_SET type. --- data/templates/frr/bgpd.frr.tmpl | 3 +++ interface-definitions/include/bgp/protocol-common-config.xml.i | 6 ++++++ smoketest/scripts/cli/test_protocols_bgp.py | 2 ++ 3 files changed, 11 insertions(+) (limited to 'interface-definitions') diff --git a/data/templates/frr/bgpd.frr.tmpl b/data/templates/frr/bgpd.frr.tmpl index c04475070..e49aaf12c 100644 --- a/data/templates/frr/bgpd.frr.tmpl +++ b/data/templates/frr/bgpd.frr.tmpl @@ -534,6 +534,9 @@ router bgp {{ local_as }} {{ 'vrf ' ~ vrf if vrf is defined and vrf is not none {% if parameters.no_fast_external_failover is defined %} no bgp fast-external-failover {% endif %} +{% if parameters.reject_as_sets is defined %} + bgp reject-as-sets +{% endif %} {% if parameters.router_id is defined and parameters.router_id is not none %} bgp router-id {{ parameters.router_id }} {% endif %} diff --git a/interface-definitions/include/bgp/protocol-common-config.xml.i b/interface-definitions/include/bgp/protocol-common-config.xml.i index 20ac59c5b..82c8e55a0 100644 --- a/interface-definitions/include/bgp/protocol-common-config.xml.i +++ b/interface-definitions/include/bgp/protocol-common-config.xml.i @@ -1430,6 +1430,12 @@ + + + Reject routes with AS_SET or AS_CONFED_SET flag + + + #include diff --git a/smoketest/scripts/cli/test_protocols_bgp.py b/smoketest/scripts/cli/test_protocols_bgp.py index 983f6ecd3..87673f459 100755 --- a/smoketest/scripts/cli/test_protocols_bgp.py +++ b/smoketest/scripts/cli/test_protocols_bgp.py @@ -244,6 +244,7 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase): self.cli_set(base_path + ['parameters', 'conditional-advertisement', 'timer', cond_adv_timer]) self.cli_set(base_path + ['parameters', 'fast-convergence']) self.cli_set(base_path + ['parameters', 'minimum-holdtime', min_hold_time]) + self.cli_set(base_path + ['parameters', 'reject-as-sets']) # AFI maximum path support self.cli_set(base_path + ['address-family', 'ipv4-unicast', 'maximum-paths', 'ebgp', max_path_v4]) @@ -268,6 +269,7 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase): self.assertIn(f' bgp bestpath bandwidth default-weight-for-missing', frrconfig) self.assertIn(f' bgp bestpath compare-routerid', frrconfig) self.assertIn(f' bgp minimum-holdtime {min_hold_time}', frrconfig) + self.assertIn(f' bgp reject-as-sets', frrconfig) self.assertNotIn(f'bgp ebgp-requires-policy', frrconfig) afiv4_config = self.getFRRconfig(' address-family ipv4 unicast') -- cgit v1.2.3 From aca9d98c818407b8009bb861ec0f5a817a9a5637 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sun, 12 Dec 2021 10:10:10 +0100 Subject: bgp: T4069: add "parameters shutdown" CLI option Administrative shutdown of all peers of a bgp instance. Drop all BGP peers, but preserve their configurations. The peers are notified in accordance with RFC 8203 by sending a NOTIFICATION message with error code Cease and subcode Administrative Shutdown prior to terminating connections. This global shutdown is independent of the neighbor shutdown, meaning that individually shut down peers will not be affected by lifting it. --- data/templates/frr/bgpd.frr.tmpl | 3 +++ interface-definitions/include/bgp/protocol-common-config.xml.i | 6 ++++++ smoketest/scripts/cli/test_protocols_bgp.py | 2 ++ 3 files changed, 11 insertions(+) (limited to 'interface-definitions') diff --git a/data/templates/frr/bgpd.frr.tmpl b/data/templates/frr/bgpd.frr.tmpl index e49aaf12c..01e62a94b 100644 --- a/data/templates/frr/bgpd.frr.tmpl +++ b/data/templates/frr/bgpd.frr.tmpl @@ -540,6 +540,9 @@ router bgp {{ local_as }} {{ 'vrf ' ~ vrf if vrf is defined and vrf is not none {% if parameters.router_id is defined and parameters.router_id is not none %} bgp router-id {{ parameters.router_id }} {% endif %} +{% if parameters.shutdown is defined %} + bgp shutdown +{% endif %} {% endif %} {% if timers is defined and timers.keepalive is defined and timers.holdtime is defined %} timers bgp {{ timers.keepalive }} {{ timers.holdtime }} diff --git a/interface-definitions/include/bgp/protocol-common-config.xml.i b/interface-definitions/include/bgp/protocol-common-config.xml.i index 82c8e55a0..2b2d6fa82 100644 --- a/interface-definitions/include/bgp/protocol-common-config.xml.i +++ b/interface-definitions/include/bgp/protocol-common-config.xml.i @@ -1436,6 +1436,12 @@ + + + Administrative shutdown of the BGP instance + + + #include diff --git a/smoketest/scripts/cli/test_protocols_bgp.py b/smoketest/scripts/cli/test_protocols_bgp.py index 87673f459..74aff71eb 100755 --- a/smoketest/scripts/cli/test_protocols_bgp.py +++ b/smoketest/scripts/cli/test_protocols_bgp.py @@ -245,6 +245,7 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase): self.cli_set(base_path + ['parameters', 'fast-convergence']) self.cli_set(base_path + ['parameters', 'minimum-holdtime', min_hold_time]) self.cli_set(base_path + ['parameters', 'reject-as-sets']) + self.cli_set(base_path + ['parameters', 'shutdown']) # AFI maximum path support self.cli_set(base_path + ['address-family', 'ipv4-unicast', 'maximum-paths', 'ebgp', max_path_v4]) @@ -270,6 +271,7 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase): self.assertIn(f' bgp bestpath compare-routerid', frrconfig) self.assertIn(f' bgp minimum-holdtime {min_hold_time}', frrconfig) self.assertIn(f' bgp reject-as-sets', frrconfig) + self.assertIn(f' bgp shutdown', frrconfig) self.assertNotIn(f'bgp ebgp-requires-policy', frrconfig) afiv4_config = self.getFRRconfig(' address-family ipv4 unicast') -- cgit v1.2.3 From 6fab523565015fd4da5d7295bad684cb969e5d12 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sun, 12 Dec 2021 10:11:49 +0100 Subject: bgp: T4069: add "parameters suppress-fib-pending" CLI option This command is applicable at the global level and at an individual bgp level. If applied at the global level all bgp instances will wait for fib installation before announcing routes and there is no way to turn it off for a particular BGP vrf. --- data/templates/frr/bgpd.frr.tmpl | 5 ++++- interface-definitions/include/bgp/protocol-common-config.xml.i | 8 +++++++- smoketest/scripts/cli/test_protocols_bgp.py | 4 +++- 3 files changed, 14 insertions(+), 3 deletions(-) (limited to 'interface-definitions') diff --git a/data/templates/frr/bgpd.frr.tmpl b/data/templates/frr/bgpd.frr.tmpl index 01e62a94b..9bb42fd2d 100644 --- a/data/templates/frr/bgpd.frr.tmpl +++ b/data/templates/frr/bgpd.frr.tmpl @@ -543,8 +543,11 @@ router bgp {{ local_as }} {{ 'vrf ' ~ vrf if vrf is defined and vrf is not none {% if parameters.shutdown is defined %} bgp shutdown {% endif %} +{% if parameters.suppress_fib_pending is defined %} + bgp suppress-fib-pending +{% endif %} {% endif %} {% if timers is defined and timers.keepalive is defined and timers.holdtime is defined %} timers bgp {{ timers.keepalive }} {{ timers.holdtime }} {% endif %} -exit \ No newline at end of file +exit diff --git a/interface-definitions/include/bgp/protocol-common-config.xml.i b/interface-definitions/include/bgp/protocol-common-config.xml.i index 2b2d6fa82..8214d0779 100644 --- a/interface-definitions/include/bgp/protocol-common-config.xml.i +++ b/interface-definitions/include/bgp/protocol-common-config.xml.i @@ -1442,6 +1442,12 @@ + + + Advertise only routes that are programmed in kernel to peers + + + #include @@ -1491,4 +1497,4 @@ #include - \ No newline at end of file + diff --git a/smoketest/scripts/cli/test_protocols_bgp.py b/smoketest/scripts/cli/test_protocols_bgp.py index 74aff71eb..a2bcd0685 100755 --- a/smoketest/scripts/cli/test_protocols_bgp.py +++ b/smoketest/scripts/cli/test_protocols_bgp.py @@ -246,6 +246,7 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase): self.cli_set(base_path + ['parameters', 'minimum-holdtime', min_hold_time]) self.cli_set(base_path + ['parameters', 'reject-as-sets']) self.cli_set(base_path + ['parameters', 'shutdown']) + self.cli_set(base_path + ['parameters', 'suppress-fib-pending']) # AFI maximum path support self.cli_set(base_path + ['address-family', 'ipv4-unicast', 'maximum-paths', 'ebgp', max_path_v4]) @@ -272,6 +273,7 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase): self.assertIn(f' bgp minimum-holdtime {min_hold_time}', frrconfig) self.assertIn(f' bgp reject-as-sets', frrconfig) self.assertIn(f' bgp shutdown', frrconfig) + self.assertIn(f' bgp suppress-fib-pending', frrconfig) self.assertNotIn(f'bgp ebgp-requires-policy', frrconfig) afiv4_config = self.getFRRconfig(' address-family ipv4 unicast') @@ -786,4 +788,4 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase): self.assertIn(f' exit-address-family', afi_config) if __name__ == '__main__': - unittest.main(verbosity=2) \ No newline at end of file + unittest.main(verbosity=2) -- cgit v1.2.3 From c233c7e6d058f31283d7d0f2f924c35cd148ee5f Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sun, 12 Dec 2021 10:28:06 +0100 Subject: xml: bgp: rename afi-common.xml.i -> neighbor-afi-ipv4-ipv6-common.xml.i --- interface-definitions/include/bgp/afi-common.xml.i | 142 --------------------- .../bgp/neighbor-afi-ipv4-ipv6-common.xml.i | 142 +++++++++++++++++++++ .../bgp/neighbor-afi-ipv4-labeled-unicast.xml.i | 2 +- .../include/bgp/neighbor-afi-ipv4-multicast.xml.i | 2 +- .../include/bgp/neighbor-afi-ipv4-unicast.xml.i | 2 +- .../include/bgp/neighbor-afi-ipv4-vpn.xml.i | 2 +- .../bgp/neighbor-afi-ipv6-labeled-unicast.xml.i | 2 +- .../include/bgp/neighbor-afi-ipv6-multicast.xml.i | 2 +- .../include/bgp/neighbor-afi-ipv6-unicast.xml.i | 2 +- .../include/bgp/neighbor-afi-ipv6-vpn.xml.i | 2 +- 10 files changed, 150 insertions(+), 150 deletions(-) delete mode 100644 interface-definitions/include/bgp/afi-common.xml.i create mode 100644 interface-definitions/include/bgp/neighbor-afi-ipv4-ipv6-common.xml.i (limited to 'interface-definitions') diff --git a/interface-definitions/include/bgp/afi-common.xml.i b/interface-definitions/include/bgp/afi-common.xml.i deleted file mode 100644 index 62beff40c..000000000 --- a/interface-definitions/include/bgp/afi-common.xml.i +++ /dev/null @@ -1,142 +0,0 @@ - - - - Use addpath to advertise all paths to a neighbor - - - - - - Use addpath to advertise the bestpath per each neighboring AS - - - -#include - - - AS for routes sent to this peer to be the local AS - - - -#include - - - Disable sending community attributes to this peer - - - - - Disable sending extended community attributes to this peer - - - - - - Disable sending standard community attributes to this peer - - - - - - - - Access-list to filter route updates to/from this peer-group - - - - - Access-list to filter outgoing route updates to this peer-group - - policy access-list - - - u32:1-65535 - Access-list to filter outgoing route updates to this peer-group - - - - - - - - - Access-list to filter incoming route updates from this peer-group - - policy access-list - - - u32:1-65535 - Access-list to filter incoming route updates from this peer-group - - - - - - - - -#include - - - Maximum number of prefixes to accept from this peer - - u32:1-4294967295 - Prefix limit - - - - - - - - - Maximum number of prefixes to be sent to this peer - - u32:1-4294967295 - Prefix limit - - - - - - -#include - - - Remove private AS numbers from AS path in outbound route updates - - - -#include -#include -#include -#include - - - Route-map to selectively unsuppress suppressed routes - - policy route-map - - - txt - Route map name - - - ^[-_a-zA-Z0-9.]+$ - - Name of route-map can only contain alpha-numeric letters, hyphen and underscores - - - - - Default weight for routes from this peer - - u32:1-65535 - Default weight - - - - - - - diff --git a/interface-definitions/include/bgp/neighbor-afi-ipv4-ipv6-common.xml.i b/interface-definitions/include/bgp/neighbor-afi-ipv4-ipv6-common.xml.i new file mode 100644 index 000000000..498662b0a --- /dev/null +++ b/interface-definitions/include/bgp/neighbor-afi-ipv4-ipv6-common.xml.i @@ -0,0 +1,142 @@ + + + + Use addpath to advertise all paths to a neighbor + + + + + + Use addpath to advertise the bestpath per each neighboring AS + + + +#include + + + AS for routes sent to this peer to be the local AS + + + +#include + + + Disable sending community attributes to this peer + + + + + Disable sending extended community attributes to this peer + + + + + + Disable sending standard community attributes to this peer + + + + + + + + Access-list to filter route updates to/from this peer-group + + + + + Access-list to filter outgoing route updates to this peer-group + + policy access-list + + + u32:1-65535 + Access-list to filter outgoing route updates to this peer-group + + + + + + + + + Access-list to filter incoming route updates from this peer-group + + policy access-list + + + u32:1-65535 + Access-list to filter incoming route updates from this peer-group + + + + + + + + +#include + + + Maximum number of prefixes to accept from this peer + + u32:1-4294967295 + Prefix limit + + + + + + + + + Maximum number of prefixes to be sent to this peer + + u32:1-4294967295 + Prefix limit + + + + + + +#include + + + Remove private AS numbers from AS path in outbound route updates + + + +#include +#include +#include +#include + + + Route-map to selectively unsuppress suppressed routes + + policy route-map + + + txt + Route map name + + + ^[-_a-zA-Z0-9.]+$ + + Name of route-map can only contain alpha-numeric letters, hyphen and underscores + + + + + Default weight for routes from this peer + + u32:1-65535 + Default weight + + + + + + + diff --git a/interface-definitions/include/bgp/neighbor-afi-ipv4-labeled-unicast.xml.i b/interface-definitions/include/bgp/neighbor-afi-ipv4-labeled-unicast.xml.i index 45a440fd8..0eae29f5e 100644 --- a/interface-definitions/include/bgp/neighbor-afi-ipv4-labeled-unicast.xml.i +++ b/interface-definitions/include/bgp/neighbor-afi-ipv4-labeled-unicast.xml.i @@ -13,7 +13,7 @@ #include - #include + #include #include diff --git a/interface-definitions/include/bgp/neighbor-afi-ipv4-multicast.xml.i b/interface-definitions/include/bgp/neighbor-afi-ipv4-multicast.xml.i index 6526169ca..4bb6df7c3 100644 --- a/interface-definitions/include/bgp/neighbor-afi-ipv4-multicast.xml.i +++ b/interface-definitions/include/bgp/neighbor-afi-ipv4-multicast.xml.i @@ -13,7 +13,7 @@ #include - #include + #include #include diff --git a/interface-definitions/include/bgp/neighbor-afi-ipv4-unicast.xml.i b/interface-definitions/include/bgp/neighbor-afi-ipv4-unicast.xml.i index b7b7ca5b5..0094ce874 100644 --- a/interface-definitions/include/bgp/neighbor-afi-ipv4-unicast.xml.i +++ b/interface-definitions/include/bgp/neighbor-afi-ipv4-unicast.xml.i @@ -13,7 +13,7 @@ #include - #include + #include #include diff --git a/interface-definitions/include/bgp/neighbor-afi-ipv4-vpn.xml.i b/interface-definitions/include/bgp/neighbor-afi-ipv4-vpn.xml.i index 838327bc9..220f22fe3 100644 --- a/interface-definitions/include/bgp/neighbor-afi-ipv4-vpn.xml.i +++ b/interface-definitions/include/bgp/neighbor-afi-ipv4-vpn.xml.i @@ -5,7 +5,7 @@ #include - #include + #include diff --git a/interface-definitions/include/bgp/neighbor-afi-ipv6-labeled-unicast.xml.i b/interface-definitions/include/bgp/neighbor-afi-ipv6-labeled-unicast.xml.i index f680b7357..995183571 100644 --- a/interface-definitions/include/bgp/neighbor-afi-ipv6-labeled-unicast.xml.i +++ b/interface-definitions/include/bgp/neighbor-afi-ipv6-labeled-unicast.xml.i @@ -14,7 +14,7 @@ #include #include - #include + #include #include diff --git a/interface-definitions/include/bgp/neighbor-afi-ipv6-multicast.xml.i b/interface-definitions/include/bgp/neighbor-afi-ipv6-multicast.xml.i index 1f8db8361..bb713c313 100644 --- a/interface-definitions/include/bgp/neighbor-afi-ipv6-multicast.xml.i +++ b/interface-definitions/include/bgp/neighbor-afi-ipv6-multicast.xml.i @@ -6,7 +6,7 @@ #include #include - #include + #include #include diff --git a/interface-definitions/include/bgp/neighbor-afi-ipv6-unicast.xml.i b/interface-definitions/include/bgp/neighbor-afi-ipv6-unicast.xml.i index f6b812c28..26a5e7090 100644 --- a/interface-definitions/include/bgp/neighbor-afi-ipv6-unicast.xml.i +++ b/interface-definitions/include/bgp/neighbor-afi-ipv6-unicast.xml.i @@ -14,7 +14,7 @@ #include #include - #include + #include #include diff --git a/interface-definitions/include/bgp/neighbor-afi-ipv6-vpn.xml.i b/interface-definitions/include/bgp/neighbor-afi-ipv6-vpn.xml.i index c0df71cf3..5c6811986 100644 --- a/interface-definitions/include/bgp/neighbor-afi-ipv6-vpn.xml.i +++ b/interface-definitions/include/bgp/neighbor-afi-ipv6-vpn.xml.i @@ -6,7 +6,7 @@ #include #include - #include + #include -- cgit v1.2.3 From ebccc291865132d2dd03edd2a56d400dd087ef43 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sun, 12 Dec 2021 12:29:46 +0100 Subject: bgp: T3967: add support for conditional advertisement The BGP conditional advertisement feature uses the non-exist-map or the exist-map and the advertise-map keywords of the neighbor advertise-map command in order to track routes by the route prefix. non-exist-map ============= * If a route prefix is not present in the output of non-exist-map command, then advertise the route specified by the advertise-map command. * If a route prefix is present in the output of non-exist-map command, then do not advertise the route specified by the addvertise-map command. exist-map ========= * If a route prefix is present in the output of exist-map command, then advertise the route specified by the advertise-map command. * If a route prefix is not present in the output of exist-map command, then do not advertise the route specified by the advertise-map command. This feature is useful when some prefixes are advertised to one of its peers only if the information from the other peer is not present (due to failure in peering session or partial reachability etc). The conditional BGP announcements are sent in addition to the normal announcements that a BGP router sends to its peer. CLI nodes can be found under: * set protocols bgp neighbor address-family conditional-advertisement * set protocols bgp peer-group

address-family conditional-advertisement --- data/templates/frr/bgpd.frr.tmpl | 11 +++ .../bgp/neighbor-afi-ipv4-ipv6-common.xml.i | 55 ++++++++++++++ smoketest/scripts/cli/test_protocols_bgp.py | 84 ++++++++++++++++++---- src/conf_mode/protocols_bgp.py | 22 ++++++ 4 files changed, 157 insertions(+), 15 deletions(-) (limited to 'interface-definitions') diff --git a/data/templates/frr/bgpd.frr.tmpl b/data/templates/frr/bgpd.frr.tmpl index 9bb42fd2d..45e0544b7 100644 --- a/data/templates/frr/bgpd.frr.tmpl +++ b/data/templates/frr/bgpd.frr.tmpl @@ -146,6 +146,17 @@ {% if afi_config.as_override is defined %} neighbor {{ neighbor }} as-override {% endif %} +{% if afi_config.conditionally_advertise is defined and afi_config.conditionally_advertise is not none %} +{% if afi_config.conditionally_advertise.advertise_map is defined and afi_config.conditionally_advertise.advertise_map is not none %} +{% set exist_non_exist_map = 'exist-map' %} +{% if afi_config.conditionally_advertise.exist_map is defined and afi_config.conditionally_advertise.exist_map is not none %} +{% set exist_non_exist_map = 'exist-map ' ~ afi_config.conditionally_advertise.exist_map %} +{% elif afi_config.conditionally_advertise.non_exist_map is defined and afi_config.conditionally_advertise.non_exist_map is not none %} +{% set exist_non_exist_map = 'non-exist-map ' ~ afi_config.conditionally_advertise.non_exist_map %} +{% endif %} + neighbor {{ neighbor }} advertise-map {{ afi_config.conditionally_advertise.advertise_map }} {{ exist_non_exist_map }} +{% endif %} +{% endif %} {% if afi_config.remove_private_as is defined %} neighbor {{ neighbor }} remove-private-AS {% endif %} diff --git a/interface-definitions/include/bgp/neighbor-afi-ipv4-ipv6-common.xml.i b/interface-definitions/include/bgp/neighbor-afi-ipv4-ipv6-common.xml.i index 498662b0a..f3fc4444c 100644 --- a/interface-definitions/include/bgp/neighbor-afi-ipv4-ipv6-common.xml.i +++ b/interface-definitions/include/bgp/neighbor-afi-ipv4-ipv6-common.xml.i @@ -11,6 +11,61 @@ + + + Use route-map to conditionally advertise routes + + + + + Route-map to conditionally advertise routes + + policy route-map + + + txt + Route map name + + + ^[-_a-zA-Z0-9.]+$ + + Name of route-map can only contain alpha-numeric letters, hyphen and underscores + + + + + Advertise routes only if prefixes in exist-map are installed in BGP table + + policy route-map + + + txt + Route map name + + + ^[-_a-zA-Z0-9.]+$ + + Name of route-map can only contain alpha-numeric letters, hyphen and underscores + + + + + Advertise routes only if prefixes in non-exist-map are not installed in BGP table + + policy route-map + + + txt + Route map name + + + ^[-_a-zA-Z0-9.]+$ + + Name of route-map can only contain alpha-numeric letters, hyphen and underscores + + + + #include diff --git a/smoketest/scripts/cli/test_protocols_bgp.py b/smoketest/scripts/cli/test_protocols_bgp.py index 22cc3785f..d7230baf4 100755 --- a/smoketest/scripts/cli/test_protocols_bgp.py +++ b/smoketest/scripts/cli/test_protocols_bgp.py @@ -59,11 +59,14 @@ neighbor_config = { 'no_cap_nego' : '', 'port' : '667', 'cap_strict' : '', + 'advertise_map': route_map_in, + 'non_exist_map': route_map_out, 'pfx_list_in' : prefix_list_in, 'pfx_list_out' : prefix_list_out, 'no_send_comm_std' : '', }, '192.0.2.3' : { + 'advertise_map': route_map_in, 'description' : 'foo bar baz', 'remote_as' : '200', 'passive' : '', @@ -72,6 +75,8 @@ neighbor_config = { 'peer_group' : 'foo', }, '2001:db8::1' : { + 'advertise_map': route_map_in, + 'exist_map' : route_map_out, 'cap_dynamic' : '', 'cap_ext_next' : '', 'remote_as' : '123', @@ -104,6 +109,8 @@ neighbor_config = { peer_group_config = { 'foo' : { + 'advertise_map': route_map_in, + 'exist_map' : route_map_out, 'bfd' : '', 'remote_as' : '100', 'passive' : '', @@ -113,6 +120,7 @@ peer_group_config = { 'ttl_security': '5', }, 'foo-bar' : { + 'advertise_map': route_map_in, 'description' : 'foo peer bar group', 'remote_as' : '200', 'shutdown' : '', @@ -122,8 +130,9 @@ peer_group_config = { 'pfx_list_out' : prefix_list_out, 'no_send_comm_ext' : '', }, - 'baz' : { 'foo-bar_baz' : { + 'advertise_map': route_map_in, + 'non_exist_map': route_map_out, 'bfd_profile' : bfd_profile, 'cap_dynamic' : '', 'cap_ext_next' : '', @@ -137,23 +146,34 @@ peer_group_config = { } class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase): - def setUp(self): - self.cli_set(['policy', 'route-map', route_map_in, 'rule', '10', 'action', 'permit']) - self.cli_set(['policy', 'route-map', route_map_out, 'rule', '10', 'action', 'permit']) - self.cli_set(['policy', 'prefix-list', prefix_list_in, 'rule', '10', 'action', 'permit']) - self.cli_set(['policy', 'prefix-list', prefix_list_in, 'rule', '10', 'prefix', '192.0.2.0/25']) - self.cli_set(['policy', 'prefix-list', prefix_list_out, 'rule', '10', 'action', 'permit']) - self.cli_set(['policy', 'prefix-list', prefix_list_out, 'rule', '10', 'prefix', '192.0.2.128/25']) - - self.cli_set(['policy', 'prefix-list6', prefix_list_in6, 'rule', '10', 'action', 'permit']) - self.cli_set(['policy', 'prefix-list6', prefix_list_in6, 'rule', '10', 'prefix', '2001:db8:1000::/64']) - self.cli_set(['policy', 'prefix-list6', prefix_list_out6, 'rule', '10', 'action', 'deny']) - self.cli_set(['policy', 'prefix-list6', prefix_list_out6, 'rule', '10', 'prefix', '2001:db8:2000::/64']) + @classmethod + def setUpClass(cls): + super(cls, cls).setUpClass() + + # ensure we can also run this test on a live system - so lets clean + # out the current configuration :) + cls.cli_delete(cls, base_path) + + cls.cli_set(cls, ['policy', 'route-map', route_map_in, 'rule', '10', 'action', 'permit']) + cls.cli_set(cls, ['policy', 'route-map', route_map_out, 'rule', '10', 'action', 'permit']) + cls.cli_set(cls, ['policy', 'prefix-list', prefix_list_in, 'rule', '10', 'action', 'permit']) + cls.cli_set(cls, ['policy', 'prefix-list', prefix_list_in, 'rule', '10', 'prefix', '192.0.2.0/25']) + cls.cli_set(cls, ['policy', 'prefix-list', prefix_list_out, 'rule', '10', 'action', 'permit']) + cls.cli_set(cls, ['policy', 'prefix-list', prefix_list_out, 'rule', '10', 'prefix', '192.0.2.128/25']) + + cls.cli_set(cls, ['policy', 'prefix-list6', prefix_list_in6, 'rule', '10', 'action', 'permit']) + cls.cli_set(cls, ['policy', 'prefix-list6', prefix_list_in6, 'rule', '10', 'prefix', '2001:db8:1000::/64']) + cls.cli_set(cls, ['policy', 'prefix-list6', prefix_list_out6, 'rule', '10', 'action', 'deny']) + cls.cli_set(cls, ['policy', 'prefix-list6', prefix_list_out6, 'rule', '10', 'prefix', '2001:db8:2000::/64']) + + @classmethod + def tearDownClass(cls): + cls.cli_delete(cls, ['policy']) + def setUp(self): self.cli_set(base_path + ['local-as', ASN]) def tearDown(self): - self.cli_delete(['policy']) self.cli_delete(['vrf']) self.cli_delete(base_path) self.cli_commit() @@ -212,7 +232,13 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase): self.assertIn(f' neighbor {peer} addpath-tx-all-paths', frrconfig) if 'addpath_per_as' in peer_config: self.assertIn(f' neighbor {peer} addpath-tx-bestpath-per-AS', frrconfig) - + if 'advertise_map' in peer_config: + base = f' neighbor {peer} advertise-map {peer_config["advertise_map"]}' + if 'exist_map' in peer_config: + base = f'{base} exist-map {peer_config["exist_map"]}' + if 'non_exist_map' in peer_config: + base = f'{base} non-exist-map {peer_config["non_exist_map"]}' + self.assertIn(base, frrconfig) def test_bgp_01_simple(self): router_id = '127.0.0.1' @@ -353,6 +379,20 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase): if 'addpath_per_as' in peer_config: self.cli_set(base_path + ['neighbor', peer, 'address-family', afi, 'addpath-tx-per-as']) + # Conditional advertisement + if 'advertise_map' in peer_config: + self.cli_set(base_path + ['neighbor', peer, 'address-family', afi, 'conditionally-advertise', 'advertise-map', peer_config["advertise_map"]]) + # Either exist-map or non-exist-map needs to be specified + if 'exist_map' not in peer_config and 'non_exist_map' not in peer_config: + with self.assertRaises(ConfigSessionError): + self.cli_commit() + self.cli_set(base_path + ['neighbor', peer, 'address-family', afi, 'conditionally-advertise', 'exist-map', route_map_in]) + + if 'exist_map' in peer_config: + self.cli_set(base_path + ['neighbor', peer, 'address-family', afi, 'conditionally-advertise', 'exist-map', peer_config["exist_map"]]) + if 'non_exist_map' in peer_config: + self.cli_set(base_path + ['neighbor', peer, 'address-family', afi, 'conditionally-advertise', 'non-exist-map', peer_config["non_exist_map"]]) + # commit changes self.cli_commit() @@ -421,6 +461,20 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase): if 'addpath_per_as' in config: self.cli_set(base_path + ['peer-group', peer_group, 'address-family', 'ipv4-unicast', 'addpath-tx-per-as']) + # Conditional advertisement + if 'advertise_map' in config: + self.cli_set(base_path + ['peer-group', peer_group, 'address-family', 'ipv4-unicast', 'conditionally-advertise', 'advertise-map', config["advertise_map"]]) + # Either exist-map or non-exist-map needs to be specified + if 'exist_map' not in config and 'non_exist_map' not in config: + with self.assertRaises(ConfigSessionError): + self.cli_commit() + self.cli_set(base_path + ['peer-group', peer_group, 'address-family', 'ipv4-unicast', 'conditionally-advertise', 'exist-map', route_map_in]) + + if 'exist_map' in config: + self.cli_set(base_path + ['peer-group', peer_group, 'address-family', 'ipv4-unicast', 'conditionally-advertise', 'exist-map', config["exist_map"]]) + if 'non_exist_map' in config: + self.cli_set(base_path + ['peer-group', peer_group, 'address-family', 'ipv4-unicast', 'conditionally-advertise', 'non-exist-map', config["non_exist_map"]]) + for peer, peer_config in neighbor_config.items(): if 'peer_group' in peer_config: self.cli_set(base_path + ['neighbor', peer, 'peer-group', peer_config['peer_group']]) diff --git a/src/conf_mode/protocols_bgp.py b/src/conf_mode/protocols_bgp.py index 03fb17ba7..d8704727c 100755 --- a/src/conf_mode/protocols_bgp.py +++ b/src/conf_mode/protocols_bgp.py @@ -183,6 +183,28 @@ def verify(bgp): raise ConfigError(f'Neighbor "{peer}" cannot have both ipv6-unicast and ipv6-labeled-unicast configured at the same time!') afi_config = peer_config['address_family'][afi] + + if 'conditionally_advertise' in afi_config: + if 'advertise_map' not in afi_config['conditionally_advertise']: + raise ConfigError('Must speficy advertise-map when conditionally-advertise is in use!') + # Verify advertise-map (which is a route-map) exists + verify_route_map(afi_config['conditionally_advertise']['advertise_map'], bgp) + + if ('exist_map' not in afi_config['conditionally_advertise'] and + 'non_exist_map' not in afi_config['conditionally_advertise']): + raise ConfigError('Must either speficy exist-map or non-exist-map when ' \ + 'conditionally-advertise is in use!') + + if {'exist_map', 'non_exist_map'} <= set(afi_config['conditionally_advertise']): + raise ConfigError('Can not specify both exist-map and non-exist-map for ' \ + 'conditionally-advertise!') + + if 'exist_map' in afi_config['conditionally_advertise']: + verify_route_map(afi_config['conditionally_advertise']['exist_map'], bgp) + + if 'non_exist_map' in afi_config['conditionally_advertise']: + verify_route_map(afi_config['conditionally_advertise']['non_exist_map'], bgp) + # Validate if configured Prefix list exists if 'prefix_list' in afi_config: for tmp in ['import', 'export']: -- cgit v1.2.3 From 0e3c35e6517f5cfebb4206c735a2ea976a7fd383 Mon Sep 17 00:00:00 2001 From: John Estabrook Date: Fri, 10 Dec 2021 14:41:23 -0600 Subject: http-api: T4071: allow API to bind to unix domain socket --- data/templates/https/nginx.default.tmpl | 4 ++++ interface-definitions/https.xml.in | 6 ++++++ python/vyos/defaults.py | 5 +++-- src/conf_mode/http-api.py | 11 +++++++---- src/conf_mode/https.py | 2 ++ src/services/vyos-http-api-server | 14 +++++++++----- 6 files changed, 31 insertions(+), 11 deletions(-) (limited to 'interface-definitions') diff --git a/data/templates/https/nginx.default.tmpl b/data/templates/https/nginx.default.tmpl index 9d73baeee..ac9203e83 100644 --- a/data/templates/https/nginx.default.tmpl +++ b/data/templates/https/nginx.default.tmpl @@ -44,7 +44,11 @@ server { # proxy settings for HTTP API, if enabled; 503, if not location ~ /(retrieve|configure|config-file|image|generate|show|docs|openapi.json|redoc|graphql) { {% if server.api %} +{% if server.api.socket %} + proxy_pass http://unix:/run/api.sock; +{% else %} proxy_pass http://localhost:{{ server.api.port }}; +{% endif %} proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_read_timeout 600; diff --git a/interface-definitions/https.xml.in b/interface-definitions/https.xml.in index d26cd5e7a..33e43a432 100644 --- a/interface-definitions/https.xml.in +++ b/interface-definitions/https.xml.in @@ -101,6 +101,12 @@ + + + Run server on Unix domain socket + + + diff --git a/python/vyos/defaults.py b/python/vyos/defaults.py index f355c4919..c77b695bd 100644 --- a/python/vyos/defaults.py +++ b/python/vyos/defaults.py @@ -46,8 +46,9 @@ https_data = { api_data = { 'listen_address' : '127.0.0.1', 'port' : '8080', - 'strict' : 'false', - 'debug' : 'false', + 'socket' : False, + 'strict' : False, + 'debug' : False, 'api_keys' : [ {"id": "testapp", "key": "qwerty"} ] } diff --git a/src/conf_mode/http-api.py b/src/conf_mode/http-api.py index 4bfcbeb47..cd0191599 100755 --- a/src/conf_mode/http-api.py +++ b/src/conf_mode/http-api.py @@ -31,7 +31,7 @@ from vyos.util import call from vyos import airbag airbag.enable() -config_file = '/etc/vyos/http-api.conf' +api_conf_file = '/etc/vyos/http-api.conf' vyos_conf_scripts_dir=vyos.defaults.directories['conf_mode'] @@ -55,10 +55,13 @@ def get_config(config=None): conf.set_level('service https api') if conf.exists('strict'): - http_api['strict'] = 'true' + http_api['strict'] = True if conf.exists('debug'): - http_api['debug'] = 'true' + http_api['debug'] = True + + if conf.exists('socket'): + http_api['socket'] = True if conf.exists('port'): port = conf.return_value('port') @@ -88,7 +91,7 @@ def generate(http_api): if not os.path.exists('/etc/vyos'): os.mkdir('/etc/vyos') - with open(config_file, 'w') as f: + with open(api_conf_file, 'w') as f: json.dump(http_api, f, indent=2) return None diff --git a/src/conf_mode/https.py b/src/conf_mode/https.py index cd5073aa2..053ee5d4a 100755 --- a/src/conf_mode/https.py +++ b/src/conf_mode/https.py @@ -191,6 +191,8 @@ def generate(https): vhosts = https.get('api-restrict', {}).get('virtual-host', []) if vhosts: api_data['vhost'] = vhosts[:] + if 'socket' in list(api_settings): + api_data['socket'] = True if api_data: vhost_list = api_data.get('vhost', []) diff --git a/src/services/vyos-http-api-server b/src/services/vyos-http-api-server index aa7ac6708..f79058683 100755 --- a/src/services/vyos-http-api-server +++ b/src/services/vyos-http-api-server @@ -640,15 +640,19 @@ if __name__ == '__main__': app.state.vyos_session = config_session app.state.vyos_keys = server_config['api_keys'] - app.state.vyos_debug = bool(server_config['debug'] == 'true') - app.state.vyos_strict = bool(server_config['strict'] == 'true') + app.state.vyos_debug = server_config['debug'] + app.state.vyos_strict = server_config['strict'] api.graphql.state.settings['app'] = app try: - uvicorn.run(app, host=server_config["listen_address"], - port=int(server_config["port"]), - proxy_headers=True) + if not server_config['socket']: + uvicorn.run(app, host=server_config["listen_address"], + port=int(server_config["port"]), + proxy_headers=True) + else: + uvicorn.run(app, uds="/run/api.sock", + proxy_headers=True) except OSError as err: logger.critical(f"OSError {err}") sys.exit(1) -- cgit v1.2.3 From 55f8ede2d09a9ad095f9ec5c2a729f8c5fb6aafa Mon Sep 17 00:00:00 2001 From: John Estabrook Date: Wed, 15 Dec 2021 14:45:25 -0600 Subject: http-api: T4076: allow setting CORS option 'Access-Control-Allow-Origin' --- interface-definitions/https.xml.in | 13 +++++++++++++ src/conf_mode/http-api.py | 6 ++++++ src/services/vyos-http-api-server | 18 +++++++++++++----- 3 files changed, 32 insertions(+), 5 deletions(-) (limited to 'interface-definitions') diff --git a/interface-definitions/https.xml.in b/interface-definitions/https.xml.in index 33e43a432..6fea2f1f6 100644 --- a/interface-definitions/https.xml.in +++ b/interface-definitions/https.xml.in @@ -107,6 +107,19 @@ + + + Set CORS options + + + + + Allow resource request from origin + + + + + diff --git a/src/conf_mode/http-api.py b/src/conf_mode/http-api.py index cd0191599..ea0743cd5 100755 --- a/src/conf_mode/http-api.py +++ b/src/conf_mode/http-api.py @@ -67,6 +67,12 @@ def get_config(config=None): port = conf.return_value('port') http_api['port'] = port + if conf.exists('cors'): + http_api['cors'] = {} + if conf.exists('cors allow-origin'): + origins = conf.return_values('cors allow-origin') + http_api['cors']['origins'] = origins[:] + if conf.exists('keys'): for name in conf.list_nodes('keys id'): if conf.exists('keys id {0} key'.format(name)): diff --git a/src/services/vyos-http-api-server b/src/services/vyos-http-api-server index f79058683..06871f1d6 100755 --- a/src/services/vyos-http-api-server +++ b/src/services/vyos-http-api-server @@ -32,6 +32,7 @@ from fastapi.responses import HTMLResponse from fastapi.exceptions import RequestValidationError from fastapi.routing import APIRoute from pydantic import BaseModel, StrictStr, validator +from starlette.middleware.cors import CORSMiddleware from starlette.datastructures import FormData from starlette.formparsers import FormParser, MultiPartParser from multipart.multipart import parse_options_header @@ -610,13 +611,19 @@ def show_op(data: ShowModel): # GraphQL integration ### -from api.graphql.bindings import generate_schema +def graphql_init(fast_api_app): + from api.graphql.bindings import generate_schema -api.graphql.state.init() + api.graphql.state.init() + api.graphql.state.settings['app'] = app -schema = generate_schema() + schema = generate_schema() -app.add_route('/graphql', GraphQL(schema, debug=True)) + if app.state.vyos_origins: + origins = app.state.vyos_origins + app.add_route('/graphql', CORSMiddleware(GraphQL(schema, debug=True), allow_origins=origins, allow_methods=("GET", "POST", "OPTIONS"))) + else: + app.add_route('/graphql', GraphQL(schema, debug=True)) ### @@ -642,8 +649,9 @@ if __name__ == '__main__': app.state.vyos_debug = server_config['debug'] app.state.vyos_strict = server_config['strict'] + app.state.vyos_origins = server_config.get('cors', {}).get('origins', []) - api.graphql.state.settings['app'] = app + graphql_init(app) try: if not server_config['socket']: -- cgit v1.2.3