From a378822f26268c1e8cbfcf754e5cad5c310c7c3c Mon Sep 17 00:00:00 2001 From: Viacheslav Date: Tue, 27 Jul 2021 18:34:45 +0000 Subject: pbr: T3702: Add rules match fwmark --- interface-definitions/policy-local-route.xml.in | 12 ++++++ smoketest/scripts/cli/test_policy.py | 53 +++++++++++++++++++++++++ src/conf_mode/policy-local-route.py | 39 +++++++++++++++--- 3 files changed, 98 insertions(+), 6 deletions(-) diff --git a/interface-definitions/policy-local-route.xml.in b/interface-definitions/policy-local-route.xml.in index 3769c3748..86445b65d 100644 --- a/interface-definitions/policy-local-route.xml.in +++ b/interface-definitions/policy-local-route.xml.in @@ -40,6 +40,18 @@ + + + Match fwmark value + + u32:1-2147483647 + Address to match against + + + + + + Source address or prefix diff --git a/smoketest/scripts/cli/test_policy.py b/smoketest/scripts/cli/test_policy.py index 2d7b78048..485cc274a 100755 --- a/smoketest/scripts/cli/test_policy.py +++ b/smoketest/scripts/cli/test_policy.py @@ -1116,5 +1116,58 @@ class TestPolicy(VyOSUnitTestSHIM.TestCase): self.assertEqual(tmp, original) + # Test set table for fwmark + def test_fwmark_table_id(self): + path = base_path + ['local-route'] + + fwmk = '24' + rule = '101' + table = '154' + + self.cli_set(path + ['rule', rule, 'set', 'table', table]) + self.cli_set(path + ['rule', rule, 'fwmark', fwmk]) + + self.cli_commit() + + # Check generated configuration + + # Expected values + original = """ + 101: from all fwmark 0x18 lookup 154 + """ + tmp = cmd('ip rule show prio 101') + original = original.split() + tmp = tmp.split() + + self.assertEqual(tmp, original) + + # Test set table for sources with fwmark + def test_fwmark_sources_table_id(self): + path = base_path + ['local-route'] + + sources = ['203.0.113.11', '203.0.113.12'] + fwmk = '23' + rule = '100' + table = '150' + for src in sources: + self.cli_set(path + ['rule', rule, 'set', 'table', table]) + self.cli_set(path + ['rule', rule, 'source', src]) + self.cli_set(path + ['rule', rule, 'fwmark', fwmk]) + + self.cli_commit() + + # Check generated configuration + + # Expected values + original = """ + 100: from 203.0.113.11 fwmark 0x17 lookup 150 + 100: from 203.0.113.12 fwmark 0x17 lookup 150 + """ + tmp = cmd('ip rule show prio 100') + original = original.split() + tmp = tmp.split() + + self.assertEqual(tmp, original) + if __name__ == '__main__': unittest.main(verbosity=2) diff --git a/src/conf_mode/policy-local-route.py b/src/conf_mode/policy-local-route.py index 013f22665..0b7ceedeb 100755 --- a/src/conf_mode/policy-local-route.py +++ b/src/conf_mode/policy-local-route.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2020 VyOS maintainers and contributors +# Copyright (C) 2020-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 @@ -44,17 +44,26 @@ def get_config(config=None): if tmp: for rule in (tmp or []): src = leaf_node_changed(conf, ['policy', 'local-route', 'rule', rule, 'source']) + fwmk = leaf_node_changed(conf, ['policy', 'local-route', 'rule', rule, 'fwmark']) if src: dict = dict_merge({'rule_remove' : {rule : {'source' : src}}}, dict) pbr.update(dict) + if fwmk: + dict = dict_merge({'rule_remove' : {rule : {'fwmark' : fwmk}}}, dict) + pbr.update(dict) # delete policy local-route rule x source x.x.x.x + # delete policy local-route rule x fwmark x if 'rule' in pbr: for rule in pbr['rule']: src = leaf_node_changed(conf, ['policy', 'local-route', 'rule', rule, 'source']) + fwmk = leaf_node_changed(conf, ['policy', 'local-route', 'rule', rule, 'fwmark']) if src: dict = dict_merge({'rule_remove' : {rule : {'source' : src}}}, dict) pbr.update(dict) + if fwmk: + dict = dict_merge({'rule_remove' : {rule : {'fwmark' : fwmk}}}, dict) + pbr.update(dict) return pbr @@ -65,8 +74,8 @@ def verify(pbr): if 'rule' in pbr: for rule in pbr['rule']: - if 'source' not in pbr['rule'][rule]: - raise ConfigError('Source address required!') + if 'source' not in pbr['rule'][rule] and 'fwmark' not in pbr['rule'][rule]: + raise ConfigError('Source address or fwmark is required!') else: if 'set' not in pbr['rule'][rule] or 'table' not in pbr['rule'][rule]['set']: raise ConfigError('Table set is required!') @@ -86,16 +95,34 @@ def apply(pbr): # Delete old rule if needed if 'rule_remove' in pbr: for rule in pbr['rule_remove']: - for src in pbr['rule_remove'][rule]['source']: - call(f'ip rule del prio {rule} from {src}') + if 'source' in pbr['rule_remove'][rule]: + for src in pbr['rule_remove'][rule]['source']: + call(f'ip rule del prio {rule} from {src}') + if 'fwmark' in pbr['rule_remove'][rule]: + for fwmk in pbr['rule_remove'][rule]['fwmark']: + call(f'ip rule del prio {rule} from all fwmark {fwmk}') # Generate new config if 'rule' in pbr: for rule in pbr['rule']: table = pbr['rule'][rule]['set']['table'] - if pbr['rule'][rule]['source']: + # Only source in the rule + # set policy local-route rule 100 source '203.0.113.1' + if 'source' in pbr['rule'][rule] and not 'fwmark' in pbr['rule'][rule]: for src in pbr['rule'][rule]['source']: call(f'ip rule add prio {rule} from {src} lookup {table}') + # Only fwmark in the rule + # set policy local-route rule 101 fwmark '23' + if 'fwmark' in pbr['rule'][rule] and not 'source' in pbr['rule'][rule]: + for fwmk in pbr['rule'][rule]['fwmark']: + call(f'ip rule add prio {rule} from all fwmark {fwmk} lookup {table}') + # Source and fwmark in the rule + # set policy local-route rule 100 source '203.0.113.1' + # set policy local-route rule 100 fwmark '23' + if 'source' in pbr['rule'][rule] and 'fwmark' in pbr['rule'][rule]: + for src in pbr['rule'][rule]['source']: + for fwmk in pbr['rule'][rule]['fwmark']: + call(f'ip rule add prio {rule} from {src} fwmark {fwmk} lookup {table}') return None -- cgit v1.2.3 From 29463355e6ec98195ffd52e018bc775cd4199456 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Wed, 11 Aug 2021 14:58:12 +0200 Subject: accel-ppp: T3731: eliminate service name from error message VyOS will automatically append the subsystem name (pppoe-server or sstp) when something goes wrong. No need to hardcode this into the error string a second time. --- python/vyos/configverify.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/python/vyos/configverify.py b/python/vyos/configverify.py index 58028b604..4279e6982 100644 --- a/python/vyos/configverify.py +++ b/python/vyos/configverify.py @@ -344,7 +344,7 @@ def verify_accel_ppp_base_service(config): # vertify auth settings if dict_search('authentication.mode', config) == 'local': if not dict_search('authentication.local_users', config): - raise ConfigError('PPPoE local auth mode requires local users to be configured!') + raise ConfigError('Authentication mode local requires local users to be configured!') for user in dict_search('authentication.local_users.username', config): user_config = config['authentication']['local_users']['username'][user] @@ -368,7 +368,7 @@ def verify_accel_ppp_base_service(config): raise ConfigError(f'Missing RADIUS secret key for server "{server}"') if 'gateway_address' not in config: - raise ConfigError('PPPoE server requires gateway-address to be configured!') + raise ConfigError('Server requires gateway-address to be configured!') if 'name_server_ipv4' in config: if len(config['name_server_ipv4']) > 2: -- cgit v1.2.3 From 82fbacd13f852374df2ecdd394b983325445f81b Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Wed, 11 Aug 2021 18:08:22 +0200 Subject: smoketest: ospf: add logger to passive-interface test --- smoketest/scripts/cli/test_protocols_ospf.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/smoketest/scripts/cli/test_protocols_ospf.py b/smoketest/scripts/cli/test_protocols_ospf.py index 623d40497..59862ca3d 100755 --- a/smoketest/scripts/cli/test_protocols_ospf.py +++ b/smoketest/scripts/cli/test_protocols_ospf.py @@ -14,6 +14,8 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . +import logging +import sys import unittest from base_vyostest_shim import VyOSUnitTestSHIM @@ -27,6 +29,8 @@ base_path = ['protocols', 'ospf'] route_map = 'foo-bar-baz10' +log = logging.getLogger('TestProtocolsOSPF') + class TestProtocolsOSPF(VyOSUnitTestSHIM.TestCase): def setUp(self): self.cli_set(['policy', 'route-map', route_map, 'rule', '10', 'action', 'permit']) @@ -202,10 +206,11 @@ class TestProtocolsOSPF(VyOSUnitTestSHIM.TestCase): for interface in interfaces: self.assertIn(f' no passive-interface {interface}', frrconfig) # default except: - tmp1 = cmd('sudo dmesg') - tmp2 = cmd('tail -n 250 /var/log/messages') - tmp3 = cmd('vtysh -c "show run"') - self.fail(f'Now we can hopefully see why OSPF fails:\n{tmp1}\n\n{tmp2}\n\n{tmp3}') + log.debug(frrconfig) + log.debug(cmd('sudo dmesg')) + log.debug(cmd('sudo cat /var/log/messages')) + log.debug(cmd('vtysh -c "show run"')) + self.fail('Now we can hopefully see why OSPF fails!') def test_ospf_08_redistribute(self): metric = '15' @@ -345,4 +350,5 @@ class TestProtocolsOSPF(VyOSUnitTestSHIM.TestCase): self.assertNotIn(zebra_route_map, frrconfig) if __name__ == '__main__': + logging.basicConfig(stream=sys.stderr, level=logging.DEBUG) unittest.main(verbosity=2) -- cgit v1.2.3 From e7c69ead85af485b2b881f24b929a4bcc779a4ce Mon Sep 17 00:00:00 2001 From: John Estabrook Date: Wed, 11 Aug 2021 11:06:39 -0500 Subject: xml: T3732: simplifications for merging defaultValue and default-less nodes --- scripts/override-default | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/scripts/override-default b/scripts/override-default index c8a0ff1da..228aca30f 100755 --- a/scripts/override-default +++ b/scripts/override-default @@ -62,26 +62,26 @@ def override_element(l: list): def collect_and_override(dir_name): """ - Collect elements with defaultValue tag into dictionary indexed by tuple - of (name: str, ancestor path: str). + Collect elements with defaultValue tag into dictionary indexed by name + attributes of ancestor path. """ for fname in glob.glob(f'{dir_name}/*.xml'): tree = etree.parse(fname) root = tree.getroot() defv = {} - xpath_str = f'//defaultValue' + xpath_str = '//defaultValue' xp = tree.xpath(xpath_str) for element in xp: ap = element.xpath('ancestor::*[@name]') ap_name = [el.get("name") for el in ap] - ap_path_str = ' '.join(ap_name[:-1]) - defv.setdefault((ap_name[-1], ap_path_str), []).append(element) + ap_path_str = ' '.join(ap_name) + defv.setdefault(ap_path_str, []).append(element) for k, v in defv.items(): if len(v) > 1: - logger.info(f"overridding default in {k[0]}, path '{k[1]}'") + logger.info(f"overridding default in path '{k}'") override_element(v) revised_str = etree.tostring(root, encoding='unicode', pretty_print=True) -- cgit v1.2.3 From bff7f7f4e56b3541a9d895bb991c5517d9391143 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Wed, 11 Aug 2021 21:03:05 +0200 Subject: GitHub: fix typo in PR template --- .github/PULL_REQUEST_TEMPLATE.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 04ca4070d..61ee1d9ff 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,5 +1,5 @@ - + ## Change Summary -- cgit v1.2.3 From b95403a7b4a6a220e41ef27b5247065eff782fd3 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Wed, 11 Aug 2021 20:07:31 +0200 Subject: bgp: T3739: add common route-distinguisher XML building block --- .../include/bgp/afi-l2vpn-common.xml.i | 13 +---------- .../include/bgp/protocol-common-config.xml.i | 26 ++-------------------- .../include/bgp/route-distinguisher.xml.i | 14 ++++++++++++ 3 files changed, 17 insertions(+), 36 deletions(-) create mode 100644 interface-definitions/include/bgp/route-distinguisher.xml.i diff --git a/interface-definitions/include/bgp/afi-l2vpn-common.xml.i b/interface-definitions/include/bgp/afi-l2vpn-common.xml.i index 1673f25a5..aaa69e6c8 100644 --- a/interface-definitions/include/bgp/afi-l2vpn-common.xml.i +++ b/interface-definitions/include/bgp/afi-l2vpn-common.xml.i @@ -11,17 +11,6 @@ - - - Route Distinguisher - - txt - 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}$ - - - +#include #include diff --git a/interface-definitions/include/bgp/protocol-common-config.xml.i b/interface-definitions/include/bgp/protocol-common-config.xml.i index 5080ce588..552e85aa4 100644 --- a/interface-definitions/include/bgp/protocol-common-config.xml.i +++ b/interface-definitions/include/bgp/protocol-common-config.xml.i @@ -372,18 +372,7 @@ - - - Route Distinguisher - - txt - Route Distinguisher, asn:xxx - - - ^[0-9]{1,10}:[0-9]{1,5}$ - - - + #include MPLS label value assigned to route @@ -772,18 +761,7 @@ - - - Route Distinguisher - - txt - Route Distinguisher, asn:xxx - - - ^[0-9]{1,10}:[0-9]{1,5}$ - - - + #include MPLS label value assigned to route diff --git a/interface-definitions/include/bgp/route-distinguisher.xml.i b/interface-definitions/include/bgp/route-distinguisher.xml.i new file mode 100644 index 000000000..fdfbe7076 --- /dev/null +++ b/interface-definitions/include/bgp/route-distinguisher.xml.i @@ -0,0 +1,14 @@ + + + + Route Distinguisher + + txt + 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}$ + + + + -- cgit v1.2.3 From 8681a62fbb413ad3a613ebedb430919a940beef7 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Wed, 11 Aug 2021 21:11:48 +0200 Subject: bgp: evpn: T3739: add prefix-list match support FRR 7.5.1 supports: vyos(config-route-map)# match evpn default-route default EVPN type-5 route rd Route Distinguisher route-type Match route-type vni Match VNI This commit adds a proper VyOS CLI abstraction. --- data/templates/frr/policy.frr.tmpl | 12 ++++++++++++ interface-definitions/policy.xml.in | 38 ++++++++++++++++++++++++++++++++++++ smoketest/scripts/cli/test_policy.py | 33 +++++++++++++++++++++++++++++++ 3 files changed, 83 insertions(+) diff --git a/data/templates/frr/policy.frr.tmpl b/data/templates/frr/policy.frr.tmpl index b5649b44e..89bd558dc 100644 --- a/data/templates/frr/policy.frr.tmpl +++ b/data/templates/frr/policy.frr.tmpl @@ -165,6 +165,18 @@ route-map {{ route_map }} {{ rule_config.action }} {{ rule }} {% if rule_config.match.extcommunity is defined and rule_config.match.extcommunity is not none %} match extcommunity {{ rule_config.match.extcommunity }} {% endif %} +{% if rule_config.match.evpn is defined and rule_config.match.evpn.default_route is defined %} + match evpn default-route +{% endif %} +{% if rule_config.match.evpn is defined and rule_config.match.evpn.rd is defined and rule_config.match.evpn.rd is not none %} + match evpn rd {{ rule_config.match.evpn.rd }} +{% endif %} +{% if rule_config.match.evpn is defined and rule_config.match.evpn.route_type is defined and rule_config.match.evpn.route_type is not none %} + match evpn route-type {{ rule_config.match.evpn.route_type }} +{% endif %} +{% if rule_config.match.evpn is defined and rule_config.match.evpn.vni is defined and rule_config.match.evpn.vni is not none %} + match evpn vni {{ rule_config.match.evpn.vni }} +{% endif %} {% if rule_config.match.interface is defined and rule_config.match.interface is not none %} match interface {{ rule_config.match.interface }} {% endif %} diff --git a/interface-definitions/policy.xml.in b/interface-definitions/policy.xml.in index 5a3c58fa8..fb62d2f89 100644 --- a/interface-definitions/policy.xml.in +++ b/interface-definitions/policy.xml.in @@ -535,6 +535,44 @@ + + + Ethernet Virtual Private Network + + + + + Default EVPN type-5 route + + + + #include + + + Match route-type + + macip multicast prefix + + + macip + mac-ip route + + + multicast + IMET route + + + prefix + Prefix route + + + ^(macip|multicast|prefix)$ + + + + #include + + BGP extended community to match diff --git a/smoketest/scripts/cli/test_policy.py b/smoketest/scripts/cli/test_policy.py index 2d7b78048..66d3f3812 100755 --- a/smoketest/scripts/cli/test_policy.py +++ b/smoketest/scripts/cli/test_policy.py @@ -804,6 +804,19 @@ class TestPolicy(VyOSUnitTestSHIM.TestCase): }, }, }, + 'evpn-configuration' : { + 'rule' : { + '10' : { + 'action' : 'permit', + 'match' : { + 'evpn-default-route' : '', + 'evpn-rd' : '100:300', + 'evpn-route-type' : 'prefix', + 'evpn-vni' : '1234', + }, + }, + }, + }, } self.cli_set(['policy', 'access-list', access_list, 'rule', '10', 'action', 'permit']) @@ -847,6 +860,14 @@ class TestPolicy(VyOSUnitTestSHIM.TestCase): if 'community' in rule_config['match']: self.cli_set(path + ['rule', rule, 'match', 'community', 'community-list', rule_config['match']['community']]) self.cli_set(path + ['rule', rule, 'match', 'community', 'exact-match']) + if 'evpn-default-route' in rule_config['match']: + self.cli_set(path + ['rule', rule, 'match', 'evpn', 'default-route']) + if 'evpn-rd' in rule_config['match']: + self.cli_set(path + ['rule', rule, 'match', 'evpn', 'rd', rule_config['match']['evpn-rd']]) + if 'evpn-route-type' in rule_config['match']: + self.cli_set(path + ['rule', rule, 'match', 'evpn', 'route-type', rule_config['match']['evpn-route-type']]) + if 'evpn-vni' in rule_config['match']: + self.cli_set(path + ['rule', rule, 'match', 'evpn', 'vni', rule_config['match']['evpn-vni']]) if 'extcommunity' in rule_config['match']: self.cli_set(path + ['rule', rule, 'match', 'extcommunity', rule_config['match']['extcommunity']]) if 'interface' in rule_config['match']: @@ -967,6 +988,18 @@ class TestPolicy(VyOSUnitTestSHIM.TestCase): if 'community' in rule_config['match']: tmp = f'match community {rule_config["match"]["community"]} exact-match' self.assertIn(tmp, config) + if 'evpn-default-route' in rule_config['match']: + tmp = f'match evpn default-route' + self.assertIn(tmp, config) + if 'evpn-rd' in rule_config['match']: + tmp = f'match evpn rd {rule_config["match"]["evpn-rd"]}' + self.assertIn(tmp, config) + if 'evpn-route-type' in rule_config['match']: + tmp = f'match evpn route-type {rule_config["match"]["evpn-route-type"]}' + self.assertIn(tmp, config) + if 'evpn-vni' in rule_config['match']: + tmp = f'match evpn vni {rule_config["match"]["evpn-vni"]}' + self.assertIn(tmp, config) if 'extcommunity' in rule_config['match']: tmp = f'match extcommunity {rule_config["match"]["extcommunity"]}' self.assertIn(tmp, config) -- cgit v1.2.3 From ec6dc94a5144ea4ac75ba6fac4d85b4f66337ae2 Mon Sep 17 00:00:00 2001 From: John Estabrook Date: Wed, 11 Aug 2021 11:54:28 -0500 Subject: xml: T3732: merge leafNode with defaultValue with leafNode(s) of same path --- scripts/override-default | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/scripts/override-default b/scripts/override-default index 228aca30f..0c49087c8 100755 --- a/scripts/override-default +++ b/scripts/override-default @@ -27,6 +27,7 @@ import sys import glob import logging +from copy import deepcopy from lxml import etree debug = False @@ -60,6 +61,28 @@ def override_element(l: list): for el in parents: el.getparent().remove(el) +def merge_remaining(l: list, elementtree): + """ + Merge (now) single leaf node containing 'defaultValue' with leaf nodes + of same path and no 'defaultValue'. + """ + for p in l: + p = p.split() + path_str = f'/interfaceDefinition/*' + path_list = [] + for i in range(len(p)): + path_list.append(f'[@name="{p[i]}"]') + path_str += '/children/*'.join(path_list) + rp = elementtree.xpath(path_str) + if len(rp) > 1: + for el in rp[1:]: + # in practice there will only be one child of the path, + # either defaultValue or Properties, since + # override_element() has already run + for child in el: + rp[0].append(deepcopy(child)) + el.getparent().remove(el) + def collect_and_override(dir_name): """ Collect elements with defaultValue tag into dictionary indexed by name @@ -84,6 +107,9 @@ def collect_and_override(dir_name): logger.info(f"overridding default in path '{k}'") override_element(v) + to_merge = list(defv) + merge_remaining(to_merge, tree) + revised_str = etree.tostring(root, encoding='unicode', pretty_print=True) with open(f'{fname}', 'w') as f: -- cgit v1.2.3 From 0f7833483c0fe4982747bbbace45a83fae793257 Mon Sep 17 00:00:00 2001 From: FileGo Date: Thu, 12 Aug 2021 10:08:23 +0100 Subject: dns: T3744: fixed dns fwd statistics formatting --- src/op_mode/dns_forwarding_statistics.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/op_mode/dns_forwarding_statistics.py b/src/op_mode/dns_forwarding_statistics.py index 1fb61d263..d79b6c024 100755 --- a/src/op_mode/dns_forwarding_statistics.py +++ b/src/op_mode/dns_forwarding_statistics.py @@ -11,7 +11,7 @@ PDNS_CMD='/usr/bin/rec_control --socket-dir=/run/powerdns' OUT_TMPL_SRC = """ DNS forwarding statistics: -Cache entries: {{ cache_entries -}} +Cache entries: {{ cache_entries }} Cache size: {{ cache_size }} kbytes """ -- cgit v1.2.3 From 7e52a7079afb522d1456833023ad58fa8b05e880 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Thu, 12 Aug 2021 20:59:37 +0200 Subject: login: T3746: inform users about pending reboots --- debian/vyos-1x.install | 1 + src/etc/update-motd.d/99-reboot | 7 +++++++ 2 files changed, 8 insertions(+) create mode 100755 src/etc/update-motd.d/99-reboot diff --git a/debian/vyos-1x.install b/debian/vyos-1x.install index 2ed25755f..7ca568eff 100644 --- a/debian/vyos-1x.install +++ b/debian/vyos-1x.install @@ -10,6 +10,7 @@ etc/sudoers.d etc/systemd etc/sysctl.d etc/udev +etc/update-motd.d etc/vyos lib/ opt/ diff --git a/src/etc/update-motd.d/99-reboot b/src/etc/update-motd.d/99-reboot new file mode 100755 index 000000000..718be1a7a --- /dev/null +++ b/src/etc/update-motd.d/99-reboot @@ -0,0 +1,7 @@ +#!/bin/vbash +source /opt/vyatta/etc/functions/script-template +if [ -f /run/systemd/shutdown/scheduled ]; then + echo + run show reboot +fi +exit -- cgit v1.2.3 From ef694deb9a620ab4f81e08ac2bf6228404aaac36 Mon Sep 17 00:00:00 2001 From: Kroy Date: Thu, 12 Aug 2021 18:02:00 -0500 Subject: T3749: Moving some counters into the proper loop --- src/conf_mode/containers.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/conf_mode/containers.py b/src/conf_mode/containers.py index 21b47f42a..7544bd840 100755 --- a/src/conf_mode/containers.py +++ b/src/conf_mode/containers.py @@ -142,9 +142,9 @@ def verify(container): # Add new network if 'network' in container: - v4_prefix = 0 - v6_prefix = 0 for network, network_config in container['network'].items(): + v4_prefix = 0 + v6_prefix = 0 # If ipv4-prefix not defined for user-defined network if 'prefix' not in network_config: raise ConfigError(f'prefix for network "{net}" must be defined!') -- cgit v1.2.3 From f3df9e97c6bedd305133e860654fc0213c12fd6b Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Fri, 13 Aug 2021 12:34:11 +0200 Subject: nginx: T3740: use bracketize_ipv6 Jinja2 filter on server address --- data/templates/https/nginx.default.tmpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/templates/https/nginx.default.tmpl b/data/templates/https/nginx.default.tmpl index b40ddcc74..2f8aa06c2 100644 --- a/data/templates/https/nginx.default.tmpl +++ b/data/templates/https/nginx.default.tmpl @@ -17,7 +17,7 @@ server { listen {{ server.port }} ssl; listen [::]:{{ server.port }} ssl; {% else %} - listen {{ server.address }}:{{ server.port }} ssl; + listen {{ server.address | bracketize_ipv6 }}:{{ server.port }} ssl; {% endif %} {% for name in server.name %} -- cgit v1.2.3 From de88a17ba97200cdb3bd07ac3d12eab5d5fa6c73 Mon Sep 17 00:00:00 2001 From: Viacheslav Date: Wed, 11 Aug 2021 17:21:01 +0000 Subject: isis: T3708: Fix errors in MTU calculation --- src/conf_mode/protocols_isis.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/conf_mode/protocols_isis.py b/src/conf_mode/protocols_isis.py index d4c82249b..4cf0312e9 100755 --- a/src/conf_mode/protocols_isis.py +++ b/src/conf_mode/protocols_isis.py @@ -113,9 +113,13 @@ def verify(isis): # Interface MTU must be >= configured lsp-mtu mtu = Interface(interface).get_mtu() area_mtu = isis['lsp_mtu'] - if mtu < int(area_mtu): - raise ConfigError(f'Interface {interface} has MTU {mtu}, minimum ' \ - f'area MTU is {area_mtu}!') + # Recommended maximum PDU size = interface MTU - 3 bytes + recom_area_mtu = mtu - 3 + if mtu < int(area_mtu) or int(area_mtu) > recom_area_mtu: + raise ConfigError(f'Interface {interface} has MTU {mtu}, ' \ + f'current area MTU is {area_mtu}! \n' \ + f'Recommended area lsp-mtu {recom_area_mtu} or less ' \ + '(calculated on MTU size).') if 'vrf' in isis: # If interface specific options are set, we must ensure that the -- cgit v1.2.3 From c7b8e12beb7c90a5531c81303fa4837bd9f51782 Mon Sep 17 00:00:00 2001 From: Viacheslav Date: Fri, 9 Jul 2021 14:01:52 +0000 Subject: policy: T3673: Add set large-comm-list-delete for route-map --- data/templates/frr/policy.frr.tmpl | 3 +++ interface-definitions/policy.xml.in | 12 ++++++++++++ 2 files changed, 15 insertions(+) diff --git a/data/templates/frr/policy.frr.tmpl b/data/templates/frr/policy.frr.tmpl index b5649b44e..57ab0f363 100644 --- a/data/templates/frr/policy.frr.tmpl +++ b/data/templates/frr/policy.frr.tmpl @@ -271,6 +271,9 @@ route-map {{ route_map }} {{ rule_config.action }} {{ rule }} {% if rule_config.set.large_community is defined and rule_config.set.large_community is not none %} set large-community {{ rule_config.set.large_community }} {% endif %} +{% if rule_config.set.large_comm_list_delete is defined and rule_config.set.large_comm_list_delete is not none %} + set large-comm-list {{ rule_config.set.large_comm_list_delete }} delete +{% endif %} {% if rule_config.set.local_preference is defined and rule_config.set.local_preference is not none %} set local-preference {{ rule_config.set.local_preference }} {% endif %} diff --git a/interface-definitions/policy.xml.in b/interface-definitions/policy.xml.in index 5a3c58fa8..02da76be4 100644 --- a/interface-definitions/policy.xml.in +++ b/interface-definitions/policy.xml.in @@ -1124,6 +1124,18 @@ + + + Delete BGP communities matching the large community-list + + policy large-community-list + + + txt + BGP large community-list + + + Border Gateway Protocol (BGP) local preference attribute -- cgit v1.2.3 From 655876f4c22c0f4ea839a81f4af09d6016e19197 Mon Sep 17 00:00:00 2001 From: Viacheslav Date: Fri, 13 Aug 2021 15:48:14 +0000 Subject: openvpn: T3738: Disable authentication option for server mode --- src/conf_mode/interfaces-openvpn.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/conf_mode/interfaces-openvpn.py b/src/conf_mode/interfaces-openvpn.py index 74e29ed82..6be4e918b 100755 --- a/src/conf_mode/interfaces-openvpn.py +++ b/src/conf_mode/interfaces-openvpn.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2019-2020 VyOS maintainers and contributors +# Copyright (C) 2019-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 @@ -273,6 +273,9 @@ def verify(openvpn): if openvpn['protocol'] == 'tcp-active': raise ConfigError('Protocol "tcp-active" is not valid in server mode') + if dict_search('authentication.username', openvpn) or dict_search('authentication.password', openvpn): + raise ConfigError('Cannot specify "authentication" in server mode') + if 'remote_port' in openvpn: raise ConfigError('Cannot specify "remote-port" in server mode') -- cgit v1.2.3 From e5c61fc80119556bb1b61a80868d4a3470e07319 Mon Sep 17 00:00:00 2001 From: John Estabrook Date: Fri, 13 Aug 2021 08:57:37 -0500 Subject: xml: T3234: update instead of overwrite on repeated path --- python/vyos/xml/load.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/python/vyos/xml/load.py b/python/vyos/xml/load.py index 37479c6e1..0578bef80 100644 --- a/python/vyos/xml/load.py +++ b/python/vyos/xml/load.py @@ -125,14 +125,20 @@ def _format_nodes(inside, conf, xml): for node in nodes: name = node.pop('@name') into = inside + [name] - r[name] = _format_node(into, node, xml) + if name in r: + r[name].update(_format_node(into, node, xml)) + else: + r[name] = _format_node(into, node, xml) r[name][kw.node] = nodename xml[kw.tags].append(' '.join(into)) else: node = nodes name = node.pop('@name') into = inside + [name] - r[name] = _format_node(inside + [name], node, xml) + if name in r: + r[name].update(_format_node(inside + [name], node, xml)) + else: + r[name] = _format_node(inside + [name], node, xml) r[name][kw.node] = nodename xml[kw.tags].append(' '.join(into)) return r -- cgit v1.2.3 From 87be4e407a499c884898f753678ef9eb874e0d5d Mon Sep 17 00:00:00 2001 From: sarthurdev <965089+sarthurdev@users.noreply.github.com> Date: Fri, 13 Aug 2021 18:33:21 +0200 Subject: pki: T3752: Fix file output for certificate requests --- src/op_mode/pki.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/op_mode/pki.py b/src/op_mode/pki.py index 297270cf1..b34ea3c2b 100755 --- a/src/op_mode/pki.py +++ b/src/op_mode/pki.py @@ -813,7 +813,7 @@ if __name__ == '__main__': elif args.self_sign: generate_certificate_selfsign(args.certificate, install=args.install, file=args.file) else: - generate_certificate_request(name=args.certificate, install=args.install) + generate_certificate_request(name=args.certificate, install=args.install, file=args.file) elif args.crl: generate_certificate_revocation_list(args.crl, install=args.install, file=args.file) elif args.ssh: -- cgit v1.2.3 From da94e0a736874d9a6420ec1aa754efcec684b390 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Fri, 13 Aug 2021 21:11:58 +0200 Subject: vrf: T3734: T3728: vni must be configured with a higher priority then bgpd When removing bgp (vrf) instances the assigned VRF vni must be deleted from FRR prior the removal of the bgp settings (T3734). This is now done by moving the CLI command "set vrf name red vni 1000" to a dedicated Python script with a priority higher then bgp. --- data/configd-include.json | 1 + data/templates/frr/vrf-vni.frr.tmpl | 7 ++++ data/templates/frr/vrf.frr.tmpl | 9 ----- interface-definitions/vrf.xml.in | 15 +++++++- src/conf_mode/vrf.py | 20 ---------- src/conf_mode/vrf_vni.py | 76 +++++++++++++++++++++++++++++++++++++ 6 files changed, 98 insertions(+), 30 deletions(-) create mode 100644 data/templates/frr/vrf-vni.frr.tmpl delete mode 100644 data/templates/frr/vrf.frr.tmpl create mode 100755 src/conf_mode/vrf_vni.py diff --git a/data/configd-include.json b/data/configd-include.json index 3b4e2925b..2d7ea149b 100644 --- a/data/configd-include.json +++ b/data/configd-include.json @@ -69,5 +69,6 @@ "vpn_pptp.py", "vpn_sstp.py", "vrf.py", +"vrf_vni.py", "vrrp.py" ] diff --git a/data/templates/frr/vrf-vni.frr.tmpl b/data/templates/frr/vrf-vni.frr.tmpl new file mode 100644 index 000000000..51d4ede1b --- /dev/null +++ b/data/templates/frr/vrf-vni.frr.tmpl @@ -0,0 +1,7 @@ +{% if vrf is defined and vrf is not none %} +vrf {{ vrf }} +{% if vni is defined and vni is not none %} + vni {{ vni }} +{% endif %} + exit-vrf +{% endif %} diff --git a/data/templates/frr/vrf.frr.tmpl b/data/templates/frr/vrf.frr.tmpl deleted file mode 100644 index 299c9719e..000000000 --- a/data/templates/frr/vrf.frr.tmpl +++ /dev/null @@ -1,9 +0,0 @@ -{% if name is defined and name is not none %} -{% for vrf, vrf_config in name.items() %} -vrf {{ vrf }} -{% if vrf_config.vni is defined and vrf_config.vni is not none %} - vni {{ vrf_config.vni }} -{% endif %} - exit-vrf -{% endfor %} -{% endif %} diff --git a/interface-definitions/vrf.xml.in b/interface-definitions/vrf.xml.in index 9d513945c..76d6df386 100644 --- a/interface-definitions/vrf.xml.in +++ b/interface-definitions/vrf.xml.in @@ -85,7 +85,20 @@ VRF routing table must be in range from 100 to 65535 - #include + + + Virtual Network Identifier + + 822 + + 0-16777214 + VXLAN virtual network identifier + + + + + + diff --git a/src/conf_mode/vrf.py b/src/conf_mode/vrf.py index c1cfc1dcb..919083ac4 100755 --- a/src/conf_mode/vrf.py +++ b/src/conf_mode/vrf.py @@ -24,7 +24,6 @@ from vyos.config import Config from vyos.configdict import node_changed from vyos.ifconfig import Interface from vyos.template import render -from vyos.template import render_to_string from vyos.util import call from vyos.util import cmd from vyos.util import dict_search @@ -32,12 +31,9 @@ from vyos.util import get_interface_config from vyos.util import popen from vyos.util import run from vyos import ConfigError -from vyos import frr from vyos import airbag airbag.enable() -frr_daemon = 'zebra' - config_file = r'/etc/iproute2/rt_tables.d/vyos-vrf.conf' def list_rules(): @@ -131,7 +127,6 @@ def verify(vrf): def generate(vrf): render(config_file, 'vrf/vrf.conf.tmpl', vrf) - vrf['new_frr_config'] = render_to_string('frr/vrf.frr.tmpl', vrf) # Render nftables zones config vrf['nft_vrf_zones'] = NamedTemporaryFile().name render(vrf['nft_vrf_zones'], 'firewall/nftables-vrf-zones.tmpl', vrf) @@ -242,21 +237,6 @@ def apply(vrf): if tmp == 0: cmd('nft delete table inet vrf_zones') - # T3694: Somehow we hit a priority inversion here as we need to remove the - # VRF assigned VNI before we can remove a BGP bound VRF instance. Maybe - # move this to an individual helper script that set's up the VNI for the - # given VRF after any routing protocol. - # - # # add configuration to FRR - # frr_cfg = frr.FRRConfig() - # frr_cfg.load_configuration(frr_daemon) - # frr_cfg.modify_section(f'^vrf [a-zA-Z-]*$', '') - # frr_cfg.add_before(r'(interface .*|line vty)', vrf['new_frr_config']) - # frr_cfg.commit_configuration(frr_daemon) - # - # # Save configuration to /run/frr/config/frr.conf - # frr.save_configuration() - return None if __name__ == '__main__': diff --git a/src/conf_mode/vrf_vni.py b/src/conf_mode/vrf_vni.py new file mode 100755 index 000000000..87ee8f2d1 --- /dev/null +++ b/src/conf_mode/vrf_vni.py @@ -0,0 +1,76 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2020-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 sys import argv +from sys import exit + +from vyos.config import Config +from vyos.template import render_to_string +from vyos import ConfigError +from vyos import frr +from vyos import airbag +airbag.enable() + +frr_daemon = 'zebra' + +def get_config(config=None): + if config: + conf = config + else: + conf = Config() + + # This script only works with a passed VRF name + if len(argv) < 1: + raise NotImplementedError + vrf = argv[1] + + # "assemble" dict - easier here then use a full blown get_config_dict() + # on a single leafNode + vni = { 'vrf' : vrf } + tmp = conf.return_value(['vrf', 'name', vrf, 'vni']) + if tmp: vni.update({ 'vni' : tmp }) + + return vni + +def verify(vni): + return None + +def generate(vni): + vni['new_frr_config'] = render_to_string('frr/vrf-vni.frr.tmpl', vni) + return None + +def apply(vni): + # add configuration to FRR + frr_cfg = frr.FRRConfig() + frr_cfg.load_configuration(frr_daemon) + frr_cfg.modify_section(f'^vrf [a-zA-Z-]*$', '') + frr_cfg.add_before(r'(interface .*|line vty)', vni['new_frr_config']) + frr_cfg.commit_configuration(frr_daemon) + + # Save configuration to /run/frr/config/frr.conf + frr.save_configuration() + + 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 2b8854761c8ed419b2a2f1e02810c3f68f1d72b6 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Fri, 13 Aug 2021 21:15:58 +0200 Subject: vyos.util: "harden" is_systemd_service_running() function Inspired by the comments of https://unix.stackexchange.com/a/435317 use a more robust approach. A service can be "active" but not "running" (e.g. restarting with a configuration error). We can now test if a systemd unit is "activated" and if it is "running" at all. >>> from vyos.util import is_systemd_service_active >>> from vyos.util import is_systemd_service_running >>> is_systemd_service_active('ssh') True >>> is_systemd_service_running('sshd') False >>> is_systemd_service_running('ssh') True --- python/vyos/util.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/python/vyos/util.py b/python/vyos/util.py index 59f9f1c44..60171746a 100644 --- a/python/vyos/util.py +++ b/python/vyos/util.py @@ -805,8 +805,16 @@ def make_incremental_progressbar(increment: float): while True: yield +def is_systemd_service_active(service): + """ Test is a specified systemd service is activated. + Returns True if service is active, false otherwise. + Copied from: https://unix.stackexchange.com/a/435317 """ + tmp = cmd(f'systemctl show --value -p ActiveState {service}') + return bool((tmp == 'active')) + def is_systemd_service_running(service): """ Test is a specified systemd service is actually running. - Returns True if service is running, false otherwise. """ - tmp = run(f'systemctl is-active --quiet {service}') - return bool((tmp == 0)) + Returns True if service is running, false otherwise. + Copied from: https://unix.stackexchange.com/a/435317 """ + tmp = cmd(f'systemctl show --value -p SubState {service}') + return bool((tmp == 'running')) -- cgit v1.2.3 From f6da95a6f0b91b5ccb606eaf7fb32f191b360b44 Mon Sep 17 00:00:00 2001 From: jack9603301 Date: Sat, 14 Aug 2021 19:54:39 +0800 Subject: op-mode: nat: T3648: Modify the operation mode script implementation of NAT to fix the existing problem --- src/op_mode/show_nat_rules.py | 84 +++++++++++++++++++++++-------------------- 1 file changed, 46 insertions(+), 38 deletions(-) diff --git a/src/op_mode/show_nat_rules.py b/src/op_mode/show_nat_rules.py index 0f40ecabe..4a059c848 100755 --- a/src/op_mode/show_nat_rules.py +++ b/src/op_mode/show_nat_rules.py @@ -67,46 +67,54 @@ if args.source or args.destination: continue interface = dict_search('match.right', data['expr'][0]) srcdest = '' - for i in [1, 2]: - srcdest_json = dict_search('match.right', data['expr'][i]) - if not srcdest_json: - continue - - if isinstance(srcdest_json,str): - srcdest += srcdest_json + ' ' - elif 'prefix' in srcdest_json: - addr_tmp = dict_search('match.right.prefix.addr', data['expr'][i]) - len_tmp = dict_search('match.right.prefix.len', data['expr'][i]) - if addr_tmp and len_tmp: - srcdest = addr_tmp + '/' + str(len_tmp) + ' ' - elif 'set' in srcdest_json: - if isinstance(srcdest_json['set'][0],str): - srcdest += 'port ' + str(srcdest_json['set'][0]) + ' ' - else: - port_range = srcdest_json['set'][0]['range'] - srcdest += 'port ' + str(port_range[0]) + '-' + str(port_range[1]) + ' ' - + srcdests = [] tran_addr = '' - tran_addr_json = dict_search('snat.addr' if args.source else 'dnat.addr', data['expr'][3]) - if tran_addr_json: - if isinstance(tran_addr_json,str): - tran_addr = tran_addr_json - elif 'prefix' in tran_addr_json: - addr_tmp = dict_search('snat.addr.prefix.addr' if args.source else 'dnat.addr.prefix.addr', data['expr'][3]) - len_tmp = dict_search('snat.addr.prefix.len' if args.source else 'dnat.addr.prefix.len', data['expr'][3]) - if addr_tmp and len_tmp: - tran_addr = addr_tmp + '/' + str(len_tmp) - else: - if 'masquerade' in data['expr'][3]: - tran_addr = 'masquerade' - elif 'log' in data['expr'][3]: - continue - - tran_port = dict_search('snat.port' if args.source else 'dnat.port', data['expr'][3]) - if tran_port: - tran_addr += ' port ' + str(tran_port) + for i in range(1,len(data['expr']) + 1): + srcdest_json = dict_search('match.right', data['expr'][i]) + if srcdest_json: + if isinstance(srcdest_json,str): + if srcdest != '': + srcdests.append(srcdest) + srcdest = '' + srcdest = srcdest_json + ' ' + elif 'prefix' in srcdest_json: + addr_tmp = dict_search('match.right.prefix.addr', data['expr'][i]) + len_tmp = dict_search('match.right.prefix.len', data['expr'][i]) + if addr_tmp and len_tmp: + srcdest = addr_tmp + '/' + str(len_tmp) + ' ' + elif 'set' in srcdest_json: + if isinstance(srcdest_json['set'][0],int): + srcdest += 'port ' + str(srcdest_json['set'][0]) + ' ' + else: + port_range = srcdest_json['set'][0]['range'] + srcdest += 'port ' + str(port_range[0]) + '-' + str(port_range[1]) + ' ' + + tran_addr_json = dict_search('snat' if args.source else 'dnat', data['expr'][i]) + if tran_addr_json: + if isinstance(tran_addr_json['addr'],str): + tran_addr += tran_addr_json['addr'] + ' ' + elif 'prefix' in tran_addr_json['addr']: + addr_tmp = dict_search('snat.addr.prefix.addr' if args.source else 'dnat.addr.prefix.addr', data['expr'][3]) + len_tmp = dict_search('snat.addr.prefix.len' if args.source else 'dnat.addr.prefix.len', data['expr'][3]) + if addr_tmp and len_tmp: + tran_addr += addr_tmp + '/' + str(len_tmp) + ' ' + + if isinstance(tran_addr_json['port'],int): + tran_addr += 'port ' + tran_addr_json['port'] + + else: + if 'masquerade' in data['expr'][i]: + tran_addr = 'masquerade' + elif 'log' in data['expr'][i]: + continue - print(format_nat_rule.format(rule, srcdest, tran_addr, interface)) + if srcdest != '': + srcdests.append(srcdest) + srcdest = '' + print(format_nat_rule.format(rule, srcdests[0], tran_addr, interface)) + + for i in range(1, list(srcdest)): + print(format_nat_rule.format(' ', srcdests[i], ' ', ' ')) exit(0) else: -- cgit v1.2.3 From e36a4e684b95c4fd1e9ab042270fafd95e697586 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sat, 14 Aug 2021 18:13:50 +0200 Subject: ospf: T3236: use proper daemon named template file --- data/templates/frr/ospf.frr.tmpl | 179 ------------------------------------- data/templates/frr/ospf6d.frr.tmpl | 89 ++++++++++++++++++ data/templates/frr/ospfd.frr.tmpl | 179 +++++++++++++++++++++++++++++++++++++ data/templates/frr/ospfv3.frr.tmpl | 89 ------------------ src/conf_mode/protocols_ospf.py | 2 +- src/conf_mode/protocols_ospfv3.py | 4 +- 6 files changed, 271 insertions(+), 271 deletions(-) delete mode 100644 data/templates/frr/ospf.frr.tmpl create mode 100644 data/templates/frr/ospf6d.frr.tmpl create mode 100644 data/templates/frr/ospfd.frr.tmpl delete mode 100644 data/templates/frr/ospfv3.frr.tmpl diff --git a/data/templates/frr/ospf.frr.tmpl b/data/templates/frr/ospf.frr.tmpl deleted file mode 100644 index 36aa699a9..000000000 --- a/data/templates/frr/ospf.frr.tmpl +++ /dev/null @@ -1,179 +0,0 @@ -! -{% if interface is defined and interface is not none %} -{% for iface, iface_config in interface.items() %} -interface {{ iface }} {{ 'vrf ' + vrf if vrf is defined and vrf is not none }} -{% if iface_config.authentication is defined and iface_config.authentication is not none %} -{% if iface_config.authentication.plaintext_password is defined and iface_config.authentication.plaintext_password is not none %} - ip ospf authentication-key {{ iface_config.authentication.plaintext_password }} -{% elif iface_config.authentication.md5 is defined %} - ip ospf authentication message-digest -{% if iface_config.authentication.md5.key_id is defined and iface_config.authentication.md5.key_id is not none %} -{% for key, key_config in iface_config.authentication.md5.key_id.items() %} - ip ospf message-digest-key {{ key }} md5 {{ key_config.md5_key }} -{% endfor %} -{% endif %} -{% endif %} -{% endif %} -{% if iface_config.cost is defined and iface_config.cost is not none %} - ip ospf cost {{ iface_config.cost }} -{% endif %} -{% if iface_config.priority is defined and iface_config.priority is not none %} - ip ospf priority {{ iface_config.priority }} -{% endif %} -{% if iface_config.hello_interval is defined and iface_config.hello_interval is not none %} - ip ospf hello-interval {{ iface_config.hello_interval }} -{% endif %} -{% if iface_config.retransmit_interval is defined and iface_config.retransmit_interval is not none %} - ip ospf retransmit-interval {{ iface_config.retransmit_interval }} -{% endif %} -{% if iface_config.transmit_delay is defined and iface_config.transmit_delay is not none %} - ip ospf transmit-delay {{ iface_config.transmit_delay }} -{% endif %} -{% if iface_config.dead_interval is defined and iface_config.dead_interval is not none %} - ip ospf dead-interval {{ iface_config.dead_interval }} -{% elif iface_config.hello_multiplier is defined and iface_config.hello_multiplier is not none %} - ip ospf dead-interval minimal hello-multiplier {{ iface_config.hello_multiplier }} -{% endif %} -{% if iface_config.bfd is defined %} - ip ospf bfd -{% endif %} -{% if iface_config.mtu_ignore is defined %} - ip ospf mtu-ignore -{% endif %} -{% if iface_config.network is defined and iface_config.network is not none %} - ip ospf network {{ iface_config.network }} -{% endif %} -{% if iface_config.bandwidth is defined and iface_config.bandwidth is not none %} - bandwidth {{ iface_config.bandwidth }} -{% endif %} -! -{% endfor %} -{% endif %} -! -router ospf {{ 'vrf ' + vrf if vrf is defined and vrf is not none }} -{% if access_list is defined and access_list is not none %} -{% for acl, acl_config in access_list.items() %} -{% for protocol in acl_config.export if acl_config.export is defined %} - distribute-list {{ acl }} out {{ protocol }} -{% endfor %} -{% endfor %} -{% endif %} -{% if area is defined and area is not none %} -{% for area_id, area_config in area.items() %} -{% if area_config.area_type is defined and area_config.area_type is not none %} -{% for type, type_config in area_config.area_type.items() if type != 'normal' %} - area {{ area_id }} {{ type }} {{ 'no-summary' if type_config.no_summary is defined }} -{% if type_config.default_cost is defined and type_config.default_cost is not none %} - area {{ area_id }} default-cost {{ type_config.default_cost }} -{% endif %} -{% endfor %} -{% endif %} -{% if area_config.authentication is defined and area_config.authentication is not none %} - area {{ area_id }} authentication {{ 'message-digest' if area_config.authentication == 'md5' }} -{% endif %} -{% for network in area_config.network if area_config.network is defined %} - network {{ network }} area {{ area_id }} -{% endfor %} -{% if area_config.range is defined and area_config.range is not none %} -{% for range, range_config in area_config.range.items() %} -{% if range_config.cost is defined and range_config.cost is not none %} - area {{ area_id }} range {{ range }} cost {{ range_config.cost }} -{% endif %} -{% if range_config.not_advertise is defined %} - area {{ area_id }} range {{ range }} not-advertise -{% endif %} -{% if range_config.substitute is defined and range_config.substitute is not none %} - area {{ area_id }} range {{ range }} substitute {{ range_config.substitute }} -{% endif %} -{% endfor %} -{% endif %} -{% if area_config.shortcut is defined and area_config.shortcut is not none %} - area {{ area_id }} shortcut {{ area_config.shortcut }} -{% endif %} -{% if area_config.virtual_link is defined and area_config.virtual_link is not none %} -{% for link, link_config in area_config.virtual_link.items() %} -{% if link_config.authentication is defined and link_config.authentication is not none %} -{% if link_config.authentication.plaintext_password is defined and link_config.authentication.plaintext_password is not none %} - area {{ area_id }} virtual-link {{ link }} authentication-key {{ link_config.authentication.plaintext_password }} -{% elif link_config.authentication.md5 is defined and link_config.authentication.md5.key_id is defined and link_config.authentication.md5.key_id is not none %} -{% for key, key_config in link_config.authentication.md5.key_id.items() %} - area {{ area_id }} virtual-link {{ link }} message-digest-key {{ key }} md5 {{ key_config.md5_key }} -{% endfor %} -{% endif %} -{% endif %} -{# The following values are default values #} - area {{ area_id }} virtual-link {{ link }} hello-interval {{ link_config.hello_interval }} retransmit-interval {{ link_config.retransmit_interval }} transmit-delay {{ link_config.transmit_delay }} dead-interval {{ link_config.dead_interval }} -{% endfor %} -{% endif %} -{% endfor %} -{% endif %} -{% if auto_cost is defined and auto_cost.reference_bandwidth is defined and auto_cost.reference_bandwidth is not none %} - auto-cost reference-bandwidth {{ auto_cost.reference_bandwidth }} -{% endif %} -{% if default_information is defined and default_information.originate is defined and default_information.originate is not none %} - default-information originate {{ 'always' if default_information.originate.always is defined }} {{ 'metric ' + default_information.originate.metric if default_information.originate.metric is defined }} {{ 'metric-type ' + default_information.originate.metric_type if default_information.originate.metric_type is defined }} {{ 'route-map ' + default_information.originate.route_map if default_information.originate.route_map is defined }} -{% endif %} -{% if default_metric is defined and default_metric is not none %} - default-metric {{ default_metric }} -{% endif %} -{% if distance is defined and distance is not none %} -{% if distance.global is defined and distance.global is not none %} - distance {{ distance.global }} -{% endif %} -{% if distance.ospf is defined and distance.ospf is not none %} - distance ospf {{ 'intra-area ' + distance.ospf.intra_area if distance.ospf.intra_area is defined }} {{ 'inter-area ' + distance.ospf.inter_area if distance.ospf.inter_area is defined }} {{ 'external ' + distance.ospf.external if distance.ospf.external is defined }} -{% endif %} -{% endif %} -{% if log_adjacency_changes is defined %} - log-adjacency-changes {{ "detail" if log_adjacency_changes.detail is defined }} -{% endif %} -{% if max_metric is defined and max_metric.router_lsa is defined and max_metric.router_lsa is not none %} -{% if max_metric.router_lsa.administrative is defined %} - max-metric router-lsa administrative -{% endif %} -{% if max_metric.router_lsa.on_shutdown is defined and max_metric.router_lsa.on_shutdown is not none %} - max-metric router-lsa on-shutdown {{ max_metric.router_lsa.on_shutdown }} -{% endif %} -{% if max_metric.router_lsa.on_startup is defined and max_metric.router_lsa.on_startup is not none %} - max-metric router-lsa on-startup {{ max_metric.router_lsa.on_startup }} -{% endif %} -{% endif %} -{% if mpls_te is defined and mpls_te.enable is defined %} - mpls-te on - mpls-te router-address {{ mpls_te.router_address }} -{% endif %} -{% if neighbor is defined and neighbor is not none%} -{% for address, address_config in neighbor.items() %} - neighbor {{ address }} {{ 'priority ' + address_config.priority if address_config.priority is defined }} {{ 'poll-interval ' + address_config.poll_interval if address_config.poll_interval is defined }} -{% endfor %} -{% endif %} -{% if parameters is defined and parameters is not none %} -{% if parameters.abr_type is defined and parameters.abr_type is not none %} - ospf abr-type {{ parameters.abr_type }} -{% endif %} -{% if parameters.router_id is defined and parameters.router_id is not none %} - ospf router-id {{ parameters.router_id }} -{% endif %} -{% endif %} -{% for interface in passive_interface if passive_interface is defined %} - passive-interface {{ interface }} -{% endfor %} -{% for interface in passive_interface_exclude if passive_interface_exclude is defined %} -{% if interface.startswith('vlink') %} -{% set interface = interface.upper() %} -{% endif %} - no passive-interface {{ interface }} -{% endfor %} -{% if redistribute is defined and redistribute is not none %} -{% for protocol, options in redistribute.items() %} - redistribute {{ protocol }} {{ 'metric ' + options.metric if options.metric is defined }} {{ 'metric-type ' + options.metric_type if options.metric_type is defined }} {{ 'route-map ' + options.route_map if options.route_map is defined }} -{% endfor %} -{% endif %} -{% if refresh is defined and refresh.timers is defined and refresh.timers is not none %} - refresh timer {{ refresh.timers }} -{% endif %} -{% if timers is defined and timers.throttle is defined and timers.throttle.spf is defined and timers.throttle.spf is not none %} -{# Timer values have default values #} - timers throttle spf {{ timers.throttle.spf.delay }} {{ timers.throttle.spf.initial_holdtime }} {{ timers.throttle.spf.max_holdtime }} -{% endif %} -! diff --git a/data/templates/frr/ospf6d.frr.tmpl b/data/templates/frr/ospf6d.frr.tmpl new file mode 100644 index 000000000..0026c0d2c --- /dev/null +++ b/data/templates/frr/ospf6d.frr.tmpl @@ -0,0 +1,89 @@ +! +{% if interface is defined and interface is not none %} +{% for iface, iface_config in interface.items() %} +interface {{ iface }} +{% if iface_config.cost is defined and iface_config.cost is not none %} + ipv6 ospf6 cost {{ iface_config.cost }} +{% endif %} +{% if iface_config.priority is defined and iface_config.priority is not none %} + ipv6 ospf6 priority {{ iface_config.priority }} +{% endif %} +{% if iface_config.hello_interval is defined and iface_config.hello_interval is not none %} + ipv6 ospf6 hello-interval {{ iface_config.hello_interval }} +{% endif %} +{% if iface_config.retransmit_interval is defined and iface_config.retransmit_interval is not none %} + ipv6 ospf6 retransmit-interval {{ iface_config.retransmit_interval }} +{% endif %} +{% if iface_config.transmit_delay is defined and iface_config.transmit_delay is not none %} + ipv6 ospf6 transmit-delay {{ iface_config.transmit_delay }} +{% endif %} +{% if iface_config.dead_interval is defined and iface_config.dead_interval is not none %} + ipv6 ospf6 dead-interval {{ iface_config.dead_interval }} +{% endif %} +{% if iface_config.bfd is defined %} + ipv6 ospf6 bfd +{% endif %} +{% if iface_config.mtu_ignore is defined %} + ipv6 ospf6 mtu-ignore +{% endif %} +{% if iface_config.ifmtu is defined and iface_config.ifmtu is not none %} + ipv6 ospf6 ifmtu {{ iface_config.ifmtu }} +{% endif %} +{% if iface_config.network is defined and iface_config.network is not none %} + ipv6 ospf6 network {{ iface_config.network }} +{% endif %} +{% if iface_config.instance_id is defined and iface_config.instance_id is not none %} + ipv6 ospf6 instance-id {{ iface_config.instance_id }} +{% endif %} +{% if iface_config.passive is defined %} + ipv6 ospf6 passive +{% endif %} +! +{% endfor %} +{% endif %} +! +router ospf6 +{% if area is defined and area is not none %} +{% for area_id, area_config in area.items() %} +{% if area_config.interface is defined and area_config.interface is not none %} +{% for interface in area_config.interface %} + interface {{ interface }} area {{ area_id }} +{% endfor %} +{% endif %} +{% if area_config.area_type is defined and area_config.area_type is not none %} +{% for type, type_config in area_config.area_type.items() %} + area {{ area_id }} {{ type }} {{ 'no-summary' if type_config.no_summary is defined }} +{% endfor %} +{% endif %} +{% if area_config.range is defined and area_config.range is not none %} +{% for prefix, prefix_config in area_config.range.items() %} + area {{ area_id }} range {{ prefix }} {{ 'advertise' if prefix_config.advertise is defined }} {{ 'not-advertise' if prefix_config.not_advertise is defined }} +{% endfor %} +{% endif %} +{% if area_config.export_list is defined and area_config.export_list is not none %} + area {{ area_id }} export-list {{ area_config.export_list }} +{% endif %} +{% if area_config.import_list is defined and area_config.import_list is not none %} + area {{ area_id }} import-list {{ area_config.import_list }} +{% endif %} +{% endfor %} +{% endif %} +{% if distance is defined and distance is not none %} +{% if distance.global is defined and distance.global is not none %} + distance {{ distance.global }} +{% endif %} +{% if distance.ospfv3 is defined and distance.ospfv3 is not none %} + distance ospf6 {{ 'intra-area ' + distance.ospfv3.intra_area if distance.ospfv3.intra_area is defined }} {{ 'inter-area ' + distance.ospfv3.inter_area if distance.ospfv3.inter_area is defined }} {{ 'external ' + distance.ospfv3.external if distance.ospfv3.external is defined }} +{% endif %} +{% endif %} +{% if parameters is defined and parameters is not none %} +{% if parameters.router_id is defined and parameters.router_id is not none %} + ospf6 router-id {{ parameters.router_id }} +{% endif %} +{% endif %} +{% if redistribute is defined and redistribute is not none %} +{% for protocol, options in redistribute.items() %} + redistribute {{ protocol }} {{ 'route-map ' + options.route_map if options.route_map is defined }} +{% endfor %} +{% endif %} +! diff --git a/data/templates/frr/ospfd.frr.tmpl b/data/templates/frr/ospfd.frr.tmpl new file mode 100644 index 000000000..763d0666c --- /dev/null +++ b/data/templates/frr/ospfd.frr.tmpl @@ -0,0 +1,179 @@ +! +{% if interface is defined and interface is not none %} +{% for iface, iface_config in interface.items() %} +interface {{ iface }} {{ 'vrf ' + vrf if vrf is defined and vrf is not none }} +{% if iface_config.authentication is defined and iface_config.authentication is not none %} +{% if iface_config.authentication.plaintext_password is defined and iface_config.authentication.plaintext_password is not none %} + ip ospf authentication-key {{ iface_config.authentication.plaintext_password }} +{% elif iface_config.authentication.md5 is defined %} + ip ospf authentication message-digest +{% if iface_config.authentication.md5.key_id is defined and iface_config.authentication.md5.key_id is not none %} +{% for key, key_config in iface_config.authentication.md5.key_id.items() %} + ip ospf message-digest-key {{ key }} md5 {{ key_config.md5_key }} +{% endfor %} +{% endif %} +{% endif %} +{% endif %} +{% if iface_config.cost is defined and iface_config.cost is not none %} + ip ospf cost {{ iface_config.cost }} +{% endif %} +{% if iface_config.priority is defined and iface_config.priority is not none %} + ip ospf priority {{ iface_config.priority }} +{% endif %} +{% if iface_config.hello_interval is defined and iface_config.hello_interval is not none %} + ip ospf hello-interval {{ iface_config.hello_interval }} +{% endif %} +{% if iface_config.retransmit_interval is defined and iface_config.retransmit_interval is not none %} + ip ospf retransmit-interval {{ iface_config.retransmit_interval }} +{% endif %} +{% if iface_config.transmit_delay is defined and iface_config.transmit_delay is not none %} + ip ospf transmit-delay {{ iface_config.transmit_delay }} +{% endif %} +{% if iface_config.dead_interval is defined and iface_config.dead_interval is not none %} + ip ospf dead-interval {{ iface_config.dead_interval }} +{% elif iface_config.hello_multiplier is defined and iface_config.hello_multiplier is not none %} + ip ospf dead-interval minimal hello-multiplier {{ iface_config.hello_multiplier }} +{% endif %} +{% if iface_config.bfd is defined %} + ip ospf bfd +{% endif %} +{% if iface_config.mtu_ignore is defined %} + ip ospf mtu-ignore +{% endif %} +{% if iface_config.network is defined and iface_config.network is not none %} + ip ospf network {{ iface_config.network }} +{% endif %} +{% if iface_config.bandwidth is defined and iface_config.bandwidth is not none %} + bandwidth {{ iface_config.bandwidth }} +{% endif %} +! +{% endfor %} +{% endif %} +! +router ospf {{ 'vrf ' + vrf if vrf is defined and vrf is not none }} +{% if access_list is defined and access_list is not none %} +{% for acl, acl_config in access_list.items() %} +{% for protocol in acl_config.export if acl_config.export is defined %} + distribute-list {{ acl }} out {{ protocol }} +{% endfor %} +{% endfor %} +{% endif %} +{% if area is defined and area is not none %} +{% for area_id, area_config in area.items() %} +{% if area_config.area_type is defined and area_config.area_type is not none %} +{% for type, type_config in area_config.area_type.items() if type != 'normal' %} + area {{ area_id }} {{ type }} {{ 'no-summary' if type_config.no_summary is defined }} +{% if type_config.default_cost is defined and type_config.default_cost is not none %} + area {{ area_id }} default-cost {{ type_config.default_cost }} +{% endif %} +{% endfor %} +{% endif %} +{% if area_config.authentication is defined and area_config.authentication is not none %} + area {{ area_id }} authentication {{ 'message-digest' if area_config.authentication == 'md5' }} +{% endif %} +{% for network in area_config.network if area_config.network is defined %} + network {{ network }} area {{ area_id }} +{% endfor %} +{% if area_config.range is defined and area_config.range is not none %} +{% for range, range_config in area_config.range.items() %} +{% if range_config.cost is defined and range_config.cost is not none %} + area {{ area_id }} range {{ range }} cost {{ range_config.cost }} +{% endif %} +{% if range_config.not_advertise is defined %} + area {{ area_id }} range {{ range }} not-advertise +{% endif %} +{% if range_config.substitute is defined and range_config.substitute is not none %} + area {{ area_id }} range {{ range }} substitute {{ range_config.substitute }} +{% endif %} +{% endfor %} +{% endif %} +{% if area_config.shortcut is defined and area_config.shortcut is not none %} + area {{ area_id }} shortcut {{ area_config.shortcut }} +{% endif %} +{% if area_config.virtual_link is defined and area_config.virtual_link is not none %} +{% for link, link_config in area_config.virtual_link.items() %} +{% if link_config.authentication is defined and link_config.authentication is not none %} +{% if link_config.authentication.plaintext_password is defined and link_config.authentication.plaintext_password is not none %} + area {{ area_id }} virtual-link {{ link }} authentication-key {{ link_config.authentication.plaintext_password }} +{% elif link_config.authentication.md5 is defined and link_config.authentication.md5.key_id is defined and link_config.authentication.md5.key_id is not none %} +{% for key, key_config in link_config.authentication.md5.key_id.items() %} + area {{ area_id }} virtual-link {{ link }} message-digest-key {{ key }} md5 {{ key_config.md5_key }} +{% endfor %} +{% endif %} +{% endif %} +{# The following values are default values #} + area {{ area_id }} virtual-link {{ link }} hello-interval {{ link_config.hello_interval }} retransmit-interval {{ link_config.retransmit_interval }} transmit-delay {{ link_config.transmit_delay }} dead-interval {{ link_config.dead_interval }} +{% endfor %} +{% endif %} +{% endfor %} +{% endif %} +{% if auto_cost is defined and auto_cost.reference_bandwidth is defined and auto_cost.reference_bandwidth is not none %} + auto-cost reference-bandwidth {{ auto_cost.reference_bandwidth }} +{% endif %} +{% if default_information is defined and default_information.originate is defined and default_information.originate is not none %} + default-information originate {{ 'always' if default_information.originate.always is defined }} {{ 'metric ' + default_information.originate.metric if default_information.originate.metric is defined }} {{ 'metric-type ' + default_information.originate.metric_type if default_information.originate.metric_type is defined }} {{ 'route-map ' + default_information.originate.route_map if default_information.originate.route_map is defined }} +{% endif %} +{% if default_metric is defined and default_metric is not none %} + default-metric {{ default_metric }} +{% endif %} +{% if distance is defined and distance is not none %} +{% if distance.global is defined and distance.global is not none %} + distance {{ distance.global }} +{% endif %} +{% if distance.ospf is defined and distance.ospf is not none %} + distance ospf {{ 'intra-area ' + distance.ospf.intra_area if distance.ospf.intra_area is defined }} {{ 'inter-area ' + distance.ospf.inter_area if distance.ospf.inter_area is defined }} {{ 'external ' + distance.ospf.external if distance.ospf.external is defined }} +{% endif %} +{% endif %} +{% if log_adjacency_changes is defined %} + log-adjacency-changes {{ "detail" if log_adjacency_changes.detail is defined }} +{% endif %} +{% if max_metric is defined and max_metric.router_lsa is defined and max_metric.router_lsa is not none %} +{% if max_metric.router_lsa.administrative is defined %} + max-metric router-lsa administrative +{% endif %} +{% if max_metric.router_lsa.on_shutdown is defined and max_metric.router_lsa.on_shutdown is not none %} + max-metric router-lsa on-shutdown {{ max_metric.router_lsa.on_shutdown }} +{% endif %} +{% if max_metric.router_lsa.on_startup is defined and max_metric.router_lsa.on_startup is not none %} + max-metric router-lsa on-startup {{ max_metric.router_lsa.on_startup }} +{% endif %} +{% endif %} +{% if mpls_te is defined and mpls_te.enable is defined %} + mpls-te on + mpls-te router-address {{ mpls_te.router_address }} +{% endif %} +{% if neighbor is defined and neighbor is not none%} +{% for address, address_config in neighbor.items() %} + neighbor {{ address }} {{ 'priority ' + address_config.priority if address_config.priority is defined }} {{ 'poll-interval ' + address_config.poll_interval if address_config.poll_interval is defined }} +{% endfor %} +{% endif %} +{% if parameters is defined and parameters is not none %} +{% if parameters.abr_type is defined and parameters.abr_type is not none %} + ospf abr-type {{ parameters.abr_type }} +{% endif %} +{% if parameters.router_id is defined and parameters.router_id is not none %} + ospf router-id {{ parameters.router_id }} +{% endif %} +{% endif %} +{% for interface in passive_interface if passive_interface is defined and passive_interface == 'default' %} + passive-interface default +{% endfor %} +{% for interface in passive_interface_exclude if passive_interface_exclude is defined %} +{% if interface.startswith('vlink') %} +{% set interface = interface.upper() %} +{% endif %} + no passive-interface {{ interface }} +{% endfor %} +{% if redistribute is defined and redistribute is not none %} +{% for protocol, options in redistribute.items() %} + redistribute {{ protocol }} {{ 'metric ' + options.metric if options.metric is defined }} {{ 'metric-type ' + options.metric_type if options.metric_type is defined }} {{ 'route-map ' + options.route_map if options.route_map is defined }} +{% endfor %} +{% endif %} +{% if refresh is defined and refresh.timers is defined and refresh.timers is not none %} + refresh timer {{ refresh.timers }} +{% endif %} +{% if timers is defined and timers.throttle is defined and timers.throttle.spf is defined and timers.throttle.spf is not none %} +{# Timer values have default values #} + timers throttle spf {{ timers.throttle.spf.delay }} {{ timers.throttle.spf.initial_holdtime }} {{ timers.throttle.spf.max_holdtime }} +{% endif %} +! diff --git a/data/templates/frr/ospfv3.frr.tmpl b/data/templates/frr/ospfv3.frr.tmpl deleted file mode 100644 index 0026c0d2c..000000000 --- a/data/templates/frr/ospfv3.frr.tmpl +++ /dev/null @@ -1,89 +0,0 @@ -! -{% if interface is defined and interface is not none %} -{% for iface, iface_config in interface.items() %} -interface {{ iface }} -{% if iface_config.cost is defined and iface_config.cost is not none %} - ipv6 ospf6 cost {{ iface_config.cost }} -{% endif %} -{% if iface_config.priority is defined and iface_config.priority is not none %} - ipv6 ospf6 priority {{ iface_config.priority }} -{% endif %} -{% if iface_config.hello_interval is defined and iface_config.hello_interval is not none %} - ipv6 ospf6 hello-interval {{ iface_config.hello_interval }} -{% endif %} -{% if iface_config.retransmit_interval is defined and iface_config.retransmit_interval is not none %} - ipv6 ospf6 retransmit-interval {{ iface_config.retransmit_interval }} -{% endif %} -{% if iface_config.transmit_delay is defined and iface_config.transmit_delay is not none %} - ipv6 ospf6 transmit-delay {{ iface_config.transmit_delay }} -{% endif %} -{% if iface_config.dead_interval is defined and iface_config.dead_interval is not none %} - ipv6 ospf6 dead-interval {{ iface_config.dead_interval }} -{% endif %} -{% if iface_config.bfd is defined %} - ipv6 ospf6 bfd -{% endif %} -{% if iface_config.mtu_ignore is defined %} - ipv6 ospf6 mtu-ignore -{% endif %} -{% if iface_config.ifmtu is defined and iface_config.ifmtu is not none %} - ipv6 ospf6 ifmtu {{ iface_config.ifmtu }} -{% endif %} -{% if iface_config.network is defined and iface_config.network is not none %} - ipv6 ospf6 network {{ iface_config.network }} -{% endif %} -{% if iface_config.instance_id is defined and iface_config.instance_id is not none %} - ipv6 ospf6 instance-id {{ iface_config.instance_id }} -{% endif %} -{% if iface_config.passive is defined %} - ipv6 ospf6 passive -{% endif %} -! -{% endfor %} -{% endif %} -! -router ospf6 -{% if area is defined and area is not none %} -{% for area_id, area_config in area.items() %} -{% if area_config.interface is defined and area_config.interface is not none %} -{% for interface in area_config.interface %} - interface {{ interface }} area {{ area_id }} -{% endfor %} -{% endif %} -{% if area_config.area_type is defined and area_config.area_type is not none %} -{% for type, type_config in area_config.area_type.items() %} - area {{ area_id }} {{ type }} {{ 'no-summary' if type_config.no_summary is defined }} -{% endfor %} -{% endif %} -{% if area_config.range is defined and area_config.range is not none %} -{% for prefix, prefix_config in area_config.range.items() %} - area {{ area_id }} range {{ prefix }} {{ 'advertise' if prefix_config.advertise is defined }} {{ 'not-advertise' if prefix_config.not_advertise is defined }} -{% endfor %} -{% endif %} -{% if area_config.export_list is defined and area_config.export_list is not none %} - area {{ area_id }} export-list {{ area_config.export_list }} -{% endif %} -{% if area_config.import_list is defined and area_config.import_list is not none %} - area {{ area_id }} import-list {{ area_config.import_list }} -{% endif %} -{% endfor %} -{% endif %} -{% if distance is defined and distance is not none %} -{% if distance.global is defined and distance.global is not none %} - distance {{ distance.global }} -{% endif %} -{% if distance.ospfv3 is defined and distance.ospfv3 is not none %} - distance ospf6 {{ 'intra-area ' + distance.ospfv3.intra_area if distance.ospfv3.intra_area is defined }} {{ 'inter-area ' + distance.ospfv3.inter_area if distance.ospfv3.inter_area is defined }} {{ 'external ' + distance.ospfv3.external if distance.ospfv3.external is defined }} -{% endif %} -{% endif %} -{% if parameters is defined and parameters is not none %} -{% if parameters.router_id is defined and parameters.router_id is not none %} - ospf6 router-id {{ parameters.router_id }} -{% endif %} -{% endif %} -{% if redistribute is defined and redistribute is not none %} -{% for protocol, options in redistribute.items() %} - redistribute {{ protocol }} {{ 'route-map ' + options.route_map if options.route_map is defined }} -{% endfor %} -{% endif %} -! diff --git a/src/conf_mode/protocols_ospf.py b/src/conf_mode/protocols_ospf.py index 78c1c82bd..82126cb11 100755 --- a/src/conf_mode/protocols_ospf.py +++ b/src/conf_mode/protocols_ospf.py @@ -177,7 +177,7 @@ def generate(ospf): ospf['protocol'] = 'ospf' # required for frr/vrf.route-map.frr.tmpl ospf['frr_zebra_config'] = render_to_string('frr/vrf.route-map.frr.tmpl', ospf) - ospf['frr_ospfd_config'] = render_to_string('frr/ospf.frr.tmpl', ospf) + ospf['frr_ospfd_config'] = render_to_string('frr/ospfd.frr.tmpl', ospf) return None def apply(ospf): diff --git a/src/conf_mode/protocols_ospfv3.py b/src/conf_mode/protocols_ospfv3.py index fef0f509b..536ffa690 100755 --- a/src/conf_mode/protocols_ospfv3.py +++ b/src/conf_mode/protocols_ospfv3.py @@ -65,7 +65,7 @@ def verify(ospfv3): if 'ifmtu' in if_config: mtu = Interface(ifname).get_mtu() if int(if_config['ifmtu']) > int(mtu): - raise ConfigError(f'OSPFv3 ifmtu cannot go beyond physical MTU of "{mtu}"') + raise ConfigError(f'OSPFv3 ifmtu can not exceed physical MTU of "{mtu}"') return None @@ -74,7 +74,7 @@ def generate(ospfv3): ospfv3['new_frr_config'] = '' return None - ospfv3['new_frr_config'] = render_to_string('frr/ospfv3.frr.tmpl', ospfv3) + ospfv3['new_frr_config'] = render_to_string('frr/ospf6d.frr.tmpl', ospfv3) return None def apply(ospfv3): -- cgit v1.2.3 From f96435277e4c2b1e305f10f39206e281693f6232 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sat, 14 Aug 2021 18:24:13 +0200 Subject: op-mode: combine two "show vpn" definitions --- op-mode-definitions/show-vpn.xml.in | 20 -------------------- op-mode-definitions/vpn-ipsec.xml.in | 6 ++++++ 2 files changed, 6 insertions(+), 20 deletions(-) delete mode 100644 op-mode-definitions/show-vpn.xml.in diff --git a/op-mode-definitions/show-vpn.xml.in b/op-mode-definitions/show-vpn.xml.in deleted file mode 100644 index 3fbc74ad1..000000000 --- a/op-mode-definitions/show-vpn.xml.in +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - - Show active remote access Virtual Private Network (VPN) sessions - - - - - Show active VPN server sessions - - ${vyos_op_scripts_dir}/show_vpn_ra.py - - - - - - diff --git a/op-mode-definitions/vpn-ipsec.xml.in b/op-mode-definitions/vpn-ipsec.xml.in index 20f275e9b..18b72b87c 100644 --- a/op-mode-definitions/vpn-ipsec.xml.in +++ b/op-mode-definitions/vpn-ipsec.xml.in @@ -140,6 +140,12 @@ sudo ip xfrm policy list + + + Show active VPN server sessions + + ${vyos_op_scripts_dir}/show_vpn_ra.py + Show all active IPSec Security Associations (SA) -- cgit v1.2.3 From b4629d14d8ec5c780f665222205a396eb4dc3158 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sat, 14 Aug 2021 18:24:54 +0200 Subject: op-mode: vpn: use over absolute path --- op-mode-definitions/vpn-ipsec.xml.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/op-mode-definitions/vpn-ipsec.xml.in b/op-mode-definitions/vpn-ipsec.xml.in index 18b72b87c..3d997c143 100644 --- a/op-mode-definitions/vpn-ipsec.xml.in +++ b/op-mode-definitions/vpn-ipsec.xml.in @@ -184,7 +184,7 @@ if pgrep charon >/dev/null ; then sudo /usr/sbin/ipsec statusall ; else echo "IPSec process not running" ; fi - if pgrep charon >/dev/null ; then sudo /usr/libexec/vyos/op_mode/show_ipsec_sa.py ; else echo "IPSec process not running" ; fi + if pgrep charon >/dev/null ; then sudo ${vyos_op_scripts_dir}/show_ipsec_sa.py ; else echo "IPSec process not running" ; fi -- cgit v1.2.3 From 1229665d353a070e14ee9cceafbfdb107d669745 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sat, 14 Aug 2021 19:01:53 +0200 Subject: op-mode: ipsec: T3745: "show vpn ipse sa" improve sorting --- src/op_mode/show_ipsec_sa.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/op_mode/show_ipsec_sa.py b/src/op_mode/show_ipsec_sa.py index e491267fd..c964caaeb 100755 --- a/src/op_mode/show_ipsec_sa.py +++ b/src/op_mode/show_ipsec_sa.py @@ -23,6 +23,12 @@ import hurry.filesize import vyos.util +def convert(text): + return int(text) if text.isdigit() else text.lower() + +def alphanum_key(key): + return [convert(c) for c in re.split('([0-9]+)', str(key))] + def format_output(conns, sas): sa_data = [] @@ -111,7 +117,7 @@ if __name__ == '__main__': headers = ["Connection", "State", "Uptime", "Bytes In/Out", "Packets In/Out", "Remote address", "Remote ID", "Proposal"] sa_data = format_output(conns, sas) - sa_data = sorted(sa_data, key=lambda peer: peer[0]) + sa_data = sorted(sa_data, key=alphanum_key) output = tabulate.tabulate(sa_data, headers) print(output) except PermissionError: -- cgit v1.2.3 From a74e67a778a6c698e44cbc6c5d184d03c9c12396 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sat, 14 Aug 2021 20:13:31 +0200 Subject: vyos.util: T1503: use build in methods to determine current user for commit_in_progress() --- python/vyos/util.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/python/vyos/util.py b/python/vyos/util.py index 60171746a..8af46a6ee 100644 --- a/python/vyos/util.py +++ b/python/vyos/util.py @@ -562,12 +562,13 @@ def commit_in_progress(): # Since this will be used in scripts that modify the config outside of the CLI # framework, those knowingly have root permissions. # For everything else, we add a safeguard. - from psutil import process_iter, NoSuchProcess + from psutil import process_iter + from psutil import NoSuchProcess + from getpass import getuser from vyos.defaults import commit_lock - idu = cmd('/usr/bin/id -u') - if idu != '0': - raise OSError("This functions needs root permissions to return correct results") + if getuser() != 'root': + raise OSError('This functions needs to be run as root to return correct results!') for proc in process_iter(): try: -- cgit v1.2.3 From e7d841d2854d8e0ebb95cb6f0bd83e84fba3a9fa Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sat, 14 Aug 2021 20:40:28 +0200 Subject: smoketest: shim: remove superfluous sleep() in getFRRconfig() The sleep was intended to handle a FRR issue where the config was/is somehow now available in vtysh even with the commit was done. This rather feels like a race-condition and is fixed in the subsequent commit. --- smoketest/scripts/cli/base_vyostest_shim.py | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/smoketest/scripts/cli/base_vyostest_shim.py b/smoketest/scripts/cli/base_vyostest_shim.py index 18e49f47f..c0665fa0c 100644 --- a/smoketest/scripts/cli/base_vyostest_shim.py +++ b/smoketest/scripts/cli/base_vyostest_shim.py @@ -74,17 +74,9 @@ class VyOSUnitTestSHIM: def getFRRconfig(self, string, end='$', endsection='^!'): """ Retrieve current "running configuration" from FRR """ command = f'vtysh -c "show run" | sed -n "/^{string}{end}/,/{endsection}/p"' - - count = 0 - tmp = '' - while count < 10 and tmp == '': - # Let FRR settle after a config change first before harassing it again - sleep(1) - tmp = cmd(command) - count += 1 - - if self.debug or tmp == '': + out = cmd(command) + if self.debug: import pprint print(f'\n\ncommand "{command}" returned:\n') - pprint.pprint(tmp) - return tmp + pprint.pprint(out) + return out -- cgit v1.2.3 From 9b21e4a769385514f0bf625d665d588266dc6de4 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sat, 14 Aug 2021 20:41:28 +0200 Subject: smoketest: shim: wait for commit to be completed This completes commit e7d841d285 ("smoketest: shim: remove superfluous sleep() in getFRRconfig()"). --- smoketest/scripts/cli/base_vyostest_shim.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/smoketest/scripts/cli/base_vyostest_shim.py b/smoketest/scripts/cli/base_vyostest_shim.py index c0665fa0c..419530c3d 100644 --- a/smoketest/scripts/cli/base_vyostest_shim.py +++ b/smoketest/scripts/cli/base_vyostest_shim.py @@ -20,7 +20,9 @@ from time import sleep from vyos.configsession import ConfigSession from vyos.configsession import ConfigSessionError from vyos import ConfigError +from vyos.defaults import commit_lock from vyos.util import cmd +from vyos.util import run save_config = '/tmp/vyos-smoketest-save' @@ -70,6 +72,9 @@ class VyOSUnitTestSHIM: def cli_commit(self): self._session.commit() + # during a commit there is a process opening commit_lock, and run() returns 0 + while run(f'sudo lsof | grep -q {commit_lock}') == 0: + sleep(0.250) def getFRRconfig(self, string, end='$', endsection='^!'): """ Retrieve current "running configuration" from FRR """ -- cgit v1.2.3 From 43fcc0db006a12024df1b49d6f3c9e99ce165226 Mon Sep 17 00:00:00 2001 From: Lulu Cathrinus Grimalkin Date: Sun, 15 Aug 2021 13:10:20 +0300 Subject: conntrack: T3275: migrate 'disable' syntax to 'enable' syntax for the new default behavior --- .../include/conntrack-module-disable.xml.i | 8 ---- interface-definitions/system-conntrack.xml.in | 44 ++++++++-------------- src/conf_mode/conntrack.py | 2 +- src/migration-scripts/conntrack/2-to-3 | 37 ++++++++++++++++++ 4 files changed, 53 insertions(+), 38 deletions(-) delete mode 100644 interface-definitions/include/conntrack-module-disable.xml.i create mode 100755 src/migration-scripts/conntrack/2-to-3 diff --git a/interface-definitions/include/conntrack-module-disable.xml.i b/interface-definitions/include/conntrack-module-disable.xml.i deleted file mode 100644 index f891225e0..000000000 --- a/interface-definitions/include/conntrack-module-disable.xml.i +++ /dev/null @@ -1,8 +0,0 @@ - - - - Disable connection tracking helper - - - - diff --git a/interface-definitions/system-conntrack.xml.in b/interface-definitions/system-conntrack.xml.in index fa73df3db..c408e9bdd 100644 --- a/interface-definitions/system-conntrack.xml.in +++ b/interface-definitions/system-conntrack.xml.in @@ -37,64 +37,50 @@ - Connection tracking modules settings + Connection tracking modules - FTP connection tracking settings + FTP connection tracking + - - #include - - H.323 connection tracking settings + H.323 connection tracking + - - #include - - NFS connection tracking settings + NFS connection tracking + - - #include - - PPTP connection tracking settings + PPTP connection tracking + - - #include - - SIP connection tracking settings + SIP connection tracking + - - #include - - SQLnet connection tracking settings + SQLnet connection tracking + - - #include - - TFTP connection tracking settings + TFTP connection tracking + - - #include - diff --git a/src/conf_mode/conntrack.py b/src/conf_mode/conntrack.py index 4e6e39c0f..b305265db 100755 --- a/src/conf_mode/conntrack.py +++ b/src/conf_mode/conntrack.py @@ -97,7 +97,7 @@ def apply(conntrack): # Depending on the enable/disable state of the ALG (Application Layer Gateway) # modules we need to either insmod or rmmod the helpers. for module, module_config in module_map.items(): - if dict_search(f'modules.{module}.disable', conntrack) != None: + if dict_search(f'modules.{module}', conntrack) is None: if 'ko' in module_config: for mod in module_config['ko']: # Only remove the module if it's loaded diff --git a/src/migration-scripts/conntrack/2-to-3 b/src/migration-scripts/conntrack/2-to-3 new file mode 100755 index 000000000..8a8b43279 --- /dev/null +++ b/src/migration-scripts/conntrack/2-to-3 @@ -0,0 +1,37 @@ +#!/usr/bin/env python3 + +# Conntrack syntax version 3 +# Enables all conntrack modules (previous default behaviour) and omits manually disabled modules. + +import sys + +from vyos.configtree import ConfigTree +from vyos.version import get_version + +if len(sys.argv) < 1: + print('Must specify file name!') + sys.exit(1) + +filename = sys.argv[1] + +with open(filename, 'r') as f: + config = ConfigTree(f.read()) + +module_path = ['system', 'conntrack', 'modules'] + +# Go over all conntrack modules available as of v1.3.0. +for module in ['ftp', 'h323', 'nfs', 'pptp', 'sip', 'sqlnet', 'tftp']: + # 'disable' is being phased out. + if config.exists(module_path + [module, 'disable']): + config.delete(module_path + [module]) + # If it wasn't manually 'disable'd, it was enabled by default. + else: + config.set(module_path + [module]) + +try: + if config.exists(module_path): + with open(filename, 'w') as f: + f.write(config.to_string()) +except OSError as e: + print(f'Failed to save the modified config: {e}') + sys.exit(1) -- cgit v1.2.3 From d3ae6304a3eabcddba36452e9519ca7b56bb38af Mon Sep 17 00:00:00 2001 From: Boris Manojlovic Date: Sun, 15 Aug 2021 12:20:43 +0200 Subject: wireguard: T3756: fix generated qr code header --- src/op_mode/wireguard_client.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/op_mode/wireguard_client.py b/src/op_mode/wireguard_client.py index 7661254da..76c1ff7d1 100755 --- a/src/op_mode/wireguard_client.py +++ b/src/op_mode/wireguard_client.py @@ -39,10 +39,11 @@ To enable this configuration on a VyOS router you can use the following commands set interfaces wireguard {{ interface }} peer {{ name }} allowed-ips '{{ addr }}' {% endfor %} set interfaces wireguard {{ interface }} peer {{ name }} public-key '{{ pubkey }}' + +=== RoadWarrior (client) configuration === """ client_config = """ -=== RoadWarrior (client) configuration === [Interface] PrivateKey = {{ privkey }} -- cgit v1.2.3 From 2faceb16bc56f969eda2d1678a98f61fb1f4c23d Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sun, 15 Aug 2021 12:44:12 +0200 Subject: conntrack: T3275: bugfix XML generation This commit fixes an error introduced by 43fcc0db0 ("conntrack: T3275: migrate 'disable' syntax to 'enable' syntax for the new default behavior") as the option can only be used on leafNodes. THis triggered the following build error: ValueError: is only allowed in --- interface-definitions/system-conntrack.xml.in | 28 +++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/interface-definitions/system-conntrack.xml.in b/interface-definitions/system-conntrack.xml.in index c408e9bdd..daa4177c9 100644 --- a/interface-definitions/system-conntrack.xml.in +++ b/interface-definitions/system-conntrack.xml.in @@ -40,48 +40,48 @@ Connection tracking modules - + FTP connection tracking - - + + H.323 connection tracking - - + + NFS connection tracking - - + + PPTP connection tracking - - + + SIP connection tracking - - + + SQLnet connection tracking - - + + TFTP connection tracking - + -- cgit v1.2.3 From 6f87d8c910964fd0ebe9724183baa12861caa419 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sun, 15 Aug 2021 12:53:03 +0200 Subject: ospf: T3757: support to configure area at an interface level FRR supports configuring either network prefixes per area, or assign an interface to an area to participate in the routing process. This is already well known from other venders and supported by FRR. A valid VyOS OSPF configuration would then look like: vyos@vyos# show protocols ospf { interface dum0 { area 0 } interface eth0.201 { area 0 authentication { md5 { key-id 10 { md5-key vyos } } } dead-interval 40 hello-interval 10 priority 1 retransmit-interval 5 transmit-delay 1 } log-adjacency-changes { detail } parameters { abr-type cisco router-id 172.18.254.201 } passive-interface default passive-interface-exclude eth0.201 } --- data/templates/frr/ospfd.frr.tmpl | 9 ++++++--- .../include/ospf/protocol-common-config.xml.i | 17 +++++++++++++++++ src/conf_mode/protocols_ospf.py | 9 +++++++++ 3 files changed, 32 insertions(+), 3 deletions(-) diff --git a/data/templates/frr/ospfd.frr.tmpl b/data/templates/frr/ospfd.frr.tmpl index 763d0666c..67b679d66 100644 --- a/data/templates/frr/ospfd.frr.tmpl +++ b/data/templates/frr/ospfd.frr.tmpl @@ -14,6 +14,12 @@ interface {{ iface }} {{ 'vrf ' + vrf if vrf is defined and vrf is not none }} {% endif %} {% endif %} {% endif %} +{% if iface_config.area is defined and iface_config.area is not none %} + ip ospf area {{ iface_config.area }} +{% endif %} +{% if iface_config.bandwidth is defined and iface_config.bandwidth is not none %} + bandwidth {{ iface_config.bandwidth }} +{% endif %} {% if iface_config.cost is defined and iface_config.cost is not none %} ip ospf cost {{ iface_config.cost }} {% endif %} @@ -43,9 +49,6 @@ interface {{ iface }} {{ 'vrf ' + vrf if vrf is defined and vrf is not none }} {% if iface_config.network is defined and iface_config.network is not none %} ip ospf network {{ iface_config.network }} {% endif %} -{% if iface_config.bandwidth is defined and iface_config.bandwidth is not none %} - bandwidth {{ iface_config.bandwidth }} -{% endif %} ! {% endfor %} {% endif %} diff --git a/interface-definitions/include/ospf/protocol-common-config.xml.i b/interface-definitions/include/ospf/protocol-common-config.xml.i index db39b1a86..c4ca613a4 100644 --- a/interface-definitions/include/ospf/protocol-common-config.xml.i +++ b/interface-definitions/include/ospf/protocol-common-config.xml.i @@ -361,6 +361,23 @@ + + + Enable OSPF on this interface + + u32 + OSPF area ID as decimal notation + + + ipv4 + OSPF area ID in IP address notation + + + + + + + #include #include #include diff --git a/src/conf_mode/protocols_ospf.py b/src/conf_mode/protocols_ospf.py index 82126cb11..a21ea6c9f 100755 --- a/src/conf_mode/protocols_ospf.py +++ b/src/conf_mode/protocols_ospf.py @@ -157,6 +157,15 @@ def verify(ospf): raise ConfigError(f'Can not use hello-multiplier and dead-interval ' \ f'concurrently for {interface}!') + # One can not use the "network area " command and an + # per interface area assignment at the same time. FRR will error + # out using: "Please remove all network commands first." + if 'area' in ospf: + for area, area_config in ospf['area'].items(): + if 'network' in area_config: + raise ConfigError('Can not use OSPF interface area and area ' \ + 'network configuration at the same time!') + if 'vrf' in ospf: # If interface specific options are set, we must ensure that the # interface is bound to our requesting VRF. Due to the VyOS -- cgit v1.2.3 From 841360a84660974f9b8d97d08780a6a3d9faf3b7 Mon Sep 17 00:00:00 2001 From: erkin Date: Sun, 15 Aug 2021 14:50:50 +0300 Subject: smoketest: conntrack: Update smoketests for new conntrack modules syntax --- smoketest/scripts/cli/test_system_conntrack.py | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/smoketest/scripts/cli/test_system_conntrack.py b/smoketest/scripts/cli/test_system_conntrack.py index 21d626d2f..d659bfb65 100755 --- a/smoketest/scripts/cli/test_system_conntrack.py +++ b/smoketest/scripts/cli/test_system_conntrack.py @@ -148,7 +148,7 @@ class TestSystemConntrack(VyOSUnitTestSHIM.TestCase): def test_conntrack_module_disable(self): - # Some features are disabled by onloading the kernel helper module(s) + # conntrack helper modules are disabled by default modules = { 'ftp' : { 'driver' : ['nf_nat_ftp', 'nf_conntrack_ftp'], @@ -176,38 +176,39 @@ class TestSystemConntrack(VyOSUnitTestSHIM.TestCase): }, } + # load modules for module in modules: - self.cli_set(base_path + ['modules', module, 'disable']) + self.cli_set(base_path + ['modules', module]) # commit changes self.cli_commit() - # verify modules are no longer loaded on the system + # verify modules are loaded on the system for module, module_options in modules.items(): if 'driver' in module_options: for driver in module_options['driver']: - self.assertFalse(os.path.isdir(f'/sys/module/{driver}')) + self.assertTrue(os.path.isdir(f'/sys/module/{driver}')) if 'iptables' in module_options: rules = cmd('sudo iptables-save -t raw') for ruleset in module_options['iptables']: - self.assertNotIn(ruleset, rules) + self.assertIn(ruleset, rules) - # reload modules + # unload modules for module in modules: - self.cli_delete(base_path + ['modules', module, 'disable']) + self.cli_delete(base_path + ['modules', module]) # commit changes self.cli_commit() - # verify modules are again loaded on the system + # verify modules are not loaded on the system for module, module_options in modules.items(): if 'driver' in module_options: for driver in module_options['driver']: - self.assertTrue(os.path.isdir(f'/sys/module/{driver}')) + self.assertFalse(os.path.isdir(f'/sys/module/{driver}')) if 'iptables' in module_options: rules = cmd('sudo iptables-save -t raw') for ruleset in module_options['iptables']: - self.assertIn(ruleset, rules) + self.assertNotIn(ruleset, rules) def test_conntrack_hash_size(self): hash_size = '65536' -- cgit v1.2.3 From 22b6c055c193a99db40f036bfe6c485b411a8abb Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sun, 15 Aug 2021 14:53:54 +0200 Subject: smoketest: ospf: T3757: test interface area configuration --- smoketest/scripts/cli/test_protocols_ospf.py | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/smoketest/scripts/cli/test_protocols_ospf.py b/smoketest/scripts/cli/test_protocols_ospf.py index 59862ca3d..c4fd15c93 100755 --- a/smoketest/scripts/cli/test_protocols_ospf.py +++ b/smoketest/scripts/cli/test_protocols_ospf.py @@ -20,6 +20,7 @@ import unittest from base_vyostest_shim import VyOSUnitTestSHIM +from vyos.configsession import ConfigSessionError from vyos.ifconfig import Section from vyos.util import process_named_running from vyos.util import cmd @@ -349,6 +350,30 @@ class TestProtocolsOSPF(VyOSUnitTestSHIM.TestCase): frrconfig = self.getFRRconfig(zebra_route_map) self.assertNotIn(zebra_route_map, frrconfig) + def test_ospf_13_interface_area(self): + area = '0' + interfaces = Section.interfaces('ethernet') + + self.cli_set(base_path + ['area', area, 'network', '10.0.0.0/8']) + for interface in interfaces: + self.cli_set(base_path + ['interface', interface, 'area', area]) + + # we can not have bot area network and interface area set + with self.assertRaises(ConfigSessionError): + self.cli_commit() + self.cli_delete(base_path + ['area', area, 'network']) + + self.cli_commit() + + # Verify FRR ospfd configuration + frrconfig = self.getFRRconfig('router ospf') + self.assertIn(f'router ospf', frrconfig) + + for interface in interfaces: + config = self.getFRRconfig(f'interface {interface}') + self.assertIn(f'interface {interface}', config) + self.assertIn(f' ip ospf area {area}', config) + if __name__ == '__main__': logging.basicConfig(stream=sys.stderr, level=logging.DEBUG) unittest.main(verbosity=2) -- cgit v1.2.3 From 95acc6ab1d2f4fec08d013473435e187461ca07a Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sun, 15 Aug 2021 14:54:18 +0200 Subject: smoketest: ospf: fix type in testcase name #10 --- smoketest/scripts/cli/test_protocols_ospf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/smoketest/scripts/cli/test_protocols_ospf.py b/smoketest/scripts/cli/test_protocols_ospf.py index c4fd15c93..2ec0005bf 100755 --- a/smoketest/scripts/cli/test_protocols_ospf.py +++ b/smoketest/scripts/cli/test_protocols_ospf.py @@ -267,7 +267,7 @@ class TestProtocolsOSPF(VyOSUnitTestSHIM.TestCase): self.assertIn(f' network {network} area {area}', frrconfig) - def test_ospf_10_interface_configureation(self): + def test_ospf_10_interface_configuration(self): interfaces = Section.interfaces('ethernet') password = 'vyos1234' bandwidth = '10000' -- cgit v1.2.3 From ae1994f1452377bf523973f2048b52590bcbad8b Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sun, 15 Aug 2021 14:54:57 +0200 Subject: ospf: T3236: improve Jinja2 template - always use if before a loop --- data/templates/frr/ospfd.frr.tmpl | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/data/templates/frr/ospfd.frr.tmpl b/data/templates/frr/ospfd.frr.tmpl index 67b679d66..be39519c3 100644 --- a/data/templates/frr/ospfd.frr.tmpl +++ b/data/templates/frr/ospfd.frr.tmpl @@ -158,15 +158,19 @@ router ospf {{ 'vrf ' + vrf if vrf is defined and vrf is not none }} ospf router-id {{ parameters.router_id }} {% endif %} {% endif %} -{% for interface in passive_interface if passive_interface is defined and passive_interface == 'default' %} - passive-interface default -{% endfor %} -{% for interface in passive_interface_exclude if passive_interface_exclude is defined %} -{% if interface.startswith('vlink') %} +{% if passive_interface is defined and passive_interface is not none %} +{% for interface in passive_interface %} + passive-interface {{ interface }} +{% endfor %} +{% endif %} +{% if passive_interface_exclude is defined and passive_interface_exclude is not none %} +{% for interface in passive_interface_exclude if passive_interface_exclude is defined %} +{% if interface.startswith('vlink') %} {% set interface = interface.upper() %} -{% endif %} +{% endif %} no passive-interface {{ interface }} -{% endfor %} +{% endfor %} +{% endif %} {% if redistribute is defined and redistribute is not none %} {% for protocol, options in redistribute.items() %} redistribute {{ protocol }} {{ 'metric ' + options.metric if options.metric is defined }} {{ 'metric-type ' + options.metric_type if options.metric_type is defined }} {{ 'route-map ' + options.route_map if options.route_map is defined }} -- cgit v1.2.3 From 555513691f79db537a8c7430e87e846e8607ef5e Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sun, 15 Aug 2021 14:56:45 +0200 Subject: smoketest: ospf: add debug code for redistribution test --- smoketest/scripts/cli/test_protocols_ospf.py | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/smoketest/scripts/cli/test_protocols_ospf.py b/smoketest/scripts/cli/test_protocols_ospf.py index 2ec0005bf..f132fc5b5 100755 --- a/smoketest/scripts/cli/test_protocols_ospf.py +++ b/smoketest/scripts/cli/test_protocols_ospf.py @@ -229,13 +229,19 @@ class TestProtocolsOSPF(VyOSUnitTestSHIM.TestCase): # Verify FRR ospfd configuration frrconfig = self.getFRRconfig('router ospf') - self.assertIn(f'router ospf', frrconfig) - for protocol in redistribute: - if protocol in ['kernel', 'static']: - self.assertIn(f' redistribute {protocol} metric {metric} route-map {route_map}', frrconfig) - else: - self.assertIn(f' redistribute {protocol} metric {metric} metric-type {metric_type} route-map {route_map}', frrconfig) - + try: + self.assertIn(f'router ospf', frrconfig) + for protocol in redistribute: + if protocol in ['kernel', 'static']: + self.assertIn(f' redistribute {protocol} metric {metric} route-map {route_map}', frrconfig) + else: + self.assertIn(f' redistribute {protocol} metric {metric} metric-type {metric_type} route-map {route_map}', frrconfig) + except: + log.debug(frrconfig) + log.debug(cmd('sudo dmesg')) + log.debug(cmd('sudo cat /var/log/messages')) + log.debug(cmd('vtysh -c "show run"')) + self.fail('Now we can hopefully see why OSPF fails!') def test_ospf_09_virtual_link(self): networks = ['10.0.0.0/8', '172.16.0.0/12', '192.168.0.0/16'] -- cgit v1.2.3 From 1a62587a57d034681bd728b10b13a5ae5d7d2d4d Mon Sep 17 00:00:00 2001 From: Viacheslav Date: Sun, 15 Aug 2021 14:11:41 +0000 Subject: pbr: T3702: Fix incorrect splits for fwmark --- src/conf_mode/policy-local-route.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/conf_mode/policy-local-route.py b/src/conf_mode/policy-local-route.py index 0b7ceedeb..539189442 100755 --- a/src/conf_mode/policy-local-route.py +++ b/src/conf_mode/policy-local-route.py @@ -114,15 +114,15 @@ def apply(pbr): # Only fwmark in the rule # set policy local-route rule 101 fwmark '23' if 'fwmark' in pbr['rule'][rule] and not 'source' in pbr['rule'][rule]: - for fwmk in pbr['rule'][rule]['fwmark']: - call(f'ip rule add prio {rule} from all fwmark {fwmk} lookup {table}') + fwmk = pbr['rule'][rule]['fwmark'] + call(f'ip rule add prio {rule} from all fwmark {fwmk} lookup {table}') # Source and fwmark in the rule # set policy local-route rule 100 source '203.0.113.1' # set policy local-route rule 100 fwmark '23' if 'source' in pbr['rule'][rule] and 'fwmark' in pbr['rule'][rule]: + fwmk = pbr['rule'][rule]['fwmark'] for src in pbr['rule'][rule]['source']: - for fwmk in pbr['rule'][rule]['fwmark']: - call(f'ip rule add prio {rule} from {src} fwmark {fwmk} lookup {table}') + call(f'ip rule add prio {rule} from {src} fwmark {fwmk} lookup {table}') return None -- cgit v1.2.3 From 9bae7536bb6b512e6e35664bb3ebef2236011a7c Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Mon, 16 Aug 2021 18:04:15 +0200 Subject: ospf: T3757: verify() bugfix for interface area Commit 6f87d8c9 ("ospf: T3757: support to configure area at an interface level") did not allow the old way an area and netwokr was set-up as the if expression was missing a check if 'area' was set in both the interface and the ospf process. --- src/conf_mode/protocols_ospf.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/conf_mode/protocols_ospf.py b/src/conf_mode/protocols_ospf.py index a21ea6c9f..06a29106d 100755 --- a/src/conf_mode/protocols_ospf.py +++ b/src/conf_mode/protocols_ospf.py @@ -149,18 +149,18 @@ def verify(ospf): if route_map_name: verify_route_map(route_map_name, ospf) if 'interface' in ospf: - for interface in ospf['interface']: + for interface, interface_config in ospf['interface'].items(): verify_interface_exists(interface) # One can not use dead-interval and hello-multiplier at the same # time. FRR will only activate the last option set via CLI. - if {'hello_multiplier', 'dead_interval'} <= set(ospf['interface'][interface]): + if {'hello_multiplier', 'dead_interval'} <= set(interface_config): raise ConfigError(f'Can not use hello-multiplier and dead-interval ' \ f'concurrently for {interface}!') # One can not use the "network area " command and an # per interface area assignment at the same time. FRR will error # out using: "Please remove all network commands first." - if 'area' in ospf: + if 'area' in ospf and 'area' in interface_config: for area, area_config in ospf['area'].items(): if 'network' in area_config: raise ConfigError('Can not use OSPF interface area and area ' \ -- cgit v1.2.3 From 2c17993105b635c3c157e9f528a017bc9e0b556b Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Mon, 16 Aug 2021 18:10:01 +0200 Subject: conntrack: T3579: remove debug print() --- src/conf_mode/conntrack.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/conf_mode/conntrack.py b/src/conf_mode/conntrack.py index b305265db..9693de493 100755 --- a/src/conf_mode/conntrack.py +++ b/src/conf_mode/conntrack.py @@ -105,7 +105,6 @@ def apply(conntrack): cmd(f'rmmod {mod}') if 'iptables' in module_config: for rule in module_config['iptables']: - print(f'iptables --delete {rule}') cmd(f'iptables --delete {rule}') else: if 'ko' in module_config: -- cgit v1.2.3 From 18ac0c694a3081931fecc9e5e8ea48b019105d81 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Mon, 16 Aug 2021 18:25:28 +0200 Subject: conntrack: T3579: bugfix when deleting non existent iptable rules We only delete iptables rules if they really exist - if we try to delete a non- existing rule a PermissionError exception is thrown. We could either ignore the error code (that is what the old Vyatta code did), or we check what we are doing beforehand. --- src/conf_mode/conntrack.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/conf_mode/conntrack.py b/src/conf_mode/conntrack.py index 9693de493..68877f794 100755 --- a/src/conf_mode/conntrack.py +++ b/src/conf_mode/conntrack.py @@ -105,7 +105,9 @@ def apply(conntrack): cmd(f'rmmod {mod}') if 'iptables' in module_config: for rule in module_config['iptables']: - cmd(f'iptables --delete {rule}') + # Only install iptables rule if it does not exist + tmp = run(f'iptables --check {rule}') + if tmp == 0: cmd(f'iptables --delete {rule}') else: if 'ko' in module_config: for mod in module_config['ko']: @@ -114,9 +116,7 @@ def apply(conntrack): for rule in module_config['iptables']: # Only install iptables rule if it does not exist tmp = run(f'iptables --check {rule}') - if tmp > 0: - cmd(f'iptables --insert {rule}') - + if tmp > 0: cmd(f'iptables --insert {rule}') if process_named_running('conntrackd'): # Reload conntrack-sync daemon to fetch new sysctl values -- cgit v1.2.3 From d95723547949d759e9260fdc220162fd09b2df2b Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Mon, 16 Aug 2021 19:34:17 +0200 Subject: smoketest: conntrack: fix function name disable -> enable --- smoketest/scripts/cli/test_system_conntrack.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/smoketest/scripts/cli/test_system_conntrack.py b/smoketest/scripts/cli/test_system_conntrack.py index d659bfb65..a2380981b 100755 --- a/smoketest/scripts/cli/test_system_conntrack.py +++ b/smoketest/scripts/cli/test_system_conntrack.py @@ -147,7 +147,7 @@ class TestSystemConntrack(VyOSUnitTestSHIM.TestCase): self.assertEqual(get_sysctl(f'{parameter}'), parameter_config['default_value']) - def test_conntrack_module_disable(self): + def test_conntrack_module_enable(self): # conntrack helper modules are disabled by default modules = { 'ftp' : { -- cgit v1.2.3 From 94ed90e7a09b1c8fb4bfc8ad5df9ec0e1b4a15bb Mon Sep 17 00:00:00 2001 From: Viacheslav Date: Mon, 16 Aug 2021 20:27:34 +0000 Subject: openvpn: T690: Add metric for pushed routes --- data/templates/openvpn/server.conf.tmpl | 19 ++++++++++--------- interface-definitions/interfaces-openvpn.xml.in | 20 +++++++++++++++++--- 2 files changed, 27 insertions(+), 12 deletions(-) diff --git a/data/templates/openvpn/server.conf.tmpl b/data/templates/openvpn/server.conf.tmpl index d9f01310e..9b07a9ba2 100644 --- a/data/templates/openvpn/server.conf.tmpl +++ b/data/templates/openvpn/server.conf.tmpl @@ -74,6 +74,16 @@ topology {{ server.topology }} {% for subnet in server.subnet %} {% if subnet | is_ipv4 %} server {{ subnet | address_from_cidr }} {{ subnet | netmask_from_cidr }} nopool +{# First ip address is used as gateway. It's allows to use metrics #} +{% if server.push_route is defined and server.push_route is not none %} +{% for route, route_config in server.push_route.items() %} +{% if route | is_ipv4 %} +push "route {{ route | address_from_cidr }} {{ route | netmask_from_cidr }} {{ subnet | first_host_address }} {{ route_config.metric if route_config.metric is defined else "0" }}" +{% elif route | is_ipv6 %} +push "route-ipv6 {{ route }}" +{% endif %} +{% endfor %} +{% endif %} {# OpenVPN assigns the first IP address to its local interface so the pool used #} {# in net30 topology - where each client receives a /30 must start from the second subnet #} {% if server.topology is defined and server.topology == 'net30' %} @@ -106,15 +116,6 @@ management /run/openvpn/openvpn-mgmt-intf unix ccd-exclusive {% endif %} -{% if server.push_route is defined and server.push_route is not none %} -{% for route in server.push_route %} -{% if route | is_ipv4 %} -push "route {{ route | address_from_cidr }} {{ route | netmask_from_cidr }}" -{% elif route | is_ipv6 %} -push "route-ipv6 {{ route }}" -{% endif %} -{% endfor %} -{% endif %} {% if server.name_server is defined and server.name_server is not none %} {% for nameserver in server.name_server %} {% if nameserver | is_ipv4 %} diff --git a/interface-definitions/interfaces-openvpn.xml.in b/interface-definitions/interfaces-openvpn.xml.in index 7ff08ac86..286b10f9a 100644 --- a/interface-definitions/interfaces-openvpn.xml.in +++ b/interface-definitions/interfaces-openvpn.xml.in @@ -571,7 +571,7 @@ - + Route to be pushed to all clients @@ -585,9 +585,23 @@ - - + + + + Set metric for this route + + 0-4294967295 + Metric for this route + + + + + + 0 + + + Reject connections from clients that are not explicitly configured -- cgit v1.2.3 From 520d2fb73670cfaa378164896d6993a03893f2b8 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Tue, 17 Aug 2021 09:10:56 +0200 Subject: isis: T1316: use common redistribute XML building block for IPv4 and IPv6 --- .../include/isis/protocol-common-config.xml.i | 24 ++++++------- .../include/isis/redistribute-ipv4.xml.i | 42 ---------------------- .../include/isis/redistribute-ipv6.xml.i | 42 ---------------------- .../include/isis/redistribute-level-1-2.xml.i | 20 +++++++++++ 4 files changed, 32 insertions(+), 96 deletions(-) delete mode 100644 interface-definitions/include/isis/redistribute-ipv4.xml.i delete mode 100644 interface-definitions/include/isis/redistribute-ipv6.xml.i create mode 100644 interface-definitions/include/isis/redistribute-level-1-2.xml.i diff --git a/interface-definitions/include/isis/protocol-common-config.xml.i b/interface-definitions/include/isis/protocol-common-config.xml.i index af5a21f49..84e2f7bb2 100644 --- a/interface-definitions/include/isis/protocol-common-config.xml.i +++ b/interface-definitions/include/isis/protocol-common-config.xml.i @@ -447,7 +447,7 @@ Border Gateway Protocol (BGP) - #include + #include @@ -455,7 +455,7 @@ Redistribute connected routes into IS-IS - #include + #include @@ -463,7 +463,7 @@ Redistribute kernel routes into IS-IS - #include + #include @@ -471,7 +471,7 @@ Redistribute OSPF routes into IS-IS - #include + #include @@ -479,7 +479,7 @@ Redistribute RIP routes into IS-IS - #include + #include @@ -487,7 +487,7 @@ Redistribute static routes into IS-IS - #include + #include @@ -502,7 +502,7 @@ Redistribute BGP routes into IS-IS - #include + #include @@ -510,7 +510,7 @@ Redistribute connected routes into IS-IS - #include + #include @@ -518,7 +518,7 @@ Redistribute kernel routes into IS-IS - #include + #include @@ -526,7 +526,7 @@ Redistribute OSPFv3 routes into IS-IS - #include + #include @@ -534,7 +534,7 @@ Redistribute RIPng routes into IS-IS - #include + #include @@ -542,7 +542,7 @@ Redistribute static routes into IS-IS - #include + #include diff --git a/interface-definitions/include/isis/redistribute-ipv4.xml.i b/interface-definitions/include/isis/redistribute-ipv4.xml.i deleted file mode 100644 index fbb6210c7..000000000 --- a/interface-definitions/include/isis/redistribute-ipv4.xml.i +++ /dev/null @@ -1,42 +0,0 @@ - - - - Redistribute into level-1 - - - - - Metric for redistributed routes - - u32:0-16777215 - ISIS default metric - - - - - - - #include - - - - - Redistribute into level-2 - - - - - Metric for redistributed routes - - u32:0-16777215 - ISIS default metric - - - - - - - #include - - - diff --git a/interface-definitions/include/isis/redistribute-ipv6.xml.i b/interface-definitions/include/isis/redistribute-ipv6.xml.i deleted file mode 100644 index 7e679e38a..000000000 --- a/interface-definitions/include/isis/redistribute-ipv6.xml.i +++ /dev/null @@ -1,42 +0,0 @@ - - - - Redistribute into level-1 - - - - - Metric for redistributed routes - - u32:0-16777215 - ISIS default metric - - - - - - - #include - - - - - Redistribute into level-2 - - - - - Metric for redistributed routes - - u32:0-16777215 - ISIS default metric - - - - - - - #include - - - \ No newline at end of file diff --git a/interface-definitions/include/isis/redistribute-level-1-2.xml.i b/interface-definitions/include/isis/redistribute-level-1-2.xml.i new file mode 100644 index 000000000..abb85274f --- /dev/null +++ b/interface-definitions/include/isis/redistribute-level-1-2.xml.i @@ -0,0 +1,20 @@ + + + + Redistribute into level-1 + + + #include + #include + + + + + Redistribute into level-2 + + + #include + #include + + + -- cgit v1.2.3 From 61170a4e644e04eb60f404c34abd1171736601e1 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Tue, 17 Aug 2021 09:57:53 +0200 Subject: xml: include: add error message to port-number --- interface-definitions/include/port-number.xml.i | 1 + 1 file changed, 1 insertion(+) diff --git a/interface-definitions/include/port-number.xml.i b/interface-definitions/include/port-number.xml.i index b62aef32b..6820df0c4 100644 --- a/interface-definitions/include/port-number.xml.i +++ b/interface-definitions/include/port-number.xml.i @@ -9,6 +9,7 @@ + Port number must be in range 1 to 65535 -- cgit v1.2.3 From 7c9525563cc11bdfb3565230b662f0570ee563ad Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Tue, 17 Aug 2021 09:58:13 +0200 Subject: xml: vxlan: use port-number building block --- interface-definitions/interfaces-vxlan.xml.in | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/interface-definitions/interfaces-vxlan.xml.in b/interface-definitions/interfaces-vxlan.xml.in index 56d01dfb6..1714f1976 100644 --- a/interface-definitions/interfaces-vxlan.xml.in +++ b/interface-definitions/interfaces-vxlan.xml.in @@ -76,17 +76,8 @@ + #include - - Destination port of VXLAN tunnel (default: 8472) - - u32:1-65535 - Numeric IP port - - - - - 8472 #include -- cgit v1.2.3 From 0b1d6211a00e0267ace50d564c00643100d2b478 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Tue, 17 Aug 2021 09:58:31 +0200 Subject: xml: bcast-relay: use port-number building block --- interface-definitions/bcast-relay.xml.in | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/interface-definitions/bcast-relay.xml.in b/interface-definitions/bcast-relay.xml.in index 1b354d885..c7948ded1 100644 --- a/interface-definitions/bcast-relay.xml.in +++ b/interface-definitions/bcast-relay.xml.in @@ -49,18 +49,7 @@ - - - Destination or source port to listen and retransmit on [REQUIRED] - - u32:1-65535 - UDP port to listen on - - - - - - + #include -- cgit v1.2.3 From b541b35e6bb663f3c10e102c8da5b70edaf4fea1 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Tue, 17 Aug 2021 09:59:38 +0200 Subject: xml: tftp: use port-number building block --- interface-definitions/tftp-server.xml.in | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/interface-definitions/tftp-server.xml.in b/interface-definitions/tftp-server.xml.in index e903e8f4e..037c097ca 100644 --- a/interface-definitions/tftp-server.xml.in +++ b/interface-definitions/tftp-server.xml.in @@ -20,17 +20,8 @@ + #include - - Port number used to listen for connections - - u32:1-65535 - Numeric IP port - - - - - 69 #include -- cgit v1.2.3 From c0483db231eb86ff6f5f64064d5dfce8c602e07f Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Tue, 17 Aug 2021 09:59:55 +0200 Subject: xml: syslog: use port-number building block --- interface-definitions/system-syslog.xml.in | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/interface-definitions/system-syslog.xml.in b/interface-definitions/system-syslog.xml.in index f3dcae2f3..9280a43c8 100644 --- a/interface-definitions/system-syslog.xml.in +++ b/interface-definitions/system-syslog.xml.in @@ -195,19 +195,7 @@ - - - Destination port - - u32:1-65535 - Destination port - - - - - Invalid destination port value - - + #include Facility for logging -- cgit v1.2.3 From 6884dc45c01ac2691761fb05d7ac8b7bdc290b38 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Tue, 17 Aug 2021 10:00:21 +0200 Subject: xml: wireguard: use port-number building block --- interface-definitions/interfaces-wireguard.xml.in | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/interface-definitions/interfaces-wireguard.xml.in b/interface-definitions/interfaces-wireguard.xml.in index 773bde09c..7b6c717b9 100644 --- a/interface-definitions/interfaces-wireguard.xml.in +++ b/interface-definitions/interfaces-wireguard.xml.in @@ -102,18 +102,7 @@ - - - Port number used for tunnel endpoint - - u32:1-65535 - Numeric IP port - - - - - - + #include Interval to send keepalive messages -- cgit v1.2.3 From 5c7424f96ba7b08796d9fe9d2797ad162f26d8e0 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Tue, 17 Aug 2021 10:00:46 +0200 Subject: xml: webproxy: use port-number building block for LDAP connection --- interface-definitions/service_webproxy.xml.in | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/interface-definitions/service_webproxy.xml.in b/interface-definitions/service_webproxy.xml.in index 7cb0f7ece..01a865329 100644 --- a/interface-definitions/service_webproxy.xml.in +++ b/interface-definitions/service_webproxy.xml.in @@ -83,17 +83,8 @@ + #include - - LDAP server port to use (default: 389) - - u32:1-65535 - Port number to use - - - - - 389 -- cgit v1.2.3 From 6559d8c19710e10eddab590302f61ddf693e2e82 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Tue, 17 Aug 2021 10:01:09 +0200 Subject: xml: proxy: use port-number building block for system proxy --- interface-definitions/system-proxy.xml.in | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/interface-definitions/system-proxy.xml.in b/interface-definitions/system-proxy.xml.in index 791f41f2f..ade168522 100644 --- a/interface-definitions/system-proxy.xml.in +++ b/interface-definitions/system-proxy.xml.in @@ -15,18 +15,7 @@ - - - Proxy port - - u32:1-65535 - Numeric IP port - - - - - - + #include Proxy username -- cgit v1.2.3 From 0f498d782c8fc3645355184cc59e88a16d1b230d Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Tue, 17 Aug 2021 12:11:54 +0200 Subject: op-mode: T1513: bgp: xml: provide exact-match building block --- op-mode-definitions/include/bgp/afi-common.xml.i | 7 +------ op-mode-definitions/include/bgp/afi-ipv4-ipv6-common.xml.i | 14 ++------------ op-mode-definitions/include/bgp/exact-match.xml.i | 8 ++++++++ 3 files changed, 11 insertions(+), 18 deletions(-) create mode 100644 op-mode-definitions/include/bgp/exact-match.xml.i diff --git a/op-mode-definitions/include/bgp/afi-common.xml.i b/op-mode-definitions/include/bgp/afi-common.xml.i index 7fc59f3b0..0edeb9f81 100644 --- a/op-mode-definitions/include/bgp/afi-common.xml.i +++ b/op-mode-definitions/include/bgp/afi-common.xml.i @@ -7,12 +7,7 @@ - - - Exact match of the communities - - ${vyos_op_scripts_dir}/vtysh_wrapper.sh $@ - + #include ${vyos_op_scripts_dir}/vtysh_wrapper.sh $@ diff --git a/op-mode-definitions/include/bgp/afi-ipv4-ipv6-common.xml.i b/op-mode-definitions/include/bgp/afi-ipv4-ipv6-common.xml.i index f1b699347..a51595b7f 100644 --- a/op-mode-definitions/include/bgp/afi-ipv4-ipv6-common.xml.i +++ b/op-mode-definitions/include/bgp/afi-ipv4-ipv6-common.xml.i @@ -22,12 +22,7 @@ ${vyos_op_scripts_dir}/vtysh_wrapper.sh $@ - - - Exact match of the communities - - ${vyos_op_scripts_dir}/vtysh_wrapper.sh $@ - + #include Graceful shutdown (well-known community) @@ -105,12 +100,7 @@ - - - Show BGP routes exactly matching specified community list - - ${vyos_op_scripts_dir}/vtysh_wrapper.sh $@ - + #include ${vyos_op_scripts_dir}/vtysh_wrapper.sh $@ diff --git a/op-mode-definitions/include/bgp/exact-match.xml.i b/op-mode-definitions/include/bgp/exact-match.xml.i new file mode 100644 index 000000000..49026db9b --- /dev/null +++ b/op-mode-definitions/include/bgp/exact-match.xml.i @@ -0,0 +1,8 @@ + + + + Exact match of the communities + + ${vyos_op_scripts_dir}/vtysh_wrapper.sh $@ + + -- cgit v1.2.3 From 6c0044d12ecf762d9d362d6b09cdaa4c8227db58 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Tue, 17 Aug 2021 12:13:04 +0200 Subject: op-mode: T1513: bgp: add "show bgp large-community AA:BB:CC exat-match" --- op-mode-definitions/include/bgp/afi-common.xml.i | 3 +++ 1 file changed, 3 insertions(+) diff --git a/op-mode-definitions/include/bgp/afi-common.xml.i b/op-mode-definitions/include/bgp/afi-common.xml.i index 0edeb9f81..ef6709ae8 100644 --- a/op-mode-definitions/include/bgp/afi-common.xml.i +++ b/op-mode-definitions/include/bgp/afi-common.xml.i @@ -19,6 +19,9 @@ ${vyos_op_scripts_dir}/vtysh_wrapper.sh $@ + + #include + -- cgit v1.2.3 From 6bb2f6e22706bb1c5874a58e74585160164011f6 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Tue, 17 Aug 2021 13:31:39 +0200 Subject: op-mode: xml: bgp: fix large-community help string --- op-mode-definitions/include/bgp/afi-common.xml.i | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/op-mode-definitions/include/bgp/afi-common.xml.i b/op-mode-definitions/include/bgp/afi-common.xml.i index ef6709ae8..e28dfe5d7 100644 --- a/op-mode-definitions/include/bgp/afi-common.xml.i +++ b/op-mode-definitions/include/bgp/afi-common.xml.i @@ -13,7 +13,7 @@ - List of large-community numbers + Display routes matching the large-communities AA:BB:CC -- cgit v1.2.3 From 90f778fc04b322867f6fec023328330adc3fd85f Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Tue, 17 Aug 2021 13:32:21 +0200 Subject: op-mode: T1513: bgp: add "show bgp large-community-list" commnad --- op-mode-definitions/include/bgp/afi-common.xml.i | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/op-mode-definitions/include/bgp/afi-common.xml.i b/op-mode-definitions/include/bgp/afi-common.xml.i index e28dfe5d7..4d5f56656 100644 --- a/op-mode-definitions/include/bgp/afi-common.xml.i +++ b/op-mode-definitions/include/bgp/afi-common.xml.i @@ -23,6 +23,18 @@ #include + + + Display routes matching the large-community-list + + policy large-community-list + + + ${vyos_op_scripts_dir}/vtysh_wrapper.sh $@ + + #include + + RIB advertisement statistics -- cgit v1.2.3 From c33ea3f44218f2937966bf178cd5dfd0332d4df9 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Tue, 17 Aug 2021 13:45:32 +0200 Subject: policy: T2425: update help test for BGP communities --- interface-definitions/policy.xml.in | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/interface-definitions/policy.xml.in b/interface-definitions/policy.xml.in index 02da76be4..175c912d5 100644 --- a/interface-definitions/policy.xml.in +++ b/interface-definitions/policy.xml.in @@ -139,7 +139,7 @@ - Border Gateway Protocol (BGP) autonomous system path filter + Add a BGP autonomous system path filter txt AS path list name @@ -176,10 +176,10 @@ - Border Gateway Protocol (BGP) autonomous system path filter + Add a BGP community list entry txt - Border Gateway Protocol (BGP) community-list filter + BGP community-list name @@ -236,10 +236,10 @@ - Border Gateway Protocol (BGP) extended community-list filter + Add a BGP extended community list entry txt - Border Gateway Protocol (BGP) extended community-list filter + BGP extended community-list name @@ -281,10 +281,10 @@ - Border Gateway Protocol (BGP) large-community-list filter + Add a BGP large community list entry txt - Border Gateway Protocol (BGP) large-community-list filter + BGP large-community-list name @@ -770,7 +770,7 @@ - Border Gateway Protocol (BGP) origin code to match + BGP origin code to match egp igp incomplete @@ -872,7 +872,7 @@ - Border Gateway Protocol (BGP) aggregator attribute + BGP aggregator attribute @@ -921,13 +921,13 @@ - Border Gateway Protocol (BGP) atomic aggregate attribute + BGP atomic aggregate attribute - Border Gateway Protocol (BGP) communities matching a community-list + BGP communities matching a community-list @@ -1138,7 +1138,7 @@ - Border Gateway Protocol (BGP) local preference attribute + BGP local preference attribute u32:0-4294967295 Local preference value @@ -1208,7 +1208,7 @@ - Border Gateway Protocol (BGP) originator ID attribute + BGP originator ID attribute ipv4 Orignator IP address @@ -1261,7 +1261,7 @@ - Border Gateway Protocol (BGP) weight attribute + BGP weight attribute u32:0-4294967295 BGP weight -- cgit v1.2.3 From 3535340f0b8db459e89f350016f30c1aa99a2f0f Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Tue, 17 Aug 2021 13:46:22 +0200 Subject: policy: T2425: add missing constraints for extended and large community lists --- interface-definitions/policy.xml.in | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/interface-definitions/policy.xml.in b/interface-definitions/policy.xml.in index 175c912d5..b3510a716 100644 --- a/interface-definitions/policy.xml.in +++ b/interface-definitions/policy.xml.in @@ -241,6 +241,10 @@ txt BGP extended community-list name + + ^[a-zA-Z0-9]+$ + + Should be an alphanumeric name #include @@ -286,6 +290,10 @@ txt BGP large-community-list name + + ^[a-zA-Z0-9]+$ + + Should be an alphanumeric name #include -- cgit v1.2.3 From 359f0c97f0e6d75f0b5065811efe9ea3eacb5a37 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Tue, 17 Aug 2021 13:46:33 +0200 Subject: policy: T2425: add missing validator for large-community-lists without the validators FRR commit errors would happen. --- interface-definitions/policy.xml.in | 12 +++++++++-- src/validators/large-community-list | 41 +++++++++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 2 deletions(-) create mode 100755 src/validators/large-community-list diff --git a/interface-definitions/policy.xml.in b/interface-definitions/policy.xml.in index b3510a716..b5ea2058a 100644 --- a/interface-definitions/policy.xml.in +++ b/interface-definitions/policy.xml.in @@ -315,9 +315,17 @@ Regular expression to match against a large community list - <aa:nn:nn> - Large Community value + ASN:NN:NN + BGP large-community-list filter + + + IP:NN:NN + BGP large-community-list filter (IPv4 address format) + + + + Malformed large-community-list diff --git a/src/validators/large-community-list b/src/validators/large-community-list new file mode 100755 index 000000000..6c9a3a148 --- /dev/null +++ b/src/validators/large-community-list @@ -0,0 +1,41 @@ +#!/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 sys + +from vyos.template import is_ipv4 + +pattern = '(.*):(.*):(.*)' + +if __name__ == '__main__': + if len(sys.argv) != 2: + sys.exit(1) + + value = sys.argv[1].split(':') + if not len(value) == 3: + sys.exit(1) + + # Check if the first string is an IPv4 address + if is_ipv4(value[0]): + if value[1].isdigit() and value[2].isdigit(): + sys.exit(0) + # If first string is not an IP address it must be a number + else: + if value[0].isdigit() and value[1].isdigit() and value[2].isdigit(): + sys.exit(0) + + sys.exit(1) -- cgit v1.2.3 From 8155f959b8976133b4723529f28cbad75d24596f Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Tue, 17 Aug 2021 17:44:06 +0200 Subject: policy: T2425: bgp ext-community-list name also supports - and _ --- interface-definitions/policy.xml.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface-definitions/policy.xml.in b/interface-definitions/policy.xml.in index b5ea2058a..cce4186ff 100644 --- a/interface-definitions/policy.xml.in +++ b/interface-definitions/policy.xml.in @@ -242,7 +242,7 @@ BGP extended community-list name - ^[a-zA-Z0-9]+$ + ^[-_a-zA-Z0-9]+$ Should be an alphanumeric name -- cgit v1.2.3 From 46771076d90bc8ebef7d1f9b69a29b8cf73c6000 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Tue, 17 Aug 2021 17:44:21 +0200 Subject: policy: T2425: bgp large-community-list name also supports - and _ --- interface-definitions/policy.xml.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface-definitions/policy.xml.in b/interface-definitions/policy.xml.in index cce4186ff..ce190b046 100644 --- a/interface-definitions/policy.xml.in +++ b/interface-definitions/policy.xml.in @@ -291,7 +291,7 @@ BGP large-community-list name - ^[a-zA-Z0-9]+$ + ^[-_a-zA-Z0-9]+$ Should be an alphanumeric name -- cgit v1.2.3 From 387732762169a11e9502d89b08c728f0c40c4953 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Tue, 17 Aug 2021 18:18:46 +0200 Subject: bgp: T2174: create building block for path-limit which is used in IPv4/IPv6 AFI --- .../include/bgp/afi-path-limit.xml.i | 14 ++++++++++++ .../include/bgp/protocol-common-config.xml.i | 26 ++-------------------- 2 files changed, 16 insertions(+), 24 deletions(-) create mode 100644 interface-definitions/include/bgp/afi-path-limit.xml.i diff --git a/interface-definitions/include/bgp/afi-path-limit.xml.i b/interface-definitions/include/bgp/afi-path-limit.xml.i new file mode 100644 index 000000000..e3d630a57 --- /dev/null +++ b/interface-definitions/include/bgp/afi-path-limit.xml.i @@ -0,0 +1,14 @@ + + + + AS-path hopcount limit + + u32:0-255 + AS path hop count limit + + + + + + + diff --git a/interface-definitions/include/bgp/protocol-common-config.xml.i b/interface-definitions/include/bgp/protocol-common-config.xml.i index 552e85aa4..7ec320b61 100644 --- a/interface-definitions/include/bgp/protocol-common-config.xml.i +++ b/interface-definitions/include/bgp/protocol-common-config.xml.i @@ -490,18 +490,7 @@ - - - AS-path hopcount limit - - u32:0-255 - AS path hop count limit - - - - - - + #include #include @@ -661,18 +650,7 @@ - - - AS-path hopcount limit - - u32:0-255 - AS path hop count limit - - - - - - + #include #include -- cgit v1.2.3 From 1faa8728239cc5309cdaa82453b886710ad83699 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Tue, 17 Aug 2021 19:09:47 +0200 Subject: bgp: T3759: add l3vpn import/export vpn command for IPv4/IPv6 AFI --- data/templates/frr/bgpd.frr.tmpl | 6 +++++ .../include/bgp/afi-export-import.xml.i | 28 ++++++++++++++++++++++ .../include/bgp/protocol-common-config.xml.i | 2 ++ smoketest/scripts/cli/test_protocols_bgp.py | 20 ++++++++++++++++ 4 files changed, 56 insertions(+) create mode 100644 interface-definitions/include/bgp/afi-export-import.xml.i diff --git a/data/templates/frr/bgpd.frr.tmpl b/data/templates/frr/bgpd.frr.tmpl index aa297876b..a21a2fefe 100644 --- a/data/templates/frr/bgpd.frr.tmpl +++ b/data/templates/frr/bgpd.frr.tmpl @@ -308,6 +308,12 @@ router bgp {{ local_as }} {{ 'vrf ' ~ vrf if vrf is defined and vrf is not none {% endfor %} {% endif %} {% endif %} +{% if afi_config.export is defined and afi_config.export.vpn is defined %} + export vpn +{% endif %} +{% if afi_config.import is defined and afi_config.import.vpn is defined %} + import vpn +{% endif %} {% if afi_config.local_install is defined and afi_config.local_install is not none %} {% for interface in afi_config.local_install.interface %} local-install {{ interface }} diff --git a/interface-definitions/include/bgp/afi-export-import.xml.i b/interface-definitions/include/bgp/afi-export-import.xml.i new file mode 100644 index 000000000..ad54c723e --- /dev/null +++ b/interface-definitions/include/bgp/afi-export-import.xml.i @@ -0,0 +1,28 @@ + + + + Export routes from this address-family + + + + + to/from default instance VPN RIB + + + + + + + + Import routes to this address-family + + + + + to/from default instance VPN RIB + + + + + + diff --git a/interface-definitions/include/bgp/protocol-common-config.xml.i b/interface-definitions/include/bgp/protocol-common-config.xml.i index 7ec320b61..da32fcc52 100644 --- a/interface-definitions/include/bgp/protocol-common-config.xml.i +++ b/interface-definitions/include/bgp/protocol-common-config.xml.i @@ -478,6 +478,7 @@ + #include BGP network @@ -638,6 +639,7 @@ + #include Import BGP network/prefix into multicast IPv6 RIB diff --git a/smoketest/scripts/cli/test_protocols_bgp.py b/smoketest/scripts/cli/test_protocols_bgp.py index c3a2ffbf9..22e892e26 100755 --- a/smoketest/scripts/cli/test_protocols_bgp.py +++ b/smoketest/scripts/cli/test_protocols_bgp.py @@ -710,5 +710,25 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase): self.assertIn(f'router bgp {ASN}', frrconfig) self.assertIn(f' neighbor {neighbor} solo', frrconfig) + def test_bgp_14_vpn(self): + remote_asn = str(int(ASN) + 150) + neighbor = '192.0.2.55' + + self.cli_set(base_path + ['local-as', ASN]) + # testing only one AFI is sufficient as it's generic code + self.cli_set(base_path + ['address-family', 'ipv6-unicast', 'export', 'vpn']) + self.cli_set(base_path + ['address-family', 'ipv6-unicast', 'import', 'vpn']) + + # commit changes + self.cli_commit() + + # Verify FRR bgpd configuration + frrconfig = self.getFRRconfig(f'router bgp {ASN}') + self.assertIn(f'router bgp {ASN}', frrconfig) + self.assertIn(f' address-family ipv6 unicast', frrconfig) + self.assertIn(f' export vpn', frrconfig) + self.assertIn(f' import vpn', frrconfig) + self.assertIn(f' exit-address-family', frrconfig) + if __name__ == '__main__': unittest.main(verbosity=2) \ No newline at end of file -- cgit v1.2.3 From 51f7ce31bc60ea9933848bc23efda9386e39a151 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Tue, 17 Aug 2021 22:02:33 +0200 Subject: xml: cleanup - replace format "text" with "txt" as required by the BASH helpers --- interface-definitions/service_webproxy.xml.in | 2 +- interface-definitions/vrrp.xml.in | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/interface-definitions/service_webproxy.xml.in b/interface-definitions/service_webproxy.xml.in index 01a865329..64747420b 100644 --- a/interface-definitions/service_webproxy.xml.in +++ b/interface-definitions/service_webproxy.xml.in @@ -205,7 +205,7 @@ Cache peer options (default: "no-query default") - text + txt Cache peer options diff --git a/interface-definitions/vrrp.xml.in b/interface-definitions/vrrp.xml.in index bb551296f..7bbe25347 100644 --- a/interface-definitions/vrrp.xml.in +++ b/interface-definitions/vrrp.xml.in @@ -45,7 +45,7 @@ VRRP password - text + txt Password string (up to 8 characters) @@ -285,7 +285,7 @@ Sync group member - text + txt VRRP group name -- cgit v1.2.3 From da453ff4a810463febacdba7246d19ac6075b9b7 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Tue, 17 Aug 2021 22:04:23 +0200 Subject: bgp: T2771: adjust verify() logic to common coding style for validation --- src/conf_mode/protocols_bgp.py | 33 ++++++++++++++------------------- 1 file changed, 14 insertions(+), 19 deletions(-) diff --git a/src/conf_mode/protocols_bgp.py b/src/conf_mode/protocols_bgp.py index 9ecfd07fe..fb128db7b 100755 --- a/src/conf_mode/protocols_bgp.py +++ b/src/conf_mode/protocols_bgp.py @@ -221,27 +221,22 @@ def verify(bgp): raise ConfigError(f'Peer-group "{peer_group}" requires remote-as to be set!') # Throw an error if the global administrative distance parameters aren't all filled out. - if dict_search('parameters.distance', bgp) == None: - pass - else: - if dict_search('parameters.distance.global', bgp): - for key in ['external', 'internal', 'local']: - if dict_search(f'parameters.distance.global.{key}', bgp) == None: - raise ConfigError('Missing mandatory configuration option for '\ - f'global administrative distance {key}!') - - # Throw an error if the address family specific administrative distance parameters aren't all filled out. - if dict_search('address_family', bgp) == None: - pass - else: - for address_family_name in dict_search('address_family', bgp): - if dict_search(f'address_family.{address_family_name}.distance', bgp) == None: - pass - else: + if dict_search('parameters.distance.global', bgp) != None: + for key in ['external', 'internal', 'local']: + if dict_search(f'parameters.distance.global.{key}', bgp) == None: + raise ConfigError('Missing mandatory configuration option for '\ + f'global administrative distance {key}!') + + # Address Family specific validation + if 'address_family' in bgp: + for afi, afi_config in bgp['address_family'].items(): + if 'distance' in afi_config: + # Throw an error if the address family specific administrative + # distance parameters aren't all filled out. for key in ['external', 'internal', 'local']: - if dict_search(f'address_family.{address_family_name}.distance.{key}', bgp) == None: + if key not in afi_config['distance']: raise ConfigError('Missing mandatory configuration option for '\ - f'{address_family_name} administrative distance {key}!') + f'{afi} administrative distance {key}!') return None -- cgit v1.2.3 From 053f586fa7a5a69b3b6be81339c73d8550d67fc6 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Tue, 17 Aug 2021 22:05:21 +0200 Subject: bgp: T3759: add l3vpn "import vrf" commands --- data/templates/frr/bgpd.frr.tmpl | 9 +++++++- .../include/bgp/afi-export-import.xml.i | 13 +++++++++++ smoketest/scripts/cli/test_protocols_bgp.py | 25 ++++++++++++++++++---- src/conf_mode/protocols_bgp.py | 11 ++++++++++ 4 files changed, 53 insertions(+), 5 deletions(-) diff --git a/data/templates/frr/bgpd.frr.tmpl b/data/templates/frr/bgpd.frr.tmpl index a21a2fefe..2f2e94ce0 100644 --- a/data/templates/frr/bgpd.frr.tmpl +++ b/data/templates/frr/bgpd.frr.tmpl @@ -311,8 +311,15 @@ router bgp {{ local_as }} {{ 'vrf ' ~ vrf if vrf is defined and vrf is not none {% if afi_config.export is defined and afi_config.export.vpn is defined %} export vpn {% endif %} -{% if afi_config.import is defined and afi_config.import.vpn is defined %} +{% if afi_config.import is defined and afi_config.import is not none %} +{% if afi_config.import.vpn is defined %} import vpn +{% endif %} +{% if afi_config.import.vrf is defined and afi_config.import.vrf is not none %} +{% for vrf in afi_config.import.vrf %} + import vrf {{ vrf }} +{% endfor %} +{% endif %} {% endif %} {% if afi_config.local_install is defined and afi_config.local_install is not none %} {% for interface in afi_config.local_install.interface %} diff --git a/interface-definitions/include/bgp/afi-export-import.xml.i b/interface-definitions/include/bgp/afi-export-import.xml.i index ad54c723e..86817cdb3 100644 --- a/interface-definitions/include/bgp/afi-export-import.xml.i +++ b/interface-definitions/include/bgp/afi-export-import.xml.i @@ -23,6 +23,19 @@ + + + VRF to import from + + txt + VRF instance name + + + vrf name + + + + diff --git a/smoketest/scripts/cli/test_protocols_bgp.py b/smoketest/scripts/cli/test_protocols_bgp.py index 22e892e26..7a470abf9 100755 --- a/smoketest/scripts/cli/test_protocols_bgp.py +++ b/smoketest/scripts/cli/test_protocols_bgp.py @@ -628,6 +628,9 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase): # templates and Jinja2 FRR template. table = '1000' + self.cli_set(base_path + ['local-as', ASN]) + # testing only one AFI is sufficient as it's generic code + for vrf in vrfs: vrf_base = ['vrf', 'name', vrf] self.cli_set(vrf_base + ['table', table]) @@ -636,15 +639,26 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase): self.cli_set(vrf_base + ['protocols', 'bgp', 'route-map', route_map_in]) table = str(int(table) + 1000) + # import VRF routes do main RIB + self.cli_set(base_path + ['address-family', 'ipv6-unicast', 'import', 'vrf', vrf]) + self.cli_commit() + # Verify FRR bgpd configuration + frrconfig = self.getFRRconfig(f'router bgp {ASN}') + self.assertIn(f'router bgp {ASN}', frrconfig) + self.assertIn(f' address-family ipv6 unicast', frrconfig) + + for vrf in vrfs: + self.assertIn(f' import vrf {vrf}', frrconfig) + # Verify FRR bgpd configuration - frrconfig = self.getFRRconfig(f'router bgp {ASN} vrf {vrf}') - self.assertIn(f'router bgp {ASN} vrf {vrf}', frrconfig) - self.assertIn(f' bgp router-id {router_id}', frrconfig) + frr_vrf_config = self.getFRRconfig(f'router bgp {ASN} vrf {vrf}') + self.assertIn(f'router bgp {ASN} vrf {vrf}', frr_vrf_config) + self.assertIn(f' bgp router-id {router_id}', frr_vrf_config) - # CCC: Currently this is not working as FRR() class does not support + # XXX: Currently this is not working as FRR() class does not support # route-maps for multiple vrfs because the modify_section() only works # on lines and not text blocks. # @@ -694,6 +708,7 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase): self.assertIn(f' neighbor {interface} activate', frrconfig) self.assertIn(f' exit-address-family', frrconfig) + def test_bgp_13_solo(self): remote_asn = str(int(ASN) + 150) neighbor = '192.0.2.55' @@ -710,9 +725,11 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase): self.assertIn(f'router bgp {ASN}', frrconfig) self.assertIn(f' neighbor {neighbor} solo', frrconfig) + def test_bgp_14_vpn(self): remote_asn = str(int(ASN) + 150) neighbor = '192.0.2.55' + vrf_name = 'red' self.cli_set(base_path + ['local-as', ASN]) # testing only one AFI is sufficient as it's generic code diff --git a/src/conf_mode/protocols_bgp.py b/src/conf_mode/protocols_bgp.py index fb128db7b..7fc4a2247 100755 --- a/src/conf_mode/protocols_bgp.py +++ b/src/conf_mode/protocols_bgp.py @@ -23,6 +23,7 @@ from vyos.config import Config from vyos.configdict import dict_merge from vyos.configverify import verify_prefix_list from vyos.configverify import verify_route_map +from vyos.configverify import verify_vrf from vyos.template import is_ip from vyos.template import is_interface from vyos.template import render_to_string @@ -238,6 +239,16 @@ def verify(bgp): raise ConfigError('Missing mandatory configuration option for '\ f'{afi} administrative distance {key}!') + if afi in ['ipv4_unicast', 'ipv6_unicast']: + if 'import' in afi_config and 'vrf' in afi_config['import']: + # Check if VRF exists + verify_vrf(afi_config['import']['vrf']) + + # FRR error: please unconfigure vpn to vrf commands before + # using import vrf commands + if 'vpn' in afi_config['import'] or dict_search('export.vpn', afi_config) != None: + raise ConfigError('Please unconfigure VPN to VRF commands before '\ + 'using "import vrf" commands!') return None def generate(bgp): -- cgit v1.2.3 From 63b755f85afb12ae6b609d76ef7d1b4cc2ff5a98 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Wed, 18 Aug 2021 09:06:10 +0200 Subject: policy: T2425: import exact Perl match criteria for large-community-list --- src/validators/large-community-list | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/src/validators/large-community-list b/src/validators/large-community-list index 6c9a3a148..c07268e81 100755 --- a/src/validators/large-community-list +++ b/src/validators/large-community-list @@ -29,13 +29,8 @@ if __name__ == '__main__': if not len(value) == 3: sys.exit(1) - # Check if the first string is an IPv4 address - if is_ipv4(value[0]): - if value[1].isdigit() and value[2].isdigit(): - sys.exit(0) - # If first string is not an IP address it must be a number - else: - if value[0].isdigit() and value[1].isdigit() and value[2].isdigit(): - sys.exit(0) + if not (re.match(pattern, sys.argv[1]) and + (is_ipv4(value[0]) or value[0].isdigit()) and value[1].isdigit()): + sys.exit(1) - sys.exit(1) + sys.exit(0) -- cgit v1.2.3 From bd2669f36c2e5a211b9147c50c98155c385bfae7 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Wed, 18 Aug 2021 09:26:44 +0200 Subject: ndppd: T2518: add missing if statement for translation address in Jinja2 template This triggered a bug during smoketesting. File "/usr/share/vyos/templates/proxy-ndp/ndppd.conf.tmpl", line 24, in top-level template code {% if config.translation.address is defined and config.translation.address | is_ip_network %} File "/usr/lib/python3/dist-packages/jinja2/environment.py", line 471, in getattr return getattr(obj, attribute) jinja2.exceptions.UndefinedError: 'dict object' has no attribute 'translation' --- data/templates/proxy-ndp/ndppd.conf.tmpl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/data/templates/proxy-ndp/ndppd.conf.tmpl b/data/templates/proxy-ndp/ndppd.conf.tmpl index ccd1d37ad..502dab5b8 100644 --- a/data/templates/proxy-ndp/ndppd.conf.tmpl +++ b/data/templates/proxy-ndp/ndppd.conf.tmpl @@ -6,10 +6,10 @@ # interface. # # For some services, such as nat66, because it runs -# stateless, it needs to rely on NDP Proxy to respond +# stateless, it needs to rely on NDP Proxy to respond # to NDP requests. # -# When using nat66 source rules, NDP Proxy needs +# When using nat66 source rules, NDP Proxy needs # to be enabled # ######################################################## @@ -21,7 +21,7 @@ {% if config.outbound_interface not in global.ndppd_interfaces %} {% set global.ndppd_interfaces = global.ndppd_interfaces + [config.outbound_interface] %} {% endif %} -{% if config.translation.address is defined and config.translation.address | is_ip_network %} +{% if config.translation is defined and config.translation.address is defined and config.translation.address | is_ip_network %} {% set global.ndppd_prefixs = global.ndppd_prefixs + [{'interface':config.outbound_interface,'rule':config.translation.address}] %} {% endif %} {% endif %} @@ -41,4 +41,4 @@ proxy {{ interface }} { {% endif %} {% endfor %} } -{% endfor %} +{% endfor %} -- cgit v1.2.3 From c9ad82ef583a1f44f84c7e69124f2a5d868da857 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Wed, 18 Aug 2021 09:30:33 +0200 Subject: nat66: ndppd: T2518: rename Jinja2 template folder to match common naming convention --- data/templates/ndppd/ndppd.conf.tmpl | 44 ++++++++++++++++++++++++++++++++ data/templates/proxy-ndp/ndppd.conf.tmpl | 44 -------------------------------- src/conf_mode/nat66.py | 10 ++++---- 3 files changed, 49 insertions(+), 49 deletions(-) create mode 100644 data/templates/ndppd/ndppd.conf.tmpl delete mode 100644 data/templates/proxy-ndp/ndppd.conf.tmpl diff --git a/data/templates/ndppd/ndppd.conf.tmpl b/data/templates/ndppd/ndppd.conf.tmpl new file mode 100644 index 000000000..502dab5b8 --- /dev/null +++ b/data/templates/ndppd/ndppd.conf.tmpl @@ -0,0 +1,44 @@ +######################################################## +# +# autogenerated by nat66.py +# +# The configuration file must define one upstream +# interface. +# +# For some services, such as nat66, because it runs +# stateless, it needs to rely on NDP Proxy to respond +# to NDP requests. +# +# When using nat66 source rules, NDP Proxy needs +# to be enabled +# +######################################################## + +{% set global = namespace(ndppd_interfaces = [],ndppd_prefixs = []) %} +{% if source is defined and source.rule is defined and source.rule is not none %} +{% for rule, config in source.rule.items() if config.disable is not defined %} +{% if config.outbound_interface is defined %} +{% if config.outbound_interface not in global.ndppd_interfaces %} +{% set global.ndppd_interfaces = global.ndppd_interfaces + [config.outbound_interface] %} +{% endif %} +{% if config.translation is defined and config.translation.address is defined and config.translation.address | is_ip_network %} +{% set global.ndppd_prefixs = global.ndppd_prefixs + [{'interface':config.outbound_interface,'rule':config.translation.address}] %} +{% endif %} +{% endif %} +{% endfor %} +{% endif %} + +{% for interface in global.ndppd_interfaces %} +proxy {{ interface }} { + router yes + timeout 500 + ttl 30000 +{% for map in global.ndppd_prefixs %} +{% if map.interface == interface %} + rule {{ map.rule }} { + static + } +{% endif %} +{% endfor %} +} +{% endfor %} diff --git a/data/templates/proxy-ndp/ndppd.conf.tmpl b/data/templates/proxy-ndp/ndppd.conf.tmpl deleted file mode 100644 index 502dab5b8..000000000 --- a/data/templates/proxy-ndp/ndppd.conf.tmpl +++ /dev/null @@ -1,44 +0,0 @@ -######################################################## -# -# autogenerated by nat66.py -# -# The configuration file must define one upstream -# interface. -# -# For some services, such as nat66, because it runs -# stateless, it needs to rely on NDP Proxy to respond -# to NDP requests. -# -# When using nat66 source rules, NDP Proxy needs -# to be enabled -# -######################################################## - -{% set global = namespace(ndppd_interfaces = [],ndppd_prefixs = []) %} -{% if source is defined and source.rule is defined and source.rule is not none %} -{% for rule, config in source.rule.items() if config.disable is not defined %} -{% if config.outbound_interface is defined %} -{% if config.outbound_interface not in global.ndppd_interfaces %} -{% set global.ndppd_interfaces = global.ndppd_interfaces + [config.outbound_interface] %} -{% endif %} -{% if config.translation is defined and config.translation.address is defined and config.translation.address | is_ip_network %} -{% set global.ndppd_prefixs = global.ndppd_prefixs + [{'interface':config.outbound_interface,'rule':config.translation.address}] %} -{% endif %} -{% endif %} -{% endfor %} -{% endif %} - -{% for interface in global.ndppd_interfaces %} -proxy {{ interface }} { - router yes - timeout 500 - ttl 30000 -{% for map in global.ndppd_prefixs %} -{% if map.interface == interface %} - rule {{ map.rule }} { - static - } -{% endif %} -{% endfor %} -} -{% endfor %} diff --git a/src/conf_mode/nat66.py b/src/conf_mode/nat66.py index e2bd6417d..2314b6623 100755 --- a/src/conf_mode/nat66.py +++ b/src/conf_mode/nat66.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2020 VyOS maintainers and contributors +# Copyright (C) 2020-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 @@ -55,7 +55,7 @@ def get_config(config=None): conf = config else: conf = Config() - + base = ['nat66'] nat = conf.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True) @@ -90,7 +90,7 @@ def get_config(config=None): # be done only once if not get_handler(condensed_json, 'PREROUTING', 'NAT_CONNTRACK'): nat['helper_functions'] = 'add' - + # Retrieve current table handler positions nat['pre_ct_ignore'] = get_handler(condensed_json, 'PREROUTING', 'VYATTA_CT_IGNORE') nat['pre_ct_conntrack'] = get_handler(condensed_json, 'PREROUTING', 'VYATTA_CT_PREROUTING_HOOK') @@ -109,7 +109,7 @@ def verify(nat): if 'helper_functions' in nat and nat['helper_functions'] != 'has': if not (nat['pre_ct_conntrack'] or nat['out_ct_conntrack']): raise Exception('could not determine nftable ruleset handlers') - + if dict_search('source.rule', nat): for rule, config in dict_search('source.rule', nat).items(): err_msg = f'Source NAT66 configuration error in rule {rule}:' @@ -145,7 +145,7 @@ def verify(nat): def generate(nat): render(iptables_nat_config, 'firewall/nftables-nat66.tmpl', nat, permission=0o755) - render(ndppd_config, 'proxy-ndp/ndppd.conf.tmpl', nat, permission=0o755) + render(ndppd_config, 'ndppd/ndppd.conf.tmpl', nat, permission=0o755) return None def apply(nat): -- cgit v1.2.3 From 6b2c3906c3ef1e8a72d6923fcea9cc340e59dd82 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Wed, 18 Aug 2021 09:51:29 +0200 Subject: nptv6: T2518: add missing verify() stage for mandatory translation address --- src/conf_mode/nat66.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/conf_mode/nat66.py b/src/conf_mode/nat66.py index 2314b6623..8a3adc807 100755 --- a/src/conf_mode/nat66.py +++ b/src/conf_mode/nat66.py @@ -124,6 +124,8 @@ def verify(nat): if addr != None: if addr != 'masquerade' and not is_ipv6(addr): raise ConfigError(f'Warning: IPv6 address {addr} is not a valid address') + else: + raise ConfigError(f'{err_msg} translation address not specified') prefix = dict_search('source.prefix', config) if prefix != None: -- cgit v1.2.3 From 39ba9c31853838739659f4842d02a7e931107710 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Wed, 18 Aug 2021 09:52:40 +0200 Subject: nptv6: T2518: remove superfluous else clause on missing outbound-interface --- src/conf_mode/nat66.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/conf_mode/nat66.py b/src/conf_mode/nat66.py index 8a3adc807..f8bc073bb 100755 --- a/src/conf_mode/nat66.py +++ b/src/conf_mode/nat66.py @@ -114,11 +114,10 @@ def verify(nat): for rule, config in dict_search('source.rule', nat).items(): err_msg = f'Source NAT66 configuration error in rule {rule}:' if 'outbound_interface' not in config: - raise ConfigError(f'{err_msg}\n' \ - 'outbound-interface not specified') - else: - if config['outbound_interface'] not in interfaces(): - print(f'WARNING: rule "{rule}" interface "{config["outbound_interface"]}" does not exist on this system') + raise ConfigError(f'{err_msg} outbound-interface not specified') + + if config['outbound_interface'] not in interfaces(): + print(f'WARNING: rule "{rule}" interface "{config["outbound_interface"]}" does not exist on this system') addr = dict_search('translation.address', config) if addr != None: -- cgit v1.2.3 From 2606f95cb07f55716f4b205a0e88be259936703e Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Wed, 18 Aug 2021 09:53:27 +0200 Subject: nat: T2198: remove superfluous else clause on missing outbound-interface --- src/conf_mode/nat.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/conf_mode/nat.py b/src/conf_mode/nat.py index dae958774..59939d0fb 100755 --- a/src/conf_mode/nat.py +++ b/src/conf_mode/nat.py @@ -139,12 +139,10 @@ def verify(nat): for rule, config in dict_search('source.rule', nat).items(): err_msg = f'Source NAT configuration error in rule {rule}:' if 'outbound_interface' not in config: - raise ConfigError(f'{err_msg}\n' \ - 'outbound-interface not specified') - else: - if config['outbound_interface'] not in 'any' and config['outbound_interface'] not in interfaces(): - print(f'WARNING: rule "{rule}" interface "{config["outbound_interface"]}" does not exist on this system') + raise ConfigError(f'{err_msg} outbound-interface not specified') + if config['outbound_interface'] not in 'any' and config['outbound_interface'] not in interfaces(): + print(f'WARNING: rule "{rule}" interface "{config["outbound_interface"]}" does not exist on this system') addr = dict_search('translation.address', config) if addr != None: -- cgit v1.2.3 From c788c26d5b7a1e175afc866a69fb720006a9dc1f Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Wed, 18 Aug 2021 09:53:40 +0200 Subject: smoketest: nat66: migrate setUp -> setUpClass to run code only once ... minor change to speed-up test time --- smoketest/scripts/cli/test_nat66.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/smoketest/scripts/cli/test_nat66.py b/smoketest/scripts/cli/test_nat66.py index dca92c97d..7721105e0 100755 --- a/smoketest/scripts/cli/test_nat66.py +++ b/smoketest/scripts/cli/test_nat66.py @@ -31,10 +31,13 @@ src_path = base_path + ['source'] dst_path = base_path + ['destination'] class TestNAT66(VyOSUnitTestSHIM.TestCase): - def setUp(self): + @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 :) - self.cli_delete(base_path) + cls.cli_delete(cls, base_path) def tearDown(self): self.cli_delete(base_path) @@ -183,4 +186,4 @@ class TestNAT66(VyOSUnitTestSHIM.TestCase): self.cli_commit() if __name__ == '__main__': - unittest.main(verbosity=2) + unittest.main(verbosity=2, failfast=True) -- cgit v1.2.3 From 8c1bc03f7088e49a7ca7b1dfc3612e4975c1371f Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Wed, 18 Aug 2021 09:54:21 +0200 Subject: smoketest: ssh: migrate setUp -> setUpClass to run code only once ... minor change to speed-up test time --- smoketest/scripts/cli/test_service_ssh.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/smoketest/scripts/cli/test_service_ssh.py b/smoketest/scripts/cli/test_service_ssh.py index c76f709b1..ded4d8301 100755 --- a/smoketest/scripts/cli/test_service_ssh.py +++ b/smoketest/scripts/cli/test_service_ssh.py @@ -41,10 +41,13 @@ def get_config_value(key): return tmp class TestServiceSSH(VyOSUnitTestSHIM.TestCase): - def setUp(self): + @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 :) - self.cli_delete(base_path) + cls.cli_delete(cls, base_path) def tearDown(self): # delete testing SSH config -- cgit v1.2.3 From 4058e389f1ca1398ad4fca790839c638a58f7aa3 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Wed, 18 Aug 2021 10:55:53 +0200 Subject: bgp: T3759: import/export is for AFI "ipv4 unicast" not "ipv4 multicast" --- interface-definitions/include/bgp/protocol-common-config.xml.i | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface-definitions/include/bgp/protocol-common-config.xml.i b/interface-definitions/include/bgp/protocol-common-config.xml.i index da32fcc52..6a4e1de3f 100644 --- a/interface-definitions/include/bgp/protocol-common-config.xml.i +++ b/interface-definitions/include/bgp/protocol-common-config.xml.i @@ -93,6 +93,7 @@ + #include BGP network @@ -639,7 +640,6 @@ - #include Import BGP network/prefix into multicast IPv6 RIB -- cgit v1.2.3 From 73c0e87109855f9b3372540b4ace03c6fb517c2a Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Wed, 18 Aug 2021 11:12:08 +0200 Subject: bgp: T3759: add l3vpn "label vpn export" commands Add the following new commands: * set protocols bgp address-family ipv4-unicast label vpn export (auto | 0-1048575) * set protocols bgp address-family ipv6-unicast label vpn export (auto | 0-1048575) --- data/templates/frr/bgpd.frr.tmpl | 3 ++ interface-definitions/include/bgp/afi-label.xml.i | 36 ++++++++++++++++++++++ .../include/bgp/protocol-common-config.xml.i | 6 ++-- smoketest/scripts/cli/test_protocols_bgp.py | 7 +++-- 4 files changed, 48 insertions(+), 4 deletions(-) create mode 100644 interface-definitions/include/bgp/afi-label.xml.i diff --git a/data/templates/frr/bgpd.frr.tmpl b/data/templates/frr/bgpd.frr.tmpl index 2f2e94ce0..3ee6a5cb3 100644 --- a/data/templates/frr/bgpd.frr.tmpl +++ b/data/templates/frr/bgpd.frr.tmpl @@ -321,6 +321,9 @@ router bgp {{ local_as }} {{ 'vrf ' ~ vrf if vrf is defined and vrf is not none {% endfor %} {% endif %} {% endif %} +{% if afi_config.label is defined and afi_config.label.vpn is defined and afi_config.label.vpn.export is defined and afi_config.label.vpn.export is not none %} + label vpn export {{ afi_config.label.vpn.export }} +{% endif %} {% if afi_config.local_install is defined and afi_config.local_install is not none %} {% for interface in afi_config.local_install.interface %} local-install {{ interface }} diff --git a/interface-definitions/include/bgp/afi-label.xml.i b/interface-definitions/include/bgp/afi-label.xml.i new file mode 100644 index 000000000..f8cf57a9c --- /dev/null +++ b/interface-definitions/include/bgp/afi-label.xml.i @@ -0,0 +1,36 @@ + + + + Label value for VRF + + + + + Between current address-family and vpn + + + + + For routes leaked from current address-family to VPN + + auto + + + auto + Automatically assign a label + + + u32:0-1048575 + Label Value + + + + ^(auto)$ + + + + + + + + diff --git a/interface-definitions/include/bgp/protocol-common-config.xml.i b/interface-definitions/include/bgp/protocol-common-config.xml.i index 6a4e1de3f..58e3c5798 100644 --- a/interface-definitions/include/bgp/protocol-common-config.xml.i +++ b/interface-definitions/include/bgp/protocol-common-config.xml.i @@ -94,6 +94,8 @@ #include + #include + #include BGP network @@ -115,7 +117,6 @@ #include - #include Redistribute routes from other protocols into BGP @@ -480,6 +481,8 @@ #include + #include + #include BGP network @@ -496,7 +499,6 @@ #include - #include Redistribute routes from other protocols into BGP diff --git a/smoketest/scripts/cli/test_protocols_bgp.py b/smoketest/scripts/cli/test_protocols_bgp.py index 7a470abf9..d3d061229 100755 --- a/smoketest/scripts/cli/test_protocols_bgp.py +++ b/smoketest/scripts/cli/test_protocols_bgp.py @@ -730,11 +730,14 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase): remote_asn = str(int(ASN) + 150) neighbor = '192.0.2.55' vrf_name = 'red' + label = 'auto' self.cli_set(base_path + ['local-as', ASN]) # testing only one AFI is sufficient as it's generic code - self.cli_set(base_path + ['address-family', 'ipv6-unicast', 'export', 'vpn']) - self.cli_set(base_path + ['address-family', 'ipv6-unicast', 'import', 'vpn']) + for afi in ['ipv4-unicast', 'ipv6-unicast']: + self.cli_set(base_path + ['address-family', afi, 'export', 'vpn']) + self.cli_set(base_path + ['address-family', afi, 'import', 'vpn']) + self.cli_set(base_path + ['address-family', afi, 'label', 'vpn', 'export', label]) # commit changes self.cli_commit() -- cgit v1.2.3 From 231095f95df129ef9e051ec218a1c3fe9e99cd98 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Wed, 18 Aug 2021 12:44:15 +0200 Subject: bgp: T2387: fix indention when writing address-family config --- data/templates/frr/bgpd.frr.tmpl | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/data/templates/frr/bgpd.frr.tmpl b/data/templates/frr/bgpd.frr.tmpl index 3ee6a5cb3..60ec566b5 100644 --- a/data/templates/frr/bgpd.frr.tmpl +++ b/data/templates/frr/bgpd.frr.tmpl @@ -294,17 +294,17 @@ router bgp {{ local_as }} {{ 'vrf ' ~ vrf if vrf is defined and vrf is not none {% if afi_config.advertise is defined and afi_config.advertise is not none %} {% for adv_afi, adv_afi_config in afi_config.advertise.items() %} {% if adv_afi_config.unicast is defined and adv_afi_config.unicast is not none %} - advertise {{ adv_afi }} unicast {{ 'route-map ' ~ adv_afi_config.unicast.route_map if adv_afi_config.unicast.route_map is defined }} + advertise {{ adv_afi }} unicast {{ 'route-map ' ~ adv_afi_config.unicast.route_map if adv_afi_config.unicast.route_map is defined }} {% endif %} {% endfor %} {% endif %} {% if afi_config.distance is defined and afi_config.distance is not none %} {% if afi_config.distance is defined and afi_config.distance.external is defined and afi_config.distance.internal is defined and afi_config.distance.local is defined %} - distance bgp {{ afi_config.distance.external }} {{ afi_config.distance.internal }} {{ afi_config.distance.local }} + distance bgp {{ afi_config.distance.external }} {{ afi_config.distance.internal }} {{ afi_config.distance.local }} {% endif %} {% if afi_config.distance.prefix is defined and afi_config.distance.prefix is not none %} {% for prefix in afi_config.distance.prefix %} - distance {{ afi_config.distance.prefix[prefix].distance }} {{ prefix }} + distance {{ afi_config.distance.prefix[prefix].distance }} {{ prefix }} {% endfor %} {% endif %} {% endif %} @@ -326,7 +326,7 @@ router bgp {{ local_as }} {{ 'vrf ' ~ vrf if vrf is defined and vrf is not none {% endif %} {% if afi_config.local_install is defined and afi_config.local_install is not none %} {% for interface in afi_config.local_install.interface %} - local-install {{ interface }} + local-install {{ interface }} {% endfor %} {% endif %} {% if afi_config.advertise_all_vni is defined %} @@ -342,13 +342,13 @@ router bgp {{ local_as }} {{ 'vrf ' ~ vrf if vrf is defined and vrf is not none advertise-svi-ip {% endif %} {% if afi_config.rt_auto_derive is defined %} - autort rfc8365-compatible + autort rfc8365-compatible {% endif %} {% if afi_config.flooding is defined and afi_config.flooding.disable is defined %} - flooding disable + flooding disable {% endif %} {% if afi_config.flooding is defined and afi_config.flooding.head_end_replication is defined %} - flooding head-end-replication + flooding head-end-replication {% endif %} {% if afi_config.rd is defined and afi_config.rd is not none %} rd {{ afi_config.rd }} -- cgit v1.2.3 From 1cc2ac26106f8efad6defaba9ba4d1296d75cf1f Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Wed, 18 Aug 2021 12:47:01 +0200 Subject: bgp: T3759: add l3vpn "rd" route-distinguisher commands Add the following new commands: * set protocols bgp address-family ipv4-unicast rd vpn export * set protocols bgp address-family ipv6-unicast rd vpn export --- data/templates/frr/bgpd.frr.tmpl | 7 ++++-- interface-definitions/include/bgp/afi-rd.xml.i | 28 ++++++++++++++++++++++ .../include/bgp/protocol-common-config.xml.i | 2 ++ .../include/bgp/route-distinguisher.xml.i | 2 +- smoketest/scripts/cli/test_protocols_bgp.py | 16 +++++++++---- 5 files changed, 48 insertions(+), 7 deletions(-) create mode 100644 interface-definitions/include/bgp/afi-rd.xml.i diff --git a/data/templates/frr/bgpd.frr.tmpl b/data/templates/frr/bgpd.frr.tmpl index 60ec566b5..f3579ccd7 100644 --- a/data/templates/frr/bgpd.frr.tmpl +++ b/data/templates/frr/bgpd.frr.tmpl @@ -257,6 +257,9 @@ router bgp {{ local_as }} {{ 'vrf ' ~ vrf if vrf is defined and vrf is not none address-family ipv6 flowspec {% elif afi == 'l2vpn_evpn' %} address-family l2vpn evpn +{% if afi_config.rd is defined and afi_config.rd is not none %} + rd {{ afi_config.rd }} +{% endif %} {% endif %} {% if afi_config.aggregate_address is defined and afi_config.aggregate_address is not none %} {% for ip in afi_config.aggregate_address %} @@ -350,8 +353,8 @@ router bgp {{ local_as }} {{ 'vrf ' ~ vrf if vrf is defined and vrf is not none {% if afi_config.flooding is defined and afi_config.flooding.head_end_replication is defined %} flooding head-end-replication {% endif %} -{% if afi_config.rd is defined and afi_config.rd is not none %} - rd {{ afi_config.rd }} +{% if afi_config.rd is defined and afi_config.rd.vpn is defined and afi_config.rd.vpn.export is defined %} + rd vpn export {{ afi_config.rd.vpn.export }} {% endif %} {% if afi_config.route_target is defined and afi_config.route_target is not none %} {% if afi_config.route_target.both is defined and afi_config.route_target.both is not none %} diff --git a/interface-definitions/include/bgp/afi-rd.xml.i b/interface-definitions/include/bgp/afi-rd.xml.i new file mode 100644 index 000000000..c4d29268c --- /dev/null +++ b/interface-definitions/include/bgp/afi-rd.xml.i @@ -0,0 +1,28 @@ + + + + Specify route distinguisher + + + + + Between current address-family and VPN + + + + + For routes leaked from current address-family to VPN + + ASN:NN_OR_IP-ADDRESS:NN + 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/interface-definitions/include/bgp/protocol-common-config.xml.i b/interface-definitions/include/bgp/protocol-common-config.xml.i index 58e3c5798..53be8b553 100644 --- a/interface-definitions/include/bgp/protocol-common-config.xml.i +++ b/interface-definitions/include/bgp/protocol-common-config.xml.i @@ -117,6 +117,7 @@ #include + #include Redistribute routes from other protocols into BGP @@ -499,6 +500,7 @@ #include + #include Redistribute routes from other protocols into BGP diff --git a/interface-definitions/include/bgp/route-distinguisher.xml.i b/interface-definitions/include/bgp/route-distinguisher.xml.i index fdfbe7076..6d0aa3ef1 100644 --- a/interface-definitions/include/bgp/route-distinguisher.xml.i +++ b/interface-definitions/include/bgp/route-distinguisher.xml.i @@ -3,7 +3,7 @@ Route Distinguisher - txt + ASN:NN_OR_IP-ADDRESS:NN Route Distinguisher, (x.x.x.x:yyy|xxxx:yyyy) diff --git a/smoketest/scripts/cli/test_protocols_bgp.py b/smoketest/scripts/cli/test_protocols_bgp.py index d3d061229..4149b0bdd 100755 --- a/smoketest/scripts/cli/test_protocols_bgp.py +++ b/smoketest/scripts/cli/test_protocols_bgp.py @@ -731,6 +731,7 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase): neighbor = '192.0.2.55' vrf_name = 'red' label = 'auto' + rd = f'{neighbor}:{ASN}' self.cli_set(base_path + ['local-as', ASN]) # testing only one AFI is sufficient as it's generic code @@ -739,16 +740,23 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase): self.cli_set(base_path + ['address-family', afi, 'import', 'vpn']) self.cli_set(base_path + ['address-family', afi, 'label', 'vpn', 'export', label]) + self.cli_set(base_path + ['address-family', 'ipv4-unicast', 'rd', 'vpn', 'export', rd]) + # commit changes self.cli_commit() # Verify FRR bgpd configuration frrconfig = self.getFRRconfig(f'router bgp {ASN}') self.assertIn(f'router bgp {ASN}', frrconfig) - self.assertIn(f' address-family ipv6 unicast', frrconfig) - self.assertIn(f' export vpn', frrconfig) - self.assertIn(f' import vpn', frrconfig) - self.assertIn(f' exit-address-family', frrconfig) + for afi in ['ipv4', 'ipv6']: + self.assertIn(f' address-family {afi} unicast', frrconfig) + self.assertIn(f' export vpn', frrconfig) + self.assertIn(f' import vpn', frrconfig) + self.assertIn(f' label vpn export {label}', frrconfig) + self.assertIn(f' exit-address-family', frrconfig) + + self.assertIn(f' address-family ipv4 unicast', frrconfig) + self.assertIn(f' rd vpn export {rd}', frrconfig) if __name__ == '__main__': unittest.main(verbosity=2) \ No newline at end of file -- cgit v1.2.3 From ef3f17d7e7fb34c5309e3b3e80c398f6b95cebb1 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Wed, 18 Aug 2021 12:47:31 +0200 Subject: bgp: T3759: fix indention when writing address-family config --- data/templates/frr/bgpd.frr.tmpl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/data/templates/frr/bgpd.frr.tmpl b/data/templates/frr/bgpd.frr.tmpl index f3579ccd7..51235e4cb 100644 --- a/data/templates/frr/bgpd.frr.tmpl +++ b/data/templates/frr/bgpd.frr.tmpl @@ -312,20 +312,20 @@ router bgp {{ local_as }} {{ 'vrf ' ~ vrf if vrf is defined and vrf is not none {% endif %} {% endif %} {% if afi_config.export is defined and afi_config.export.vpn is defined %} - export vpn + export vpn {% endif %} {% if afi_config.import is defined and afi_config.import is not none %} {% if afi_config.import.vpn is defined %} - import vpn + import vpn {% endif %} {% if afi_config.import.vrf is defined and afi_config.import.vrf is not none %} {% for vrf in afi_config.import.vrf %} - import vrf {{ vrf }} + import vrf {{ vrf }} {% endfor %} {% endif %} {% endif %} {% if afi_config.label is defined and afi_config.label.vpn is defined and afi_config.label.vpn.export is defined and afi_config.label.vpn.export is not none %} - label vpn export {{ afi_config.label.vpn.export }} + label vpn export {{ afi_config.label.vpn.export }} {% endif %} {% if afi_config.local_install is defined and afi_config.local_install is not none %} {% for interface in afi_config.local_install.interface %} -- cgit v1.2.3 From 0df3e23d57192a1e1e9d12c00e3ca532138eed87 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Wed, 18 Aug 2021 12:48:22 +0200 Subject: bgp: T3759: fix "label vpn" help string --- interface-definitions/include/bgp/afi-label.xml.i | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface-definitions/include/bgp/afi-label.xml.i b/interface-definitions/include/bgp/afi-label.xml.i index f8cf57a9c..f7a1f609f 100644 --- a/interface-definitions/include/bgp/afi-label.xml.i +++ b/interface-definitions/include/bgp/afi-label.xml.i @@ -6,7 +6,7 @@ - Between current address-family and vpn + Between current address-family and VPN -- cgit v1.2.3 From 8ce0cd9f09940f8c365ea1cc76b1b14197fdfd2a Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Wed, 18 Aug 2021 12:53:16 +0200 Subject: bgp: evpn: T1513: re-structure route-target XML blocks Make the both, export and import XML block re-usable by the IPv4 AFI. --- .../include/bgp/afi-l2vpn-common.xml.i | 11 +++++- .../include/bgp/route-target-both.xml.i | 14 +++++++ .../include/bgp/route-target-export.xml.i | 14 +++++++ .../include/bgp/route-target-import.xml.i | 14 +++++++ .../include/bgp/route-target.xml.i | 45 ---------------------- 5 files changed, 52 insertions(+), 46 deletions(-) create mode 100644 interface-definitions/include/bgp/route-target-both.xml.i create mode 100644 interface-definitions/include/bgp/route-target-export.xml.i create mode 100644 interface-definitions/include/bgp/route-target-import.xml.i delete mode 100644 interface-definitions/include/bgp/route-target.xml.i diff --git a/interface-definitions/include/bgp/afi-l2vpn-common.xml.i b/interface-definitions/include/bgp/afi-l2vpn-common.xml.i index aaa69e6c8..a9a833851 100644 --- a/interface-definitions/include/bgp/afi-l2vpn-common.xml.i +++ b/interface-definitions/include/bgp/afi-l2vpn-common.xml.i @@ -12,5 +12,14 @@ #include -#include + + + Route Target + + + #include + #include + #include + + diff --git a/interface-definitions/include/bgp/route-target-both.xml.i b/interface-definitions/include/bgp/route-target-both.xml.i new file mode 100644 index 000000000..2381f420f --- /dev/null +++ b/interface-definitions/include/bgp/route-target-both.xml.i @@ -0,0 +1,14 @@ + + + + Route Target both import and export + + txt + Route target (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/interface-definitions/include/bgp/route-target-export.xml.i b/interface-definitions/include/bgp/route-target-export.xml.i new file mode 100644 index 000000000..0542000a5 --- /dev/null +++ b/interface-definitions/include/bgp/route-target-export.xml.i @@ -0,0 +1,14 @@ + + + + Route Target export + + txt + Route target (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/interface-definitions/include/bgp/route-target-import.xml.i b/interface-definitions/include/bgp/route-target-import.xml.i new file mode 100644 index 000000000..6a954885c --- /dev/null +++ b/interface-definitions/include/bgp/route-target-import.xml.i @@ -0,0 +1,14 @@ + + + + Route Target import + + txt + Route target (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/interface-definitions/include/bgp/route-target.xml.i b/interface-definitions/include/bgp/route-target.xml.i deleted file mode 100644 index 674b6db15..000000000 --- a/interface-definitions/include/bgp/route-target.xml.i +++ /dev/null @@ -1,45 +0,0 @@ - - - - Route Target - - - - - Route Target both import and export - - txt - Route target (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}$ - - - - - - Route Target export - - txt - Route target (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}$ - - - - - - Route Target import - - txt - Route target (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}$ - - - - - - -- cgit v1.2.3 From cbf1998ae9529be43c2605a5b771363aa496ff40 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Wed, 18 Aug 2021 12:54:26 +0200 Subject: bgp: T3759: add l3vpn "route-target vpn" commands Add the following new commands: * set protocols bgp address-family ipv4-unicast route-target vpn both 1.1.1.1:100 * set protocols bgp address-family ipv4-unicast route-target vpn export 1.1.1.1:100 * set protocols bgp address-family ipv4-unicast route-target vpn import 1.1.1.1:100 --- data/templates/frr/bgpd.frr.tmpl | 21 +++++++++++++++++---- .../include/bgp/protocol-common-config.xml.i | 17 +++++++++++++++++ smoketest/scripts/cli/test_protocols_bgp.py | 6 ++++++ 3 files changed, 40 insertions(+), 4 deletions(-) diff --git a/data/templates/frr/bgpd.frr.tmpl b/data/templates/frr/bgpd.frr.tmpl index 51235e4cb..7adb007c8 100644 --- a/data/templates/frr/bgpd.frr.tmpl +++ b/data/templates/frr/bgpd.frr.tmpl @@ -357,14 +357,27 @@ router bgp {{ local_as }} {{ 'vrf ' ~ vrf if vrf is defined and vrf is not none rd vpn export {{ afi_config.rd.vpn.export }} {% endif %} {% if afi_config.route_target is defined and afi_config.route_target is not none %} +{% if afi_config.route_target.vpn is defined and afi_config.route_target.vpn is not none %} +{% if afi_config.route_target.vpn.both is defined and afi_config.route_target.vpn.both is not none %} + route-target vpn both {{ afi_config.route_target.vpn.both }} +{% else %} +{% if afi_config.route_target.vpn.export is defined and afi_config.route_target.vpn.export is not none %} + route-target vpn export {{ afi_config.route_target.vpn.export }} +{% endif %} +{% if afi_config.route_target.vpn.import is defined and afi_config.route_target.vpn.import is not none %} + route-target vpn import {{ afi_config.route_target.vpn.import }} +{% endif %} +{% endif %} +{% endif %} {% if afi_config.route_target.both is defined and afi_config.route_target.both is not none %} route-target both {{ afi_config.route_target.both }} -{% endif %} -{% if afi_config.route_target.export is defined and afi_config.route_target.export is not none %} +{% else %} +{% if afi_config.route_target.export is defined and afi_config.route_target.export is not none %} route-target export {{ afi_config.route_target.export }} -{% endif %} -{% if afi_config.route_target.import is defined and afi_config.route_target.import is not none %} +{% endif %} +{% if afi_config.route_target.import is defined and afi_config.route_target.import is not none %} route-target import {{ afi_config.route_target.import }} +{% endif %} {% endif %} {% endif %} {% if afi_config.vni is defined and afi_config.vni is not none %} diff --git a/interface-definitions/include/bgp/protocol-common-config.xml.i b/interface-definitions/include/bgp/protocol-common-config.xml.i index 53be8b553..6fb9adf93 100644 --- a/interface-definitions/include/bgp/protocol-common-config.xml.i +++ b/interface-definitions/include/bgp/protocol-common-config.xml.i @@ -118,6 +118,23 @@ #include + + + Specify route distinguisher + + + + + Between current address-family and VPN + + + #include + #include + #include + + + + Redistribute routes from other protocols into BGP diff --git a/smoketest/scripts/cli/test_protocols_bgp.py b/smoketest/scripts/cli/test_protocols_bgp.py index 4149b0bdd..d1492e831 100755 --- a/smoketest/scripts/cli/test_protocols_bgp.py +++ b/smoketest/scripts/cli/test_protocols_bgp.py @@ -732,6 +732,8 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase): vrf_name = 'red' label = 'auto' rd = f'{neighbor}:{ASN}' + rt_export = f'{neighbor}:1002' + rt_import = f'{neighbor}:1003' self.cli_set(base_path + ['local-as', ASN]) # testing only one AFI is sufficient as it's generic code @@ -741,6 +743,8 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase): self.cli_set(base_path + ['address-family', afi, 'label', 'vpn', 'export', label]) self.cli_set(base_path + ['address-family', 'ipv4-unicast', 'rd', 'vpn', 'export', rd]) + self.cli_set(base_path + ['address-family', 'ipv4-unicast', 'route-target', 'vpn', 'export', rt_export]) + self.cli_set(base_path + ['address-family', 'ipv4-unicast', 'route-target', 'vpn', 'import', rt_import]) # commit changes self.cli_commit() @@ -757,6 +761,8 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase): self.assertIn(f' address-family ipv4 unicast', frrconfig) self.assertIn(f' rd vpn export {rd}', frrconfig) + self.assertIn(f' rt vpn export {rt_export}', frrconfig) + self.assertIn(f' rt vpn import {rt_import}', frrconfig) if __name__ == '__main__': unittest.main(verbosity=2) \ No newline at end of file -- cgit v1.2.3 From 522402ecb797222e69f6017a84d1b65cfdc5c70e Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Wed, 18 Aug 2021 13:58:04 +0200 Subject: bgp: evpn: T1513: fix indention when writing address-family config --- data/templates/frr/bgpd.frr.tmpl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/data/templates/frr/bgpd.frr.tmpl b/data/templates/frr/bgpd.frr.tmpl index 7adb007c8..143960e6b 100644 --- a/data/templates/frr/bgpd.frr.tmpl +++ b/data/templates/frr/bgpd.frr.tmpl @@ -370,13 +370,13 @@ router bgp {{ local_as }} {{ 'vrf ' ~ vrf if vrf is defined and vrf is not none {% endif %} {% endif %} {% if afi_config.route_target.both is defined and afi_config.route_target.both is not none %} - route-target both {{ afi_config.route_target.both }} + route-target both {{ afi_config.route_target.both }} {% else %} {% if afi_config.route_target.export is defined and afi_config.route_target.export is not none %} - route-target export {{ afi_config.route_target.export }} + route-target export {{ afi_config.route_target.export }} {% endif %} {% if afi_config.route_target.import is defined and afi_config.route_target.import is not none %} - route-target import {{ afi_config.route_target.import }} + route-target import {{ afi_config.route_target.import }} {% endif %} {% endif %} {% endif %} -- cgit v1.2.3 From 47261d051759a743a36089aa0be7cecd348710c1 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Wed, 18 Aug 2021 14:06:00 +0200 Subject: bgp: evpn: T1513: VNI rt and rd are only supported under EVPN VRF --- src/conf_mode/protocols_bgp.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/conf_mode/protocols_bgp.py b/src/conf_mode/protocols_bgp.py index 7fc4a2247..bc7acaf6c 100755 --- a/src/conf_mode/protocols_bgp.py +++ b/src/conf_mode/protocols_bgp.py @@ -249,6 +249,16 @@ def verify(bgp): if 'vpn' in afi_config['import'] or dict_search('export.vpn', afi_config) != None: raise ConfigError('Please unconfigure VPN to VRF commands before '\ 'using "import vrf" commands!') + + if afi in ['l2vpn_evpn'] and 'vrf' not in bgp: + # Some L2VPN EVPN AFI options are only supported under VRF + if 'vni' in afi_config: + for vni, vni_config in afi_config['vni'].items(): + if 'rd' in vni_config: + raise ConfigError('VNI route-distinguisher is only supported under EVPN VRF') + if 'route_target' in vni_config: + raise ConfigError('VNI route-target is only supported under EVPN VRF') + return None def generate(bgp): -- cgit v1.2.3 From b30ca1c18cdb6d01a4550fb623ed3489f33efec3 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Wed, 18 Aug 2021 17:33:11 +0200 Subject: Debian: containers: T2216: add missing dependency on uidmap Fixes the following error: vyos@vyos:~$ podman logs 2511d118563f WARN[0000] The cgroupv2 manager is set to systemd but there is no systemd user session available WARN[0000] For using systemd, you may need to login using an user session WARN[0000] Alternatively, you can enable lingering with: `loginctl enable-linger 1002` (possibly as root) WARN[0000] Falling back to --cgroup-manager=cgroupfs Error: cannot find newuidmap: exec: "newuidmap": executable file not found in $PATH --- debian/control | 1 + 1 file changed, 1 insertion(+) diff --git a/debian/control b/debian/control index c5cbeb7d4..f3a26e73e 100644 --- a/debian/control +++ b/debian/control @@ -156,6 +156,7 @@ Depends: traceroute, tuned, udp-broadcast-relay, + uidmap, usb-modeswitch, usbutils, vyatta-bash, -- cgit v1.2.3 From 7a873eb6ecfa3bbc7b581a034d641e73429e1b6b Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Thu, 19 Aug 2021 12:02:46 +0200 Subject: ipsec: T3764: bugfix missing IKE and ESP lifetime values During the migration the IKE and ESP key/re-key lifetime settings got lost in translation. This is now fixed and the values/defaults correspond to VyOS 1.3. --- data/templates/ipsec/swanctl/peer.tmpl | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/data/templates/ipsec/swanctl/peer.tmpl b/data/templates/ipsec/swanctl/peer.tmpl index dd29ea7d4..7bb41d523 100644 --- a/data/templates/ipsec/swanctl/peer.tmpl +++ b/data/templates/ipsec/swanctl/peer.tmpl @@ -17,10 +17,10 @@ {% if ike.key_exchange is defined and ike.key_exchange == "ikev1" and ike.mode is defined and ike.mode == "aggressive" %} aggressive = yes {% endif %} + rekey_time = {{ ike.lifetime }} mobike = {{ "yes" if ike.mobike is not defined or ike.mobike == "enable" else "no" }} {% if peer[0:1] == '@' %} keyingtries = 0 - rekey_time = 0 reauth_time = 0 {% elif peer_conf.connection_type is not defined or peer_conf.connection_type == 'initiate' %} keyingtries = 0 @@ -57,6 +57,7 @@ {% set vti_esp = esp_group[ peer_conf.vti.esp_group ] if peer_conf.vti.esp_group is defined else esp_group[ peer_conf.default_esp_group ] %} peer_{{ name }}_vti { esp_proposals = {{ vti_esp | get_esp_ike_cipher | join(',') }} + life_time = {{ vti_esp.lifetime }} local_ts = 0.0.0.0/0,::/0 remote_ts = 0.0.0.0/0,::/0 updown = "/etc/ipsec.d/vti-up-down {{ peer_conf.vti.bind }} {{ peer_conf.dhcp_interface if peer_conf.dhcp_interface is defined else 'no' }}" @@ -87,6 +88,7 @@ {% set remote_suffix = '[{0}/{1}]'.format(proto, remote_port) if proto or remote_port else '' %} peer_{{ name }}_tunnel_{{ tunnel_id }} { esp_proposals = {{ tunnel_esp | get_esp_ike_cipher | join(',') }} + life_time = {{ tunnel_esp.lifetime }} {% if tunnel_esp.mode is not defined or tunnel_esp.mode == 'tunnel' %} {% if tunnel_conf.local is defined and tunnel_conf.local.prefix is defined %} {% set local_prefix = tunnel_conf.local.prefix if 'any' not in tunnel_conf.local.prefix else ['0.0.0.0/0', '::/0'] %} -- cgit v1.2.3 From 1d221c04b225d8082c4472361c1612e9ad8727c8 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Thu, 19 Aug 2021 12:33:15 +0200 Subject: ipsec: T3764: add additional quantifier for IKE and ESP lifetime Commit 7a873eb6 ("ipsec: T3764: bugfix missing IKE and ESP lifetime values") re-added the lost in translation IKE/ESP rekey values. But it did not specify the unit, which is s(econd). --- data/templates/ipsec/swanctl/peer.tmpl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/data/templates/ipsec/swanctl/peer.tmpl b/data/templates/ipsec/swanctl/peer.tmpl index 7bb41d523..019f9e0d7 100644 --- a/data/templates/ipsec/swanctl/peer.tmpl +++ b/data/templates/ipsec/swanctl/peer.tmpl @@ -17,7 +17,7 @@ {% if ike.key_exchange is defined and ike.key_exchange == "ikev1" and ike.mode is defined and ike.mode == "aggressive" %} aggressive = yes {% endif %} - rekey_time = {{ ike.lifetime }} + rekey_time = {{ ike.lifetime }}s mobike = {{ "yes" if ike.mobike is not defined or ike.mobike == "enable" else "no" }} {% if peer[0:1] == '@' %} keyingtries = 0 @@ -57,7 +57,7 @@ {% set vti_esp = esp_group[ peer_conf.vti.esp_group ] if peer_conf.vti.esp_group is defined else esp_group[ peer_conf.default_esp_group ] %} peer_{{ name }}_vti { esp_proposals = {{ vti_esp | get_esp_ike_cipher | join(',') }} - life_time = {{ vti_esp.lifetime }} + life_time = {{ vti_esp.lifetime }}s local_ts = 0.0.0.0/0,::/0 remote_ts = 0.0.0.0/0,::/0 updown = "/etc/ipsec.d/vti-up-down {{ peer_conf.vti.bind }} {{ peer_conf.dhcp_interface if peer_conf.dhcp_interface is defined else 'no' }}" @@ -88,7 +88,7 @@ {% set remote_suffix = '[{0}/{1}]'.format(proto, remote_port) if proto or remote_port else '' %} peer_{{ name }}_tunnel_{{ tunnel_id }} { esp_proposals = {{ tunnel_esp | get_esp_ike_cipher | join(',') }} - life_time = {{ tunnel_esp.lifetime }} + life_time = {{ tunnel_esp.lifetime }}s {% if tunnel_esp.mode is not defined or tunnel_esp.mode == 'tunnel' %} {% if tunnel_conf.local is defined and tunnel_conf.local.prefix is defined %} {% set local_prefix = tunnel_conf.local.prefix if 'any' not in tunnel_conf.local.prefix else ['0.0.0.0/0', '::/0'] %} -- cgit v1.2.3 From 95955aa9a7294bdbb4fe9e5bd0c1d58512f894ec Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Thu, 19 Aug 2021 12:34:35 +0200 Subject: ipsec: dmvpn: T3764: bugfix mixed up IKE/ESP lifetime variable IKE lifetime is life_time, and ESP lifetime is rekey_time. --- data/templates/ipsec/swanctl/profile.tmpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/templates/ipsec/swanctl/profile.tmpl b/data/templates/ipsec/swanctl/profile.tmpl index 0a7268405..66bcaa776 100644 --- a/data/templates/ipsec/swanctl/profile.tmpl +++ b/data/templates/ipsec/swanctl/profile.tmpl @@ -7,7 +7,7 @@ dmvpn-{{ name }}-{{ interface }} { proposals = {{ ike_group[profile_conf.ike_group] | get_esp_ike_cipher | join(',') }} version = {{ ike.key_exchange[4:] if ike is defined and ike.key_exchange is defined else "0" }} - rekey_time = {{ ike.lifetime }}s + life_time = {{ ike.lifetime }}s keyingtries = 0 {% if profile_conf.authentication is defined and profile_conf.authentication.mode is defined and profile_conf.authentication.mode == 'pre-shared-secret' %} local { -- cgit v1.2.3 From 8114975668ac5d0f59bbc6abdc0df91e4c0d9995 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Thu, 19 Aug 2021 12:36:03 +0200 Subject: smoketest: ipsec: T3764: extend testcases for IKE/ESP lifetime --- smoketest/scripts/cli/test_vpn_ipsec.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/smoketest/scripts/cli/test_vpn_ipsec.py b/smoketest/scripts/cli/test_vpn_ipsec.py index a34387dc9..f33268083 100755 --- a/smoketest/scripts/cli/test_vpn_ipsec.py +++ b/smoketest/scripts/cli/test_vpn_ipsec.py @@ -182,8 +182,10 @@ class TestVPNIPsec(VyOSUnitTestSHIM.TestCase): swanctl_conf_lines = [ f'version = 2', f'auth = psk', + f'rekey_time = 28800s', # default value f'proposals = aes128-sha1-modp1024', f'esp_proposals = aes128-sha1-modp1024', + f'life_time = 3600s', # default value f'local_addrs = {local_address} # dhcp:no', f'remote_addrs = {peer_ip}', f'mode = tunnel', @@ -255,6 +257,8 @@ class TestVPNIPsec(VyOSUnitTestSHIM.TestCase): def test_04_dmvpn(self): tunnel_if = 'tun100' nhrp_secret = 'secret' + ike_lifetime = '3600' + esp_lifetime = '1800' # Tunnel self.cli_set(tunnel_path + [tunnel_if, 'address', '172.16.253.134/29']) @@ -272,7 +276,7 @@ class TestVPNIPsec(VyOSUnitTestSHIM.TestCase): # IKE/ESP Groups self.cli_set(base_path + ['esp-group', esp_group, 'compression', 'disable']) - self.cli_set(base_path + ['esp-group', esp_group, 'lifetime', '1800']) + self.cli_set(base_path + ['esp-group', esp_group, 'lifetime', esp_lifetime]) self.cli_set(base_path + ['esp-group', esp_group, 'mode', 'transport']) self.cli_set(base_path + ['esp-group', esp_group, 'pfs', 'dh-group2']) self.cli_set(base_path + ['esp-group', esp_group, 'proposal', '2', 'encryption', 'aes256']) @@ -282,7 +286,7 @@ class TestVPNIPsec(VyOSUnitTestSHIM.TestCase): self.cli_set(base_path + ['ike-group', ike_group, 'ikev2-reauth', 'no']) self.cli_set(base_path + ['ike-group', ike_group, 'key-exchange', 'ikev1']) - self.cli_set(base_path + ['ike-group', ike_group, 'lifetime', '3600']) + self.cli_set(base_path + ['ike-group', ike_group, 'lifetime', ike_lifetime]) self.cli_set(base_path + ['ike-group', ike_group, 'proposal', '2', 'dh-group', '2']) self.cli_set(base_path + ['ike-group', ike_group, 'proposal', '2', 'encryption', 'aes256']) self.cli_set(base_path + ['ike-group', ike_group, 'proposal', '2', 'hash', 'sha1']) @@ -300,7 +304,8 @@ class TestVPNIPsec(VyOSUnitTestSHIM.TestCase): swanctl_lines = [ f'proposals = aes128-sha1-modp1024,aes256-sha1-modp1024', f'version = 1', - f'rekey_time = 3600s', + f'life_time = {ike_lifetime}s', + f'rekey_time = {esp_lifetime}s', f'esp_proposals = aes128-sha1-modp1024,aes256-sha1-modp1024,3des-md5-modp1024', f'local_ts = dynamic[gre]', f'remote_ts = dynamic[gre]', -- cgit v1.2.3 From 40f5359d2d34e322272b79f3905d26954311daa6 Mon Sep 17 00:00:00 2001 From: John Estabrook Date: Fri, 13 Aug 2021 12:01:04 -0500 Subject: xml: T3474: add syntaxVersion processing to python xml lib --- python/vyos/xml/__init__.py | 2 ++ python/vyos/xml/definition.py | 6 ++++++ python/vyos/xml/kw.py | 1 + python/vyos/xml/load.py | 7 ++++++- 4 files changed, 15 insertions(+), 1 deletion(-) diff --git a/python/vyos/xml/__init__.py b/python/vyos/xml/__init__.py index 0ef0c85ce..e0eacb2d1 100644 --- a/python/vyos/xml/__init__.py +++ b/python/vyos/xml/__init__.py @@ -46,6 +46,8 @@ def is_tag(lpath): def is_leaf(lpath, flat=True): return load_configuration().is_leaf(lpath, flat) +def component_versions(): + return load_configuration().component_versions() def defaults(lpath, flat=False): return load_configuration().defaults(lpath, flat) diff --git a/python/vyos/xml/definition.py b/python/vyos/xml/definition.py index f556c5ced..5e0d5282c 100644 --- a/python/vyos/xml/definition.py +++ b/python/vyos/xml/definition.py @@ -30,6 +30,7 @@ class XML(dict): self[kw.owners] = {} self[kw.default] = {} self[kw.tags] = [] + self[kw.component_version] = {} dict.__init__(self) @@ -248,6 +249,11 @@ class XML(dict): # @lru_cache(maxsize=100) # XXX: need to use cachetool instead - for later + def component_versions(self) -> dict: + sort_component = sorted(self[kw.component_version].items(), + key = lambda kv: kv[0]) + return dict(sort_component) + def defaults(self, lpath, flat): d = self[kw.default] for k in lpath: diff --git a/python/vyos/xml/kw.py b/python/vyos/xml/kw.py index 58d47e751..48226ce96 100644 --- a/python/vyos/xml/kw.py +++ b/python/vyos/xml/kw.py @@ -32,6 +32,7 @@ priorities = '[priorities]' owners = '[owners]' tags = '[tags]' default = '[default]' +component_version = '[component_version]' # nodes diff --git a/python/vyos/xml/load.py b/python/vyos/xml/load.py index 0578bef80..c3022f3d6 100644 --- a/python/vyos/xml/load.py +++ b/python/vyos/xml/load.py @@ -115,7 +115,12 @@ def _format_nodes(inside, conf, xml): nodetype = 'tagNode' nodename = kw.tagNode elif 'syntaxVersion' in conf.keys(): - conf.pop('syntaxVersion') + sv = conf.pop('syntaxVersion') + if isinstance(sv, list): + for v in sv: + xml[kw.component_version][v['@component']] = v['@version'] + else: + xml[kw.component_version][sv['@component']] = sv['@version'] continue else: _fatal(conf.keys()) -- cgit v1.2.3 From a4e1a9235ff5e4aef29cc013239c6d842af5c1ae Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Thu, 19 Aug 2021 18:28:00 +0200 Subject: op-mode: bgp: T3759: add "show bgp ipv4|ipv6 vpn" commands --- .../include/bgp/afi-ipv4-ipv6-vpn.xml.i | 23 ++++++++++++++++++++++ .../include/bgp/show-bgp-common.xml.i | 2 ++ 2 files changed, 25 insertions(+) create mode 100644 op-mode-definitions/include/bgp/afi-ipv4-ipv6-vpn.xml.i diff --git a/op-mode-definitions/include/bgp/afi-ipv4-ipv6-vpn.xml.i b/op-mode-definitions/include/bgp/afi-ipv4-ipv6-vpn.xml.i new file mode 100644 index 000000000..ba6edb256 --- /dev/null +++ b/op-mode-definitions/include/bgp/afi-ipv4-ipv6-vpn.xml.i @@ -0,0 +1,23 @@ + + + + Network in the BGP routing table to display + + <x.x.x.x> <x.x.x.x/x> <h:h:h:h:h:h:h:h> <h:h:h:h:h:h:h:h/x> + + + + #include + + ${vyos_op_scripts_dir}/vtysh_wrapper.sh $@ + + + + VPN Address Family modifier + + + #include + #include + + + diff --git a/op-mode-definitions/include/bgp/show-bgp-common.xml.i b/op-mode-definitions/include/bgp/show-bgp-common.xml.i index b86b09056..0664b11fc 100644 --- a/op-mode-definitions/include/bgp/show-bgp-common.xml.i +++ b/op-mode-definitions/include/bgp/show-bgp-common.xml.i @@ -20,6 +20,7 @@ #include #include + #include @@ -41,6 +42,7 @@ #include #include + #include -- cgit v1.2.3 From f0d13929835351d3d4519e2faa364a20888edb2e Mon Sep 17 00:00:00 2001 From: John Estabrook Date: Thu, 19 Aug 2021 11:36:06 -0500 Subject: T3768: Revert "T1950: Add support for reading component versions ..." This reverts commit 29e438755c8bd2b9598a2016a3c42891f0cbfa1d. --- python/vyos/defaults.py | 2 -- python/vyos/systemversions.py | 28 ++-------------------------- 2 files changed, 2 insertions(+), 28 deletions(-) diff --git a/python/vyos/defaults.py b/python/vyos/defaults.py index 03006c383..2c24e007f 100644 --- a/python/vyos/defaults.py +++ b/python/vyos/defaults.py @@ -34,8 +34,6 @@ cfg_vintage = 'vyos' commit_lock = '/opt/vyatta/config/.lock' -version_file = '/usr/share/vyos/component-versions.json' - https_data = { 'listen_addresses' : { '*': ['_'] } } diff --git a/python/vyos/systemversions.py b/python/vyos/systemversions.py index 5c4deca29..9b3f4f413 100644 --- a/python/vyos/systemversions.py +++ b/python/vyos/systemversions.py @@ -16,15 +16,12 @@ import os import re import sys -import json - import vyos.defaults def get_system_versions(): """ - Get component versions from running system: read vyatta directory - structure for versions, then read vyos JSON file. It is a critical - error if either migration directory or JSON file is unreadable. + Get component versions from running system; critical failure if + unable to read migration directory. """ system_versions = {} @@ -39,25 +36,4 @@ def get_system_versions(): pair = info.split('@') system_versions[pair[0]] = int(pair[1]) - version_dict = {} - path = vyos.defaults.version_file - - if os.path.isfile(path): - with open(path, 'r') as f: - try: - version_dict = json.load(f) - except ValueError as err: - print(f"\nValue error in {path}: {err}") - sys.exit(1) - - for k, v in version_dict.items(): - if not isinstance(v, int): - print(f"\nType error in {path}; expecting Dict[str, int]") - sys.exit(1) - existing = system_versions.get(k) - if existing is None: - system_versions[k] = v - elif v > existing: - system_versions[k] = v - return system_versions -- cgit v1.2.3 From 3866ada47728b2845d82b93a3e78d9229ba07999 Mon Sep 17 00:00:00 2001 From: John Estabrook Date: Thu, 19 Aug 2021 11:38:03 -0500 Subject: xml: T3768: drop early XML syntaxVersion implementation --- interface-definitions/https.xml.in | 1 - 1 file changed, 1 deletion(-) diff --git a/interface-definitions/https.xml.in b/interface-definitions/https.xml.in index b65a89b56..b0532e249 100644 --- a/interface-definitions/https.xml.in +++ b/interface-definitions/https.xml.in @@ -1,7 +1,6 @@ - -- cgit v1.2.3 From 9ac5e1c8479049ae63747db36f4a0ef13ce6bc01 Mon Sep 17 00:00:00 2001 From: John Estabrook Date: Thu, 19 Aug 2021 11:41:00 -0500 Subject: Revert "xml: T1962: Add script to process syntaxVersion tags during build" This reverts commit 0ecc2c26f7ac939e4e23c14f5027ac7592c25761. --- Makefile | 7 +----- scripts/build-component-versions | 47 ---------------------------------------- 2 files changed, 1 insertion(+), 53 deletions(-) delete mode 100755 scripts/build-component-versions diff --git a/Makefile b/Makefile index c8f6ac7c9..0aa35d17d 100644 --- a/Makefile +++ b/Makefile @@ -63,11 +63,6 @@ op_mode_definitions: $(op_xml_obj) # could mask help strings or mandatory priority statements #find $(OP_TMPL_DIR) -name node.def -type f -empty -exec false {} + || sh -c 'echo "There are empty node.def files! Check your interface definitions." && exit 1' -.PHONY: component_versions -.ONESHELL: -component_versions: interface_definitions - $(CURDIR)/scripts/build-component-versions $(BUILD_DIR)/interface-definitions $(DATA_DIR) - .PHONY: vyshim vyshim: $(MAKE) -C $(SHIM_DIR) @@ -77,7 +72,7 @@ vyxdp: $(MAKE) -C $(XDP_DIR) .PHONY: all -all: clean interface_definitions op_mode_definitions component_versions vyshim +all: clean interface_definitions op_mode_definitions vyshim .PHONY: clean clean: diff --git a/scripts/build-component-versions b/scripts/build-component-versions deleted file mode 100755 index 5362dbdd4..000000000 --- a/scripts/build-component-versions +++ /dev/null @@ -1,47 +0,0 @@ -#!/usr/bin/env python3 - -import sys -import os -import argparse -import json - -from lxml import etree as ET - -parser = argparse.ArgumentParser() -parser.add_argument('INPUT_DIR', type=str, - help="Directory containing XML interface definition files") -parser.add_argument('OUTPUT_DIR', type=str, - help="Output directory for JSON file") - -args = parser.parse_args() - -input_dir = args.INPUT_DIR -output_dir = args.OUTPUT_DIR - -version_dict = {} - -for filename in os.listdir(input_dir): - filepath = os.path.join(input_dir, filename) - print(filepath) - try: - xml = ET.parse(filepath) - except Exception as e: - print("Failed to load interface definition file {0}".format(filename)) - print(e) - sys.exit(1) - - root = xml.getroot() - version_data = root.iterfind("syntaxVersion") - for ver in version_data: - component = ver.get("component") - version = int(ver.get("version")) - - v = version_dict.get(component) - if v is None: - version_dict[component] = version - elif version > v: - version_dict[component] = version - -out_file = os.path.join(output_dir, 'component-versions.json') -with open(out_file, 'w') as f: - json.dump(version_dict, f, indent=4, sort_keys=True) -- cgit v1.2.3 From 2278785d4327c961a2c3a8e36637f4e464ad7230 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Fri, 20 Aug 2021 13:57:16 +0200 Subject: ipsec: T1210: add missing if clause around unique key --- data/templates/ipsec/swanctl/remote_access.tmpl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/data/templates/ipsec/swanctl/remote_access.tmpl b/data/templates/ipsec/swanctl/remote_access.tmpl index 456842488..f906836c6 100644 --- a/data/templates/ipsec/swanctl/remote_access.tmpl +++ b/data/templates/ipsec/swanctl/remote_access.tmpl @@ -10,7 +10,9 @@ send_certreq = no rekey_time = {{ ike.lifetime }}s keyingtries = 0 +{% if rw_conf.unique is defined and rw_conf.unique is not none %} unique = {{ rw_conf.unique }} +{% endif %} {% if rw_conf.pool is defined and rw_conf.pool is not none %} pools = {{ rw_conf.pool | join(',') }} {% endif %} -- cgit v1.2.3 From 0a8a0188033d6b27c521f082fdddae9873dd5d3d Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Thu, 19 Aug 2021 13:07:18 +0200 Subject: xml: remove superfluous "interface" prefix from interface includes --- .../include/interface/arp-cache-timeout.xml.i | 16 +++++++++++ .../include/interface/description.xml.i | 11 ++++++++ .../include/interface/dial-on-demand.xml.i | 8 ++++++ .../include/interface/disable-arp-filter.xml.i | 8 ++++++ .../include/interface/disable-forwarding.xml.i | 8 ++++++ .../include/interface/disable-link-detect.xml.i | 8 ++++++ .../include/interface/disable.xml.i | 8 ++++++ .../include/interface/eapol.xml.i | 11 ++++++++ .../include/interface/enable-arp-accept.xml.i | 8 ++++++ .../include/interface/enable-arp-announce.xml.i | 8 ++++++ .../include/interface/enable-arp-ignore.xml.i | 8 ++++++ .../include/interface/enable-proxy-arp.xml.i | 8 ++++++ .../include/interface/hw-id.xml.i | 14 ++++++++++ .../interface/interface-arp-cache-timeout.xml.i | 16 ----------- .../include/interface/interface-description.xml.i | 11 -------- .../interface/interface-dial-on-demand.xml.i | 8 ------ .../interface/interface-disable-arp-filter.xml.i | 8 ------ .../interface/interface-disable-forwarding.xml.i | 8 ------ .../interface/interface-disable-link-detect.xml.i | 8 ------ .../include/interface/interface-disable.xml.i | 8 ------ .../include/interface/interface-eapol.xml.i | 11 -------- .../interface/interface-enable-arp-accept.xml.i | 8 ------ .../interface/interface-enable-arp-announce.xml.i | 8 ------ .../interface/interface-enable-arp-ignore.xml.i | 8 ------ .../interface/interface-enable-proxy-arp.xml.i | 8 ------ .../include/interface/interface-hw-id.xml.i | 14 ---------- .../include/interface/interface-ipv4-options.xml.i | 18 ------------ .../include/interface/interface-ipv6-options.xml.i | 12 -------- .../include/interface/interface-mac.xml.i | 14 ---------- .../include/interface/interface-mirror.xml.i | 25 ----------------- .../interface/interface-mtu-1200-16000.xml.i | 16 ----------- .../interface/interface-mtu-1450-16000.xml.i | 16 ----------- .../include/interface/interface-mtu-64-8024.xml.i | 16 ----------- .../include/interface/interface-mtu-68-1500.xml.i | 16 ----------- .../include/interface/interface-mtu-68-16000.xml.i | 16 ----------- .../interface-parameters-dont-fragment.xml.i | 8 ------ .../interface/interface-parameters-flowlabel.xml.i | 15 ---------- .../interface/interface-parameters-key.xml.i | 15 ---------- .../interface/interface-parameters-tos.xml.i | 16 ----------- .../interface/interface-parameters-ttl.xml.i | 20 -------------- .../interface/interface-proxy-arp-pvlan.xml.i | 8 ------ .../interface/interface-source-validation.xml.i | 25 ----------------- .../include/interface/interface-vrf.xml.i | 14 ---------- .../include/interface/interface-xdp.xml.i | 8 ------ .../include/interface/ipv4-options.xml.i | 18 ++++++++++++ .../include/interface/ipv6-options.xml.i | 12 ++++++++ interface-definitions/include/interface/mac.xml.i | 14 ++++++++++ .../include/interface/mirror.xml.i | 25 +++++++++++++++++ .../include/interface/mtu-1200-16000.xml.i | 16 +++++++++++ .../include/interface/mtu-1450-16000.xml.i | 16 +++++++++++ .../include/interface/mtu-64-8024.xml.i | 16 +++++++++++ .../include/interface/mtu-68-1500.xml.i | 16 +++++++++++ .../include/interface/mtu-68-16000.xml.i | 16 +++++++++++ .../interface/parameters-dont-fragment.xml.i | 8 ++++++ .../include/interface/parameters-flowlabel.xml.i | 15 ++++++++++ .../include/interface/parameters-key.xml.i | 15 ++++++++++ .../include/interface/parameters-tos.xml.i | 16 +++++++++++ .../include/interface/parameters-ttl.xml.i | 20 ++++++++++++++ .../include/interface/proxy-arp-pvlan.xml.i | 8 ++++++ .../include/interface/source-validation.xml.i | 25 +++++++++++++++++ .../include/interface/vif-s.xml.i | 32 +++++++++++----------- interface-definitions/include/interface/vif.xml.i | 16 +++++------ interface-definitions/include/interface/vrf.xml.i | 14 ++++++++++ interface-definitions/include/interface/xdp.xml.i | 8 ++++++ interface-definitions/interfaces-bonding.xml.in | 20 +++++++------- interface-definitions/interfaces-bridge.xml.in | 18 ++++++------ interface-definitions/interfaces-dummy.xml.in | 8 +++--- interface-definitions/interfaces-ethernet.xml.in | 24 ++++++++-------- interface-definitions/interfaces-geneve.xml.in | 20 +++++++------- interface-definitions/interfaces-l2tpv3.xml.in | 14 +++++----- interface-definitions/interfaces-loopback.xml.in | 4 +-- interface-definitions/interfaces-macsec.xml.in | 12 ++++---- interface-definitions/interfaces-openvpn.xml.in | 8 +++--- interface-definitions/interfaces-pppoe.xml.in | 12 ++++---- .../interfaces-pseudo-ethernet.xml.in | 16 +++++------ interface-definitions/interfaces-tunnel.xml.in | 22 +++++++-------- interface-definitions/interfaces-vti.xml.in | 8 +++--- interface-definitions/interfaces-vxlan.xml.in | 22 +++++++-------- interface-definitions/interfaces-wireguard.xml.in | 12 ++++---- interface-definitions/interfaces-wireless.xml.in | 16 +++++------ interface-definitions/interfaces-wwan.xml.in | 16 +++++------ interface-definitions/ntp.xml.in | 2 +- .../service_console-server.xml.in | 2 +- interface-definitions/snmp.xml.in | 2 +- interface-definitions/ssh.xml.in | 2 +- interface-definitions/system-login.xml.in | 2 +- interface-definitions/vpn_sstp.xml.in | 2 +- interface-definitions/vrf.xml.in | 4 +-- 88 files changed, 560 insertions(+), 560 deletions(-) create mode 100644 interface-definitions/include/interface/arp-cache-timeout.xml.i create mode 100644 interface-definitions/include/interface/description.xml.i create mode 100644 interface-definitions/include/interface/dial-on-demand.xml.i create mode 100644 interface-definitions/include/interface/disable-arp-filter.xml.i create mode 100644 interface-definitions/include/interface/disable-forwarding.xml.i create mode 100644 interface-definitions/include/interface/disable-link-detect.xml.i create mode 100644 interface-definitions/include/interface/disable.xml.i create mode 100644 interface-definitions/include/interface/eapol.xml.i create mode 100644 interface-definitions/include/interface/enable-arp-accept.xml.i create mode 100644 interface-definitions/include/interface/enable-arp-announce.xml.i create mode 100644 interface-definitions/include/interface/enable-arp-ignore.xml.i create mode 100644 interface-definitions/include/interface/enable-proxy-arp.xml.i create mode 100644 interface-definitions/include/interface/hw-id.xml.i delete mode 100644 interface-definitions/include/interface/interface-arp-cache-timeout.xml.i delete mode 100644 interface-definitions/include/interface/interface-description.xml.i delete mode 100644 interface-definitions/include/interface/interface-dial-on-demand.xml.i delete mode 100644 interface-definitions/include/interface/interface-disable-arp-filter.xml.i delete mode 100644 interface-definitions/include/interface/interface-disable-forwarding.xml.i delete mode 100644 interface-definitions/include/interface/interface-disable-link-detect.xml.i delete mode 100644 interface-definitions/include/interface/interface-disable.xml.i delete mode 100644 interface-definitions/include/interface/interface-eapol.xml.i delete mode 100644 interface-definitions/include/interface/interface-enable-arp-accept.xml.i delete mode 100644 interface-definitions/include/interface/interface-enable-arp-announce.xml.i delete mode 100644 interface-definitions/include/interface/interface-enable-arp-ignore.xml.i delete mode 100644 interface-definitions/include/interface/interface-enable-proxy-arp.xml.i delete mode 100644 interface-definitions/include/interface/interface-hw-id.xml.i delete mode 100644 interface-definitions/include/interface/interface-ipv4-options.xml.i delete mode 100644 interface-definitions/include/interface/interface-ipv6-options.xml.i delete mode 100644 interface-definitions/include/interface/interface-mac.xml.i delete mode 100644 interface-definitions/include/interface/interface-mirror.xml.i delete mode 100644 interface-definitions/include/interface/interface-mtu-1200-16000.xml.i delete mode 100644 interface-definitions/include/interface/interface-mtu-1450-16000.xml.i delete mode 100644 interface-definitions/include/interface/interface-mtu-64-8024.xml.i delete mode 100644 interface-definitions/include/interface/interface-mtu-68-1500.xml.i delete mode 100644 interface-definitions/include/interface/interface-mtu-68-16000.xml.i delete mode 100644 interface-definitions/include/interface/interface-parameters-dont-fragment.xml.i delete mode 100644 interface-definitions/include/interface/interface-parameters-flowlabel.xml.i delete mode 100644 interface-definitions/include/interface/interface-parameters-key.xml.i delete mode 100644 interface-definitions/include/interface/interface-parameters-tos.xml.i delete mode 100644 interface-definitions/include/interface/interface-parameters-ttl.xml.i delete mode 100644 interface-definitions/include/interface/interface-proxy-arp-pvlan.xml.i delete mode 100644 interface-definitions/include/interface/interface-source-validation.xml.i delete mode 100644 interface-definitions/include/interface/interface-vrf.xml.i delete mode 100644 interface-definitions/include/interface/interface-xdp.xml.i create mode 100644 interface-definitions/include/interface/ipv4-options.xml.i create mode 100644 interface-definitions/include/interface/ipv6-options.xml.i create mode 100644 interface-definitions/include/interface/mac.xml.i create mode 100644 interface-definitions/include/interface/mirror.xml.i create mode 100644 interface-definitions/include/interface/mtu-1200-16000.xml.i create mode 100644 interface-definitions/include/interface/mtu-1450-16000.xml.i create mode 100644 interface-definitions/include/interface/mtu-64-8024.xml.i create mode 100644 interface-definitions/include/interface/mtu-68-1500.xml.i create mode 100644 interface-definitions/include/interface/mtu-68-16000.xml.i create mode 100644 interface-definitions/include/interface/parameters-dont-fragment.xml.i create mode 100644 interface-definitions/include/interface/parameters-flowlabel.xml.i create mode 100644 interface-definitions/include/interface/parameters-key.xml.i create mode 100644 interface-definitions/include/interface/parameters-tos.xml.i create mode 100644 interface-definitions/include/interface/parameters-ttl.xml.i create mode 100644 interface-definitions/include/interface/proxy-arp-pvlan.xml.i create mode 100644 interface-definitions/include/interface/source-validation.xml.i create mode 100644 interface-definitions/include/interface/vrf.xml.i create mode 100644 interface-definitions/include/interface/xdp.xml.i diff --git a/interface-definitions/include/interface/arp-cache-timeout.xml.i b/interface-definitions/include/interface/arp-cache-timeout.xml.i new file mode 100644 index 000000000..3fb64f1ff --- /dev/null +++ b/interface-definitions/include/interface/arp-cache-timeout.xml.i @@ -0,0 +1,16 @@ + + + + ARP cache entry timeout in seconds + + 1-86400 + ARP cache entry timout in seconds (default 30) + + + + + ARP cache entry timeout must be between 1 and 86400 seconds + + 30 + + diff --git a/interface-definitions/include/interface/description.xml.i b/interface-definitions/include/interface/description.xml.i new file mode 100644 index 000000000..8579cf7d1 --- /dev/null +++ b/interface-definitions/include/interface/description.xml.i @@ -0,0 +1,11 @@ + + + + Interface specific description + + .{1,256}$ + + Description too long (limit 256 characters) + + + diff --git a/interface-definitions/include/interface/dial-on-demand.xml.i b/interface-definitions/include/interface/dial-on-demand.xml.i new file mode 100644 index 000000000..30e8c7e97 --- /dev/null +++ b/interface-definitions/include/interface/dial-on-demand.xml.i @@ -0,0 +1,8 @@ + + + + Establishment connection automatically when traffic is sent + + + + diff --git a/interface-definitions/include/interface/disable-arp-filter.xml.i b/interface-definitions/include/interface/disable-arp-filter.xml.i new file mode 100644 index 000000000..a69455d58 --- /dev/null +++ b/interface-definitions/include/interface/disable-arp-filter.xml.i @@ -0,0 +1,8 @@ + + + + Disable ARP filter on this interface + + + + diff --git a/interface-definitions/include/interface/disable-forwarding.xml.i b/interface-definitions/include/interface/disable-forwarding.xml.i new file mode 100644 index 000000000..a32e5376f --- /dev/null +++ b/interface-definitions/include/interface/disable-forwarding.xml.i @@ -0,0 +1,8 @@ + + + + Disable IPv4 forwarding on this interface + + + + diff --git a/interface-definitions/include/interface/disable-link-detect.xml.i b/interface-definitions/include/interface/disable-link-detect.xml.i new file mode 100644 index 000000000..b101ec292 --- /dev/null +++ b/interface-definitions/include/interface/disable-link-detect.xml.i @@ -0,0 +1,8 @@ + + + + Ignore link state changes + + + + diff --git a/interface-definitions/include/interface/disable.xml.i b/interface-definitions/include/interface/disable.xml.i new file mode 100644 index 000000000..b76bd3f53 --- /dev/null +++ b/interface-definitions/include/interface/disable.xml.i @@ -0,0 +1,8 @@ + + + + Administratively disable interface + + + + diff --git a/interface-definitions/include/interface/eapol.xml.i b/interface-definitions/include/interface/eapol.xml.i new file mode 100644 index 000000000..c4cdeae0c --- /dev/null +++ b/interface-definitions/include/interface/eapol.xml.i @@ -0,0 +1,11 @@ + + + + Extensible Authentication Protocol over Local Area Network + + + #include + #include + + + diff --git a/interface-definitions/include/interface/enable-arp-accept.xml.i b/interface-definitions/include/interface/enable-arp-accept.xml.i new file mode 100644 index 000000000..90f6bc3db --- /dev/null +++ b/interface-definitions/include/interface/enable-arp-accept.xml.i @@ -0,0 +1,8 @@ + + + + Enable ARP accept on this interface + + + + diff --git a/interface-definitions/include/interface/enable-arp-announce.xml.i b/interface-definitions/include/interface/enable-arp-announce.xml.i new file mode 100644 index 000000000..cf02fce0b --- /dev/null +++ b/interface-definitions/include/interface/enable-arp-announce.xml.i @@ -0,0 +1,8 @@ + + + + Enable ARP announce on this interface + + + + diff --git a/interface-definitions/include/interface/enable-arp-ignore.xml.i b/interface-definitions/include/interface/enable-arp-ignore.xml.i new file mode 100644 index 000000000..5bb444f35 --- /dev/null +++ b/interface-definitions/include/interface/enable-arp-ignore.xml.i @@ -0,0 +1,8 @@ + + + + Enable ARP ignore on this interface + + + + diff --git a/interface-definitions/include/interface/enable-proxy-arp.xml.i b/interface-definitions/include/interface/enable-proxy-arp.xml.i new file mode 100644 index 000000000..27e497f84 --- /dev/null +++ b/interface-definitions/include/interface/enable-proxy-arp.xml.i @@ -0,0 +1,8 @@ + + + + Enable proxy-arp on this interface + + + + diff --git a/interface-definitions/include/interface/hw-id.xml.i b/interface-definitions/include/interface/hw-id.xml.i new file mode 100644 index 000000000..a3a1eec7c --- /dev/null +++ b/interface-definitions/include/interface/hw-id.xml.i @@ -0,0 +1,14 @@ + + + + Associate Ethernet Interface with given Media Access Control (MAC) address + + macaddr + Hardware (MAC) address + + + + + + + diff --git a/interface-definitions/include/interface/interface-arp-cache-timeout.xml.i b/interface-definitions/include/interface/interface-arp-cache-timeout.xml.i deleted file mode 100644 index b269fecd8..000000000 --- a/interface-definitions/include/interface/interface-arp-cache-timeout.xml.i +++ /dev/null @@ -1,16 +0,0 @@ - - - - ARP cache entry timeout in seconds - - 1-86400 - ARP cache entry timout in seconds (default 30) - - - - - ARP cache entry timeout must be between 1 and 86400 seconds - - 30 - - diff --git a/interface-definitions/include/interface/interface-description.xml.i b/interface-definitions/include/interface/interface-description.xml.i deleted file mode 100644 index d618b50d2..000000000 --- a/interface-definitions/include/interface/interface-description.xml.i +++ /dev/null @@ -1,11 +0,0 @@ - - - - Interface specific description - - .{1,256}$ - - Description too long (limit 256 characters) - - - diff --git a/interface-definitions/include/interface/interface-dial-on-demand.xml.i b/interface-definitions/include/interface/interface-dial-on-demand.xml.i deleted file mode 100644 index 66edd9678..000000000 --- a/interface-definitions/include/interface/interface-dial-on-demand.xml.i +++ /dev/null @@ -1,8 +0,0 @@ - - - - Establishment connection automatically when traffic is sent - - - - diff --git a/interface-definitions/include/interface/interface-disable-arp-filter.xml.i b/interface-definitions/include/interface/interface-disable-arp-filter.xml.i deleted file mode 100644 index 49cddaf76..000000000 --- a/interface-definitions/include/interface/interface-disable-arp-filter.xml.i +++ /dev/null @@ -1,8 +0,0 @@ - - - - Disable ARP filter on this interface - - - - diff --git a/interface-definitions/include/interface/interface-disable-forwarding.xml.i b/interface-definitions/include/interface/interface-disable-forwarding.xml.i deleted file mode 100644 index cb6ef0475..000000000 --- a/interface-definitions/include/interface/interface-disable-forwarding.xml.i +++ /dev/null @@ -1,8 +0,0 @@ - - - - Disable IPv4 forwarding on this interface - - - - diff --git a/interface-definitions/include/interface/interface-disable-link-detect.xml.i b/interface-definitions/include/interface/interface-disable-link-detect.xml.i deleted file mode 100644 index c528885b2..000000000 --- a/interface-definitions/include/interface/interface-disable-link-detect.xml.i +++ /dev/null @@ -1,8 +0,0 @@ - - - - Ignore link state changes - - - - diff --git a/interface-definitions/include/interface/interface-disable.xml.i b/interface-definitions/include/interface/interface-disable.xml.i deleted file mode 100644 index d90e6395b..000000000 --- a/interface-definitions/include/interface/interface-disable.xml.i +++ /dev/null @@ -1,8 +0,0 @@ - - - - Administratively disable interface - - - - diff --git a/interface-definitions/include/interface/interface-eapol.xml.i b/interface-definitions/include/interface/interface-eapol.xml.i deleted file mode 100644 index 270ec5b13..000000000 --- a/interface-definitions/include/interface/interface-eapol.xml.i +++ /dev/null @@ -1,11 +0,0 @@ - - - - Extensible Authentication Protocol over Local Area Network - - - #include - #include - - - diff --git a/interface-definitions/include/interface/interface-enable-arp-accept.xml.i b/interface-definitions/include/interface/interface-enable-arp-accept.xml.i deleted file mode 100644 index 7c5d51857..000000000 --- a/interface-definitions/include/interface/interface-enable-arp-accept.xml.i +++ /dev/null @@ -1,8 +0,0 @@ - - - - Enable ARP accept on this interface - - - - diff --git a/interface-definitions/include/interface/interface-enable-arp-announce.xml.i b/interface-definitions/include/interface/interface-enable-arp-announce.xml.i deleted file mode 100644 index f44599c54..000000000 --- a/interface-definitions/include/interface/interface-enable-arp-announce.xml.i +++ /dev/null @@ -1,8 +0,0 @@ - - - - Enable ARP announce on this interface - - - - diff --git a/interface-definitions/include/interface/interface-enable-arp-ignore.xml.i b/interface-definitions/include/interface/interface-enable-arp-ignore.xml.i deleted file mode 100644 index 3ea39613c..000000000 --- a/interface-definitions/include/interface/interface-enable-arp-ignore.xml.i +++ /dev/null @@ -1,8 +0,0 @@ - - - - Enable ARP ignore on this interface - - - - diff --git a/interface-definitions/include/interface/interface-enable-proxy-arp.xml.i b/interface-definitions/include/interface/interface-enable-proxy-arp.xml.i deleted file mode 100644 index dbdeeb7a7..000000000 --- a/interface-definitions/include/interface/interface-enable-proxy-arp.xml.i +++ /dev/null @@ -1,8 +0,0 @@ - - - - Enable proxy-arp on this interface - - - - diff --git a/interface-definitions/include/interface/interface-hw-id.xml.i b/interface-definitions/include/interface/interface-hw-id.xml.i deleted file mode 100644 index 989cd9cb7..000000000 --- a/interface-definitions/include/interface/interface-hw-id.xml.i +++ /dev/null @@ -1,14 +0,0 @@ - - - - Associate Ethernet Interface with given Media Access Control (MAC) address - - macaddr - Hardware (MAC) address - - - - - - - diff --git a/interface-definitions/include/interface/interface-ipv4-options.xml.i b/interface-definitions/include/interface/interface-ipv4-options.xml.i deleted file mode 100644 index c2d0677b7..000000000 --- a/interface-definitions/include/interface/interface-ipv4-options.xml.i +++ /dev/null @@ -1,18 +0,0 @@ - - - - IPv4 routing parameters - - - #include - #include - #include - #include - #include - #include - #include - #include - #include - - - diff --git a/interface-definitions/include/interface/interface-ipv6-options.xml.i b/interface-definitions/include/interface/interface-ipv6-options.xml.i deleted file mode 100644 index dcd5a8710..000000000 --- a/interface-definitions/include/interface/interface-ipv6-options.xml.i +++ /dev/null @@ -1,12 +0,0 @@ - - - - IPv6 routing parameters - - - #include - #include - #include - - - diff --git a/interface-definitions/include/interface/interface-mac.xml.i b/interface-definitions/include/interface/interface-mac.xml.i deleted file mode 100644 index d7107ad23..000000000 --- a/interface-definitions/include/interface/interface-mac.xml.i +++ /dev/null @@ -1,14 +0,0 @@ - - - - Media Access Control (MAC) address - - macaddr - Hardware (MAC) address - - - - - - - diff --git a/interface-definitions/include/interface/interface-mirror.xml.i b/interface-definitions/include/interface/interface-mirror.xml.i deleted file mode 100644 index b3b45fb43..000000000 --- a/interface-definitions/include/interface/interface-mirror.xml.i +++ /dev/null @@ -1,25 +0,0 @@ - - - - Incoming/outgoing packet mirroring destination - - - - - Mirror the ingress traffic of the interface to the destination interface - - - - - - - - Mirror the egress traffic of the interface to the destination interface - - - - - - - - diff --git a/interface-definitions/include/interface/interface-mtu-1200-16000.xml.i b/interface-definitions/include/interface/interface-mtu-1200-16000.xml.i deleted file mode 100644 index 3241ba912..000000000 --- a/interface-definitions/include/interface/interface-mtu-1200-16000.xml.i +++ /dev/null @@ -1,16 +0,0 @@ - - - - Maximum Transmission Unit (MTU) - - 1200-16000 - Maximum Transmission Unit in byte - - - - - MTU must be between 1200 and 16000 - - 1500 - - diff --git a/interface-definitions/include/interface/interface-mtu-1450-16000.xml.i b/interface-definitions/include/interface/interface-mtu-1450-16000.xml.i deleted file mode 100644 index 0a35bbbaa..000000000 --- a/interface-definitions/include/interface/interface-mtu-1450-16000.xml.i +++ /dev/null @@ -1,16 +0,0 @@ - - - - Maximum Transmission Unit (MTU) - - 1450-16000 - Maximum Transmission Unit in byte - - - - - MTU must be between 1450 and 16000 - - 1500 - - diff --git a/interface-definitions/include/interface/interface-mtu-64-8024.xml.i b/interface-definitions/include/interface/interface-mtu-64-8024.xml.i deleted file mode 100644 index f75de02ba..000000000 --- a/interface-definitions/include/interface/interface-mtu-64-8024.xml.i +++ /dev/null @@ -1,16 +0,0 @@ - - - - Maximum Transmission Unit (MTU) - - 64-8024 - Maximum Transmission Unit in byte - - - - - MTU must be between 64 and 8024 - - 1500 - - diff --git a/interface-definitions/include/interface/interface-mtu-68-1500.xml.i b/interface-definitions/include/interface/interface-mtu-68-1500.xml.i deleted file mode 100644 index 9e6fe8760..000000000 --- a/interface-definitions/include/interface/interface-mtu-68-1500.xml.i +++ /dev/null @@ -1,16 +0,0 @@ - - - - Maximum Transmission Unit (MTU) - - 68-1500 - Maximum Transmission Unit in byte - - - - - MTU must be between 68 and 1500 - - 1500 - - diff --git a/interface-definitions/include/interface/interface-mtu-68-16000.xml.i b/interface-definitions/include/interface/interface-mtu-68-16000.xml.i deleted file mode 100644 index 83af7bbd4..000000000 --- a/interface-definitions/include/interface/interface-mtu-68-16000.xml.i +++ /dev/null @@ -1,16 +0,0 @@ - - - - Maximum Transmission Unit (MTU) - - 68-16000 - Maximum Transmission Unit in byte - - - - - MTU must be between 68 and 16000 - - 1500 - - diff --git a/interface-definitions/include/interface/interface-parameters-dont-fragment.xml.i b/interface-definitions/include/interface/interface-parameters-dont-fragment.xml.i deleted file mode 100644 index 166c31115..000000000 --- a/interface-definitions/include/interface/interface-parameters-dont-fragment.xml.i +++ /dev/null @@ -1,8 +0,0 @@ - - - - Specifies the usage of the dont fragment (DF) bit - - - - diff --git a/interface-definitions/include/interface/interface-parameters-flowlabel.xml.i b/interface-definitions/include/interface/interface-parameters-flowlabel.xml.i deleted file mode 100644 index ed075e40d..000000000 --- a/interface-definitions/include/interface/interface-parameters-flowlabel.xml.i +++ /dev/null @@ -1,15 +0,0 @@ - - - - Specifies the flow label to use in outgoing packets - - 0x0-0x0FFFFF - Tunnel key, 'inherit' or hex value - - - ^((0x){0,1}(0?[0-9A-Fa-f]{1,5})|inherit)$ - - Must be 'inherit' or a number - - - diff --git a/interface-definitions/include/interface/interface-parameters-key.xml.i b/interface-definitions/include/interface/interface-parameters-key.xml.i deleted file mode 100644 index 6c59f7879..000000000 --- a/interface-definitions/include/interface/interface-parameters-key.xml.i +++ /dev/null @@ -1,15 +0,0 @@ - - - - Tunnel key (only GRE tunnels) - - u32 - Tunnel key - - - - - key must be between 0-4294967295 - - - diff --git a/interface-definitions/include/interface/interface-parameters-tos.xml.i b/interface-definitions/include/interface/interface-parameters-tos.xml.i deleted file mode 100644 index 83b4e0671..000000000 --- a/interface-definitions/include/interface/interface-parameters-tos.xml.i +++ /dev/null @@ -1,16 +0,0 @@ - - - - Specifies TOS value to use in outgoing packets - - 0-99 - Type of Service (TOS) - - - - - TOS must be between 0 and 99 - - inherit - - diff --git a/interface-definitions/include/interface/interface-parameters-ttl.xml.i b/interface-definitions/include/interface/interface-parameters-ttl.xml.i deleted file mode 100644 index df193cf24..000000000 --- a/interface-definitions/include/interface/interface-parameters-ttl.xml.i +++ /dev/null @@ -1,20 +0,0 @@ - - - - Specifies TTL value to use in outgoing packets - - 0 - Inherit - copy value from original IP header - - - 1-255 - Time to Live - - - - - TTL must be between 0 and 255 - - 0 - - diff --git a/interface-definitions/include/interface/interface-proxy-arp-pvlan.xml.i b/interface-definitions/include/interface/interface-proxy-arp-pvlan.xml.i deleted file mode 100644 index 153dfc072..000000000 --- a/interface-definitions/include/interface/interface-proxy-arp-pvlan.xml.i +++ /dev/null @@ -1,8 +0,0 @@ - - - - Enable private VLAN proxy ARP on this interface - - - - diff --git a/interface-definitions/include/interface/interface-source-validation.xml.i b/interface-definitions/include/interface/interface-source-validation.xml.i deleted file mode 100644 index 70914f2e9..000000000 --- a/interface-definitions/include/interface/interface-source-validation.xml.i +++ /dev/null @@ -1,25 +0,0 @@ - - - - Source validation by reversed path (RFC3704) - - strict loose disable - - - strict - Enable Strict Reverse Path Forwarding as defined in RFC3704 - - - loose - Enable Loose Reverse Path Forwarding as defined in RFC3704 - - - disable - No source validation - - - ^(strict|loose|disable)$ - - - - diff --git a/interface-definitions/include/interface/interface-vrf.xml.i b/interface-definitions/include/interface/interface-vrf.xml.i deleted file mode 100644 index ef6ca1241..000000000 --- a/interface-definitions/include/interface/interface-vrf.xml.i +++ /dev/null @@ -1,14 +0,0 @@ - - - - VRF instance name - - text - VRF instance name - - - vrf name - - - - diff --git a/interface-definitions/include/interface/interface-xdp.xml.i b/interface-definitions/include/interface/interface-xdp.xml.i deleted file mode 100644 index 0253f6dad..000000000 --- a/interface-definitions/include/interface/interface-xdp.xml.i +++ /dev/null @@ -1,8 +0,0 @@ - - - - Enable eXpress Data Path - - - - diff --git a/interface-definitions/include/interface/ipv4-options.xml.i b/interface-definitions/include/interface/ipv4-options.xml.i new file mode 100644 index 000000000..10884b6eb --- /dev/null +++ b/interface-definitions/include/interface/ipv4-options.xml.i @@ -0,0 +1,18 @@ + + + + IPv4 routing parameters + + + #include + #include + #include + #include + #include + #include + #include + #include + #include + + + diff --git a/interface-definitions/include/interface/ipv6-options.xml.i b/interface-definitions/include/interface/ipv6-options.xml.i new file mode 100644 index 000000000..e57c242b0 --- /dev/null +++ b/interface-definitions/include/interface/ipv6-options.xml.i @@ -0,0 +1,12 @@ + + + + IPv6 routing parameters + + + #include + #include + #include + + + diff --git a/interface-definitions/include/interface/mac.xml.i b/interface-definitions/include/interface/mac.xml.i new file mode 100644 index 000000000..705330dce --- /dev/null +++ b/interface-definitions/include/interface/mac.xml.i @@ -0,0 +1,14 @@ + + + + Media Access Control (MAC) address + + macaddr + Hardware (MAC) address + + + + + + + diff --git a/interface-definitions/include/interface/mirror.xml.i b/interface-definitions/include/interface/mirror.xml.i new file mode 100644 index 000000000..2959551f0 --- /dev/null +++ b/interface-definitions/include/interface/mirror.xml.i @@ -0,0 +1,25 @@ + + + + Incoming/outgoing packet mirroring destination + + + + + Mirror the ingress traffic of the interface to the destination interface + + + + + + + + Mirror the egress traffic of the interface to the destination interface + + + + + + + + diff --git a/interface-definitions/include/interface/mtu-1200-16000.xml.i b/interface-definitions/include/interface/mtu-1200-16000.xml.i new file mode 100644 index 000000000..ccd986d55 --- /dev/null +++ b/interface-definitions/include/interface/mtu-1200-16000.xml.i @@ -0,0 +1,16 @@ + + + + Maximum Transmission Unit (MTU) + + 1200-16000 + Maximum Transmission Unit in byte + + + + + MTU must be between 1200 and 16000 + + 1500 + + diff --git a/interface-definitions/include/interface/mtu-1450-16000.xml.i b/interface-definitions/include/interface/mtu-1450-16000.xml.i new file mode 100644 index 000000000..2dc3a2029 --- /dev/null +++ b/interface-definitions/include/interface/mtu-1450-16000.xml.i @@ -0,0 +1,16 @@ + + + + Maximum Transmission Unit (MTU) + + 1450-16000 + Maximum Transmission Unit in byte + + + + + MTU must be between 1450 and 16000 + + 1500 + + diff --git a/interface-definitions/include/interface/mtu-64-8024.xml.i b/interface-definitions/include/interface/mtu-64-8024.xml.i new file mode 100644 index 000000000..9b8bc4697 --- /dev/null +++ b/interface-definitions/include/interface/mtu-64-8024.xml.i @@ -0,0 +1,16 @@ + + + + Maximum Transmission Unit (MTU) + + 64-8024 + Maximum Transmission Unit in byte + + + + + MTU must be between 64 and 8024 + + 1500 + + diff --git a/interface-definitions/include/interface/mtu-68-1500.xml.i b/interface-definitions/include/interface/mtu-68-1500.xml.i new file mode 100644 index 000000000..e3b70302f --- /dev/null +++ b/interface-definitions/include/interface/mtu-68-1500.xml.i @@ -0,0 +1,16 @@ + + + + Maximum Transmission Unit (MTU) + + 68-1500 + Maximum Transmission Unit in byte + + + + + MTU must be between 68 and 1500 + + 1500 + + diff --git a/interface-definitions/include/interface/mtu-68-16000.xml.i b/interface-definitions/include/interface/mtu-68-16000.xml.i new file mode 100644 index 000000000..b610ab3e2 --- /dev/null +++ b/interface-definitions/include/interface/mtu-68-16000.xml.i @@ -0,0 +1,16 @@ + + + + Maximum Transmission Unit (MTU) + + 68-16000 + Maximum Transmission Unit in byte + + + + + MTU must be between 68 and 16000 + + 1500 + + diff --git a/interface-definitions/include/interface/parameters-dont-fragment.xml.i b/interface-definitions/include/interface/parameters-dont-fragment.xml.i new file mode 100644 index 000000000..d34f0a97b --- /dev/null +++ b/interface-definitions/include/interface/parameters-dont-fragment.xml.i @@ -0,0 +1,8 @@ + + + + Specifies the usage of the dont fragment (DF) bit + + + + diff --git a/interface-definitions/include/interface/parameters-flowlabel.xml.i b/interface-definitions/include/interface/parameters-flowlabel.xml.i new file mode 100644 index 000000000..7fa571634 --- /dev/null +++ b/interface-definitions/include/interface/parameters-flowlabel.xml.i @@ -0,0 +1,15 @@ + + + + Specifies the flow label to use in outgoing packets + + 0x0-0x0FFFFF + Tunnel key, 'inherit' or hex value + + + ^((0x){0,1}(0?[0-9A-Fa-f]{1,5})|inherit)$ + + Must be 'inherit' or a number + + + diff --git a/interface-definitions/include/interface/parameters-key.xml.i b/interface-definitions/include/interface/parameters-key.xml.i new file mode 100644 index 000000000..25a6c0303 --- /dev/null +++ b/interface-definitions/include/interface/parameters-key.xml.i @@ -0,0 +1,15 @@ + + + + Tunnel key (only GRE tunnels) + + u32 + Tunnel key + + + + + key must be between 0-4294967295 + + + diff --git a/interface-definitions/include/interface/parameters-tos.xml.i b/interface-definitions/include/interface/parameters-tos.xml.i new file mode 100644 index 000000000..83b4e0671 --- /dev/null +++ b/interface-definitions/include/interface/parameters-tos.xml.i @@ -0,0 +1,16 @@ + + + + Specifies TOS value to use in outgoing packets + + 0-99 + Type of Service (TOS) + + + + + TOS must be between 0 and 99 + + inherit + + diff --git a/interface-definitions/include/interface/parameters-ttl.xml.i b/interface-definitions/include/interface/parameters-ttl.xml.i new file mode 100644 index 000000000..da5ce69c2 --- /dev/null +++ b/interface-definitions/include/interface/parameters-ttl.xml.i @@ -0,0 +1,20 @@ + + + + Specifies TTL value to use in outgoing packets + + 0 + Inherit - copy value from original IP header + + + 1-255 + Time to Live + + + + + TTL must be between 0 and 255 + + 0 + + diff --git a/interface-definitions/include/interface/proxy-arp-pvlan.xml.i b/interface-definitions/include/interface/proxy-arp-pvlan.xml.i new file mode 100644 index 000000000..c00b2fe85 --- /dev/null +++ b/interface-definitions/include/interface/proxy-arp-pvlan.xml.i @@ -0,0 +1,8 @@ + + + + Enable private VLAN proxy ARP on this interface + + + + diff --git a/interface-definitions/include/interface/source-validation.xml.i b/interface-definitions/include/interface/source-validation.xml.i new file mode 100644 index 000000000..f38065f4d --- /dev/null +++ b/interface-definitions/include/interface/source-validation.xml.i @@ -0,0 +1,25 @@ + + + + Source validation by reversed path (RFC3704) + + strict loose disable + + + strict + Enable Strict Reverse Path Forwarding as defined in RFC3704 + + + loose + Enable Loose Reverse Path Forwarding as defined in RFC3704 + + + disable + No source validation + + + ^(strict|loose|disable)$ + + + + diff --git a/interface-definitions/include/interface/vif-s.xml.i b/interface-definitions/include/interface/vif-s.xml.i index 17d1746be..3fd69d9d1 100644 --- a/interface-definitions/include/interface/vif-s.xml.i +++ b/interface-definitions/include/interface/vif-s.xml.i @@ -9,11 +9,11 @@ #include - #include + #include #include #include - #include - #include + #include + #include Protocol used for service VLAN (default: 802.1ad) @@ -35,10 +35,10 @@ 802.1ad - #include - #include - #include - #include + #include + #include + #include + #include QinQ TAG-C Virtual Local Area Network (VLAN) ID @@ -49,19 +49,19 @@ #include - #include + #include #include #include - #include - #include - #include - #include - #include - #include - #include + #include + #include + #include + #include + #include + #include + #include - #include + #include diff --git a/interface-definitions/include/interface/vif.xml.i b/interface-definitions/include/interface/vif.xml.i index 9e89cbbf6..8daafeaf4 100644 --- a/interface-definitions/include/interface/vif.xml.i +++ b/interface-definitions/include/interface/vif.xml.i @@ -13,12 +13,12 @@ #include - #include + #include #include #include - #include - #include - #include + #include + #include + #include VLAN egress QoS @@ -45,10 +45,10 @@ QoS mapping should be in the format of '0:7 2:3' with numbers 0-9 - #include - #include - #include - #include + #include + #include + #include + #include diff --git a/interface-definitions/include/interface/vrf.xml.i b/interface-definitions/include/interface/vrf.xml.i new file mode 100644 index 000000000..5ad978a27 --- /dev/null +++ b/interface-definitions/include/interface/vrf.xml.i @@ -0,0 +1,14 @@ + + + + VRF instance name + + text + VRF instance name + + + vrf name + + + + diff --git a/interface-definitions/include/interface/xdp.xml.i b/interface-definitions/include/interface/xdp.xml.i new file mode 100644 index 000000000..10223e766 --- /dev/null +++ b/interface-definitions/include/interface/xdp.xml.i @@ -0,0 +1,8 @@ + + + + Enable eXpress Data Path + + + + diff --git a/interface-definitions/interfaces-bonding.xml.in b/interface-definitions/interfaces-bonding.xml.in index 4bfc6e730..05e0d8461 100644 --- a/interface-definitions/interfaces-bonding.xml.in +++ b/interface-definitions/interfaces-bonding.xml.in @@ -49,13 +49,13 @@ - #include + #include #include #include - #include - #include - #include - #include + #include + #include + #include + #include Bonding transmit hash policy @@ -89,9 +89,9 @@ layer2 - #include - #include - #include + #include + #include + #include Minimum number of member interfaces required up before enabling bond @@ -182,7 +182,7 @@ - #include + #include Primary device interface @@ -193,7 +193,7 @@ #include #include - #include + #include diff --git a/interface-definitions/interfaces-bridge.xml.in b/interface-definitions/interfaces-bridge.xml.in index 1af002142..ddfc5ade4 100644 --- a/interface-definitions/interfaces-bridge.xml.in +++ b/interface-definitions/interfaces-bridge.xml.in @@ -34,13 +34,13 @@ 300 - #include + #include #include #include - #include - #include - #include - #include + #include + #include + #include + #include Forwarding delay @@ -82,10 +82,10 @@ - #include - #include - #include - #include + #include + #include + #include + #include Enable VLAN aware bridge diff --git a/interface-definitions/interfaces-dummy.xml.in b/interface-definitions/interfaces-dummy.xml.in index 84c6903c7..2bc88c1a7 100644 --- a/interface-definitions/interfaces-dummy.xml.in +++ b/interface-definitions/interfaces-dummy.xml.in @@ -17,17 +17,17 @@ #include - #include - #include + #include + #include IPv4 routing parameters - #include + #include - #include + #include diff --git a/interface-definitions/interfaces-ethernet.xml.in b/interface-definitions/interfaces-ethernet.xml.in index cb451f5be..ca076e3fa 100644 --- a/interface-definitions/interfaces-ethernet.xml.in +++ b/interface-definitions/interfaces-ethernet.xml.in @@ -20,7 +20,7 @@ #include - #include + #include #include #include @@ -29,8 +29,8 @@ - #include - #include + #include + #include Duplex mode @@ -56,13 +56,13 @@ auto - #include - #include - #include - #include - #include - #include - #include + #include + #include + #include + #include + #include + #include + #include Configurable offload options @@ -202,8 +202,8 @@ #include #include - #include - #include + #include + #include diff --git a/interface-definitions/interfaces-geneve.xml.in b/interface-definitions/interfaces-geneve.xml.in index bdcbc3f5e..2ca7dd9f6 100644 --- a/interface-definitions/interfaces-geneve.xml.in +++ b/interface-definitions/interfaces-geneve.xml.in @@ -17,12 +17,12 @@ #include - #include - #include - #include - #include - #include - #include + #include + #include + #include + #include + #include + #include GENEVE tunnel parameters @@ -33,9 +33,9 @@ IPv4 specific tunnel parameters - #include - #include - #include + #include + #include + #include @@ -43,7 +43,7 @@ IPv6 specific tunnel parameters - #include + #include diff --git a/interface-definitions/interfaces-l2tpv3.xml.in b/interface-definitions/interfaces-l2tpv3.xml.in index 8835a6b1d..9edc98ef6 100644 --- a/interface-definitions/interfaces-l2tpv3.xml.in +++ b/interface-definitions/interfaces-l2tpv3.xml.in @@ -17,7 +17,7 @@ #include - #include + #include UDP destination port for L2TPv3 tunnel (default: 5000) @@ -31,7 +31,7 @@ 5000 - #include + #include Encapsulation type (default: UDP) @@ -53,10 +53,10 @@ udp - #include - #include + #include + #include #include - #include + #include 1488 @@ -84,7 +84,7 @@ - #include + #include #include @@ -123,7 +123,7 @@ - #include + #include diff --git a/interface-definitions/interfaces-loopback.xml.in b/interface-definitions/interfaces-loopback.xml.in index 5d0ca5b0a..7be15ab89 100644 --- a/interface-definitions/interfaces-loopback.xml.in +++ b/interface-definitions/interfaces-loopback.xml.in @@ -17,13 +17,13 @@ #include - #include + #include IPv4 routing parameters - #include + #include diff --git a/interface-definitions/interfaces-macsec.xml.in b/interface-definitions/interfaces-macsec.xml.in index fce88b21c..e88cb4794 100644 --- a/interface-definitions/interfaces-macsec.xml.in +++ b/interface-definitions/interfaces-macsec.xml.in @@ -17,8 +17,8 @@ #include - #include - #include + #include + #include Security/Encryption Settings @@ -111,14 +111,14 @@ - #include - #include - #include + #include + #include + #include 1460 #include - #include + #include diff --git a/interface-definitions/interfaces-openvpn.xml.in b/interface-definitions/interfaces-openvpn.xml.in index 286b10f9a..01e6bf2fb 100644 --- a/interface-definitions/interfaces-openvpn.xml.in +++ b/interface-definitions/interfaces-openvpn.xml.in @@ -33,7 +33,7 @@ - #include + #include OpenVPN interface device-type (default: tun) @@ -54,7 +54,7 @@ tun - #include + #include Data Encryption settings @@ -165,7 +165,7 @@ - #include + #include Hashing Algorithm @@ -740,7 +740,7 @@ - #include + #include diff --git a/interface-definitions/interfaces-pppoe.xml.in b/interface-definitions/interfaces-pppoe.xml.in index 96479e057..1bbfa63af 100644 --- a/interface-definitions/interfaces-pppoe.xml.in +++ b/interface-definitions/interfaces-pppoe.xml.in @@ -26,7 +26,7 @@ #include - #include + #include Default route insertion behaviour (default: auto) @@ -53,9 +53,9 @@ auto #include - #include - #include - #include + #include + #include + #include Delay before disconnecting idle session (in seconds) @@ -70,7 +70,7 @@ IPv4 routing parameters - #include + #include @@ -108,7 +108,7 @@ - #include + #include 1492 diff --git a/interface-definitions/interfaces-pseudo-ethernet.xml.in b/interface-definitions/interfaces-pseudo-ethernet.xml.in index 136841290..366892032 100644 --- a/interface-definitions/interfaces-pseudo-ethernet.xml.in +++ b/interface-definitions/interfaces-pseudo-ethernet.xml.in @@ -17,16 +17,16 @@ #include - #include + #include #include #include - #include - #include - #include - #include - #include + #include + #include + #include + #include + #include #include - #include + #include Receive mode (default: private) @@ -56,7 +56,7 @@ private - #include + #include #include #include diff --git a/interface-definitions/interfaces-tunnel.xml.in b/interface-definitions/interfaces-tunnel.xml.in index b994bdafc..c059ef624 100644 --- a/interface-definitions/interfaces-tunnel.xml.in +++ b/interface-definitions/interfaces-tunnel.xml.in @@ -16,17 +16,17 @@ - #include + #include #include - #include - #include - #include - #include + #include + #include + #include + #include 1476 - #include - #include + #include + #include #include #include #include @@ -216,9 +216,9 @@ - #include - #include - #include + #include + #include + #include 64 @@ -251,7 +251,7 @@ 4 - #include + #include Hoplimit diff --git a/interface-definitions/interfaces-vti.xml.in b/interface-definitions/interfaces-vti.xml.in index 10e1feb6b..1b9e33d57 100644 --- a/interface-definitions/interfaces-vti.xml.in +++ b/interface-definitions/interfaces-vti.xml.in @@ -29,10 +29,10 @@ - #include - #include - #include - #include + #include + #include + #include + #include diff --git a/interface-definitions/interfaces-vxlan.xml.in b/interface-definitions/interfaces-vxlan.xml.in index 1714f1976..43b73a2e9 100644 --- a/interface-definitions/interfaces-vxlan.xml.in +++ b/interface-definitions/interfaces-vxlan.xml.in @@ -17,8 +17,8 @@ #include - #include - #include + #include + #include Multicast group address for VXLAN interface @@ -35,10 +35,10 @@ - #include - #include - #include - #include + #include + #include + #include + #include 1450 @@ -52,9 +52,9 @@ IPv4 specific tunnel parameters - #include - #include - #include + #include + #include + #include 16 @@ -65,7 +65,7 @@ IPv6 specific tunnel parameters - #include + #include @@ -83,7 +83,7 @@ #include #include #include - #include + #include #include diff --git a/interface-definitions/interfaces-wireguard.xml.in b/interface-definitions/interfaces-wireguard.xml.in index 7b6c717b9..ecb4cf331 100644 --- a/interface-definitions/interfaces-wireguard.xml.in +++ b/interface-definitions/interfaces-wireguard.xml.in @@ -17,16 +17,16 @@ #include - #include - #include - #include + #include + #include + #include #include - #include + #include 1420 - #include - #include + #include + #include A 32-bit fwmark value set on all outgoing packets diff --git a/interface-definitions/interfaces-wireless.xml.in b/interface-definitions/interfaces-wireless.xml.in index aaeb285f1..c96d9b78d 100644 --- a/interface-definitions/interfaces-wireless.xml.in +++ b/interface-definitions/interfaces-wireless.xml.in @@ -464,7 +464,7 @@ Invalid ISO/IEC 3166-1 Country Code - #include + #include #include #include @@ -473,25 +473,25 @@ - #include - #include - #include + #include + #include + #include Disassociate stations based on excessive transmission failures - #include - #include - #include + #include + #include + #include Isolate stations on the AP so they cannot see each other - #include + #include Maximum number of wireless radio stations. Excess stations will be rejected upon authentication request. diff --git a/interface-definitions/interfaces-wwan.xml.in b/interface-definitions/interfaces-wwan.xml.in index ea3184a11..6b6fa1a66 100644 --- a/interface-definitions/interfaces-wwan.xml.in +++ b/interface-definitions/interfaces-wwan.xml.in @@ -28,17 +28,17 @@ #include #include #include - #include - #include - #include - #include - #include + #include + #include + #include + #include + #include 1430 - #include - #include - #include + #include + #include + #include diff --git a/interface-definitions/ntp.xml.in b/interface-definitions/ntp.xml.in index 2bfac900b..a518a9def 100644 --- a/interface-definitions/ntp.xml.in +++ b/interface-definitions/ntp.xml.in @@ -82,7 +82,7 @@ #include - #include + #include diff --git a/interface-definitions/service_console-server.xml.in b/interface-definitions/service_console-server.xml.in index 78eb2d0ba..28aa7ea71 100644 --- a/interface-definitions/service_console-server.xml.in +++ b/interface-definitions/service_console-server.xml.in @@ -27,7 +27,7 @@ - #include + #include Serial port baud rate diff --git a/interface-definitions/snmp.xml.in b/interface-definitions/snmp.xml.in index 2654449a1..b0b7768d2 100644 --- a/interface-definitions/snmp.xml.in +++ b/interface-definitions/snmp.xml.in @@ -646,7 +646,7 @@ - #include + #include diff --git a/interface-definitions/ssh.xml.in b/interface-definitions/ssh.xml.in index 54742f1d0..c447f144d 100644 --- a/interface-definitions/ssh.xml.in +++ b/interface-definitions/ssh.xml.in @@ -146,7 +146,7 @@ - #include + #include diff --git a/interface-definitions/system-login.xml.in b/interface-definitions/system-login.xml.in index 86db3f368..fb34b7199 100644 --- a/interface-definitions/system-login.xml.in +++ b/interface-definitions/system-login.xml.in @@ -145,7 +145,7 @@ - #include + #include diff --git a/interface-definitions/vpn_sstp.xml.in b/interface-definitions/vpn_sstp.xml.in index 3576bac90..5cd331d7f 100644 --- a/interface-definitions/vpn_sstp.xml.in +++ b/interface-definitions/vpn_sstp.xml.in @@ -25,7 +25,7 @@ - #include + #include #include #include diff --git a/interface-definitions/vrf.xml.in b/interface-definitions/vrf.xml.in index 76d6df386..2ed50ec5c 100644 --- a/interface-definitions/vrf.xml.in +++ b/interface-definitions/vrf.xml.in @@ -26,8 +26,8 @@ - #include - #include + #include + #include Routing protocol parameters -- cgit v1.2.3 From fa51dafe4dcad975762cff3c390e400ca09af41a Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Fri, 20 Aug 2021 18:05:09 +0200 Subject: smoketest: bgp: add support to extract only given SAFI from FRR --- smoketest/scripts/cli/base_vyostest_shim.py | 4 ++-- smoketest/scripts/cli/test_protocols_bgp.py | 28 +++++++++++++++++----------- 2 files changed, 19 insertions(+), 13 deletions(-) diff --git a/smoketest/scripts/cli/base_vyostest_shim.py b/smoketest/scripts/cli/base_vyostest_shim.py index 419530c3d..50f80e7d1 100644 --- a/smoketest/scripts/cli/base_vyostest_shim.py +++ b/smoketest/scripts/cli/base_vyostest_shim.py @@ -76,9 +76,9 @@ class VyOSUnitTestSHIM: while run(f'sudo lsof | grep -q {commit_lock}') == 0: sleep(0.250) - def getFRRconfig(self, string, end='$', endsection='^!'): + def getFRRconfig(self, string, end='$', endsection='^!', daemon=''): """ Retrieve current "running configuration" from FRR """ - command = f'vtysh -c "show run" | sed -n "/^{string}{end}/,/{endsection}/p"' + command = f'vtysh -c "show run {daemon} no-header" | sed -n "/^{string}{end}/,/{endsection}/p"' out = cmd(command) if self.debug: import pprint diff --git a/smoketest/scripts/cli/test_protocols_bgp.py b/smoketest/scripts/cli/test_protocols_bgp.py index d1492e831..73faf196f 100755 --- a/smoketest/scripts/cli/test_protocols_bgp.py +++ b/smoketest/scripts/cli/test_protocols_bgp.py @@ -741,8 +741,11 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase): self.cli_set(base_path + ['address-family', afi, 'export', 'vpn']) self.cli_set(base_path + ['address-family', afi, 'import', 'vpn']) self.cli_set(base_path + ['address-family', afi, 'label', 'vpn', 'export', label]) + self.cli_set(base_path + ['address-family', afi, 'rd', 'vpn', 'export', rd]) + self.cli_set(base_path + ['address-family', afi, 'route-map', 'vpn', 'export', route_map_out]) + self.cli_set(base_path + ['address-family', afi, 'route-map', 'vpn', 'import', route_map_in]) + - self.cli_set(base_path + ['address-family', 'ipv4-unicast', 'rd', 'vpn', 'export', rd]) self.cli_set(base_path + ['address-family', 'ipv4-unicast', 'route-target', 'vpn', 'export', rt_export]) self.cli_set(base_path + ['address-family', 'ipv4-unicast', 'route-target', 'vpn', 'import', rt_import]) @@ -752,17 +755,20 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase): # Verify FRR bgpd configuration frrconfig = self.getFRRconfig(f'router bgp {ASN}') self.assertIn(f'router bgp {ASN}', frrconfig) - for afi in ['ipv4', 'ipv6']: - self.assertIn(f' address-family {afi} unicast', frrconfig) - self.assertIn(f' export vpn', frrconfig) - self.assertIn(f' import vpn', frrconfig) - self.assertIn(f' label vpn export {label}', frrconfig) - self.assertIn(f' exit-address-family', frrconfig) - self.assertIn(f' address-family ipv4 unicast', frrconfig) - self.assertIn(f' rd vpn export {rd}', frrconfig) - self.assertIn(f' rt vpn export {rt_export}', frrconfig) - self.assertIn(f' rt vpn import {rt_import}', frrconfig) + for afi in ['ipv4', 'ipv6']: + afi_config = self.getFRRconfig(f' address-family {afi} unicast', endsection='exit-address-family', daemon='bgpd') + self.assertIn(f'address-family {afi} unicast', afi_config) + self.assertIn(f' export vpn', afi_config) + self.assertIn(f' import vpn', afi_config) + self.assertIn(f' label vpn export {label}', afi_config) + self.assertIn(f' rd vpn export {rd}', afi_config) + self.assertIn(f' exit-address-family', afi_config) + + afi_config = self.getFRRconfig(f' address-family ipv4 unicast', endsection='exit-address-family', daemon='bgpd') + self.assertIn(f'address-family ipv4 unicast', afi_config) + self.assertIn(f' rt vpn export {rt_export}', afi_config) + self.assertIn(f' rt vpn import {rt_import}', afi_config) if __name__ == '__main__': unittest.main(verbosity=2) \ No newline at end of file -- cgit v1.2.3 From 37331e3dc629bfd8a3ddd196ff738c5670cdbac6 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Fri, 20 Aug 2021 18:14:58 +0200 Subject: smoketest: bgp: movo test for "solo" option to regular neighbor testcase --- smoketest/scripts/cli/test_protocols_bgp.py | 24 ++++++------------------ 1 file changed, 6 insertions(+), 18 deletions(-) diff --git a/smoketest/scripts/cli/test_protocols_bgp.py b/smoketest/scripts/cli/test_protocols_bgp.py index 73faf196f..dbe1a81d6 100755 --- a/smoketest/scripts/cli/test_protocols_bgp.py +++ b/smoketest/scripts/cli/test_protocols_bgp.py @@ -78,6 +78,7 @@ neighbor_config = { 'cap_over' : '', 'ttl_security' : '5', 'local_as' : '300', + 'solo' : '', 'route_map_in' : route_map_in, 'route_map_out': route_map_out, 'no_send_comm_std' : '', @@ -173,6 +174,8 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase): self.assertIn(f' neighbor {peer} password {peer_config["password"]}', frrconfig) if 'remote_as' in peer_config: self.assertIn(f' neighbor {peer} remote-as {peer_config["remote_as"]}', frrconfig) + if 'solo' in peer_config: + self.assertIn(f' neighbor {peer} solo', frrconfig) if 'shutdown' in peer_config: self.assertIn(f' neighbor {peer} shutdown', frrconfig) if 'ttl_security' in peer_config: @@ -296,6 +299,8 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase): self.cli_set(base_path + ['neighbor', peer, 'strict-capability-match']) if 'shutdown' in peer_config: self.cli_set(base_path + ['neighbor', peer, 'shutdown']) + if 'solo' in peer_config: + self.cli_set(base_path + ['neighbor', peer, 'solo']) if 'ttl_security' in peer_config: self.cli_set(base_path + ['neighbor', peer, 'ttl-security', 'hops', peer_config["ttl_security"]]) if 'update_src' in peer_config: @@ -709,24 +714,7 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase): self.assertIn(f' exit-address-family', frrconfig) - def test_bgp_13_solo(self): - remote_asn = str(int(ASN) + 150) - neighbor = '192.0.2.55' - - self.cli_set(base_path + ['local-as', ASN]) - self.cli_set(base_path + ['neighbor', neighbor, 'remote-as', remote_asn]) - self.cli_set(base_path + ['neighbor', neighbor, 'solo']) - - # commit changes - self.cli_commit() - - # Verify FRR bgpd configuration - frrconfig = self.getFRRconfig(f'router bgp {ASN}') - self.assertIn(f'router bgp {ASN}', frrconfig) - self.assertIn(f' neighbor {neighbor} solo', frrconfig) - - - def test_bgp_14_vpn(self): + def test_bgp_13_vpn(self): remote_asn = str(int(ASN) + 150) neighbor = '192.0.2.55' vrf_name = 'red' -- cgit v1.2.3 From 08d5265b06fc3b011df286df4767349e89770a62 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Fri, 20 Aug 2021 18:16:02 +0200 Subject: bgp: T3759: create common export/import building block for re-usable route-maps --- .../include/bgp/afi-route-map-export-import.xml.i | 34 ++++++++++++++++++++++ .../include/bgp/afi-route-map.xml.i | 33 +-------------------- 2 files changed, 35 insertions(+), 32 deletions(-) create mode 100644 interface-definitions/include/bgp/afi-route-map-export-import.xml.i diff --git a/interface-definitions/include/bgp/afi-route-map-export-import.xml.i b/interface-definitions/include/bgp/afi-route-map-export-import.xml.i new file mode 100644 index 000000000..eae10d312 --- /dev/null +++ b/interface-definitions/include/bgp/afi-route-map-export-import.xml.i @@ -0,0 +1,34 @@ + + + + Route-map to filter outgoing route updates + + policy route-map + + + txt + Route map name + + + ^[-_a-zA-Z0-9.]+$ + + Name of route-map can only contain alpha-numeric letters, hyphen and underscores + + + + + Route-map to filter incoming route updates + + policy route-map + + + txt + Route map name + + + ^[-_a-zA-Z0-9.]+$ + + Name of route-map can only contain alpha-numeric letters, hyphen and underscores + + + diff --git a/interface-definitions/include/bgp/afi-route-map.xml.i b/interface-definitions/include/bgp/afi-route-map.xml.i index 24a5ddd12..0b6178176 100644 --- a/interface-definitions/include/bgp/afi-route-map.xml.i +++ b/interface-definitions/include/bgp/afi-route-map.xml.i @@ -4,38 +4,7 @@ Route-map to filter route updates to/from this peer - - - Route-map to filter outgoing route updates - - policy route-map - - - txt - Route map name - - - ^[-_a-zA-Z0-9.]+$ - - Name of route-map can only contain alpha-numeric letters, hyphen and underscores - - - - - Route-map to filter incoming route updates - - 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 -- cgit v1.2.3 From 2591adc9ad25906bf58b42a70409d40d8b339ed6 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Fri, 20 Aug 2021 18:16:54 +0200 Subject: bgp: T3759: fix DOS -> UNIX line endings on route-target building blocks --- .../include/bgp/route-target-both.xml.i | 28 +++++++++++----------- .../include/bgp/route-target-export.xml.i | 28 +++++++++++----------- .../include/bgp/route-target-import.xml.i | 28 +++++++++++----------- 3 files changed, 42 insertions(+), 42 deletions(-) diff --git a/interface-definitions/include/bgp/route-target-both.xml.i b/interface-definitions/include/bgp/route-target-both.xml.i index 2381f420f..d77878812 100644 --- a/interface-definitions/include/bgp/route-target-both.xml.i +++ b/interface-definitions/include/bgp/route-target-both.xml.i @@ -1,14 +1,14 @@ - - - - Route Target both import and export - - txt - Route target (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}$ - - - - + + + + Route Target both import and export + + txt + Route target (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/interface-definitions/include/bgp/route-target-export.xml.i b/interface-definitions/include/bgp/route-target-export.xml.i index 0542000a5..0431f0fcb 100644 --- a/interface-definitions/include/bgp/route-target-export.xml.i +++ b/interface-definitions/include/bgp/route-target-export.xml.i @@ -1,14 +1,14 @@ - - - - Route Target export - - txt - Route target (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}$ - - - - + + + + Route Target export + + txt + Route target (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/interface-definitions/include/bgp/route-target-import.xml.i b/interface-definitions/include/bgp/route-target-import.xml.i index 6a954885c..aa861c428 100644 --- a/interface-definitions/include/bgp/route-target-import.xml.i +++ b/interface-definitions/include/bgp/route-target-import.xml.i @@ -1,14 +1,14 @@ - - - - Route Target import - - txt - Route target (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}$ - - - - + + + + Route Target import + + txt + Route target (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}$ + + + + -- cgit v1.2.3 From f7e3f5562458da4d80b272f1524c27727799e57b Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Fri, 20 Aug 2021 18:17:54 +0200 Subject: bgp: T3759: add IPv4/IPv6 unicast AFI route-map for VPN import/export This adds the following new commands: set protocols bgp address-family ipv4-unicast route-map vpn export foo-map-out set protocols bgp address-family ipv4-unicast route-map vpn import foo-map-in set protocols bgp address-family ipv6-unicast route-map vpn export foo-map-out set protocols bgp address-family ipv6-unicast route-map vpn import foo-map-in --- data/templates/frr/bgpd.frr.tmpl | 8 ++++++++ .../include/bgp/afi-route-map-vpn.xml.i | 17 +++++++++++++++++ .../include/bgp/protocol-common-config.xml.i | 2 ++ smoketest/scripts/cli/test_protocols_bgp.py | 2 ++ src/conf_mode/protocols_bgp.py | 5 +++++ 5 files changed, 34 insertions(+) create mode 100644 interface-definitions/include/bgp/afi-route-map-vpn.xml.i diff --git a/data/templates/frr/bgpd.frr.tmpl b/data/templates/frr/bgpd.frr.tmpl index 143960e6b..96815836b 100644 --- a/data/templates/frr/bgpd.frr.tmpl +++ b/data/templates/frr/bgpd.frr.tmpl @@ -380,6 +380,14 @@ router bgp {{ local_as }} {{ 'vrf ' ~ vrf if vrf is defined and vrf is not none {% endif %} {% endif %} {% endif %} +{% if afi_config.route_map is defined and afi_config.route_map.vpn is defined and afi_config.route_map.vpn is not none %} +{% if afi_config.route_map.vpn.export is defined and afi_config.route_map.vpn.export is not none %} + route-map vpn export {{ afi_config.route_map.vpn.export }} +{% endif %} +{% if afi_config.route_map.vpn.import is defined and afi_config.route_map.vpn.import is not none %} + route-map vpn import {{ afi_config.route_map.vpn.import }} +{% endif %} +{% endif %} {% if afi_config.vni is defined and afi_config.vni is not none %} {% for vni, vni_config in afi_config.vni.items() %} vni {{ vni }} diff --git a/interface-definitions/include/bgp/afi-route-map-vpn.xml.i b/interface-definitions/include/bgp/afi-route-map-vpn.xml.i new file mode 100644 index 000000000..e6be113c5 --- /dev/null +++ b/interface-definitions/include/bgp/afi-route-map-vpn.xml.i @@ -0,0 +1,17 @@ + + + + Route-map to filter route updates to/from this peer + + + + + Between current address-family and VPN + + + #include + + + + + diff --git a/interface-definitions/include/bgp/protocol-common-config.xml.i b/interface-definitions/include/bgp/protocol-common-config.xml.i index 6fb9adf93..a971c52b8 100644 --- a/interface-definitions/include/bgp/protocol-common-config.xml.i +++ b/interface-definitions/include/bgp/protocol-common-config.xml.i @@ -118,6 +118,7 @@ #include + #include Specify route distinguisher @@ -518,6 +519,7 @@ #include + #include Redistribute routes from other protocols into BGP diff --git a/smoketest/scripts/cli/test_protocols_bgp.py b/smoketest/scripts/cli/test_protocols_bgp.py index dbe1a81d6..f535408db 100755 --- a/smoketest/scripts/cli/test_protocols_bgp.py +++ b/smoketest/scripts/cli/test_protocols_bgp.py @@ -751,6 +751,8 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase): self.assertIn(f' import vpn', afi_config) self.assertIn(f' label vpn export {label}', afi_config) self.assertIn(f' rd vpn export {rd}', afi_config) + self.assertIn(f' route-map vpn export {route_map_out}', afi_config) + self.assertIn(f' route-map vpn import {route_map_in}', afi_config) self.assertIn(f' exit-address-family', afi_config) afi_config = self.getFRRconfig(f' address-family ipv4 unicast', endsection='exit-address-family', daemon='bgpd') diff --git a/src/conf_mode/protocols_bgp.py b/src/conf_mode/protocols_bgp.py index bc7acaf6c..7d05eed9f 100755 --- a/src/conf_mode/protocols_bgp.py +++ b/src/conf_mode/protocols_bgp.py @@ -250,6 +250,11 @@ def verify(bgp): raise ConfigError('Please unconfigure VPN to VRF commands before '\ 'using "import vrf" commands!') + # Verify that the export/import route-maps do exist + for export_import in ['export', 'import']: + tmp = dict_search(f'route_map.vpn.{export_import}', afi_config) + if tmp: verify_route_map(tmp, bgp) + if afi in ['l2vpn_evpn'] and 'vrf' not in bgp: # Some L2VPN EVPN AFI options are only supported under VRF if 'vni' in afi_config: -- cgit v1.2.3 From 1a498915efdc433dda7bd6e5fcc08703a48560c6 Mon Sep 17 00:00:00 2001 From: John Estabrook Date: Thu, 19 Aug 2021 14:30:32 -0500 Subject: T1950: write component versions to json file during migration --- python/vyos/defaults.py | 3 +++ python/vyos/migrator.py | 12 ++++++++++++ 2 files changed, 15 insertions(+) diff --git a/python/vyos/defaults.py b/python/vyos/defaults.py index 2c24e007f..dacdbdef2 100644 --- a/python/vyos/defaults.py +++ b/python/vyos/defaults.py @@ -13,6 +13,7 @@ # You should have received a copy of the GNU Lesser General Public # License along with this library. If not, see . +import os directories = { "data": "/usr/share/vyos/", @@ -34,6 +35,8 @@ cfg_vintage = 'vyos' commit_lock = '/opt/vyatta/config/.lock' +component_version_json = os.path.join(directories['data'], 'component-versions.json') + https_data = { 'listen_addresses' : { '*': ['_'] } } diff --git a/python/vyos/migrator.py b/python/vyos/migrator.py index 9a5fdef2f..37c4e5902 100644 --- a/python/vyos/migrator.py +++ b/python/vyos/migrator.py @@ -15,6 +15,7 @@ import sys import os +import json import subprocess import vyos.version import vyos.defaults @@ -165,6 +166,14 @@ class Migrator(object): versions_string, os_version_string) + def save_json_record(self, component_versions: dict): + """ + Write component versions to a json file + """ + version_file = vyos.defaults.component_version_json + with open(version_file, 'w') as f: + f.write(json.dumps(component_versions, indent=2, sort_keys=True)) + def run(self): """ Gather component versions from config file and system. @@ -182,6 +191,9 @@ class Migrator(object): sys_versions = systemversions.get_system_versions() + # save system component versions in json file for easy reference + self.save_json_record(sys_versions) + rev_versions = self.run_migration_scripts(cfg_versions, sys_versions) if rev_versions != cfg_versions: -- cgit v1.2.3 From 9fa00d537c5d7d0a5109907bba7d1825ef245ed1 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Fri, 20 Aug 2021 19:07:57 +0200 Subject: Debian: disable time consuming dh_strip_nondeterminism Skip dh_strip_nondeterminism - this is very time consuming and we have no non deterministic output (yet). --- debian/rules | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/debian/rules b/debian/rules index 70d39c481..c7a7138e1 100755 --- a/debian/rules +++ b/debian/rules @@ -18,6 +18,10 @@ DEB_TARGET_ARCH := $(shell dpkg-architecture -qDEB_TARGET_ARCH) %: dh $@ --with python3, --with quilt +# Skip dh_strip_nondeterminism - this is very time consuming +# and we have no non deterministic output (yet) +override_dh_strip_nondeterminism: + override_dh_gencontrol: dh_gencontrol -- -v$(shell (git describe --tags --long --match 'vyos/*' --dirty 2>/dev/null || echo 0.0-no.git.tag) | sed -E 's%vyos/%%' | sed -E 's%-dirty%+dirty%') -- cgit v1.2.3 From b0d9eb23747c94bd99bffaad1827f8a234cadb8b Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sat, 21 Aug 2021 08:24:48 +0200 Subject: nhrp: T3599: move PID file to /run/opennhrp --- src/systemd/opennhrp.service | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/systemd/opennhrp.service b/src/systemd/opennhrp.service index 70235f89d..c9a44de29 100644 --- a/src/systemd/opennhrp.service +++ b/src/systemd/opennhrp.service @@ -6,8 +6,8 @@ StartLimitIntervalSec=0 [Service] Type=forking -ExecStart=/usr/sbin/opennhrp -d -v -a /run/opennhrp.socket -c /run/opennhrp/opennhrp.conf -s /etc/opennhrp/opennhrp-script.py -p /run/opennhrp.pid +ExecStart=/usr/sbin/opennhrp -d -v -a /run/opennhrp.socket -c /run/opennhrp/opennhrp.conf -s /etc/opennhrp/opennhrp-script.py -p /run/opennhrp/opennhrp.pid ExecReload=/usr/bin/kill -HUP $MAINPID -PIDFile=/run/opennhrp.pid +PIDFile=/run/opennhrp/opennhrp.pid Restart=on-failure RestartSec=20 -- cgit v1.2.3 From aa58d5c58edef40619192d11b0dcafc36b1e5d89 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sat, 21 Aug 2021 08:25:15 +0200 Subject: op-mode: nhrp: T3599: check if process is running instead of checking PID file --- op-mode-definitions/nhrp.xml.in | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/op-mode-definitions/nhrp.xml.in b/op-mode-definitions/nhrp.xml.in index 9e746cc35..89508e2be 100644 --- a/op-mode-definitions/nhrp.xml.in +++ b/op-mode-definitions/nhrp.xml.in @@ -50,13 +50,13 @@ Show NHRP interface connection information - if [ -f /var/run/opennhrp.pid ]; then sudo opennhrpctl interface show; else echo OpenNHRP is not running.; fi + if pgrep opennhrp >/dev/null; then sudo opennhrpctl interface show; else echo OpenNHRP is not running; fi Show NHRP tunnel connection information - if [ -f /var/run/opennhrp.pid ]; then sudo opennhrpctl show ; else echo OpenNHRP is not running.; fi + if pgrep opennhrp >/dev/null; then sudo opennhrpctl show ; else echo OpenNHRP is not running; fi -- cgit v1.2.3 From fa07bc3b87be142572124a2ce5c077cb42272418 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sat, 21 Aug 2021 08:52:22 +0200 Subject: udev: T2490: fix substitution error reported by udev --- src/etc/udev/rules.d/90-vyos-serial.rules | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/etc/udev/rules.d/90-vyos-serial.rules b/src/etc/udev/rules.d/90-vyos-serial.rules index 3f10f4924..872fd4fea 100644 --- a/src/etc/udev/rules.d/90-vyos-serial.rules +++ b/src/etc/udev/rules.d/90-vyos-serial.rules @@ -8,7 +8,7 @@ SUBSYSTEMS=="pci", IMPORT{builtin}="hwdb --subsystem=pci" SUBSYSTEMS=="usb", IMPORT{builtin}="usb_id", IMPORT{builtin}="hwdb --subsystem=usb" # /dev/serial/by-path/, /dev/serial/by-id/ for USB devices -KERNEL!="ttyUSB[0-9]*|ttyACM[0-9]*", GOTO="serial_end" +KERNEL!="ttyUSB[0-9]*", GOTO="serial_end" SUBSYSTEMS=="usb-serial", ENV{.ID_PORT}="$attr{port_number}" @@ -18,11 +18,11 @@ IMPORT{builtin}="path_id", IMPORT{builtin}="usb_id" # # - $env{ID_PATH} usually is a name like: "pci-0000:00:10.0-usb-0:2.3.3.4:1.0-port0" so we strip the "pci-*" # portion and only use the usb part -# - Transform the USB "speach" to the tree like structure so we start with "usb0" as root-complex 0. +# - Transform the USB "speech" to the tree like structure so we start with "usb0" as root-complex 0. # (tr -d -) does the replacement # - Replace the first group after ":" to represent the bus relation (sed -e 0,/:/s//b/) indicated by "b" # - Replace the next group after ":" to represent the port relation (sed -e 0,/:/s//p/) indicated by "p" -ENV{ID_PATH}=="?*", ENV{.ID_PORT}=="", PROGRAM="/bin/sh -c 'D=$env{ID_PATH}; echo ${D:17} | tr -d - | sed -e 0,/:/s//b/ | sed -e 0,/:/s//p/'", SYMLINK+="serial/by-bus/$result" -ENV{ID_PATH}=="?*", ENV{.ID_PORT}=="?*", PROGRAM="/bin/sh -c 'D=$env{ID_PATH}; echo ${D:17} | tr -d - | sed -e 0,/:/s//b/ | sed -e 0,/:/s//p/'", SYMLINK+="serial/by-bus/$result" +ENV{ID_PATH}=="?*", ENV{.ID_PORT}=="", PROGRAM="/bin/sh -c 'echo $env{ID_PATH:17} | tr -d - | sed -e 0,/:/s//b/ | sed -e 0,/:/s//p/'", SYMLINK+="serial/by-bus/$result" +ENV{ID_PATH}=="?*", ENV{.ID_PORT}=="?*", PROGRAM="/bin/sh -c 'echo $env{ID_PATH:17} | tr -d - | sed -e 0,/:/s//b/ | sed -e 0,/:/s//p/'", SYMLINK+="serial/by-bus/$result" LABEL="serial_end" -- cgit v1.2.3 From 6bd780887c0e13dc9272ec499ebc6f01cfaf7ea6 Mon Sep 17 00:00:00 2001 From: John Estabrook Date: Sat, 21 Aug 2021 07:08:30 -0500 Subject: T1950: fix permissions on component-versions.json file --- python/vyos/migrator.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/python/vyos/migrator.py b/python/vyos/migrator.py index 37c4e5902..4574bb6d1 100644 --- a/python/vyos/migrator.py +++ b/python/vyos/migrator.py @@ -170,9 +170,15 @@ class Migrator(object): """ Write component versions to a json file """ + mask = os.umask(0o113) version_file = vyos.defaults.component_version_json - with open(version_file, 'w') as f: - f.write(json.dumps(component_versions, indent=2, sort_keys=True)) + try: + with open(version_file, 'w') as f: + f.write(json.dumps(component_versions, indent=2, sort_keys=True)) + except OSError: + pass + finally: + os.umask(mask) def run(self): """ -- cgit v1.2.3 From b7bfcb6ef0e712bb8c39241051e716a833b2ffe8 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Thu, 19 Aug 2021 18:14:13 +0200 Subject: interfaces: T3090: migrate adjust-mss from "firewall options" to "interface" level Getting rid of "set firewall options" and move it from: set firewall options interface ethX adjust-mss 1400 set firewall options interface ethX adjust-mss6 1400 to: set interfaces ethernet ethX ip adjust-mss 1400 set interfaces ethernet ethX ipv6 adjust-mss 1400 In addition add an extra option called clamp-mss-to-pmtu instead of a value. --- data/configd-include.json | 1 - interface-definitions/firewall-options.xml.in | 50 ------- .../include/interface/adjust-mss.xml.i | 23 ++++ .../include/interface/ipv4-options.xml.i | 1 + .../include/interface/ipv6-options.xml.i | 1 + interface-definitions/interfaces-pppoe.xml.in | 2 + python/vyos/ifconfig/interface.py | 66 +++++++++ python/vyos/ifconfig/pppoe.py | 4 +- smoketest/scripts/cli/base_interfaces_test.py | 22 ++- src/conf_mode/firewall_options.py | 150 --------------------- src/migration-scripts/firewall/5-to-6 | 63 +++++++++ 11 files changed, 177 insertions(+), 206 deletions(-) delete mode 100644 interface-definitions/firewall-options.xml.in create mode 100644 interface-definitions/include/interface/adjust-mss.xml.i delete mode 100755 src/conf_mode/firewall_options.py create mode 100755 src/migration-scripts/firewall/5-to-6 diff --git a/data/configd-include.json b/data/configd-include.json index 2d7ea149b..6893aaa86 100644 --- a/data/configd-include.json +++ b/data/configd-include.json @@ -6,7 +6,6 @@ "dhcpv6_relay.py", "dns_forwarding.py", "dynamic_dns.py", -"firewall_options.py", "host_name.py", "https.py", "igmp_proxy.py", diff --git a/interface-definitions/firewall-options.xml.in b/interface-definitions/firewall-options.xml.in deleted file mode 100644 index 8d9225a9a..000000000 --- a/interface-definitions/firewall-options.xml.in +++ /dev/null @@ -1,50 +0,0 @@ - - - - - - - Firewall options/Packet manipulation - 990 - - - - - Interface clamping options - - - - - - #include - - - Adjust MSS for IPv4 transit packets - - 500-1460 - TCP Maximum segment size in bytes - - - - - - - - - Adjust MSS for IPv6 transit packets - - 1280-1492 - TCP Maximum segment size in bytes - - - - - - - - - - - - - diff --git a/interface-definitions/include/interface/adjust-mss.xml.i b/interface-definitions/include/interface/adjust-mss.xml.i new file mode 100644 index 000000000..57019f02c --- /dev/null +++ b/interface-definitions/include/interface/adjust-mss.xml.i @@ -0,0 +1,23 @@ + + + + + Adjust TCP MSS value + + clamp-mss-to-pmtu + + + clamp-mss-to-pmtu + Automatically sets the MSS to the proper value + + + u32:500-65535 + TCP Maximum segment size in bytes + + + + ^(clamp-mss-to-pmtu)$ + + + + diff --git a/interface-definitions/include/interface/ipv4-options.xml.i b/interface-definitions/include/interface/ipv4-options.xml.i index 10884b6eb..bca1229c6 100644 --- a/interface-definitions/include/interface/ipv4-options.xml.i +++ b/interface-definitions/include/interface/ipv4-options.xml.i @@ -4,6 +4,7 @@ IPv4 routing parameters + #include #include #include #include diff --git a/interface-definitions/include/interface/ipv6-options.xml.i b/interface-definitions/include/interface/ipv6-options.xml.i index e57c242b0..2d2d1d3b2 100644 --- a/interface-definitions/include/interface/ipv6-options.xml.i +++ b/interface-definitions/include/interface/ipv6-options.xml.i @@ -4,6 +4,7 @@ IPv6 routing parameters + #include #include #include #include diff --git a/interface-definitions/interfaces-pppoe.xml.in b/interface-definitions/interfaces-pppoe.xml.in index 1bbfa63af..ac8fa378b 100644 --- a/interface-definitions/interfaces-pppoe.xml.in +++ b/interface-definitions/interfaces-pppoe.xml.in @@ -70,6 +70,7 @@ IPv4 routing parameters + #include #include @@ -86,6 +87,7 @@ #include + #include diff --git a/python/vyos/ifconfig/interface.py b/python/vyos/ifconfig/interface.py index a1928ba51..53b57a83f 100755 --- a/python/vyos/ifconfig/interface.py +++ b/python/vyos/ifconfig/interface.py @@ -436,6 +436,62 @@ class Interface(Control): """ return self.set_interface('arp_cache_tmo', tmo) + def set_tcp_ipv4_mss(self, mss): + """ + Set IPv4 TCP MSS value advertised when TCP SYN packets leave this + interface. Value is in bytes. + + A value of 0 will disable the MSS adjustment + + Example: + >>> from vyos.ifconfig import Interface + >>> Interface('eth0').set_tcp_ipv4_mss(1340) + """ + iptables_bin = 'iptables' + base_options = f'-A FORWARD -o {self.ifname} -p tcp -m tcp --tcp-flags SYN,RST SYN' + out = self._cmd(f'{iptables_bin}-save -t mangle') + for line in out.splitlines(): + if line.startswith(base_options): + # remove OLD MSS mangling configuration + line = line.replace('-A FORWARD', '-D FORWARD') + self._cmd(f'{iptables_bin} -t mangle {line}') + + cmd_mss = f'{iptables_bin} -t mangle {base_options} --jump TCPMSS' + if mss == 'clamp-mss-to-pmtu': + self._cmd(f'{cmd_mss} --clamp-mss-to-pmtu') + elif int(mss) > 0: + # probably add option to clamp only if bigger: + low_mss = str(int(mss) + 1) + self._cmd(f'{cmd_mss} -m tcpmss --mss {low_mss}:65535 --set-mss {mss}') + + def set_tcp_ipv6_mss(self, mss): + """ + Set IPv6 TCP MSS value advertised when TCP SYN packets leave this + interface. Value is in bytes. + + A value of 0 will disable the MSS adjustment + + Example: + >>> from vyos.ifconfig import Interface + >>> Interface('eth0').set_tcp_mss(1320) + """ + iptables_bin = 'ip6tables' + base_options = f'-A FORWARD -o {self.ifname} -p tcp -m tcp --tcp-flags SYN,RST SYN' + out = self._cmd(f'{iptables_bin}-save -t mangle') + for line in out.splitlines(): + if line.startswith(base_options): + # remove OLD MSS mangling configuration + line = line.replace('-A FORWARD', '-D FORWARD') + self._cmd(f'{iptables_bin} -t mangle {line}') + + cmd_mss = f'{iptables_bin} -t mangle {base_options} --jump TCPMSS' + if mss == 'clamp-mss-to-pmtu': + self._cmd(f'{cmd_mss} --clamp-mss-to-pmtu') + elif int(mss) > 0: + # probably add option to clamp only if bigger: + low_mss = str(int(mss) + 1) + self._cmd(f'{cmd_mss} -m tcpmss --mss {low_mss}:65535 --set-mss {mss}') + def set_arp_filter(self, arp_filter): """ Filter ARP requests @@ -1202,6 +1258,16 @@ class Interface(Control): # checked before self.set_vrf(config.get('vrf', '')) + # Configure MSS value for IPv4 TCP connections + tmp = dict_search('ip.adjust_mss', config) + value = tmp if (tmp != None) else '0' + self.set_tcp_ipv4_mss(value) + + # Configure MSS value for IPv6 TCP connections + tmp = dict_search('ipv6.adjust_mss', config) + value = tmp if (tmp != None) else '0' + self.set_tcp_ipv6_mss(value) + # Configure ARP cache timeout in milliseconds - has default value tmp = dict_search('ip.arp_cache_timeout', config) value = tmp if (tmp != None) else '30' diff --git a/python/vyos/ifconfig/pppoe.py b/python/vyos/ifconfig/pppoe.py index 65575cf99..6acf7d1c7 100644 --- a/python/vyos/ifconfig/pppoe.py +++ b/python/vyos/ifconfig/pppoe.py @@ -17,9 +17,7 @@ from vyos.ifconfig.interface import Interface @Interface.register class PPPoEIf(Interface): - default = { - 'type': 'pppoe', - } + iftype = 'pppoe' definition = { **Interface.definition, **{ diff --git a/smoketest/scripts/cli/base_interfaces_test.py b/smoketest/scripts/cli/base_interfaces_test.py index 7f69b8444..63f742a8d 100644 --- a/smoketest/scripts/cli/base_interfaces_test.py +++ b/smoketest/scripts/cli/base_interfaces_test.py @@ -556,13 +556,16 @@ class BasicInterfaceTest: if not self._test_ip: self.skipTest('not supported') + arp_tmo = '300' + mss = '1420' + for interface in self._interfaces: - arp_tmo = '300' path = self._base_path + [interface] for option in self._options.get(interface, []): self.cli_set(path + option.split()) # Options + self.cli_set(path + ['ip', 'adjust-mss', mss]) self.cli_set(path + ['ip', 'arp-cache-timeout', arp_tmo]) self.cli_set(path + ['ip', 'disable-arp-filter']) self.cli_set(path + ['ip', 'disable-forwarding']) @@ -576,6 +579,12 @@ class BasicInterfaceTest: self.cli_commit() for interface in self._interfaces: + base_options = f'-A FORWARD -o {interface} -p tcp -m tcp --tcp-flags SYN,RST SYN' + out = cmd('sudo iptables-save -t mangle') + for line in out.splitlines(): + if line.startswith(base_options): + self.assertIn(f'--set-mss {mss}', line) + tmp = read_file(f'/proc/sys/net/ipv4/neigh/{interface}/base_reachable_time_ms') self.assertEqual(tmp, str((int(arp_tmo) * 1000))) # tmo value is in milli seconds @@ -607,19 +616,28 @@ class BasicInterfaceTest: if not self._test_ipv6: self.skipTest('not supported') + mss = '1400' + dad_transmits = '10' + for interface in self._interfaces: - dad_transmits = '10' path = self._base_path + [interface] for option in self._options.get(interface, []): self.cli_set(path + option.split()) # Options + self.cli_set(path + ['ipv6', 'adjust-mss', mss]) self.cli_set(path + ['ipv6', 'disable-forwarding']) self.cli_set(path + ['ipv6', 'dup-addr-detect-transmits', dad_transmits]) self.cli_commit() for interface in self._interfaces: + base_options = f'-A FORWARD -o {interface} -p tcp -m tcp --tcp-flags SYN,RST SYN' + out = cmd('sudo ip6tables-save -t mangle') + for line in out.splitlines(): + if line.startswith(base_options): + self.assertIn(f'--set-mss {mss}', line) + tmp = read_file(f'/proc/sys/net/ipv6/conf/{interface}/forwarding') self.assertEqual('0', tmp) diff --git a/src/conf_mode/firewall_options.py b/src/conf_mode/firewall_options.py deleted file mode 100755 index 67bf5d0e2..000000000 --- a/src/conf_mode/firewall_options.py +++ /dev/null @@ -1,150 +0,0 @@ -#!/usr/bin/env python3 -# -# Copyright (C) 2018 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 sys -import os -import copy - -from vyos.config import Config -from vyos import ConfigError -from vyos.util import call - -from vyos import airbag -airbag.enable() - -default_config_data = { - 'intf_opts': [], - 'new_chain4': False, - 'new_chain6': False -} - -def get_config(config=None): - opts = copy.deepcopy(default_config_data) - if config: - conf = config - else: - conf = Config() - if not conf.exists('firewall options'): - # bail out early - return opts - else: - conf.set_level('firewall options') - - # Parse configuration of each individual instance - if conf.exists('interface'): - for intf in conf.list_nodes('interface'): - conf.set_level('firewall options interface {0}'.format(intf)) - config = { - 'intf': intf, - 'disabled': False, - 'mss4': '', - 'mss6': '' - } - - # Check if individual option is disabled - if conf.exists('disable'): - config['disabled'] = True - - # - # Get MSS value IPv4 - # - if conf.exists('adjust-mss'): - config['mss4'] = conf.return_value('adjust-mss') - - # We need a marker that a new iptables chain needs to be generated - if not opts['new_chain4']: - opts['new_chain4'] = True - - # - # Get MSS value IPv6 - # - if conf.exists('adjust-mss6'): - config['mss6'] = conf.return_value('adjust-mss6') - - # We need a marker that a new ip6tables chain needs to be generated - if not opts['new_chain6']: - opts['new_chain6'] = True - - # Append interface options to global list - opts['intf_opts'].append(config) - - return opts - -def verify(tcp): - # syntax verification is done via cli - return None - -def apply(tcp): - target = 'VYOS_FW_OPTIONS' - - # always cleanup iptables - call('iptables --table mangle --delete FORWARD --jump {} >&/dev/null'.format(target)) - call('iptables --table mangle --flush {} >&/dev/null'.format(target)) - call('iptables --table mangle --delete-chain {} >&/dev/null'.format(target)) - - # always cleanup ip6tables - call('ip6tables --table mangle --delete FORWARD --jump {} >&/dev/null'.format(target)) - call('ip6tables --table mangle --flush {} >&/dev/null'.format(target)) - call('ip6tables --table mangle --delete-chain {} >&/dev/null'.format(target)) - - # Setup new iptables rules - if tcp['new_chain4']: - call('iptables --table mangle --new-chain {} >&/dev/null'.format(target)) - call('iptables --table mangle --append FORWARD --jump {} >&/dev/null'.format(target)) - - for opts in tcp['intf_opts']: - intf = opts['intf'] - mss = opts['mss4'] - - # Check if this rule iis disabled - if opts['disabled']: - continue - - # adjust TCP MSS per interface - if mss: - call('iptables --table mangle --append {} --out-interface {} --protocol tcp ' - '--tcp-flags SYN,RST SYN --jump TCPMSS --set-mss {} >&/dev/null'.format(target, intf, mss)) - - # Setup new ip6tables rules - if tcp['new_chain6']: - call('ip6tables --table mangle --new-chain {} >&/dev/null'.format(target)) - call('ip6tables --table mangle --append FORWARD --jump {} >&/dev/null'.format(target)) - - for opts in tcp['intf_opts']: - intf = opts['intf'] - mss = opts['mss6'] - - # Check if this rule iis disabled - if opts['disabled']: - continue - - # adjust TCP MSS per interface - if mss: - call('ip6tables --table mangle --append {} --out-interface {} --protocol tcp ' - '--tcp-flags SYN,RST SYN --jump TCPMSS --set-mss {} >&/dev/null'.format(target, intf, mss)) - - return None - -if __name__ == '__main__': - - try: - c = get_config() - verify(c) - apply(c) - except ConfigError as e: - print(e) - sys.exit(1) diff --git a/src/migration-scripts/firewall/5-to-6 b/src/migration-scripts/firewall/5-to-6 new file mode 100755 index 000000000..ccb86830a --- /dev/null +++ b/src/migration-scripts/firewall/5-to-6 @@ -0,0 +1,63 @@ +#!/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 . + +# T3090: migrate "firewall options interface adjust-mss" to the +# individual interface. + +from sys import argv +from sys import exit + +from vyos.configtree import ConfigTree +from vyos.ifconfig import Section + +if (len(argv) < 1): + print("Must specify file name!") + exit(1) + +file_name = argv[1] + +with open(file_name, 'r') as f: + config_file = f.read() + +base = ['firewall', 'options', 'interface'] +config = ConfigTree(config_file) + +if not config.exists(base): + # Nothing to do + exit(0) + +for interface in config.list_nodes(base): + if config.exists(base + [interface, 'disable']): + continue + + if config.exists(base + [interface, 'adjust-mss']): + section = Section.section(interface) + tmp = config.return_value(base + [interface, 'adjust-mss']) + config.set(['interfaces', section, interface, 'ip', 'adjust-mss'], value=tmp) + + if config.exists(base + [interface, 'adjust-mss6']): + section = Section.section(interface) + tmp = config.return_value(base + [interface, 'adjust-mss6']) + config.set(['interfaces', section, interface, 'ipv6', 'adjust-mss'], value=tmp) + +config.delete(['firewall', 'options']) + +try: + with open(file_name, 'w') as f: + f.write(config.to_string()) +except OSError as e: + print("Failed to save the modified config: {}".format(e)) + exit(1) -- cgit v1.2.3 From 9d5b9dc4d6907a83204f1815fbf875989bf8fcfc Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Thu, 19 Aug 2021 19:08:27 +0200 Subject: vti: T1441: enable ipv4/ipv6 interface options Now that we are based on XFRM interfaces we can also enable specific IPv4 and IPv6 interface options. --- interface-definitions/interfaces-vti.xml.in | 2 ++ 1 file changed, 2 insertions(+) diff --git a/interface-definitions/interfaces-vti.xml.in b/interface-definitions/interfaces-vti.xml.in index 1b9e33d57..b12434ae7 100644 --- a/interface-definitions/interfaces-vti.xml.in +++ b/interface-definitions/interfaces-vti.xml.in @@ -31,6 +31,8 @@ #include #include + #include + #include #include #include -- cgit v1.2.3 From 0e751221d0832acac807e7f0bc97d7bb31230c3a Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Thu, 19 Aug 2021 20:24:39 +0200 Subject: xml: interfaces: use one common building block for "disable-forwarding" Both building blocks only differed in the help text, so use IP for both IPv4 and IPv6. --- interface-definitions/include/interface/disable-forwarding.xml.i | 2 +- .../include/interface/ipv6-disable-forwarding.xml.i | 8 -------- interface-definitions/include/interface/ipv6-options.xml.i | 2 +- 3 files changed, 2 insertions(+), 10 deletions(-) delete mode 100644 interface-definitions/include/interface/ipv6-disable-forwarding.xml.i diff --git a/interface-definitions/include/interface/disable-forwarding.xml.i b/interface-definitions/include/interface/disable-forwarding.xml.i index a32e5376f..45382ec95 100644 --- a/interface-definitions/include/interface/disable-forwarding.xml.i +++ b/interface-definitions/include/interface/disable-forwarding.xml.i @@ -1,7 +1,7 @@ - Disable IPv4 forwarding on this interface + Disable IP forwarding on this interface diff --git a/interface-definitions/include/interface/ipv6-disable-forwarding.xml.i b/interface-definitions/include/interface/ipv6-disable-forwarding.xml.i deleted file mode 100644 index 4adb77d1b..000000000 --- a/interface-definitions/include/interface/ipv6-disable-forwarding.xml.i +++ /dev/null @@ -1,8 +0,0 @@ - - - - Disable IPv6 forwarding on this interface - - - - diff --git a/interface-definitions/include/interface/ipv6-options.xml.i b/interface-definitions/include/interface/ipv6-options.xml.i index 2d2d1d3b2..f740ce0c2 100644 --- a/interface-definitions/include/interface/ipv6-options.xml.i +++ b/interface-definitions/include/interface/ipv6-options.xml.i @@ -5,8 +5,8 @@ #include + #include #include - #include #include -- cgit v1.2.3 From fd5990a019152dd6080fd134dba15928eb6535d1 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Fri, 20 Aug 2021 16:18:24 +0200 Subject: wwan: T3620: remove superfluous import statement WWAN does no londer need to render any configuration files. --- src/conf_mode/interfaces-wwan.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/conf_mode/interfaces-wwan.py b/src/conf_mode/interfaces-wwan.py index 31c599145..faa5eb628 100755 --- a/src/conf_mode/interfaces-wwan.py +++ b/src/conf_mode/interfaces-wwan.py @@ -26,7 +26,6 @@ from vyos.configverify import verify_vrf from vyos.ifconfig import WWANIf from vyos.util import cmd from vyos.util import dict_search -from vyos.template import render from vyos import ConfigError from vyos import airbag airbag.enable() -- cgit v1.2.3 From e1debb1b57a445fa2357f7dbb5b3f04383f8b1e3 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Fri, 20 Aug 2021 16:19:10 +0200 Subject: vyos.ifconfig: bridge: remove missleading comment in update() --- python/vyos/ifconfig/bridge.py | 1 - 1 file changed, 1 deletion(-) diff --git a/python/vyos/ifconfig/bridge.py b/python/vyos/ifconfig/bridge.py index 14f64a8de..27073b266 100644 --- a/python/vyos/ifconfig/bridge.py +++ b/python/vyos/ifconfig/bridge.py @@ -366,5 +366,4 @@ class BridgeIf(Interface): cmd = f'bridge vlan add dev {interface} vid {native_vlan_id} pvid untagged master' self._cmd(cmd) - # call base class first super().update(config) -- cgit v1.2.3 From 5141a8ca3a69108d20c8c82d1fa737f0e17ac974 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Fri, 20 Aug 2021 16:19:50 +0200 Subject: route: static: T2450: add next-hop interface on dhcp routes --- data/templates/frr/static_routes_macro.j2 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/templates/frr/static_routes_macro.j2 b/data/templates/frr/static_routes_macro.j2 index f10b58047..3b432b49b 100644 --- a/data/templates/frr/static_routes_macro.j2 +++ b/data/templates/frr/static_routes_macro.j2 @@ -5,7 +5,7 @@ {% if prefix_config.dhcp_interface is defined and prefix_config.dhcp_interface is not none %} {% set next_hop = prefix_config.dhcp_interface | get_dhcp_router %} {% if next_hop is defined and next_hop is not none %} -{{ ip_ipv6 }} route {{ prefix }} {{ next_hop }} +{{ ip_ipv6 }} route {{ prefix }} {{ next_hop }} {{ prefix_config.dhcp_interface }} {% endif %} {% endif %} {% if prefix_config.interface is defined and prefix_config.interface is not none %} -- cgit v1.2.3 From 9c97bd1b0214e102ac36eae8b2c3c9ff672a0bf3 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Fri, 20 Aug 2021 17:06:05 +0200 Subject: vyos.configdict: add note when using leaf_node_changed() --- python/vyos/configdict.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/python/vyos/configdict.py b/python/vyos/configdict.py index 0969a5353..a0056a142 100644 --- a/python/vyos/configdict.py +++ b/python/vyos/configdict.py @@ -108,7 +108,9 @@ def leaf_node_changed(conf, path): """ Check if a leaf node was altered. If it has been altered - values has been changed, or it was added/removed, we will return a list containing the old - value(s). If nothing has been changed, None is returned + value(s). If nothing has been changed, None is returned. + + NOTE: path must use the real CLI node name (e.g. with a hyphen!) """ from vyos.configdiff import get_config_diff D = get_config_diff(conf, key_mangling=('-', '_')) -- cgit v1.2.3 From 8c1c9e1c37be9e88e8a7ea0182a43a3396eff623 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Fri, 20 Aug 2021 17:12:05 +0200 Subject: smoketest: base: abbreviate path to /proc/sys/net --- smoketest/scripts/cli/base_interfaces_test.py | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/smoketest/scripts/cli/base_interfaces_test.py b/smoketest/scripts/cli/base_interfaces_test.py index 63f742a8d..edb604dbf 100644 --- a/smoketest/scripts/cli/base_interfaces_test.py +++ b/smoketest/scripts/cli/base_interfaces_test.py @@ -588,28 +588,30 @@ class BasicInterfaceTest: tmp = read_file(f'/proc/sys/net/ipv4/neigh/{interface}/base_reachable_time_ms') self.assertEqual(tmp, str((int(arp_tmo) * 1000))) # tmo value is in milli seconds - tmp = read_file(f'/proc/sys/net/ipv4/conf/{interface}/arp_filter') + proc_base = f'/proc/sys/net/ipv4/conf/{interface}' + + tmp = read_file(f'{proc_base}/arp_filter') self.assertEqual('0', tmp) - tmp = read_file(f'/proc/sys/net/ipv4/conf/{interface}/arp_accept') + tmp = read_file(f'{proc_base}/arp_accept') self.assertEqual('1', tmp) - tmp = read_file(f'/proc/sys/net/ipv4/conf/{interface}/arp_announce') + tmp = read_file(f'{proc_base}/arp_announce') self.assertEqual('1', tmp) - tmp = read_file(f'/proc/sys/net/ipv4/conf/{interface}/arp_ignore') + tmp = read_file(f'{proc_base}/arp_ignore') self.assertEqual('1', tmp) - tmp = read_file(f'/proc/sys/net/ipv4/conf/{interface}/forwarding') + tmp = read_file(f'{proc_base}/forwarding') self.assertEqual('0', tmp) - tmp = read_file(f'/proc/sys/net/ipv4/conf/{interface}/proxy_arp') + tmp = read_file(f'{proc_base}/proxy_arp') self.assertEqual('1', tmp) - tmp = read_file(f'/proc/sys/net/ipv4/conf/{interface}/proxy_arp_pvlan') + tmp = read_file(f'{proc_base}/proxy_arp_pvlan') self.assertEqual('1', tmp) - tmp = read_file(f'/proc/sys/net/ipv4/conf/{interface}/rp_filter') + tmp = read_file(f'{proc_base}/rp_filter') self.assertEqual('2', tmp) def test_interface_ipv6_options(self): @@ -638,10 +640,12 @@ class BasicInterfaceTest: if line.startswith(base_options): self.assertIn(f'--set-mss {mss}', line) - tmp = read_file(f'/proc/sys/net/ipv6/conf/{interface}/forwarding') + proc_base = f'/proc/sys/net/ipv6/conf/{interface}' + + tmp = read_file(f'{proc_base}/forwarding') self.assertEqual('0', tmp) - tmp = read_file(f'/proc/sys/net/ipv6/conf/{interface}/dad_transmits') + tmp = read_file(f'{proc_base}/dad_transmits') self.assertEqual(dad_transmits, tmp) def test_dhcpv6_client_options(self): -- cgit v1.2.3 From b7d30137b17da49ed5099d4d96659b363fc7bcc9 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sat, 21 Aug 2021 20:57:17 +0200 Subject: vyos.ifconfig: provide generic get_mac_synthetic() method WireGuard, Tunnel and also PPPoE all need a ways to calculate a synthetic MAC address used for the EUI64 link-local addresses. Instead of copying the code from Tunnel to WireGuard to PPPoE, use a generic implementation. --- python/vyos/ifconfig/interface.py | 29 +++++++++++++++++++++++++++++ python/vyos/ifconfig/tunnel.py | 28 ++-------------------------- python/vyos/ifconfig/wireguard.py | 27 ++------------------------- 3 files changed, 33 insertions(+), 51 deletions(-) diff --git a/python/vyos/ifconfig/interface.py b/python/vyos/ifconfig/interface.py index 53b57a83f..45a220e22 100755 --- a/python/vyos/ifconfig/interface.py +++ b/python/vyos/ifconfig/interface.py @@ -52,6 +52,10 @@ from vyos.ifconfig.vrrp import VRRP from vyos.ifconfig.operational import Operational from vyos.ifconfig import Section +from netaddr import EUI +from netaddr import mac_unix_expanded +from random import getrandbits + class Interface(Control): # This is the class which will be used to create # self.operational, it allows subclasses, such as @@ -389,6 +393,31 @@ class Interface(Control): """ return self.get_interface('mac') + def get_mac_synthetic(self): + """ + Get a synthetic MAC address. This is a common method which can be called + from derived classes to overwrite the get_mac() call in a generic way. + + NOTE: Tunnel interfaces have no "MAC" address by default. The content + of the 'address' file in /sys/class/net/device contains the + local-ip thus we generate a random MAC address instead + + Example: + >>> from vyos.ifconfig import Interface + >>> Interface('eth0').get_mac() + '00:50:ab:cd:ef:00' + """ + # we choose 40 random bytes for the MAC address, this gives + # us e.g. EUI('00-EA-EE-D6-A3-C8') or EUI('00-41-B9-0D-F2-2A') + tmp = EUI(getrandbits(48)).value + # set locally administered bit in MAC address + tmp |= 0xf20000000000 + # convert integer to "real" MAC address representation + mac = EUI(hex(tmp).split('x')[-1]) + # change dialect to use : as delimiter instead of - + mac.dialect = mac_unix_expanded + return str(mac) + def set_mac(self, mac): """ Set interface MAC (Media Access Contrl) address to given value. diff --git a/python/vyos/ifconfig/tunnel.py b/python/vyos/ifconfig/tunnel.py index 64c735824..5258a2cb1 100644 --- a/python/vyos/ifconfig/tunnel.py +++ b/python/vyos/ifconfig/tunnel.py @@ -16,10 +16,6 @@ # https://developers.redhat.com/blog/2019/05/17/an-introduction-to-linux-virtual-interfaces-tunnels/ # https://community.hetzner.com/tutorials/linux-setup-gre-tunnel -from netaddr import EUI -from netaddr import mac_unix_expanded -from random import getrandbits - from vyos.ifconfig.interface import Interface from vyos.util import dict_search from vyos.validate import assert_list @@ -163,28 +159,8 @@ class TunnelIf(Interface): self._cmd(cmd.format(**self.config)) def get_mac(self): - """ - Get current interface MAC (Media Access Contrl) address used. - - NOTE: Tunnel interfaces have no "MAC" address by default. The content - of the 'address' file in /sys/class/net/device contains the - local-ip thus we generate a random MAC address instead - - Example: - >>> from vyos.ifconfig import Interface - >>> Interface('eth0').get_mac() - '00:50:ab:cd:ef:00' - """ - # we choose 40 random bytes for the MAC address, this gives - # us e.g. EUI('00-EA-EE-D6-A3-C8') or EUI('00-41-B9-0D-F2-2A') - tmp = EUI(getrandbits(48)).value - # set locally administered bit in MAC address - tmp |= 0xf20000000000 - # convert integer to "real" MAC address representation - mac = EUI(hex(tmp).split('x')[-1]) - # change dialect to use : as delimiter instead of - - mac.dialect = mac_unix_expanded - return str(mac) + """ Get a synthetic MAC address. """ + return self.get_mac_synthetic() def update(self, config): """ General helper function which works on a dictionary retrived by diff --git a/python/vyos/ifconfig/wireguard.py b/python/vyos/ifconfig/wireguard.py index c4cf2fbbf..28b5e2991 100644 --- a/python/vyos/ifconfig/wireguard.py +++ b/python/vyos/ifconfig/wireguard.py @@ -17,9 +17,6 @@ import os import time from datetime import timedelta -from netaddr import EUI -from netaddr import mac_unix_expanded -from random import getrandbits from hurry.filesize import size from hurry.filesize import alternative @@ -159,28 +156,8 @@ class WireGuardIf(Interface): } def get_mac(self): - """ - Get current interface MAC (Media Access Contrl) address used. - - NOTE: Tunnel interfaces have no "MAC" address by default. The content - of the 'address' file in /sys/class/net/device contains the - local-ip thus we generate a random MAC address instead - - Example: - >>> from vyos.ifconfig import Interface - >>> Interface('eth0').get_mac() - '00:50:ab:cd:ef:00' - """ - # we choose 40 random bytes for the MAC address, this gives - # us e.g. EUI('00-EA-EE-D6-A3-C8') or EUI('00-41-B9-0D-F2-2A') - tmp = EUI(getrandbits(48)).value - # set locally administered bit in MAC address - tmp |= 0xf20000000000 - # convert integer to "real" MAC address representation - mac = EUI(hex(tmp).split('x')[-1]) - # change dialect to use : as delimiter instead of - - mac.dialect = mac_unix_expanded - return str(mac) + """ Get a synthetic MAC address. """ + return self.get_mac_synthetic() def update(self, config): """ General helper function which works on a dictionary retrived by -- cgit v1.2.3 From 0d127067b692c7610b49287b11e2fde69016c70f Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Fri, 20 Aug 2021 17:16:26 +0200 Subject: pppoe: T3090: support forward disable on a PPPoE link --- interface-definitions/interfaces-pppoe.xml.in | 2 ++ 1 file changed, 2 insertions(+) diff --git a/interface-definitions/interfaces-pppoe.xml.in b/interface-definitions/interfaces-pppoe.xml.in index ac8fa378b..25a82a86b 100644 --- a/interface-definitions/interfaces-pppoe.xml.in +++ b/interface-definitions/interfaces-pppoe.xml.in @@ -71,6 +71,7 @@ #include + #include #include @@ -88,6 +89,7 @@ #include + #include -- cgit v1.2.3 From 64c9fdef02323309e97b2bb682604ada52d651e8 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Fri, 20 Aug 2021 17:13:32 +0200 Subject: pppoe: T3090: migrate to vyos.ifconfig library to use the full potential Now that MSS clamping is done on the "per-interface" level the entire PPPoE stuff would have needed to get a full copy in GNU BASH for this or, participate in the common library. Add a new PPP ip-up script named 99-vyos-pppoe-callback which will call the vyos.ifconfig.PPPoEIf.update() function to configure everything as done with all other interfaces. This removes duplicated code for VRF assignment and route installation when a PPPoE interface is brought up or down. --- Makefile | 2 +- data/templates/pppoe/ipv6-up.script.tmpl | 37 ---------- data/templates/pppoe/peer.tmpl | 8 ++- debian/vyos-1x.install | 1 + python/vyos/ifconfig/pppoe.py | 110 ++++++++++++++++++++++++++++- src/conf_mode/interfaces-pppoe.py | 93 ++++++++++++++---------- src/etc/ppp/ip-up.d/99-vyos-pppoe-callback | 59 ++++++++++++++++ 7 files changed, 230 insertions(+), 80 deletions(-) create mode 100755 src/etc/ppp/ip-up.d/99-vyos-pppoe-callback diff --git a/Makefile b/Makefile index 0aa35d17d..2c79f545d 100644 --- a/Makefile +++ b/Makefile @@ -84,7 +84,7 @@ clean: .PHONY: test test: - set -e; python3 -m compileall -q -x '/vmware-tools/scripts/' . + set -e; python3 -m compileall -q -x '/vmware-tools/scripts/, /ppp/' . PYTHONPATH=python/ python3 -m "nose" --with-xunit src --with-coverage --cover-erase --cover-xml --cover-package src/conf_mode,src/op_mode,src/completion,src/helpers,src/validators,src/tests --verbose .PHONY: sonar diff --git a/data/templates/pppoe/ipv6-up.script.tmpl b/data/templates/pppoe/ipv6-up.script.tmpl index 7e1bc33b4..da73cb4d5 100644 --- a/data/templates/pppoe/ipv6-up.script.tmpl +++ b/data/templates/pppoe/ipv6-up.script.tmpl @@ -7,43 +7,6 @@ if [ "$6" != "{{ ifname }}" ]; then exit fi -{% if ipv6 is defined and ipv6.address is defined and ipv6.address.autoconf is defined %} -# add some info to syslog -DIALER_PID=$(cat /var/run/{{ ifname }}.pid) -logger -t pppd[$DIALER_PID] "executing $0" -logger -t pppd[$DIALER_PID] "configuring interface {{ ifname }} via {{ source_interface }}" - -# Configure interface-specific Host/Router behaviour. -# Note: It is recommended to have the same setting on all interfaces; mixed -# router/host scenarios are rather uncommon. Possible values are: -# -# 0 Forwarding disabled -# 1 Forwarding enabled -# -echo 1 > /proc/sys/net/ipv6/conf/{{ ifname }}/forwarding - -# Accept Router Advertisements; autoconfigure using them. -# -# It also determines whether or not to transmit Router -# Solicitations. If and only if the functional setting is to -# accept Router Advertisements, Router Solicitations will be -# transmitted. Possible values are: -# -# 0 Do not accept Router Advertisements. -# 1 Accept Router Advertisements if forwarding is disabled. -# 2 Overrule forwarding behaviour. Accept Router Advertisements -# even if forwarding is enabled. -# -echo 2 > /proc/sys/net/ipv6/conf/{{ ifname }}/accept_ra - -# Autoconfigure addresses using Prefix Information in Router Advertisements. -echo 1 > /proc/sys/net/ipv6/conf/{{ ifname }}/autoconf -{% endif %} - -{% if dhcpv6_options is defined and dhcpv6_options.pd is defined %} -# Start wide dhcpv6 client -systemctl restart dhcp6c@{{ ifname }}.service -{% endif %} {% if default_route != 'none' %} # See https://phabricator.vyos.net/T2248 & T2220. Determine if we are enslaved diff --git a/data/templates/pppoe/peer.tmpl b/data/templates/pppoe/peer.tmpl index 0f78f9384..e8fda2cae 100644 --- a/data/templates/pppoe/peer.tmpl +++ b/data/templates/pppoe/peer.tmpl @@ -30,7 +30,7 @@ connect /bin/true noauth # Don't try to proxy ARP for the remote endpoint. User can set proxy -# arp entries up manually if they wish. More importantly, having +# arp entries up manually if they wish. More importantly, having # the "proxyarp" parameter set disables the "defaultroute" option. noproxyarp @@ -71,8 +71,14 @@ demand # passed to the ip-up.d/ip-down.s scripts which is required for VRF support. {% if 'auto' in default_route %} defaultroute +{{ 'defaultroute6' if ipv6 is defined }} {% elif 'force' in default_route %} defaultroute replacedefaultroute +{{ 'defaultroute6' if ipv6 is defined }} {% endif %} +{% else %} +nodefaultroute +noreplacedefaultroute +{{ 'nodefaultroute6' if ipv6 is defined }} {% endif %} diff --git a/debian/vyos-1x.install b/debian/vyos-1x.install index 7ca568eff..d332e0d36 100644 --- a/debian/vyos-1x.install +++ b/debian/vyos-1x.install @@ -3,6 +3,7 @@ etc/dhcp etc/ipsec.d etc/netplug etc/opennhrp +etc/ppp etc/rsyslog.d etc/securetty etc/security diff --git a/python/vyos/ifconfig/pppoe.py b/python/vyos/ifconfig/pppoe.py index 6acf7d1c7..9153863de 100644 --- a/python/vyos/ifconfig/pppoe.py +++ b/python/vyos/ifconfig/pppoe.py @@ -1,4 +1,4 @@ -# Copyright 2020 VyOS maintainers and contributors +# Copyright 2020-2021 VyOS maintainers and contributors # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public @@ -14,6 +14,7 @@ # License along with this library. If not, see . from vyos.ifconfig.interface import Interface +from vyos.util import get_interface_config @Interface.register class PPPoEIf(Interface): @@ -26,7 +27,31 @@ class PPPoEIf(Interface): }, } - # stub this interface is created in the configure script + def _remove_routes(self, vrf=''): + # Always delete default routes when interface is removed + if vrf: + vrf = f'-c "vrf {vrf}"' + self._cmd(f'vtysh -c "conf t" {vrf} -c "no ip route 0.0.0.0/0 {self.ifname} tag 210"') + self._cmd(f'vtysh -c "conf t" {vrf} -c "no ipv6 route ::/0 {self.ifname} tag 210"') + + def remove(self): + """ + Remove interface from operating system. Removing the interface + deconfigures all assigned IP addresses and clear possible DHCP(v6) + client processes. + Example: + >>> from vyos.ifconfig import Interface + >>> i = Interface('pppoe0') + >>> i.remove() + """ + + tmp = get_interface_config(self.ifname) + vrf = '' + if 'master' in tmp: + self._remove_routes(tmp['master']) + + # remove bond master which places members in disabled state + super().remove() def _create(self): # we can not create this interface as it is managed outside @@ -35,3 +60,84 @@ class PPPoEIf(Interface): def _delete(self): # we can not create this interface as it is managed outside pass + + def del_addr(self, addr): + # we can not create this interface as it is managed outside + pass + + def get_mac(self): + """ Get a synthetic MAC address. """ + return self.get_mac_synthetic() + + def update(self, config): + """ General helper function which works on a dictionary retrived by + get_config_dict(). It's main intention is to consolidate the scattered + interface setup code and provide a single point of entry when workin + on any interface. """ + + # remove old routes from an e.g. old VRF assignment + vrf = '' + if 'vrf_old' in config: + vrf = config['vrf_old'] + self._remove_routes(vrf) + + # DHCPv6 PD handling is a bit different on PPPoE interfaces, as we do + # not require an 'address dhcpv6' CLI option as with other interfaces + if 'dhcpv6_options' in config and 'pd' in config['dhcpv6_options']: + self.set_dhcpv6(True) + else: + self.set_dhcpv6(False) + + super().update(config) + + if 'default_route' not in config or config['default_route'] == 'none': + return + + # + # Set default routes pointing to pppoe interface + # + vrf = '' + sed_opt = '^ip route' + + install_v4 = True + install_v6 = True + + # generate proper configuration string when VRFs are in use + if 'vrf' in config: + tmp = config['vrf'] + vrf = f'-c "vrf {tmp}"' + sed_opt = f'vrf {tmp}' + + if config['default_route'] == 'auto': + # only add route if there is no default route present + tmp = self._cmd(f'vtysh -c "show running-config staticd no-header" | sed -n "/{sed_opt}/,/!/p"') + for line in tmp.splitlines(): + line = line.lstrip() + if line.startswith('ip route 0.0.0.0/0'): + install_v4 = False + continue + + if 'ipv6' in config and line.startswith('ipv6 route ::/0'): + install_v6 = False + continue + + elif config['default_route'] == 'force': + # Force means that all static routes are replaced with the ones from this interface + tmp = self._cmd(f'vtysh -c "show running-config staticd no-header" | sed -n "/{sed_opt}/,/!/p"') + for line in tmp.splitlines(): + if self.ifname in line: + # It makes no sense to remove a route with our interface and the later re-add it. + # This will only make traffic disappear - which is a no-no! + continue + + line = line.lstrip() + if line.startswith('ip route 0.0.0.0/0'): + self._cmd(f'vtysh -c "conf t" {vrf} -c "no {line}"') + + if 'ipv6' in config and line.startswith('ipv6 route ::/0'): + self._cmd(f'vtysh -c "conf t" {vrf} -c "no {line}"') + + if install_v4: + self._cmd(f'vtysh -c "conf t" {vrf} -c "ip route 0.0.0.0/0 {self.ifname} tag 210"') + if install_v6 and 'ipv6' in config: + self._cmd(f'vtysh -c "conf t" {vrf} -c "ipv6 route ::/0 {self.ifname} tag 210"') diff --git a/src/conf_mode/interfaces-pppoe.py b/src/conf_mode/interfaces-pppoe.py index 6c4c6c95b..584adc75e 100755 --- a/src/conf_mode/interfaces-pppoe.py +++ b/src/conf_mode/interfaces-pppoe.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2019-2020 VyOS maintainers and contributors +# Copyright (C) 2019-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 @@ -22,12 +22,16 @@ from netifaces import interfaces from vyos.config import Config from vyos.configdict import get_interface_dict +from vyos.configdict import leaf_node_changed from vyos.configverify import verify_authentication from vyos.configverify import verify_source_interface +from vyos.configverify import verify_interface_exists from vyos.configverify import verify_vrf from vyos.configverify import verify_mtu_ipv6 +from vyos.ifconfig import PPPoEIf from vyos.template import render from vyos.util import call +from vyos.util import is_systemd_service_running from vyos import ConfigError from vyos import airbag airbag.enable() @@ -44,6 +48,32 @@ def get_config(config=None): base = ['interfaces', 'pppoe'] pppoe = get_interface_dict(conf, base) + # We should only terminate the PPPoE session if critical parameters change. + # All parameters that can be changed on-the-fly (like interface description) + # should not lead to a reconnect! + tmp = leaf_node_changed(conf, ['access-concentrator']) + if tmp: pppoe.update({'shutdown_required': {}}) + + tmp = leaf_node_changed(conf, ['connect-on-demand']) + if tmp: pppoe.update({'shutdown_required': {}}) + + tmp = leaf_node_changed(conf, ['service-name']) + if tmp: pppoe.update({'shutdown_required': {}}) + + tmp = leaf_node_changed(conf, ['source-interface']) + if tmp: pppoe.update({'shutdown_required': {}}) + + tmp = leaf_node_changed(conf, ['vrf']) + # leaf_node_changed() returns a list, as VRF is a non-multi node, there + # will be only one list element + if tmp: pppoe.update({'vrf_old': tmp[0]}) + + tmp = leaf_node_changed(conf, ['authentication', 'user']) + if tmp: pppoe.update({'shutdown_required': {}}) + + tmp = leaf_node_changed(conf, ['authentication', 'password']) + if tmp: pppoe.update({'shutdown_required': {}}) + return pppoe def verify(pppoe): @@ -66,57 +96,42 @@ def generate(pppoe): # rendered into ifname = pppoe['ifname'] config_pppoe = f'/etc/ppp/peers/{ifname}' - script_pppoe_pre_up = f'/etc/ppp/ip-pre-up.d/1000-vyos-pppoe-{ifname}' - script_pppoe_ip_up = f'/etc/ppp/ip-up.d/1000-vyos-pppoe-{ifname}' - script_pppoe_ip_down = f'/etc/ppp/ip-down.d/1000-vyos-pppoe-{ifname}' - script_pppoe_ipv6_up = f'/etc/ppp/ipv6-up.d/1000-vyos-pppoe-{ifname}' - config_wide_dhcp6c = f'/run/dhcp6c/dhcp6c.{ifname}.conf' - - config_files = [config_pppoe, script_pppoe_pre_up, script_pppoe_ip_up, - script_pppoe_ip_down, script_pppoe_ipv6_up, config_wide_dhcp6c] if 'deleted' in pppoe or 'disable' in pppoe: - # stop DHCPv6-PD client - call(f'systemctl stop dhcp6c@{ifname}.service') - # Hang-up PPPoE connection - call(f'systemctl stop ppp@{ifname}.service') - - # Delete PPP configuration files - for file in config_files: - if os.path.exists(file): - os.unlink(file) + if os.path.exists(config_pppoe): + os.unlink(config_pppoe) return None # Create PPP configuration files - render(config_pppoe, 'pppoe/peer.tmpl', pppoe, permission=0o755) - - # Create script for ip-pre-up.d - render(script_pppoe_pre_up, 'pppoe/ip-pre-up.script.tmpl', pppoe, - permission=0o755) - # Create script for ip-up.d - render(script_pppoe_ip_up, 'pppoe/ip-up.script.tmpl', pppoe, - permission=0o755) - # Create script for ip-down.d - render(script_pppoe_ip_down, 'pppoe/ip-down.script.tmpl', pppoe, - permission=0o755) - # Create script for ipv6-up.d - render(script_pppoe_ipv6_up, 'pppoe/ipv6-up.script.tmpl', pppoe, - permission=0o755) - - if 'dhcpv6_options' in pppoe and 'pd' in pppoe['dhcpv6_options']: - # ipv6.tmpl relies on ifname - this should be made consitent in the - # future better then double key-ing the same value - render(config_wide_dhcp6c, 'dhcp-client/ipv6.tmpl', pppoe) + render(config_pppoe, 'pppoe/peer.tmpl', pppoe, permission=0o640) return None def apply(pppoe): + ifname = pppoe['ifname'] if 'deleted' in pppoe or 'disable' in pppoe: - call('systemctl stop ppp@{ifname}.service'.format(**pppoe)) + if os.path.isdir(f'/sys/class/net/{ifname}'): + p = PPPoEIf(ifname) + p.remove() + call(f'systemctl stop ppp@{ifname}.service') return None - call('systemctl restart ppp@{ifname}.service'.format(**pppoe)) + # reconnect should only be necessary when certain config options change, + # like ACS name, authentication, no-peer-dns, source-interface + if ((not is_systemd_service_running(f'ppp@{ifname}.service')) or + 'shutdown_required' in pppoe): + + # cleanup system (e.g. FRR routes first) + if os.path.isdir(f'/sys/class/net/{ifname}'): + p = PPPoEIf(ifname) + p.remove() + + call(f'systemctl restart ppp@{ifname}.service') + else: + if os.path.isdir(f'/sys/class/net/{ifname}'): + p = PPPoEIf(ifname) + p.update(pppoe) return None diff --git a/src/etc/ppp/ip-up.d/99-vyos-pppoe-callback b/src/etc/ppp/ip-up.d/99-vyos-pppoe-callback new file mode 100755 index 000000000..bb918a468 --- /dev/null +++ b/src/etc/ppp/ip-up.d/99-vyos-pppoe-callback @@ -0,0 +1,59 @@ +#!/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 . + +# This is a Python hook script which is invoked whenever a PPPoE session goes +# "ip-up". It will call into our vyos.ifconfig library and will then execute +# common tasks for the PPPoE interface. The reason we have to "hook" this is +# that we can not create a pppoeX interface in advance in linux and then connect +# pppd to this already existing interface. + +from sys import argv +from sys import exit + +from syslog import syslog +from syslog import openlog +from syslog import LOG_PID +from syslog import LOG_INFO + +from vyos.configquery import ConfigTreeQuery +from vyos.ifconfig import PPPoEIf +from vyos.util import read_file + +# When the ppp link comes up, this script is called with the following +# parameters +# $1 the interface name used by pppd (e.g. ppp3) +# $2 the tty device name +# $3 the tty device speed +# $4 the local IP address for the interface +# $5 the remote IP address +# $6 the parameter specified by the 'ipparam' option to pppd + +if (len(argv) < 7): + exit(1) + +interface = argv[6] +dialer_pid = read_file(f'/var/run/{interface}.pid') + +openlog(ident=f'pppd[{dialer_pid}]', facility=LOG_INFO) +syslog('executing ' + argv[0]) + +conf = ConfigTreeQuery() +pppoe = conf.get_config_dict(['interfaces', 'pppoe', argv[6]], + get_first_key=True, key_mangling=('-', '_')) +pppoe['ifname'] = argv[6] + +p = PPPoEIf(pppoe['ifname']) +p.update(pppoe) -- cgit v1.2.3 From f476e456e20393e7e7e91b73e369c9b033fbf048 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Fri, 20 Aug 2021 20:12:53 +0200 Subject: vyos.configdict: leaf_node_changed() must return empty dict when node is added vyos@vyos# show interfaces pppoe pppoe pppoe10 { + access-concentrator asdfg authentication { password bar user foo } default-route force no-peer-dns source-interface eth0.202 } vyos@vyos# python3 Python 3.9.2 (default, Feb 28 2021, 17:03:44) [GCC 10.2.1 20210110] on linux Type "help", "copyright", "credits" or "license" for more information. >>> from vyos.config import Config >>> from vyos.configdict import get_interface_dict >>> from vyos.configdict import leaf_node_changed >>> conf = Config() >>> base = ['interfaces', 'pppoe'] >>> tmp = get_interface_dict(conf, base, 'pppoe10') >>> leaf_node_changed(conf, ['access-concentrator']) >>> [''] --- python/vyos/configdict.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/python/vyos/configdict.py b/python/vyos/configdict.py index a0056a142..e15579b95 100644 --- a/python/vyos/configdict.py +++ b/python/vyos/configdict.py @@ -117,9 +117,11 @@ def leaf_node_changed(conf, path): D.set_level(conf.get_level()) (new, old) = D.get_value_diff(path) if new != old: + if old is None: + return [''] if isinstance(old, str): return [old] - elif isinstance(old, list): + if isinstance(old, list): if isinstance(new, str): new = [new] elif isinstance(new, type(None)): -- cgit v1.2.3 From b121ee14ff1961b56568b0116de3c246ea4af934 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sat, 21 Aug 2021 11:35:07 +0200 Subject: pppoe: T1318: implement missing access-concentrator CLI option --- data/templates/pppoe/peer.tmpl | 16 ++++++++-------- .../include/pppoe-access-concentrator.xml.i | 11 +++++++++++ interface-definitions/interfaces-pppoe.xml.in | 12 ++---------- interface-definitions/service_pppoe-server.xml.in | 10 ++-------- 4 files changed, 23 insertions(+), 26 deletions(-) create mode 100644 interface-definitions/include/pppoe-access-concentrator.xml.i diff --git a/data/templates/pppoe/peer.tmpl b/data/templates/pppoe/peer.tmpl index e8fda2cae..7e54e95f3 100644 --- a/data/templates/pppoe/peer.tmpl +++ b/data/templates/pppoe/peer.tmpl @@ -1,8 +1,5 @@ ### Autogenerated by interfaces-pppoe.py ### - -{% if description %} -# {{ description }} -{% endif %} +{{ '# ' ~ description if description is defined else '' }} # Require peer to provide the local IP address if it is not # specified explicitly in the config file. @@ -38,6 +35,13 @@ noproxyarp maxfail 0 plugin rp-pppoe.so +{% if access_concentrator is defined and access_concentrator is not none %} +rp_pppoe_ac '{{ access_concentrator }}' +{% endif %} +{% if service_name is defined and service_name is not none %} +rp_pppoe_service '{{ service_name }}' +{% endif %} + {{ source_interface }} persist ifname {{ ifname }} @@ -60,10 +64,6 @@ ipv6cp-use-ipaddr {% endif %} {% endif %} -{% if service_name is defined %} -rp_pppoe_service "{{ service_name }}" -{% endif %} - {% if connect_on_demand is defined %} demand # See T2249. PPP default route options should only be set when in on-demand diff --git a/interface-definitions/include/pppoe-access-concentrator.xml.i b/interface-definitions/include/pppoe-access-concentrator.xml.i new file mode 100644 index 000000000..ccfcc1c49 --- /dev/null +++ b/interface-definitions/include/pppoe-access-concentrator.xml.i @@ -0,0 +1,11 @@ + + + + Access concentrator name + + [a-zA-Z0-9]{1,100} + + Access-concentrator name must be alphanumerical only (max. 100 characters) + + + diff --git a/interface-definitions/interfaces-pppoe.xml.in b/interface-definitions/interfaces-pppoe.xml.in index 25a82a86b..6d44dfecc 100644 --- a/interface-definitions/interfaces-pppoe.xml.in +++ b/interface-definitions/interfaces-pppoe.xml.in @@ -16,15 +16,7 @@ - - - Access concentrator name (only connect to this concentrator) - - [a-zA-Z0-9]+$ - - Access concentrator name must be composed of uppper and lower case letters or numbers only - - + #include #include #include @@ -140,7 +132,7 @@ [a-zA-Z0-9]+$ - Service name must be composed of uppper and lower case letters or numbers only + Service name must be alphanumeric only diff --git a/interface-definitions/service_pppoe-server.xml.in b/interface-definitions/service_pppoe-server.xml.in index 7b96b5692..79042e0f3 100644 --- a/interface-definitions/service_pppoe-server.xml.in +++ b/interface-definitions/service_pppoe-server.xml.in @@ -8,14 +8,8 @@ 900 + #include - - Access concentrator name - - [a-zA-Z0-9]{1,100} - - access-concentrator name limited to alphanumerical characters only (max. 100) - vyos-ac @@ -129,7 +123,7 @@ [a-zA-Z0-9\-]{1,100} - servicename can contain aplhanumerical characters and dashes only (max. 100) + Service-name can contain aplhanumerical characters and dashes only (max. 100) -- cgit v1.2.3 From 081e0334c00887c373fafde761cca960667be21b Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sat, 21 Aug 2021 11:35:53 +0200 Subject: pppoe: T1318: bump priority by 1 so we can source from pseudo-ethernet interfaces --- interface-definitions/interfaces-pppoe.xml.in | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/interface-definitions/interfaces-pppoe.xml.in b/interface-definitions/interfaces-pppoe.xml.in index 6d44dfecc..57bb01258 100644 --- a/interface-definitions/interfaces-pppoe.xml.in +++ b/interface-definitions/interfaces-pppoe.xml.in @@ -5,7 +5,7 @@ Point-to-Point Protocol over Ethernet (PPPoE) - 321 + 322 ^pppoe[0-9]+$ @@ -52,9 +52,13 @@ Delay before disconnecting idle session (in seconds) - n + u32:0-86400 Idle timeout in seconds + + + + Timeout must be in range 0 to 86400 @@ -84,14 +88,7 @@ #include - - - Physical Interface used for this PPPoE session - - - - - + #include IPv4 address of local end of the PPPoE link -- cgit v1.2.3 From ddff5eba85feea2a8d6d24e1914ce6d51ce2ea74 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sat, 21 Aug 2021 13:41:20 +0200 Subject: vyos.configverify: use build-in functions for verify_interface_exists() --- python/vyos/configverify.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/python/vyos/configverify.py b/python/vyos/configverify.py index 4279e6982..7f49aa9af 100644 --- a/python/vyos/configverify.py +++ b/python/vyos/configverify.py @@ -237,8 +237,8 @@ def verify_interface_exists(ifname): Common helper function used by interface implementations to perform recurring validation if an interface actually exists. """ - from netifaces import interfaces - if ifname not in interfaces(): + import os + if not os.path.exists(f'/sys/class/net/{ifname}'): raise ConfigError(f'Interface "{ifname}" does not exist!') def verify_source_interface(config): -- cgit v1.2.3 From 387252e15b083a8eefe2bec7417194fe9b44f471 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sat, 21 Aug 2021 15:12:48 +0200 Subject: pppoe: T3641: set "noipv6" if IPv6 is not configured in newer pppd version --- data/templates/pppoe/peer.tmpl | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/data/templates/pppoe/peer.tmpl b/data/templates/pppoe/peer.tmpl index 7e54e95f3..7803a247d 100644 --- a/data/templates/pppoe/peer.tmpl +++ b/data/templates/pppoe/peer.tmpl @@ -58,10 +58,9 @@ mru {{ mtu }} {{ "usepeerdns" if no_peer_dns is not defined }} {% if ipv6 is defined %} -+ipv6 -{% if ipv6.address is defined and ipv6.address.autoconf is defined %} -ipv6cp-use-ipaddr -{% endif %} ++ipv6 {{ 'ipv6cp-use-ipaddr' if ipv6.address is defined and ipv6.address.autoconf is defined }} +{% else %} +noipv6 {% endif %} {% if connect_on_demand is defined %} -- cgit v1.2.3 From 8fc06b5f8bbfcc49e69406fd70cd5cd42fb6d39f Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sat, 21 Aug 2021 15:25:31 +0200 Subject: pppoe: T1318: set source interface next to rp-pppoe.so plugin in peer template --- data/templates/pppoe/peer.tmpl | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/data/templates/pppoe/peer.tmpl b/data/templates/pppoe/peer.tmpl index 7803a247d..928ed1238 100644 --- a/data/templates/pppoe/peer.tmpl +++ b/data/templates/pppoe/peer.tmpl @@ -34,7 +34,7 @@ noproxyarp # Unlimited connection attempts maxfail 0 -plugin rp-pppoe.so +plugin rp-pppoe.so {{ source_interface }} {% if access_concentrator is defined and access_concentrator is not none %} rp_pppoe_ac '{{ access_concentrator }}' {% endif %} @@ -42,7 +42,6 @@ rp_pppoe_ac '{{ access_concentrator }}' rp_pppoe_service '{{ service_name }}' {% endif %} -{{ source_interface }} persist ifname {{ ifname }} ipparam {{ ifname }} -- cgit v1.2.3 From 1cc2e8f9e2e06f52f3d4a9941064a40e0eb80b20 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sun, 22 Aug 2021 09:38:56 +0200 Subject: smoketest: pppoes: adjust testcase to vyos.ifconfig.interface migration Now that the PPPoE interface also makes use of the vyos.ifconfig.interface class, we need to adjust the smoketests to reflect all those changes. TODO: Start a local PPPoE server in a namespace and connect locally for deeper testing. --- smoketest/scripts/cli/test_interfaces_pppoe.py | 120 +++++++++---------------- 1 file changed, 44 insertions(+), 76 deletions(-) diff --git a/smoketest/scripts/cli/test_interfaces_pppoe.py b/smoketest/scripts/cli/test_interfaces_pppoe.py index 3412ebae0..67edce2a0 100755 --- a/smoketest/scripts/cli/test_interfaces_pppoe.py +++ b/smoketest/scripts/cli/test_interfaces_pppoe.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2019-2020 VyOS maintainers and contributors +# Copyright (C) 2019-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 @@ -22,10 +22,8 @@ from base_vyostest_shim import VyOSUnitTestSHIM from vyos.configsession import ConfigSession from vyos.configsession import ConfigSessionError -from vyos.util import read_file config_file = '/etc/ppp/peers/{}' -dhcp6c_config_file = '/run/dhcp6c/dhcp6c.{}.conf' base_path = ['interfaces', 'pppoe'] def get_config_value(interface, key): @@ -35,25 +33,26 @@ def get_config_value(interface, key): return list(line.split()) return [] -def get_dhcp6c_config_value(interface, key): - tmp = read_file(dhcp6c_config_file.format(interface)) - tmp = re.findall(r'\n?{}\s+(.*)'.format(key), tmp) - - out = [] - for item in tmp: - out.append(item.replace(';','')) - return out - +# add a classmethod to setup a temporaray PPPoE server for "proper" validation class PPPoEInterfaceTest(VyOSUnitTestSHIM.TestCase): def setUp(self): self._interfaces = ['pppoe10', 'pppoe20', 'pppoe30'] self._source_interface = 'eth0' def tearDown(self): + # Validate PPPoE client process + for interface in self._interfaces: + running = False + for proc in process_iter(): + if interface in proc.cmdline(): + running = True + break + self.assertTrue(running) + self.cli_delete(base_path) self.cli_commit() - def test_pppoe_client(self): + def test_01_pppoe_client(self): # Check if PPPoE dialer can be configured and runs for interface in self._interfaces: user = 'VyOS-user-' + interface @@ -71,8 +70,8 @@ class PPPoEInterfaceTest(VyOSUnitTestSHIM.TestCase): self.cli_commit() self.cli_set(base_path + [interface, 'source-interface', self._source_interface]) - # commit changes - self.cli_commit() + # commit changes + self.cli_commit() # verify configuration file(s) for interface in self._interfaces: @@ -88,17 +87,7 @@ class PPPoEInterfaceTest(VyOSUnitTestSHIM.TestCase): tmp = get_config_value(interface, 'ifname')[1] self.assertEqual(tmp, interface) - # Check if ppp process is running in the interface in question - running = False - for p in process_iter(): - if "pppd" in p.name(): - if interface in p.cmdline(): - running = True - - self.assertTrue(running) - - - def test_pppoe_clent_disabled_interface(self): + def test_02_pppoe_client_disabled_interface(self): # Check if PPPoE Client can be disabled for interface in self._interfaces: self.cli_set(base_path + [interface, 'authentication', 'user', 'vyos']) @@ -106,23 +95,45 @@ class PPPoEInterfaceTest(VyOSUnitTestSHIM.TestCase): self.cli_set(base_path + [interface, 'source-interface', self._source_interface]) self.cli_set(base_path + [interface, 'disable']) - self.cli_commit() + self.cli_commit() - # Validate PPPoE client process - running = False + # Validate PPPoE client process - must not run as interfaces are disabled for interface in self._interfaces: + running = False for proc in process_iter(): if interface in proc.cmdline(): running = True + break + self.assertFalse(running) + + # enable PPPoE interfaces + for interface in self._interfaces: + self.cli_delete(base_path + [interface, 'disable']) + + self.cli_commit() + - self.assertFalse(running) + def test_03_pppoe_authentication(self): + # When username or password is set - so must be the other + for interface in self._interfaces: + self.cli_set(base_path + [interface, 'authentication', 'user', 'vyos']) + self.cli_set(base_path + [interface, 'source-interface', self._source_interface]) + self.cli_set(base_path + [interface, 'ipv6', 'address', 'autoconf']) + # check validate() - if user is set, so must be the password + with self.assertRaises(ConfigSessionError): + self.cli_commit() + + self.cli_set(base_path + [interface, 'authentication', 'password', 'vyos']) + + self.cli_commit() - def test_pppoe_dhcpv6pd(self): + def test_04_pppoe_dhcpv6pd(self): # Check if PPPoE dialer can be configured with DHCPv6-PD address = '1' sla_id = '0' sla_len = '8' + for interface in self._interfaces: self.cli_set(base_path + [interface, 'authentication', 'user', 'vyos']) self.cli_set(base_path + [interface, 'authentication', 'password', 'vyos']) @@ -147,51 +158,8 @@ class PPPoEInterfaceTest(VyOSUnitTestSHIM.TestCase): self.assertEqual(tmp, 'vyos') tmp = get_config_value(interface, 'password')[1].replace('"', '') self.assertEqual(tmp, 'vyos') - - for param in ['+ipv6', 'ipv6cp-use-ipaddr']: - tmp = get_config_value(interface, param)[0] - self.assertEqual(tmp, param) - - # verify DHCPv6 prefix delegation - # will return: ['delegation', '::/56 infinity;'] - tmp = get_dhcp6c_config_value(interface, 'prefix')[1].split()[0] # mind the whitespace - self.assertEqual(tmp, '::/56') - tmp = get_dhcp6c_config_value(interface, 'prefix-interface')[0].split()[0] - self.assertEqual(tmp, self._source_interface) - tmp = get_dhcp6c_config_value(interface, 'ifid')[0] - self.assertEqual(tmp, address) - tmp = get_dhcp6c_config_value(interface, 'sla-id')[0] - self.assertEqual(tmp, sla_id) - tmp = get_dhcp6c_config_value(interface, 'sla-len')[0] - self.assertEqual(tmp, sla_len) - - # Check if ppp process is running in the interface in question - running = False - for p in process_iter(): - if "pppd" in p.name(): - running = True - self.assertTrue(running) - - # We can not check if wide-dhcpv6 process is running as it is started - # after the PPP interface gets a link to the ISP - but we can see if - # it would be started by the scripts - tmp = read_file(f'/etc/ppp/ipv6-up.d/1000-vyos-pppoe-{interface}') - tmp = re.findall(f'systemctl restart dhcp6c@{interface}.service', tmp) - self.assertTrue(tmp) - - def test_pppoe_authentication(self): - # When username or password is set - so must be the other - interface = 'pppoe0' - self.cli_set(base_path + [interface, 'authentication', 'user', 'vyos']) - self.cli_set(base_path + [interface, 'source-interface', self._source_interface]) - self.cli_set(base_path + [interface, 'ipv6', 'address', 'autoconf']) - - # check validate() - if user is set, so must be the password - with self.assertRaises(ConfigSessionError): - self.cli_commit() - - self.cli_set(base_path + [interface, 'authentication', 'password', 'vyos']) - self.cli_commit() + tmp = get_config_value(interface, '+ipv6 ipv6cp-use-ipaddr') + self.assertListEqual(tmp, ['+ipv6', 'ipv6cp-use-ipaddr']) if __name__ == '__main__': unittest.main(verbosity=2) -- cgit v1.2.3 From f21a7a9590a8b2d9a45ab8f25fdfa5d5a7e6984a Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sun, 22 Aug 2021 12:00:53 +0200 Subject: l2tp: Jinja2 add trailing newline --- data/templates/accel-ppp/l2tp.config.tmpl | 1 + 1 file changed, 1 insertion(+) diff --git a/data/templates/accel-ppp/l2tp.config.tmpl b/data/templates/accel-ppp/l2tp.config.tmpl index 070a966b7..44c96b935 100644 --- a/data/templates/accel-ppp/l2tp.config.tmpl +++ b/data/templates/accel-ppp/l2tp.config.tmpl @@ -150,3 +150,4 @@ vendor={{ radius_shaper_vendor }} [cli] tcp=127.0.0.1:2004 sessions-columns=ifname,username,calling-sid,ip,{{ ip6_column | join(',') }}{{ ',' if ip6_column }}rate-limit,type,comp,state,rx-bytes,tx-bytes,uptime + -- cgit v1.2.3 From 9ca60f94bf16b5958967d786f479842e454767dc Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sun, 22 Aug 2021 12:01:22 +0200 Subject: ipsec: T1210: op-mode command for Windows has no "profile" option --- op-mode-definitions/generate-ipsec-profile.xml.in | 31 ----------------------- 1 file changed, 31 deletions(-) diff --git a/op-mode-definitions/generate-ipsec-profile.xml.in b/op-mode-definitions/generate-ipsec-profile.xml.in index be9227971..8d1051b94 100644 --- a/op-mode-definitions/generate-ipsec-profile.xml.in +++ b/op-mode-definitions/generate-ipsec-profile.xml.in @@ -100,37 +100,6 @@ ${vyos_op_scripts_dir}/ikev2_profile_generator.py --os windows --connection "$5" --remote "$7" --name "$9" - - - - Profile name as seen under system profiles - - <name> - - - ${vyos_op_scripts_dir}/ikev2_profile_generator.py --os windows --connection "$5" --remote "$7" --name "$9" --profile "${11}" - - - - - - Profile name as seen under system profiles - - <name> - - - ${vyos_op_scripts_dir}/ikev2_profile_generator.py --os windows --connection "$5" --remote "$7" --profile "$9" - - - - Connection name as seen in the VPN application - - <name> - - - ${vyos_op_scripts_dir}/ikev2_profile_generator.py --os windows --connection "$5" --remote "$7" --profile "$9" --name "${11}" - - -- cgit v1.2.3 From a30d74f41647258d0c338c495ea7fea90dec3663 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sun, 22 Aug 2021 18:30:40 +0200 Subject: container: op-mode: T3765: add "connect container mysql-server" command --- op-mode-definitions/connect.xml.in | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/op-mode-definitions/connect.xml.in b/op-mode-definitions/connect.xml.in index 8f19eac70..88f91ae32 100644 --- a/op-mode-definitions/connect.xml.in +++ b/op-mode-definitions/connect.xml.in @@ -24,6 +24,15 @@ sudo ${vyos_op_scripts_dir}/connect_disconnect.py --connect "$3" + + + Attach to a running container + + container name + + + echo "Use Ctrl+p and Ctrl+q to detach"; sudo podman attach "$3" + -- cgit v1.2.3 From 0e4b36f4c550baf151a1bbc68e4ed1bea8cd3c94 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sun, 22 Aug 2021 19:23:25 +0200 Subject: container: op-mode: T3765: move "connect container" command to container.xml.in file --- op-mode-definitions/connect.xml.in | 9 --------- op-mode-definitions/containers.xml.in | 13 +++++++++++++ 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/op-mode-definitions/connect.xml.in b/op-mode-definitions/connect.xml.in index 88f91ae32..8f19eac70 100644 --- a/op-mode-definitions/connect.xml.in +++ b/op-mode-definitions/connect.xml.in @@ -24,15 +24,6 @@ sudo ${vyos_op_scripts_dir}/connect_disconnect.py --connect "$3" - - - Attach to a running container - - container name - - - echo "Use Ctrl+p and Ctrl+q to detach"; sudo podman attach "$3" - diff --git a/op-mode-definitions/containers.xml.in b/op-mode-definitions/containers.xml.in index a22549dd9..4c95fa315 100644 --- a/op-mode-definitions/containers.xml.in +++ b/op-mode-definitions/containers.xml.in @@ -17,6 +17,19 @@ + + + + + Attach to a running container + + container name + + + echo "Use Ctrl+p and Ctrl+q to detach"; sudo podman attach "$3" + + + -- cgit v1.2.3 From deaa1e8b65bdaf89caa9855d659dd7da459cf415 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sun, 22 Aug 2021 19:23:34 +0200 Subject: container: op-mode: T3765: add "restart container" command --- op-mode-definitions/containers.xml.in | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/op-mode-definitions/containers.xml.in b/op-mode-definitions/containers.xml.in index 4c95fa315..94dd24369 100644 --- a/op-mode-definitions/containers.xml.in +++ b/op-mode-definitions/containers.xml.in @@ -71,4 +71,17 @@ + + + + + Restart a given container + + container name + + + sudo podman restart "$3" + + + -- cgit v1.2.3 From 10a1e7b68c54cab1e802b0aa5f17929a6da9efa9 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sun, 22 Aug 2021 19:28:40 +0200 Subject: ipsec: T2816: l2tp ipsec VPN must be started after strongSwan --- interface-definitions/vpn_l2tp.xml.in | 1 + 1 file changed, 1 insertion(+) diff --git a/interface-definitions/vpn_l2tp.xml.in b/interface-definitions/vpn_l2tp.xml.in index cf31af70f..907bcaadb 100644 --- a/interface-definitions/vpn_l2tp.xml.in +++ b/interface-definitions/vpn_l2tp.xml.in @@ -5,6 +5,7 @@ L2TP Virtual Private Network (VPN) + 902 -- cgit v1.2.3 From 252bc820b0d130d8d81b5711586eca41287abdca Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sun, 22 Aug 2021 19:48:28 +0200 Subject: scripts: op-mode: use Python 'f'ormat strings on debug messages --- scripts/build-command-op-templates | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/scripts/build-command-op-templates b/scripts/build-command-op-templates index c285ee594..d0e5833cc 100755 --- a/scripts/build-command-op-templates +++ b/scripts/build-command-op-templates @@ -54,7 +54,7 @@ debug = args.debug try: xml = ET.parse(input_file) except Exception as e: - print("Failed to load interface definition file {0}".format(input_file)) + print(f"Failed to load interface definition file {input_file}") print(e) sys.exit(1) @@ -64,15 +64,15 @@ try: if not validator.validate(xml): print(validator.error_log) - print("Interface definition file {0} does not match the schema!".format(input_file)) + print(f"Interface definition file {input_file} does not match the schema!") sys.exit(1) except Exception as e: - print("Failed to load the XML schema {0}".format(schema_file)) + print(f"Failed to load the XML schema {schema_file}") print(e) sys.exit(1) if not os.access(output_dir, os.W_OK): - print("The output directory {0} is not writeable".format(output_dir)) + print(f"The output directory {output_dir} is not writeable") sys.exit(1) ## If we got this far, everything must be ok and we can convert the file @@ -160,14 +160,14 @@ def process_node(n, tmpl_dir): my_tmpl_dir.append(name) if debug: - print("Name of the node: {};\n Created directory: ".format(name), end="") + print(f"Name of the node: {name};\n Created directory: ", end="") os.makedirs(make_path(my_tmpl_dir), exist_ok=True) props = get_properties(props_elem) if node_type == "node": if debug: - print("Processing node {}".format(name)) + print(f"Processing node {name}") nodedef_path = os.path.join(make_path(my_tmpl_dir), "node.def") # Only create the "node.def" file if it exists but is empty, or if it @@ -180,9 +180,10 @@ def process_node(n, tmpl_dir): inner_nodes = children.iterfind("*") for inner_n in inner_nodes: process_node(inner_n, my_tmpl_dir) + if node_type == "tagNode": if debug: - print("Processing tag node {}".format(name)) + print(f"Processing tagNode {name}") os.makedirs(make_path(my_tmpl_dir), exist_ok=True) @@ -211,7 +212,7 @@ def process_node(n, tmpl_dir): else: # This is a leaf node if debug: - print("Processing leaf node {}".format(name)) + print(f"Processing leaf node {name}") with open(os.path.join(make_path(my_tmpl_dir), "node.def"), "w") as f: f.write(make_node_def(props, command)) -- cgit v1.2.3 From 17b5ac143c9128ac3e187d8d8167dd8fe6cbca7d Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sun, 22 Aug 2021 20:26:36 +0200 Subject: T3165: op-mode: prevent override of populated node.def file with empty content This is an extension to commit b4fdcebe ("T3165: prevent override of populated node.def file with empty content") which implemented the same thing for the configuration mode commands. --- scripts/build-command-op-templates | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/scripts/build-command-op-templates b/scripts/build-command-op-templates index d0e5833cc..a4d6d1d08 100755 --- a/scripts/build-command-op-templates +++ b/scripts/build-command-op-templates @@ -165,11 +165,11 @@ def process_node(n, tmpl_dir): props = get_properties(props_elem) + nodedef_path = os.path.join(make_path(my_tmpl_dir), "node.def") if node_type == "node": if debug: print(f"Processing node {name}") - nodedef_path = os.path.join(make_path(my_tmpl_dir), "node.def") # Only create the "node.def" file if it exists but is empty, or if it # does not exist at all. if not os.path.exists(nodedef_path) or os.path.getsize(nodedef_path) == 0: @@ -187,13 +187,11 @@ def process_node(n, tmpl_dir): os.makedirs(make_path(my_tmpl_dir), exist_ok=True) - nodedef_path = os.path.join(make_path(my_tmpl_dir), "node.def") - if not os.path.exists(nodedef_path): + # Only create the "node.def" file if it exists but is empty, or if it + # does not exist at all. + if not os.path.exists(nodedef_path) or os.path.getsize(nodedef_path) == 0: with open(nodedef_path, "w") as f: f.write('help: {0}\n'.format(props['help'])) - else: - # Something has already generated this file - pass # Create the inner node.tag part my_tmpl_dir.append("node.tag") @@ -202,8 +200,12 @@ def process_node(n, tmpl_dir): print("Created path for the tagNode: {}".format(make_path(my_tmpl_dir)), end="") # Not sure if we want partially defined tag nodes, write the file unconditionally - with open(os.path.join(make_path(my_tmpl_dir), "node.def"), "w") as f: - f.write(make_node_def(props, command)) + nodedef_path = os.path.join(make_path(my_tmpl_dir), "node.def") + # Only create the "node.def" file if it exists but is empty, or if it + # does not exist at all. + if not os.path.exists(nodedef_path) or os.path.getsize(nodedef_path) == 0: + with open(nodedef_path, "w") as f: + f.write(make_node_def(props, command)) if children is not None: inner_nodes = children.iterfind("*") @@ -214,9 +216,9 @@ def process_node(n, tmpl_dir): if debug: print(f"Processing leaf node {name}") - with open(os.path.join(make_path(my_tmpl_dir), "node.def"), "w") as f: - f.write(make_node_def(props, command)) - + if not os.path.exists(nodedef_path) or os.path.getsize(nodedef_path) == 0: + with open(nodedef_path, "w") as f: + f.write(make_node_def(props, command)) root = xml.getroot() -- cgit v1.2.3 From a1873a39f4fb9e8445b772b3fc1697f55314888a Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sun, 22 Aug 2021 20:35:46 +0200 Subject: container: op-mode: T3765: add "show log container" command --- op-mode-definitions/containers.xml.in | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/op-mode-definitions/containers.xml.in b/op-mode-definitions/containers.xml.in index 94dd24369..8cc30d3a8 100644 --- a/op-mode-definitions/containers.xml.in +++ b/op-mode-definitions/containers.xml.in @@ -69,6 +69,19 @@ + + + + + Show logs from a given container + + container name + + + sudo podman logs --names "$4" + + + -- cgit v1.2.3 From 536991d0c6f305256ce6ab5975d15116f027b7b6 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sun, 22 Aug 2021 20:41:11 +0200 Subject: xml: op-mode: remove multiple "Reset a service" help definitions That nasty workaround to always specify the same value for the node.def file as the help text is no longer necessary after commit 17b5ac14 ("T3165: op-mode: prevent override of populated node.def file with empty content". The redundant definitions are no longer necessary. --- op-mode-definitions/dns-forwarding.xml.in | 3 --- op-mode-definitions/ipv4-route.xml.in | 6 ------ op-mode-definitions/ipv6-route.xml.in | 6 ------ op-mode-definitions/openvpn.xml.in | 3 --- op-mode-definitions/pppoe-server.xml.in | 3 --- op-mode-definitions/reset-conntrack.xml.in | 3 --- op-mode-definitions/reset-vpn.xml.in | 3 --- op-mode-definitions/terminal.xml.in | 8 -------- 8 files changed, 35 deletions(-) diff --git a/op-mode-definitions/dns-forwarding.xml.in b/op-mode-definitions/dns-forwarding.xml.in index 36fe6b5ef..6574f2319 100644 --- a/op-mode-definitions/dns-forwarding.xml.in +++ b/op-mode-definitions/dns-forwarding.xml.in @@ -59,9 +59,6 @@ - - Reset a service - diff --git a/op-mode-definitions/ipv4-route.xml.in b/op-mode-definitions/ipv4-route.xml.in index aab3df0f1..8f001d5bb 100644 --- a/op-mode-definitions/ipv4-route.xml.in +++ b/op-mode-definitions/ipv4-route.xml.in @@ -20,11 +20,7 @@ - - - Reset a service - @@ -56,7 +52,6 @@ - Reset IP route @@ -68,7 +63,6 @@ sudo ip route flush cache - Flush the kernel route cache for a given route diff --git a/op-mode-definitions/ipv6-route.xml.in b/op-mode-definitions/ipv6-route.xml.in index 7f188fdb2..5f20444d4 100644 --- a/op-mode-definitions/ipv6-route.xml.in +++ b/op-mode-definitions/ipv6-route.xml.in @@ -28,11 +28,7 @@ - - - Reset a service - @@ -64,7 +60,6 @@ - Reset IPv6 route @@ -76,7 +71,6 @@ sudo ip -f inet6 route flush cache - Flush the kernel IPv6 route cache for a given route diff --git a/op-mode-definitions/openvpn.xml.in b/op-mode-definitions/openvpn.xml.in index 781fbdc9d..8f7419545 100644 --- a/op-mode-definitions/openvpn.xml.in +++ b/op-mode-definitions/openvpn.xml.in @@ -1,9 +1,6 @@ - - Reset a service - diff --git a/op-mode-definitions/pppoe-server.xml.in b/op-mode-definitions/pppoe-server.xml.in index 6efdc5a48..835e03aab 100644 --- a/op-mode-definitions/pppoe-server.xml.in +++ b/op-mode-definitions/pppoe-server.xml.in @@ -40,9 +40,6 @@ - - Reset a service - diff --git a/op-mode-definitions/reset-conntrack.xml.in b/op-mode-definitions/reset-conntrack.xml.in index 827ba4af4..9c8265f77 100644 --- a/op-mode-definitions/reset-conntrack.xml.in +++ b/op-mode-definitions/reset-conntrack.xml.in @@ -1,9 +1,6 @@ - - Reset a service - diff --git a/op-mode-definitions/reset-vpn.xml.in b/op-mode-definitions/reset-vpn.xml.in index 71dbb4ed9..94ee1c7df 100644 --- a/op-mode-definitions/reset-vpn.xml.in +++ b/op-mode-definitions/reset-vpn.xml.in @@ -1,9 +1,6 @@ - - Reset a service - diff --git a/op-mode-definitions/terminal.xml.in b/op-mode-definitions/terminal.xml.in index 9c4e629cb..2a76de146 100644 --- a/op-mode-definitions/terminal.xml.in +++ b/op-mode-definitions/terminal.xml.in @@ -40,7 +40,6 @@ builtin $3 - Control console behaviors @@ -54,13 +53,11 @@ - Control terminal behaviors - Set key behaviors @@ -77,7 +74,6 @@ - Set terminal pager to default (less) @@ -93,7 +89,6 @@ VYATTA_PAGER=$4 - Set terminal to given number of rows (0 disables paging) @@ -103,7 +98,6 @@ if [ "$4" -eq 0 ]; then VYATTA_PAGER=cat; else VYATTA_PAGER=${_vyatta_default_pager}; stty rows $4; fi - Set terminal to given number of columns @@ -117,6 +111,4 @@ - - -- cgit v1.2.3 From d225f938b2250229621c7013c47ce52b839ae169 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sun, 22 Aug 2021 20:42:21 +0200 Subject: xml: op-mode: add missing help test for "monitor protocol ospf" commands --- op-mode-definitions/monitor-protocol.xml.in | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/op-mode-definitions/monitor-protocol.xml.in b/op-mode-definitions/monitor-protocol.xml.in index 6a6bd50f3..f3af3575c 100644 --- a/op-mode-definitions/monitor-protocol.xml.in +++ b/op-mode-definitions/monitor-protocol.xml.in @@ -263,13 +263,14 @@ - Monitor the Open Shortest Path First (OSPF) protocol + Monitor Open Shortest Path First (OSPF) protocol #include - - + + Disable Open Shortest Path First (OSPF) debugging + @@ -458,6 +459,9 @@ + + Enable Open Shortest Path First (OSPF) debugging + -- cgit v1.2.3 From 68567d60626aa7ee5f990af792eb63f6887d93de Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sun, 22 Aug 2021 20:42:56 +0200 Subject: xml: op-mode: add missing help test for "reset openvpn" command --- op-mode-definitions/openvpn.xml.in | 3 +++ 1 file changed, 3 insertions(+) diff --git a/op-mode-definitions/openvpn.xml.in b/op-mode-definitions/openvpn.xml.in index 8f7419545..73cbbe501 100644 --- a/op-mode-definitions/openvpn.xml.in +++ b/op-mode-definitions/openvpn.xml.in @@ -3,6 +3,9 @@ + + Reset OpenVPN client/server connections + -- cgit v1.2.3 From 1b0d7b9e027672a95535d18e550919aff7e41ee3 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sun, 22 Aug 2021 20:51:34 +0200 Subject: log --- op-mode-definitions/containers.xml.in | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/op-mode-definitions/containers.xml.in b/op-mode-definitions/containers.xml.in index 8cc30d3a8..1ed569407 100644 --- a/op-mode-definitions/containers.xml.in +++ b/op-mode-definitions/containers.xml.in @@ -61,6 +61,15 @@ sudo ${vyos_op_scripts_dir}/containers_op.py --image + + + Show logs from a given container + + container name + + + sudo podman logs --names "$4" + Show available container networks -- cgit v1.2.3 From e99cdf40c72dec8b9019eca728aaad0f82c6030b Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sun, 22 Aug 2021 20:51:55 +0200 Subject: Makefile: T3165: do not allow empty node.def files for op-mode commands Commit 99440fc0 ("Makefile: fix logic to detect empty "node.def" files") disabled the detection of empty node.def files for op-mode commands. The generation of a duplicate and thus empty node.def file is not prohibited by commit 17b5ac14 ("T3165: op-mode: prevent override of populated node.def file with empty content") and thus the check is re-enabled! --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 2c79f545d..b582ef84c 100644 --- a/Makefile +++ b/Makefile @@ -61,7 +61,7 @@ op_mode_definitions: $(op_xml_obj) # XXX: test if there are empty node.def files - this is not allowed as these # could mask help strings or mandatory priority statements - #find $(OP_TMPL_DIR) -name node.def -type f -empty -exec false {} + || sh -c 'echo "There are empty node.def files! Check your interface definitions." && exit 1' + find $(OP_TMPL_DIR) -name node.def -type f -empty -exec false {} + || sh -c 'echo "There are empty node.def files! Check your interface definitions." && exit 1' .PHONY: vyshim vyshim: -- cgit v1.2.3 From 27095042decb58fb53766c190bb60adcb69805ef Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Mon, 23 Aug 2021 11:16:13 +0200 Subject: containers: T2216: add missing verify() step on environment variables A environment variable MUST always have a value specified. Non existing values will cause the following error: Traceback (most recent call last): File "/usr/libexec/vyos/conf_mode/containers.py", line 269, in apply(c) File "/usr/libexec/vyos/conf_mode/containers.py", line 224, in apply env_opt += " -e ".join(f"{k}={v['value']}" for k, v in container_config['environment'].items()) File "/usr/libexec/vyos/conf_mode/containers.py", line 224, in env_opt += " -e ".join(f"{k}={v['value']}" for k, v in container_config['environment'].items()) KeyError: 'value' --- src/conf_mode/containers.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/conf_mode/containers.py b/src/conf_mode/containers.py index 7544bd840..b573df889 100755 --- a/src/conf_mode/containers.py +++ b/src/conf_mode/containers.py @@ -102,7 +102,6 @@ def verify(container): if len(container_config['network']) > 1: raise ConfigError(f'Only one network can be specified for container "{name}"!') - # Check if the specified container network exists network_name = list(container_config['network'])[0] if network_name not in container['network']: @@ -127,6 +126,10 @@ def verify(container): if ip_address(address) == ip_network(network)[1]: raise ConfigError(f'Address "{address}" reserved for the container engine!') + if 'environment' in container_config: + for var, cfg in container_config['environment'].items(): + if 'value' not in cfg: + raise ConfigError(f'Environment variable {var} has no value assigned!') # Container image is a mandatory option if 'image' not in container_config: -- cgit v1.2.3 From 461e3e1b47813d808bf22c9f8e2d8d8fb427fac2 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Mon, 23 Aug 2021 11:46:20 +0200 Subject: container: T2216: add completion helper for "delete container image" --- op-mode-definitions/containers.xml.in | 3 +++ 1 file changed, 3 insertions(+) diff --git a/op-mode-definitions/containers.xml.in b/op-mode-definitions/containers.xml.in index 1ed569407..e93487105 100644 --- a/op-mode-definitions/containers.xml.in +++ b/op-mode-definitions/containers.xml.in @@ -40,6 +40,9 @@ Delete container image + + + sudo ${vyos_op_scripts_dir}/containers_op.py --remove "${4}" -- cgit v1.2.3 From 31c97abb63c7223e224c399a7d474f012ecf8a45 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Mon, 23 Aug 2021 11:46:54 +0200 Subject: containers: T2216: add environmnet variable constraint An environment variable passed to podman can only consist out of alphanumeric characters, a hypend and an underscore. --- interface-definitions/containers.xml.in | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/interface-definitions/containers.xml.in b/interface-definitions/containers.xml.in index 124b1f65e..29762c171 100644 --- a/interface-definitions/containers.xml.in +++ b/interface-definitions/containers.xml.in @@ -25,6 +25,10 @@ Add custom environment variables + + ^[-_a-zA-Z0-9]+$ + + Environment variable name must be alphanumeric and can contain hyphen and underscores -- cgit v1.2.3 From 1f6746c44c5349a35847abfb410d3826a4e7ca99 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Mon, 23 Aug 2021 11:48:00 +0200 Subject: containers: T2216: xml: impove help string for address command --- interface-definitions/containers.xml.in | 2 +- src/conf_mode/containers.py | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/interface-definitions/containers.xml.in b/interface-definitions/containers.xml.in index 29762c171..030980dba 100644 --- a/interface-definitions/containers.xml.in +++ b/interface-definitions/containers.xml.in @@ -60,7 +60,7 @@ Set IPv4 static address to container (optional) ipv4 - IPv4 address (x.x.x.1 reserved) + IPv4 address diff --git a/src/conf_mode/containers.py b/src/conf_mode/containers.py index b573df889..23f17ab55 100755 --- a/src/conf_mode/containers.py +++ b/src/conf_mode/containers.py @@ -124,7 +124,8 @@ def verify(container): # We can not use the first IP address of a network prefix as this is used by podman if ip_address(address) == ip_network(network)[1]: - raise ConfigError(f'Address "{address}" reserved for the container engine!') + raise ConfigError(f'IP address "{address}" can not be used for a container, '\ + 'reserved for the container engine!') if 'environment' in container_config: for var, cfg in container_config['environment'].items(): -- cgit v1.2.3 From e71a98562aabea53cb9b5e1958398e1e674490bb Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Mon, 23 Aug 2021 11:48:47 +0200 Subject: containers: T2216: add CLI commands to specify restart behavior and memory usage A container is limited to 256MB memory by default and will always restart on failure. --- interface-definitions/containers.xml.in | 42 +++++++++++++++++++++++++++++++++ src/conf_mode/containers.py | 22 +++++++++++++---- 2 files changed, 59 insertions(+), 5 deletions(-) diff --git a/interface-definitions/containers.xml.in b/interface-definitions/containers.xml.in index 030980dba..419802866 100644 --- a/interface-definitions/containers.xml.in +++ b/interface-definitions/containers.xml.in @@ -47,6 +47,24 @@ Image name in the hub-registry + + + Constrain the memory available to a container (default: 256MB) + + u32:0 + Unlimited + + + u32:1-16384 + Container memory in megabytes (MB) + + + + + Container memory must be in range 0 to 16384 MB + + 256 + Attach user defined network to container @@ -119,6 +137,30 @@ + + + Mount a volume into the container + + no on-failure always + + + no + Do not restart containers on exit + + + on-failure + Restart containers when they exit with a non-zero exit code, retrying indefinitely (default) + + + always + Restart containers when they exit, regardless of status, retrying indefinitely + + + ^(no|on-failure|always)$ + + + on-failure + Mount a volume into the container diff --git a/src/conf_mode/containers.py b/src/conf_mode/containers.py index 23f17ab55..fd0c5e52d 100755 --- a/src/conf_mode/containers.py +++ b/src/conf_mode/containers.py @@ -79,8 +79,17 @@ def get_config(config=None): # We have gathered the dict representation of the CLI, but there are default # options which we need to update into the dictionary retrived. default_values = defaults(base) + # container base default values can not be merged here - remove and add them later + if 'name' in default_values: + del default_values['name'] container = dict_merge(default_values, container) + # Merge per-container default values + if 'name' in container: + default_values = defaults(base + ['name']) + for name in container['name']: + container['name'][name] = dict_merge(default_values, container['name'][name]) + # Delete container network, delete containers tmp = node_changed(conf, ['container', 'network']) if tmp: container.update({'net_remove' : tmp}) @@ -216,6 +225,8 @@ def apply(container): # Check if the container has already been created if not container_exists(name): image = container_config['image'] + memory = container_config['memory'] + restart = container_config['restart'] # Currently the best way to run a command and immediately print stdout print(os.system(f'podman pull {image}')) @@ -242,19 +253,20 @@ def apply(container): # Bind volume volume = '' if 'volume' in container_config: - for vol in container_config['volume']: - svol = container_config['volume'][vol]['source'] - dvol = container_config['volume'][vol]['destination'] + for vol, vol_config in container_config['volume']: + svol = vol_config['source'] + dvol = vol_config['destination'] volume += f' -v {svol}:{dvol}' + container_base_cmd = f'podman run -dit --name {name} --memory {memory} --restart {restart} {port} {volume} {env_opt} {image}' if 'allow_host_networks' in container_config: - _cmd(f'podman run -dit --name {name} --net host {port} {volume} {env_opt} {image}') + _cmd(f'{container_base_cmd} --net host') else: for network in container_config['network']: ipparam = '' if 'address' in container_config['network'][network]: ipparam = '--ip ' + container_config['network'][network]['address'] - _cmd(f'podman run --name {name} -dit --net {network} {ipparam} {port} {volume} {env_opt} {image}') + _cmd(f'{container_base_cmd} --net {network} {ipparam}') # Else container is already created. Just start it. # It's needed after reboot. -- cgit v1.2.3 From e0b2453bd0399d387f4a4d0c4f2e12509249db27 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Mon, 23 Aug 2021 14:03:36 +0200 Subject: containers: T2216: restructure container_base_cmd to have image name at the end --- src/conf_mode/containers.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/conf_mode/containers.py b/src/conf_mode/containers.py index fd0c5e52d..7aea5f432 100755 --- a/src/conf_mode/containers.py +++ b/src/conf_mode/containers.py @@ -233,8 +233,8 @@ def apply(container): # Check/set environment options "-e foo=bar" env_opt = '' if 'environment' in container_config: - env_opt = '-e ' - env_opt += " -e ".join(f"{k}={v['value']}" for k, v in container_config['environment'].items()) + for k, v in container_config['environment'].items(): + env_opt += f" -e \"{k}={v['value']}\"" # Publish ports port = '' @@ -258,15 +258,15 @@ def apply(container): dvol = vol_config['destination'] volume += f' -v {svol}:{dvol}' - container_base_cmd = f'podman run -dit --name {name} --memory {memory} --restart {restart} {port} {volume} {env_opt} {image}' + container_base_cmd = f'podman run -dit --name {name} --memory {memory}m --restart {restart} {port} {volume} {env_opt}' if 'allow_host_networks' in container_config: - _cmd(f'{container_base_cmd} --net host') + _cmd(f'{container_base_cmd} --net host {image}') else: for network in container_config['network']: ipparam = '' if 'address' in container_config['network'][network]: ipparam = '--ip ' + container_config['network'][network]['address'] - _cmd(f'{container_base_cmd} --net {network} {ipparam}') + _cmd(f'{container_base_cmd} --net {network} {ipparam} {image}') # Else container is already created. Just start it. # It's needed after reboot. -- cgit v1.2.3 From 501e9a9a53aac71c451a8dfbbe2508ff59740a0b Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Mon, 23 Aug 2021 14:42:25 +0200 Subject: container: T2216: increase sysctl inotify watchers --- src/etc/sysctl.d/32-vyos-podman.conf | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 src/etc/sysctl.d/32-vyos-podman.conf diff --git a/src/etc/sysctl.d/32-vyos-podman.conf b/src/etc/sysctl.d/32-vyos-podman.conf new file mode 100644 index 000000000..7068bf88d --- /dev/null +++ b/src/etc/sysctl.d/32-vyos-podman.conf @@ -0,0 +1,5 @@ +# Increase inotify watchers as per https://bugzilla.redhat.com/show_bug.cgi?id=1829596 +fs.inotify.max_queued_events = 1048576 +fs.inotify.max_user_instances = 1048576 +fs.inotify.max_user_watches = 1048576 + -- cgit v1.2.3 From a1f82a06e1a1788164f52ef291a1275568912b9b Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Mon, 23 Aug 2021 14:42:54 +0200 Subject: container: T2216: op-mode now supports updating the image for a given container --- op-mode-definitions/containers.xml.in | 20 ++++++++++++++ src/op_mode/containers_op.py | 49 ++++++++++++++++++++++++----------- 2 files changed, 54 insertions(+), 15 deletions(-) diff --git a/op-mode-definitions/containers.xml.in b/op-mode-definitions/containers.xml.in index e93487105..efa72e1a4 100644 --- a/op-mode-definitions/containers.xml.in +++ b/op-mode-definitions/containers.xml.in @@ -109,4 +109,24 @@ + + + + + Update a container image + + + + + Delete container image + + container name + + + sudo ${vyos_op_scripts_dir}/containers_op.py --update "${4}" + + + + + diff --git a/src/op_mode/containers_op.py b/src/op_mode/containers_op.py index 1e3fc3a8f..bc317029c 100755 --- a/src/op_mode/containers_op.py +++ b/src/op_mode/containers_op.py @@ -15,10 +15,10 @@ # along with this program. If not, see . import argparse -from vyos.configquery import query_context, ConfigQueryError -from vyos.util import cmd -config, op = query_context() +from getpass import getuser +from vyos.configquery import ConfigTreeQuery +from vyos.util import cmd parser = argparse.ArgumentParser() parser.add_argument("-a", "--all", action="store_true", help="Show all containers") @@ -26,34 +26,53 @@ parser.add_argument("-i", "--image", action="store_true", help="Show container i parser.add_argument("-n", "--networks", action="store_true", help="Show container images") parser.add_argument("-p", "--pull", action="store", help="Pull image for container") parser.add_argument("-d", "--remove", action="store", help="Delete container image") +parser.add_argument("-u", "--update", action="store", help="Update given container image") -if not config.exists(['container']): +config = ConfigTreeQuery() +base = ['container'] +if not config.exists(base): print('Containers not configured') exit(0) +if getuser() != 'root': + raise OSError('This functions needs to be run as root to return correct results!') + if __name__ == '__main__': args = parser.parse_args() if args.all: print(cmd('podman ps --all')) - exit(0) - if args.image: + + elif args.image: print(cmd('podman image ls')) - exit(0) - if args.networks: + + elif args.networks: print(cmd('podman network ls')) - exit(0) - if args.pull: + + elif args.pull: image = args.pull try: - print(cmd(f'sudo podman image pull {image}')) + print(cmd(f'podman image pull {image}')) except: print(f'Can\'t find or download image "{image}"') - exit(0) - if args.remove: + + elif args.remove: image = args.remove try: - print(cmd(f'sudo podman image rm {image}')) + print(cmd(f'podman image rm {image}')) except: print(f'Can\'t delete image "{image}"') - exit(0) + + elif args.update: + tmp = config.get_config_dict(base + ['name', args.update], + key_mangling=('-', '_'), get_first_key=True) + try: + image = tmp['image'] + print(cmd(f'podman image pull {image}')) + except: + print(f'Can\'t find or download image "{image}"') + else: + parser.print_help() + exit(1) + + exit(0) -- cgit v1.2.3 From bf2a921d6727e16b26e6294a9fe0eb6b896a4ba5 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Mon, 23 Aug 2021 14:53:00 +0200 Subject: pki: T3642: use ConfigTreeQuery() instead of Config() from op-mode --- src/op_mode/pki.py | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/src/op_mode/pki.py b/src/op_mode/pki.py index b34ea3c2b..36891d080 100755 --- a/src/op_mode/pki.py +++ b/src/op_mode/pki.py @@ -24,7 +24,7 @@ import tabulate from cryptography import x509 from cryptography.x509.oid import ExtendedKeyUsageOID -from vyos.config import Config +from vyos.configquery import ConfigTreeQuery from vyos.configdict import dict_merge from vyos.pki import encode_certificate, encode_public_key, encode_private_key, encode_dh_parameters from vyos.pki import create_certificate, create_certificate_request, create_certificate_revocation_list @@ -41,10 +41,10 @@ CERT_REQ_END = '-----END CERTIFICATE REQUEST-----' auth_dir = '/config/auth' # Helper Functions - +conf = ConfigTreeQuery() def get_default_values(): # Fetch default x509 values - conf = Config() + base = ['pki', 'x509', 'default'] x509_defaults = conf.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True, no_tag_node_value_mangle=True) @@ -53,9 +53,7 @@ def get_default_values(): def get_config_ca_certificate(name=None): # Fetch ca certificates from config - conf = Config() base = ['pki', 'ca'] - if not conf.exists(base): return False @@ -69,9 +67,7 @@ def get_config_ca_certificate(name=None): def get_config_certificate(name=None): # Get certificates from config - conf = Config() base = ['pki', 'certificate'] - if not conf.exists(base): return False @@ -100,7 +96,6 @@ def get_certificate_ca(cert, ca_certs): def get_config_revoked_certificates(): # Fetch revoked certificates from config - conf = Config() ca_base = ['pki', 'ca'] cert_base = ['pki', 'certificate'] @@ -464,7 +459,7 @@ def generate_certificate_sign(name, ca_name, install=False, file=False): if not cert_req: print("Invalid certificate request") return None - + cert = generate_certificate(cert_req, ca_cert, ca_private_key, is_ca=False) passphrase = ask_passphrase() -- cgit v1.2.3 From 618f30225e8cd69fcbff475d2adff0785a621620 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Mon, 23 Aug 2021 14:56:08 +0200 Subject: ipsec: T1210: use ConfigTreeQuery() instead of Config() from op-mode --- src/op_mode/ikev2_profile_generator.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/op_mode/ikev2_profile_generator.py b/src/op_mode/ikev2_profile_generator.py index d45525431..990b06c12 100755 --- a/src/op_mode/ikev2_profile_generator.py +++ b/src/op_mode/ikev2_profile_generator.py @@ -21,7 +21,7 @@ from sys import exit from socket import getfqdn from cryptography.x509.oid import NameOID -from vyos.config import Config +from vyos.configquery import ConfigTreeQuery from vyos.pki import load_certificate from vyos.template import render_to_string from vyos.util import ask_input @@ -117,7 +117,7 @@ args = parser.parse_args() ipsec_base = ['vpn', 'ipsec'] config_base = ipsec_base + ['remote-access', 'connection'] pki_base = ['pki'] -conf = Config() +conf = ConfigTreeQuery() if not conf.exists(config_base): exit('IPSec remote-access is not configured!') @@ -153,7 +153,7 @@ cert = load_certificate(pki['certificate'][cert_name]['certificate']) data['ca_cn'] = ca_cert.subject.get_attributes_for_oid(NameOID.COMMON_NAME)[0].value data['cert_cn'] = cert.subject.get_attributes_for_oid(NameOID.COMMON_NAME)[0].value -data['ca_cert'] = conf.return_value(pki_base + ['ca', ca_name, 'certificate']) +data['ca_cert'] = conf.value(pki_base + ['ca', ca_name, 'certificate']) esp_proposals = conf.get_config_dict(ipsec_base + ['esp-group', data['esp_group'], 'proposal'], key_mangling=('-', '_'), get_first_key=True) -- cgit v1.2.3 From a663816d5add31ffea849839c7dccc2029d64201 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Mon, 23 Aug 2021 15:18:12 +0200 Subject: container: T2216: name of container must be alphanumeric and can contain a hyphen --- interface-definitions/containers.xml.in | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/interface-definitions/containers.xml.in b/interface-definitions/containers.xml.in index 419802866..39ef945a3 100644 --- a/interface-definitions/containers.xml.in +++ b/interface-definitions/containers.xml.in @@ -9,6 +9,10 @@ Container name + + ^[-a-zA-Z0-9]+$ + + Container name must be alphanumeric and can contain hyphens -- cgit v1.2.3 From c270fe05596c95eca20a96a8c7e561d28bfc9a82 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Mon, 23 Aug 2021 15:18:43 +0200 Subject: container: T2216: no need to query container status As VyOS CLI is the only truth for dealing with containers we do not need to query if a container is running, exists or what so ever. We simply always restart it if something changes and do not rely on the underlaying Linux status. If a users does container stuff under the hood - it will be overridden. --- src/conf_mode/containers.py | 137 +++++++++++++++++++------------------------- 1 file changed, 58 insertions(+), 79 deletions(-) diff --git a/src/conf_mode/containers.py b/src/conf_mode/containers.py index 7aea5f432..12e6953b5 100755 --- a/src/conf_mode/containers.py +++ b/src/conf_mode/containers.py @@ -23,8 +23,9 @@ from ipaddress import ip_network from vyos.config import Config from vyos.configdict import dict_merge from vyos.configdict import node_changed +from vyos.util import call from vyos.util import cmd -from vyos.util import popen +from vyos.util import run from vyos.template import render from vyos.template import is_ipv4 from vyos.template import is_ipv6 @@ -41,26 +42,6 @@ def _cmd(command): print(command) return cmd(command) -# Container management functions -def container_exists(name): - ''' - https://docs.podman.io/en/latest/_static/api.html#operation/ContainerExistsLibpod - Check if container exists. Response codes. - 204 - container exists - 404 - no such container - ''' - tmp = _cmd(f"curl --unix-socket /run/podman/podman.sock 'http://d/v3.0.0/libpod/containers/{name}/exists'") - # If container exists it return status code "0" - code can not be displayed - return (tmp == "") - -def container_status(name): - ''' - https://docs.podman.io/en/latest/_static/api.html#operation/ContainerInspectLibpod - ''' - tmp = _cmd(f"curl --unix-socket /run/podman/podman.sock 'http://d/v3.0.0/libpod/containers/{name}/json'") - data = json.loads(tmp) - return data['State']['Status'] - def ctnr_network_exists(name): # Check explicit name for network, returns True if network exists c = _cmd(f'podman network ls --quiet --filter name=^{name}$') @@ -92,7 +73,7 @@ def get_config(config=None): # Delete container network, delete containers tmp = node_changed(conf, ['container', 'network']) - if tmp: container.update({'net_remove' : tmp}) + if tmp: container.update({'network_remove' : tmp}) tmp = node_changed(conf, ['container', 'name']) if tmp: container.update({'container_remove' : tmp}) @@ -173,8 +154,8 @@ def verify(container): # A network attached to a container can not be deleted - if {'net_remove', 'name'} <= set(container): - for network in container['net_remove']: + if {'network_remove', 'name'} <= set(container): + for network in container['network_remove']: for container, container_config in container['name'].items(): if 'network' in container_config and network in container_config['network']: raise ConfigError(f'Can not remove network "{network}", used by container "{container}"!') @@ -196,14 +177,13 @@ def apply(container): # Option "--force" allows to delete containers with any status if 'container_remove' in container: for name in container['container_remove']: - if container_status(name) == 'running': - _cmd(f'podman stop {name}') - _cmd(f'podman rm --force {name}') + call(f'podman stop {name}') + call(f'podman rm --force {name}') # Delete old networks if needed - if 'net_remove' in container: - for network in container['net_remove']: - _cmd(f'podman network rm {network}') + if 'network_remove' in container: + for network in container['network_remove']: + call(f'podman network rm --force {network}') # Add network if 'network' in container: @@ -223,55 +203,54 @@ def apply(container): if 'name' in container: for name, container_config in container['name'].items(): # Check if the container has already been created - if not container_exists(name): - image = container_config['image'] - memory = container_config['memory'] - restart = container_config['restart'] - # Currently the best way to run a command and immediately print stdout - print(os.system(f'podman pull {image}')) - - # Check/set environment options "-e foo=bar" - env_opt = '' - if 'environment' in container_config: - for k, v in container_config['environment'].items(): - env_opt += f" -e \"{k}={v['value']}\"" - - # Publish ports - port = '' - if 'port' in container_config: - protocol = '' - for portmap in container_config['port']: - if 'protocol' in container_config['port'][portmap]: - protocol = container_config['port'][portmap]['protocol'] - protocol = f'/{protocol}' - else: - protocol = '/tcp' - sport = container_config['port'][portmap]['source'] - dport = container_config['port'][portmap]['destination'] - port += f' -p {sport}:{dport}{protocol}' - - # Bind volume - volume = '' - if 'volume' in container_config: - for vol, vol_config in container_config['volume']: - svol = vol_config['source'] - dvol = vol_config['destination'] - volume += f' -v {svol}:{dvol}' - - container_base_cmd = f'podman run -dit --name {name} --memory {memory}m --restart {restart} {port} {volume} {env_opt}' - if 'allow_host_networks' in container_config: - _cmd(f'{container_base_cmd} --net host {image}') - else: - for network in container_config['network']: - ipparam = '' - if 'address' in container_config['network'][network]: - ipparam = '--ip ' + container_config['network'][network]['address'] - _cmd(f'{container_base_cmd} --net {network} {ipparam} {image}') - - # Else container is already created. Just start it. - # It's needed after reboot. - elif container_status(name) != 'running': - _cmd(f'podman start {name}') + image = container_config['image'] + memory = container_config['memory'] + restart = container_config['restart'] + + # Check if requested container image exists locally. If it does not, we + # pull it. print() is the best way to have a good response from the + # polling process to the user to display progress. If the image exists + # locally, a user can update it running `update container image ` + tmp = run(f'podman image exists {image}') + if tmp != 0: print(os.system(f'podman pull {image}')) + + # Check/set environment options "-e foo=bar" + env_opt = '' + if 'environment' in container_config: + for k, v in container_config['environment'].items(): + env_opt += f" -e \"{k}={v['value']}\"" + + # Publish ports + port = '' + if 'port' in container_config: + protocol = '' + for portmap in container_config['port']: + if 'protocol' in container_config['port'][portmap]: + protocol = container_config['port'][portmap]['protocol'] + protocol = f'/{protocol}' + else: + protocol = '/tcp' + sport = container_config['port'][portmap]['source'] + dport = container_config['port'][portmap]['destination'] + port += f' -p {sport}:{dport}{protocol}' + + # Bind volume + volume = '' + if 'volume' in container_config: + for vol, vol_config in container_config['volume']: + svol = vol_config['source'] + dvol = vol_config['destination'] + volume += f' -v {svol}:{dvol}' + + container_base_cmd = f'podman run --detach --interactive --tty --replace --memory {memory}m --restart {restart} --name {name} {port} {volume} {env_opt}' + if 'allow_host_networks' in container_config: + _cmd(f'{container_base_cmd} --net host {image}') + else: + for network in container_config['network']: + ipparam = '' + if 'address' in container_config['network'][network]: + ipparam = '--ip ' + container_config['network'][network]['address'] + _cmd(f'{container_base_cmd} --net {network} {ipparam} {image}') return None -- cgit v1.2.3 From b48f7688f758921904fb6ef5185080d97c1e3920 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Mon, 23 Aug 2021 15:50:48 +0200 Subject: container: T2216: bugfix ValueError when assembling volumes A call to .items() was missing that triggered the following error: ValueError: too many values to unpack (expected 2) --- src/conf_mode/containers.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/conf_mode/containers.py b/src/conf_mode/containers.py index 12e6953b5..2c4861449 100755 --- a/src/conf_mode/containers.py +++ b/src/conf_mode/containers.py @@ -237,12 +237,14 @@ def apply(container): # Bind volume volume = '' if 'volume' in container_config: - for vol, vol_config in container_config['volume']: + for vol, vol_config in container_config['volume'].items(): svol = vol_config['source'] dvol = vol_config['destination'] volume += f' -v {svol}:{dvol}' - container_base_cmd = f'podman run --detach --interactive --tty --replace --memory {memory}m --restart {restart} --name {name} {port} {volume} {env_opt}' + container_base_cmd = f'podman run --detach --interactive --tty --replace ' \ + f'--memory {memory}m --memory-swap 0 --restart {restart} ' \ + f'--name {name} {port} {volume} {env_opt}' if 'allow_host_networks' in container_config: _cmd(f'{container_base_cmd} --net host {image}') else: -- cgit v1.2.3 From c48566ead61cb7c7c0571167c75d78b7fb6aa759 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Mon, 23 Aug 2021 15:58:43 +0200 Subject: container: T2216: increase default memory limit to 512MB --- interface-definitions/containers.xml.in | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/interface-definitions/containers.xml.in b/interface-definitions/containers.xml.in index 39ef945a3..9c366b5f4 100644 --- a/interface-definitions/containers.xml.in +++ b/interface-definitions/containers.xml.in @@ -53,7 +53,7 @@ - Constrain the memory available to a container (default: 256MB) + Constrain the memory available to a container (default: 512MB) u32:0 Unlimited @@ -67,7 +67,7 @@ Container memory must be in range 0 to 16384 MB - 256 + 512 -- cgit v1.2.3 From 8774ef9377610559d15071acec5442180c660924 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Mon, 23 Aug 2021 15:59:00 +0200 Subject: container: T2216: verify() volume paths Volumes must have both a source and destination path specified. Also the source path must exist on the current system. --- src/conf_mode/containers.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/conf_mode/containers.py b/src/conf_mode/containers.py index 2c4861449..97e84a5bb 100755 --- a/src/conf_mode/containers.py +++ b/src/conf_mode/containers.py @@ -122,6 +122,18 @@ def verify(container): if 'value' not in cfg: raise ConfigError(f'Environment variable {var} has no value assigned!') + if 'volume' in container_config: + for volume, volume_config in container_config['volume'].items(): + if 'source' not in volume_config: + raise ConfigError(f'Volume "{volume}" has no source path configured!') + + if 'destination' not in volume_config: + raise ConfigError(f'Volume "{volume}" has no destination path configured!') + + source = volume_config['source'] + if not os.path.exists(source): + raise ConfigError(f'Volume "{volume}" source path "{source}" does not exist!') + # Container image is a mandatory option if 'image' not in container_config: raise ConfigError(f'Container image for "{name}" is mandatory!') -- cgit v1.2.3 From f480f236d8bd43bbfc5928702a94d6b3684cbb68 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Mon, 23 Aug 2021 16:57:16 +0200 Subject: container: T2216: use common "generic-description.xml.i" building block --- interface-definitions/containers.xml.in | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/interface-definitions/containers.xml.in b/interface-definitions/containers.xml.in index 9c366b5f4..286b4942a 100644 --- a/interface-definitions/containers.xml.in +++ b/interface-definitions/containers.xml.in @@ -21,11 +21,7 @@ - - - Container description - - + #include Add custom environment variables -- cgit v1.2.3 From 7762e74d928e9f23360798a21c8dd970579365b6 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Mon, 23 Aug 2021 16:57:38 +0200 Subject: container: T2216: add option to "disable" a container --- interface-definitions/containers.xml.in | 1 + src/conf_mode/containers.py | 9 ++++++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/interface-definitions/containers.xml.in b/interface-definitions/containers.xml.in index 286b4942a..d990e41a3 100644 --- a/interface-definitions/containers.xml.in +++ b/interface-definitions/containers.xml.in @@ -22,6 +22,7 @@ #include + #include Add custom environment variables diff --git a/src/conf_mode/containers.py b/src/conf_mode/containers.py index 97e84a5bb..5b863fa03 100755 --- a/src/conf_mode/containers.py +++ b/src/conf_mode/containers.py @@ -214,8 +214,15 @@ def apply(container): # Add container if 'name' in container: for name, container_config in container['name'].items(): - # Check if the container has already been created image = container_config['image'] + + if 'disable' in container_config: + # check if there is a container by that name running + tmp = _cmd('podman ps -a --format "{{.Names}}"') + if name in tmp: + _cmd(f'podman stop {name}') + continue + memory = container_config['memory'] restart = container_config['restart'] -- cgit v1.2.3 From e12d00325deedb38e92bcce355833d225ab82705 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Mon, 23 Aug 2021 19:54:12 +0200 Subject: smoketest: ospf: "metric-type" also works for kernel and static redistributed routes --- smoketest/scripts/cli/test_protocols_ospf.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/smoketest/scripts/cli/test_protocols_ospf.py b/smoketest/scripts/cli/test_protocols_ospf.py index f132fc5b5..3f13eec80 100755 --- a/smoketest/scripts/cli/test_protocols_ospf.py +++ b/smoketest/scripts/cli/test_protocols_ospf.py @@ -221,8 +221,7 @@ class TestProtocolsOSPF(VyOSUnitTestSHIM.TestCase): for protocol in redistribute: self.cli_set(base_path + ['redistribute', protocol, 'metric', metric]) self.cli_set(base_path + ['redistribute', protocol, 'route-map', route_map]) - if protocol not in ['kernel', 'static']: - self.cli_set(base_path + ['redistribute', protocol, 'metric-type', metric_type]) + self.cli_set(base_path + ['redistribute', protocol, 'metric-type', metric_type]) # commit changes self.cli_commit() @@ -232,10 +231,7 @@ class TestProtocolsOSPF(VyOSUnitTestSHIM.TestCase): try: self.assertIn(f'router ospf', frrconfig) for protocol in redistribute: - if protocol in ['kernel', 'static']: - self.assertIn(f' redistribute {protocol} metric {metric} route-map {route_map}', frrconfig) - else: - self.assertIn(f' redistribute {protocol} metric {metric} metric-type {metric_type} route-map {route_map}', frrconfig) + self.assertIn(f' redistribute {protocol} metric {metric} metric-type {metric_type} route-map {route_map}', frrconfig) except: log.debug(frrconfig) log.debug(cmd('sudo dmesg')) -- cgit v1.2.3 From 209ce3d9b6fb09626a7abe3540b888566b739de8 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Mon, 23 Aug 2021 20:51:19 +0200 Subject: container: T3769: when container networks are used, always bridge the networks As VyOS is a network operation system with bridging and NATing available from the VyOS CLI, it makes no sense to let podman do it's own sort of "NAT". If one really want's to NAT into a container, use the VyOS CLI to do so. If you wan't to bridge your networks, use the VyOS CLI to do so. --- src/conf_mode/containers.py | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/src/conf_mode/containers.py b/src/conf_mode/containers.py index 5b863fa03..78664dfd9 100755 --- a/src/conf_mode/containers.py +++ b/src/conf_mode/containers.py @@ -26,6 +26,8 @@ from vyos.configdict import node_changed from vyos.util import call from vyos.util import cmd from vyos.util import run +from vyos.util import read_file +from vyos.util import write_file from vyos.template import render from vyos.template import is_ipv4 from vyos.template import is_ipv6 @@ -42,7 +44,7 @@ def _cmd(command): print(command) return cmd(command) -def ctnr_network_exists(name): +def network_exists(name): # Check explicit name for network, returns True if network exists c = _cmd(f'podman network ls --quiet --filter name=^{name}$') return bool(c) @@ -201,7 +203,7 @@ def apply(container): if 'network' in container: for network, network_config in container['network'].items(): # Check if the network has already been created - if not ctnr_network_exists(network) and 'prefix' in network_config: + if not network_exists(network) and 'prefix' in network_config: tmp = f'podman network create {network}' # we can not use list comprehension here as the --ipv6 option # must immediately follow the specified subnet!!! @@ -211,6 +213,18 @@ def apply(container): tmp += ' --ipv6' _cmd(tmp) + # Disable masquerading and use traditional bridging so VyOS + # can control firewalling/NAT by the real VyOS CLI + cni_network_config = f'/etc/cni/net.d/{network}.conflist' + tmp = read_file(cni_network_config) + config = json.loads(tmp) + if 'plugins' in config: + for count in range(0, len(config['plugins'])): + if 'ipMasq' in config['plugins'][count]: + config['plugins'][count]['ipMasq'] = False + + write_file(cni_network_config, json.dumps(config, indent=4)) + # Add container if 'name' in container: for name, container_config in container['name'].items(): -- cgit v1.2.3 From f3ff81f4f31cd3ecb5d648caff674663943db42f Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Mon, 23 Aug 2021 21:04:59 +0200 Subject: container: op-mode: T3765: "connect container" will now drop you to a shell Commit a30d74f4 (container: op-mode: T3765: add "connect container mysql-server") added a CLI op-mode command to attach to a container - users typically not want to attach and consume stdout (can be done via logs) but rather wan't to debug inside the container image. vyos@vyos:~$ connect container unifi USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND root 202 0.0 0.0 4640 828 pts/1 Ss 21:06 0:00 /bin/sh root 203 0.0 0.0 34416 2872 pts/1 R+ 21:06 0:00 \_ ps faux root 187 0.0 0.0 18388 3124 ? S 21:03 0:00 /bin/bash root 186 0.0 0.0 4640 788 ? S 21:03 0:00 /bin/sh root 185 0.0 0.0 4640 824 ? S 21:03 0:00 /bin/sh root 184 0.0 0.0 4640 836 ? S 21:03 0:00 /bin/sh root 1 0.0 0.0 18520 3228 pts/0 Ss+ 20:50 0:00 bash /usr/local/bin/docker-entrypoint.sh unifi root 12 4.8 14.2 3688080 572756 pts/0 Sl+ 20:50 0:48 java -Dunifi.datadir=/unifi/data -Dunifi.logdir=/unifi/log -Dunifi.rundir=/var/run/unifi - root 35 0.7 3.4 1102700 139752 pts/0 Sl+ 20:50 0:07 \_ bin/mongod --dbpath /usr/lib/unifi/data/db --port 27117 --unixSocketPrefix /usr/lib/un Linux 57c689f739ed 5.10.60-amd64-vyos #1 SMP Fri Aug 20 14:44:59 UTC 2021 x86_64 x86_64 x86_64 GNU/Linux --- op-mode-definitions/containers.xml.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/op-mode-definitions/containers.xml.in b/op-mode-definitions/containers.xml.in index efa72e1a4..b2b318786 100644 --- a/op-mode-definitions/containers.xml.in +++ b/op-mode-definitions/containers.xml.in @@ -26,7 +26,7 @@ container name - echo "Use Ctrl+p and Ctrl+q to detach"; sudo podman attach "$3" + sudo podman exec --interactive --tty "$3" /bin/sh -- cgit v1.2.3 From 10ab1acc801df73b2dfee1f2cfd059424d8894d5 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Tue, 24 Aug 2021 10:17:18 +0200 Subject: container: T3769: disable bridge "hairpinMode" mode After commit 209ce3d9 ("container: T3769: when container networks are used, always bridge the networks") IP masquerading (NAT) was disabled. No need to keep the haipin flag. --- src/conf_mode/containers.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/conf_mode/containers.py b/src/conf_mode/containers.py index 78664dfd9..85fc14015 100755 --- a/src/conf_mode/containers.py +++ b/src/conf_mode/containers.py @@ -222,6 +222,8 @@ def apply(container): for count in range(0, len(config['plugins'])): if 'ipMasq' in config['plugins'][count]: config['plugins'][count]['ipMasq'] = False + if 'hairpinMode' in config['plugins'][count]: + config['plugins'][count]['hairpinMode'] = False write_file(cni_network_config, json.dumps(config, indent=4)) -- cgit v1.2.3 From dd1f3e3313db14ccf2f34663e113f6383ec1a89f Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Tue, 24 Aug 2021 12:18:33 +0200 Subject: policy: T2425: rename validator large-community-list -> bgp-large-community-list ... as we will get another bgp route-target validator soon. --- interface-definitions/policy.xml.in | 2 +- src/validators/bgp-large-community-list | 36 +++++++++++++++++++++++++++++++++ src/validators/large-community-list | 36 --------------------------------- 3 files changed, 37 insertions(+), 37 deletions(-) create mode 100755 src/validators/bgp-large-community-list delete mode 100755 src/validators/large-community-list diff --git a/interface-definitions/policy.xml.in b/interface-definitions/policy.xml.in index ce190b046..bf1832832 100644 --- a/interface-definitions/policy.xml.in +++ b/interface-definitions/policy.xml.in @@ -323,7 +323,7 @@ BGP large-community-list filter (IPv4 address format) - + Malformed large-community-list diff --git a/src/validators/bgp-large-community-list b/src/validators/bgp-large-community-list new file mode 100755 index 000000000..c07268e81 --- /dev/null +++ b/src/validators/bgp-large-community-list @@ -0,0 +1,36 @@ +#!/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 sys + +from vyos.template import is_ipv4 + +pattern = '(.*):(.*):(.*)' + +if __name__ == '__main__': + if len(sys.argv) != 2: + sys.exit(1) + + value = sys.argv[1].split(':') + if not len(value) == 3: + sys.exit(1) + + if not (re.match(pattern, sys.argv[1]) and + (is_ipv4(value[0]) or value[0].isdigit()) and value[1].isdigit()): + sys.exit(1) + + sys.exit(0) diff --git a/src/validators/large-community-list b/src/validators/large-community-list deleted file mode 100755 index c07268e81..000000000 --- a/src/validators/large-community-list +++ /dev/null @@ -1,36 +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 . - -import re -import sys - -from vyos.template import is_ipv4 - -pattern = '(.*):(.*):(.*)' - -if __name__ == '__main__': - if len(sys.argv) != 2: - sys.exit(1) - - value = sys.argv[1].split(':') - if not len(value) == 3: - sys.exit(1) - - if not (re.match(pattern, sys.argv[1]) and - (is_ipv4(value[0]) or value[0].isdigit()) and value[1].isdigit()): - sys.exit(1) - - sys.exit(0) -- cgit v1.2.3 From a98b0d886f522ca9620182bd0f1a2f73a905f35a Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Tue, 24 Aug 2021 12:26:42 +0200 Subject: container: T3769: remove container when marked as "disable" --- src/conf_mode/containers.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/conf_mode/containers.py b/src/conf_mode/containers.py index 85fc14015..32320a4b2 100755 --- a/src/conf_mode/containers.py +++ b/src/conf_mode/containers.py @@ -237,6 +237,7 @@ def apply(container): tmp = _cmd('podman ps -a --format "{{.Names}}"') if name in tmp: _cmd(f'podman stop {name}') + _cmd(f'podman rm --force {name}') continue memory = container_config['memory'] -- cgit v1.2.3 From 474db49afc759eeacc2208a18995452e6fe5f6fc Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Tue, 24 Aug 2021 12:27:07 +0200 Subject: bgp: T3759: "l2vpn evpn" and ipv4/ipv6 safi route-targets differ The "l2vpn evpn" address-family route-target command only accepts a single route-target value consisting of (A.B.C.D:MN|EF:OPQR|GHJK:MN). The "ipv4-unicast or ipv6-unicast" address-family route-target command for VPNs support multiple, whitespace separated route-target values. This commit adds a new custom validator named "bgp-route-target" with a --single and a --multi option to pass one or more route-target values. --- .../include/bgp/afi-l2vpn-common.xml.i | 39 ++++++++++++++-- .../include/bgp/afi-route-target-vpn.xml.i | 52 ++++++++++++++++++++++ .../include/bgp/protocol-common-config.xml.i | 19 +------- .../include/bgp/route-target-both.xml.i | 14 ------ .../include/bgp/route-target-export.xml.i | 14 ------ .../include/bgp/route-target-import.xml.i | 14 ------ src/validators/bgp-route-target | 51 +++++++++++++++++++++ 7 files changed, 141 insertions(+), 62 deletions(-) create mode 100644 interface-definitions/include/bgp/afi-route-target-vpn.xml.i delete mode 100644 interface-definitions/include/bgp/route-target-both.xml.i delete mode 100644 interface-definitions/include/bgp/route-target-export.xml.i delete mode 100644 interface-definitions/include/bgp/route-target-import.xml.i create mode 100755 src/validators/bgp-route-target diff --git a/interface-definitions/include/bgp/afi-l2vpn-common.xml.i b/interface-definitions/include/bgp/afi-l2vpn-common.xml.i index a9a833851..8deb189ab 100644 --- a/interface-definitions/include/bgp/afi-l2vpn-common.xml.i +++ b/interface-definitions/include/bgp/afi-l2vpn-common.xml.i @@ -17,9 +17,42 @@ Route Target - #include - #include - #include + + + Route Target both import and export + + txt + Route target (A.B.C.D:MN|EF:OPQR|GHJK:MN) + + + + + + + + + Route Target import + + txt + Route target (A.B.C.D:MN|EF:OPQR|GHJK:MN) + + + + + + + + + Route Target export + + txt + 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 new file mode 100644 index 000000000..1dc184a02 --- /dev/null +++ b/interface-definitions/include/bgp/afi-route-target-vpn.xml.i @@ -0,0 +1,52 @@ + + + + Specify route distinguisher + + + + + Between current address-family and VPN + + + + + Route Target both import and export + + txt + Space separated route target list (A.B.C.D:MN|EF:OPQR|GHJK:MN) + + + + + + + + + Route Target import + + txt + Space separated route target list (A.B.C.D:MN|EF:OPQR|GHJK:MN) + + + + + + + + + Route Target export + + txt + Space separated route target list (A.B.C.D:MN|EF:OPQR|GHJK:MN) + + + + + + + + + + + diff --git a/interface-definitions/include/bgp/protocol-common-config.xml.i b/interface-definitions/include/bgp/protocol-common-config.xml.i index a971c52b8..2b22bac7d 100644 --- a/interface-definitions/include/bgp/protocol-common-config.xml.i +++ b/interface-definitions/include/bgp/protocol-common-config.xml.i @@ -119,23 +119,7 @@ #include #include - - - Specify route distinguisher - - - - - Between current address-family and VPN - - - #include - #include - #include - - - - + #include Redistribute routes from other protocols into BGP @@ -520,6 +504,7 @@ #include #include + #include Redistribute routes from other protocols into BGP diff --git a/interface-definitions/include/bgp/route-target-both.xml.i b/interface-definitions/include/bgp/route-target-both.xml.i deleted file mode 100644 index d77878812..000000000 --- a/interface-definitions/include/bgp/route-target-both.xml.i +++ /dev/null @@ -1,14 +0,0 @@ - - - - Route Target both import and export - - txt - Route target (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/interface-definitions/include/bgp/route-target-export.xml.i b/interface-definitions/include/bgp/route-target-export.xml.i deleted file mode 100644 index 0431f0fcb..000000000 --- a/interface-definitions/include/bgp/route-target-export.xml.i +++ /dev/null @@ -1,14 +0,0 @@ - - - - Route Target export - - txt - Route target (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/interface-definitions/include/bgp/route-target-import.xml.i b/interface-definitions/include/bgp/route-target-import.xml.i deleted file mode 100644 index aa861c428..000000000 --- a/interface-definitions/include/bgp/route-target-import.xml.i +++ /dev/null @@ -1,14 +0,0 @@ - - - - Route Target import - - txt - Route target (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-route-target b/src/validators/bgp-route-target new file mode 100755 index 000000000..e7e4d403f --- /dev/null +++ b/src/validators/bgp-route-target @@ -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('--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 ad2693f8d9662cff90673f9bc71ba21cbbda5b18 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Tue, 24 Aug 2021 13:08:36 +0200 Subject: smoketest: bgp: extend ipv4/ipv6 safi route-target tests Commit 474db49a ("bgp: T3759: "l2vpn evpn" and ipv4/ipv6 safi route-targets differ") made it possible to specify a whitelist separated list of route-targets, this is now validated through the smoketests. --- smoketest/scripts/cli/test_protocols_bgp.py | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/smoketest/scripts/cli/test_protocols_bgp.py b/smoketest/scripts/cli/test_protocols_bgp.py index f535408db..df9dc342b 100755 --- a/smoketest/scripts/cli/test_protocols_bgp.py +++ b/smoketest/scripts/cli/test_protocols_bgp.py @@ -720,8 +720,8 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase): vrf_name = 'red' label = 'auto' rd = f'{neighbor}:{ASN}' - rt_export = f'{neighbor}:1002' - rt_import = f'{neighbor}:1003' + rt_export = f'{neighbor}:1002 1.2.3.4:567' + rt_import = f'{neighbor}:1003 500:100' self.cli_set(base_path + ['local-as', ASN]) # testing only one AFI is sufficient as it's generic code @@ -732,10 +732,8 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase): self.cli_set(base_path + ['address-family', afi, 'rd', 'vpn', 'export', rd]) self.cli_set(base_path + ['address-family', afi, 'route-map', 'vpn', 'export', route_map_out]) self.cli_set(base_path + ['address-family', afi, 'route-map', 'vpn', 'import', route_map_in]) - - - self.cli_set(base_path + ['address-family', 'ipv4-unicast', 'route-target', 'vpn', 'export', rt_export]) - self.cli_set(base_path + ['address-family', 'ipv4-unicast', 'route-target', 'vpn', 'import', rt_import]) + self.cli_set(base_path + ['address-family', afi, 'route-target', 'vpn', 'export', rt_export]) + self.cli_set(base_path + ['address-family', afi, 'route-target', 'vpn', 'import', rt_import]) # commit changes self.cli_commit() @@ -753,12 +751,9 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase): self.assertIn(f' rd vpn export {rd}', afi_config) self.assertIn(f' route-map vpn export {route_map_out}', afi_config) self.assertIn(f' route-map vpn import {route_map_in}', afi_config) + self.assertIn(f' rt vpn export {rt_export}', afi_config) + self.assertIn(f' rt vpn import {rt_import}', afi_config) self.assertIn(f' exit-address-family', afi_config) - afi_config = self.getFRRconfig(f' address-family ipv4 unicast', endsection='exit-address-family', daemon='bgpd') - self.assertIn(f'address-family ipv4 unicast', afi_config) - self.assertIn(f' rt vpn export {rt_export}', afi_config) - self.assertIn(f' rt vpn import {rt_import}', afi_config) - if __name__ == '__main__': unittest.main(verbosity=2) \ No newline at end of file -- cgit v1.2.3 From 059307f924c604eb2bdeab19a2db8ce6d8e09f90 Mon Sep 17 00:00:00 2001 From: Daniil Baturin Date: Tue, 24 Aug 2021 09:17:58 -0500 Subject: T3773: delete the original "show system integrity" command --- op-mode-definitions/show-system.xml.in | 6 --- src/op_mode/show_system_integrity.py | 70 ---------------------------------- 2 files changed, 76 deletions(-) delete mode 100755 src/op_mode/show_system_integrity.py diff --git a/op-mode-definitions/show-system.xml.in b/op-mode-definitions/show-system.xml.in index 5e9bf719e..18a28868d 100644 --- a/op-mode-definitions/show-system.xml.in +++ b/op-mode-definitions/show-system.xml.in @@ -55,12 +55,6 @@ ${vyos_op_scripts_dir}/show_cpu.py - - - Checks overall system integrity - - sudo ${vyos_op_scripts_dir}/show_system_integrity.py - Show messages in kernel ring buffer diff --git a/src/op_mode/show_system_integrity.py b/src/op_mode/show_system_integrity.py deleted file mode 100755 index c34d41e80..000000000 --- a/src/op_mode/show_system_integrity.py +++ /dev/null @@ -1,70 +0,0 @@ -#!/usr/bin/env python3 -# -# Copyright (C) 2020 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 sys -import os -import re -import json -from datetime import datetime, timedelta - -version_file = r'/usr/share/vyos/version.json' - - -def _get_sys_build_version(): - if not os.path.exists(version_file): - return None - buf = open(version_file, 'r').read() - j = json.loads(buf) - if not 'built_on' in j: - return None - return datetime.strptime(j['built_on'], '%a %d %b %Y %H:%M %Z') - - -def _check_pkgs(build_stamp): - pkg_diffs = { - 'buildtime': str(build_stamp), - 'pkg': {} - } - - pkg_info = os.listdir('/var/lib/dpkg/info/') - for file in pkg_info: - if re.search('\.list$', file): - fts = os.stat('/var/lib/dpkg/info/' + file).st_mtime - dt_str = (datetime.utcfromtimestamp( - fts).strftime('%Y-%m-%d %H:%M:%S')) - fdt = datetime.strptime(dt_str, '%Y-%m-%d %H:%M:%S') - if fdt > build_stamp: - pkg_diffs['pkg'].update( - {str(re.sub('\.list', '', file)): str(fdt)}) - - if len(pkg_diffs['pkg']) != 0: - return pkg_diffs - else: - return None - - -if __name__ == '__main__': - built_date = _get_sys_build_version() - if not built_date: - sys.exit(1) - pkgs = _check_pkgs(built_date) - if pkgs: - print ( - "The following packages don\'t fit the image creation time\nbuild time:\t" + pkgs['buildtime']) - for k, v in pkgs['pkg'].items(): - print ("installed: " + v + '\t' + k) -- cgit v1.2.3 From b2e5f8adefd4ed9e53e14a4618fb63b3821d1d20 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Tue, 24 Aug 2021 16:44:23 +0200 Subject: op-mode: T2223: drop dead code "get_vrrp_intf()" --- src/op_mode/show_interfaces.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/op_mode/show_interfaces.py b/src/op_mode/show_interfaces.py index 20d5d9e17..241fba4f4 100755 --- a/src/op_mode/show_interfaces.py +++ b/src/op_mode/show_interfaces.py @@ -120,10 +120,6 @@ def split_text(text, used=0): yield line[1:] -def get_vrrp_intf(): - return [intf for intf in Section.interfaces() if intf.is_vrrp()] - - def get_counter_val(clear, now): """ attempt to correct a counter if it wrapped, copied from perl -- cgit v1.2.3 From df22bc2c96d5095eaec978a58bf5d2361d758a86 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Tue, 24 Aug 2021 16:52:02 +0200 Subject: vyos.ifconfig: T3772: bugfix missing VRRP interfaces When the interface name was stripped down from "eth0.201" to "eth" to determine the appropriate interface section, VRRP interfaces got left out on the call to rstrip(). VRRP interfaces now show up in "show interfaces" as they did in VyOS 1.2. vyos@vyos:~$ show interfaces Codes: S - State, L - Link, u - Up, D - Down, A - Admin Down Interface IP Address S/L Description --------- ---------- --- ----------- dum0 172.18.254.201/32 u/u eth0 - u/u eth0.10 172.16.33.8/24 u/u eth0.201 172.18.201.10/24 u/u eth1 10.1.1.2/24 u/u eth1v10 10.1.1.1/24 u/u eth2 - u/u lo 127.0.0.1/8 u/u ::1/128 --- python/vyos/ifconfig/section.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/python/vyos/ifconfig/section.py b/python/vyos/ifconfig/section.py index 173a90bb4..0e4447b9e 100644 --- a/python/vyos/ifconfig/section.py +++ b/python/vyos/ifconfig/section.py @@ -46,7 +46,7 @@ class Section: return klass @classmethod - def _basename (cls, name, vlan): + def _basename(cls, name, vlan, vrrp): """ remove the number at the end of interface name name: name of the interface @@ -56,16 +56,18 @@ class Section: name = name.rstrip('.') if vlan: name = name.rstrip('0123456789.') + if vrrp: + name = name.rstrip('0123456789v') return name @classmethod - def section(cls, name, vlan=True): + def section(cls, name, vlan=True, vrrp=True): """ return the name of a section an interface should be under name: name of the interface (eth0, dum1, ...) vlan: should we try try to remove the VLAN from the number """ - name = cls._basename(name, vlan) + name = cls._basename(name, vlan, vrrp) if name in cls._prefixes: return cls._prefixes[name].definition['section'] @@ -79,8 +81,8 @@ class Section: return list(set([cls._prefixes[_].definition['section'] for _ in cls._prefixes])) @classmethod - def klass(cls, name, vlan=True): - name = cls._basename(name, vlan) + def klass(cls, name, vlan=True, vrrp=True): + name = cls._basename(name, vlan, vrrp) if name in cls._prefixes: return cls._prefixes[name] raise ValueError(f'No type found for interface name: {name}') -- cgit v1.2.3 From 77882b889c88b3b6d9d181b043ca7cc9cd5a2782 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Wed, 25 Aug 2021 10:07:32 +0200 Subject: ipsec: T3775: Diffie Hellman Group 21 uses NIST Elliptic Curve "ecp521" ... there was a type setting ecp512 instead of ecp521. --- python/vyos/template.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/vyos/template.py b/python/vyos/template.py index 08a5712af..ee6e52e1d 100644 --- a/python/vyos/template.py +++ b/python/vyos/template.py @@ -406,7 +406,7 @@ def get_esp_ike_cipher(group_config): 'dh-group18' : 'modp8192', 'dh-group19' : 'ecp256', 'dh-group20' : 'ecp384', - 'dh-group21' : 'ecp512', + 'dh-group21' : 'ecp521', 'dh-group22' : 'modp1024s160', 'dh-group23' : 'modp2048s224', 'dh-group24' : 'modp2048s256', -- cgit v1.2.3 From 67193598ee650e97a761b6a629a658a5191c04ce Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Thu, 26 Aug 2021 08:41:07 +0200 Subject: smoketest: config: drop empty newline at EOF for "isis-small" test --- smoketest/configs/isis-small | 1 - 1 file changed, 1 deletion(-) diff --git a/smoketest/configs/isis-small b/smoketest/configs/isis-small index 2c42ac9c4..247ae32b5 100644 --- a/smoketest/configs/isis-small +++ b/smoketest/configs/isis-small @@ -102,4 +102,3 @@ system { // Warning: Do not remove the following line. // vyos-config-version: "broadcast-relay@1:cluster@1:config-management@1:conntrack@1:conntrack-sync@1:dhcp-relay@2:dhcp-server@5:dhcpv6-server@1:dns-forwarding@3:firewall@5:https@2:interfaces@18:ipoe-server@1:ipsec@5:l2tp@3:lldp@1:mdns@1:nat@5:ntp@1:pppoe-server@5:pptp@2:qos@1:quagga@7:rpki@1:salt@1:snmp@2:ssh@2:sstp@3:system@20:vrrp@2:vyos-accel-ppp@2:wanloadbalance@3:webproxy@2:zone-policy@1" // Release version: 1.3.0-rc1 - -- cgit v1.2.3 From ff04854628e7530c33877665255b15750f413541 Mon Sep 17 00:00:00 2001 From: krox2 <49796247+krox2@users.noreply.github.com> Date: Thu, 26 Aug 2021 13:22:34 +0100 Subject: ipsec: T3780: shutting down vti when tunnel is down --- src/etc/ipsec.d/vti-up-down | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/etc/ipsec.d/vti-up-down b/src/etc/ipsec.d/vti-up-down index 281c9bf2b..011013a2e 100755 --- a/src/etc/ipsec.d/vti-up-down +++ b/src/etc/ipsec.d/vti-up-down @@ -55,7 +55,7 @@ if __name__ == '__main__': syslog(f'Interface {interface} not found') sys.exit(0) - vti_link_up = (vti_link['operstate'] == 'UP' if 'operstate' in vti_link else False) + vti_link_up = (vti_link['operstate'] != 'DOWN' if 'operstate' in vti_link else False) config = ConfigTreeQuery() vti_dict = config.get_config_dict(['interfaces', 'vti', interface], -- cgit v1.2.3 From 4523e9c897b3fa8d12c1b16c830c01820fee5583 Mon Sep 17 00:00:00 2001 From: zsdc Date: Thu, 26 Aug 2021 18:15:36 +0300 Subject: wireguard: T3763: Added check for listening port availability Each wireguard interface requires a unique port for in and out connections. This commit adds the new `vyos.util` function - `check_port_availability`, and uses it to be sure that a port that is planned to be used for wireguard interface is truly available and not used by any other services (not only other wireguard interfaces). --- python/vyos/util.py | 39 +++++++++++++++++++++++++++++++++++ src/conf_mode/interfaces-wireguard.py | 5 +++++ 2 files changed, 44 insertions(+) diff --git a/python/vyos/util.py b/python/vyos/util.py index 8af46a6ee..fc2834a97 100644 --- a/python/vyos/util.py +++ b/python/vyos/util.py @@ -819,3 +819,42 @@ def is_systemd_service_running(service): Copied from: https://unix.stackexchange.com/a/435317 """ tmp = cmd(f'systemctl show --value -p SubState {service}') return bool((tmp == 'running')) + +def check_port_availability(ipaddress, port, protocol): + """ + Check if port is available and not used by any service + Return False if a port is busy or IP address does not exists + Should be used carefully for services that can start listening + dynamically, because IP address may be dynamic too + """ + from socketserver import TCPServer, UDPServer + from ipaddress import ip_address + + # verify arguments + try: + ipaddress = ip_address(ipaddress).compressed + except: + print(f'The {ipaddress} is not a valid IPv4 or IPv6 address') + return + if port not in range(1, 65536): + print(f'The port number {port} is not in the 1-65535 range') + return + if protocol not in ['tcp', 'udp']: + print( + f'The protocol {protocol} is not supported. Only tcp and udp are allowed' + ) + return + + # check port availability + try: + if protocol == 'tcp': + server = TCPServer((ipaddress, port), None, bind_and_activate=True) + if protocol == 'udp': + server = UDPServer((ipaddress, port), None, bind_and_activate=True) + server.server_close() + return True + except: + print( + f'The {protocol} port {port} on the {ipaddress} is busy or unavailable' + ) + return False diff --git a/src/conf_mode/interfaces-wireguard.py b/src/conf_mode/interfaces-wireguard.py index 4c566a5ad..ad3ddcba2 100755 --- a/src/conf_mode/interfaces-wireguard.py +++ b/src/conf_mode/interfaces-wireguard.py @@ -30,6 +30,7 @@ from vyos.configverify import verify_bridge_delete from vyos.configverify import verify_mtu_ipv6 from vyos.ifconfig import WireGuardIf from vyos.util import check_kmod +from vyos.util import check_port_availability from vyos import ConfigError from vyos import airbag airbag.enable() @@ -73,6 +74,10 @@ def verify(wireguard): if 'peer' not in wireguard: raise ConfigError('At least one Wireguard peer is required!') + if 'port' in wireguard and check_port_availability( + '0.0.0.0', int(wireguard['port']), 'udp') is not True: + raise ConfigError('The port cannot be used for the interface') + # run checks on individual configured WireGuard peer for tmp in wireguard['peer']: peer = wireguard['peer'][tmp] -- cgit v1.2.3 From 5b0fce20c2c5afd2ecfb7544e13eb623f3502443 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Wed, 25 Aug 2021 14:44:21 +0200 Subject: op-mode: T3776: drop "frr" level from "restart frr ospfd|bgpd|staticd" commands The current command to restart any of the FRR processes is: vyos@vyos:~$ restart frr Possible completions: Execute the current command bfdd Restart Bidirectional Forwarding Detection daemon bgpd Restart Border Gateway Protocol daemon ospf6d Restart OSPFv3 daemon ospfd Restart OSPFv2 daemon ripd Restart Routing Information Protocol daemon ripngd Restart RIPng daemon staticd Restart Static Route daemon zebra Restart IP routing manager daemon From a real-life example: Two engineers needed 5 minutes to figure it is under "restart frr" - that is why this commit drops the artificial "frr" level on the op-mode commands to restart routing protocol daemons. It's less intuitive to have "restart frr ospfd" or "restart frr bgpd" compared to "restart ospf" and "restart bgp" - we have the same for "restart ssh" or "restart snmp" and not "restart openssh sshd". This commit also drops the d (daemon) suffix of the op-mode comamands so the commands align with the VyOS CLI, else there would be a miss-understanding from ospf6d to ospfv3. (cherry picked from commit 8ad8b0d51bf21c583e6d687576cb1a61195e7215) --- op-mode-definitions/restart-frr.xml.in | 104 ++++++++++++++++----------------- 1 file changed, 51 insertions(+), 53 deletions(-) diff --git a/op-mode-definitions/restart-frr.xml.in b/op-mode-definitions/restart-frr.xml.in index 96ad1a650..a5ba5b11f 100644 --- a/op-mode-definitions/restart-frr.xml.in +++ b/op-mode-definitions/restart-frr.xml.in @@ -2,62 +2,60 @@ - + - Restart FRRouting daemons + Restart all routing daemons sudo ${vyos_op_scripts_dir}/restart_frr.py --action restart - - - - Restart Bidirectional Forwarding Detection daemon - - sudo ${vyos_op_scripts_dir}/restart_frr.py --action restart --daemon bfdd - - - - Restart Border Gateway Protocol daemon - - sudo ${vyos_op_scripts_dir}/restart_frr.py --action restart --daemon bgpd - - - - Restart OSPFv2 daemon - - sudo ${vyos_op_scripts_dir}/restart_frr.py --action restart --daemon ospfd - - - - Restart OSPFv3 daemon - - sudo ${vyos_op_scripts_dir}/restart_frr.py --action restart --daemon ospf6d - - - - Restart Routing Information Protocol daemon - - sudo ${vyos_op_scripts_dir}/restart_frr.py --action restart --daemon ripd - - - - Restart RIPng daemon - - sudo ${vyos_op_scripts_dir}/restart_frr.py --action restart --daemon ripngd - - - - Restart Static Route daemon - - sudo ${vyos_op_scripts_dir}/restart_frr.py --action restart --daemon staticd - - - - Restart IP routing manager daemon - - sudo ${vyos_op_scripts_dir}/restart_frr.py --action restart --daemon zebra - - - + + + + Restart Bidirectional Forwarding Detection (BFD) daemon + + sudo ${vyos_op_scripts_dir}/restart_frr.py --action restart --daemon bfdd + + + + Restart Border Gateway Protocol (BGP) routing daemon + + sudo ${vyos_op_scripts_dir}/restart_frr.py --action restart --daemon bgpd + + + + Restart Open Shortest Path First (OSPF) routing daemon + + sudo ${vyos_op_scripts_dir}/restart_frr.py --action restart --daemon ospfd + + + + Restart IPv6 Open Shortest Path First (OSPFv3) routing daemon + + sudo ${vyos_op_scripts_dir}/restart_frr.py --action restart --daemon ospf6d + + + + Restart Routing Information Protocol (RIP) routing daemon + + sudo ${vyos_op_scripts_dir}/restart_frr.py --action restart --daemon ripd + + + + Restart Routing Information Protocol NG (RIPng) routing daemon + + sudo ${vyos_op_scripts_dir}/restart_frr.py --action restart --daemon ripngd + + + + Restart static routing daemon + + sudo ${vyos_op_scripts_dir}/restart_frr.py --action restart --daemon staticd + + + + Restart Routing Information Base (RIB) manager daemon + + sudo ${vyos_op_scripts_dir}/restart_frr.py --action restart --daemon zebra + -- cgit v1.2.3 From 3cca26f6dcf74ae430cc557f67a4116adaec19fe Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Wed, 25 Aug 2021 14:55:10 +0200 Subject: op-mode: frr: T1514: add possibility to restart isis daemon (cherry picked from commit b4b2c91127289c7b62afb24304054d57357a48c5) --- op-mode-definitions/restart-frr.xml.in | 6 ++++++ src/op_mode/restart_frr.py | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/op-mode-definitions/restart-frr.xml.in b/op-mode-definitions/restart-frr.xml.in index a5ba5b11f..475bd1ee8 100644 --- a/op-mode-definitions/restart-frr.xml.in +++ b/op-mode-definitions/restart-frr.xml.in @@ -20,6 +20,12 @@ sudo ${vyos_op_scripts_dir}/restart_frr.py --action restart --daemon bgpd + + + Restart Intermediate System to Intermediate System (IS-IS) routing daemon + + sudo ${vyos_op_scripts_dir}/restart_frr.py --action restart --daemon isisd + Restart Open Shortest Path First (OSPF) routing daemon diff --git a/src/op_mode/restart_frr.py b/src/op_mode/restart_frr.py index d1b66b33f..0b2322478 100755 --- a/src/op_mode/restart_frr.py +++ b/src/op_mode/restart_frr.py @@ -155,7 +155,7 @@ def _check_args_daemon(daemons): # define program arguments cmd_args_parser = argparse.ArgumentParser(description='restart frr daemons') cmd_args_parser.add_argument('--action', choices=['restart'], required=True, help='action to frr daemons') -cmd_args_parser.add_argument('--daemon', choices=['bfdd', 'bgpd', 'ospfd', 'ospf6d', 'ripd', 'ripngd', 'staticd', 'zebra'], required=False, nargs='*', help='select single or multiple daemons') +cmd_args_parser.add_argument('--daemon', choices=['bfdd', 'bgpd', 'ospfd', 'ospf6d', 'isisd', 'ripd', 'ripngd', 'staticd', 'zebra'], required=False, nargs='*', help='select single or multiple daemons') # parse arguments cmd_args = cmd_args_parser.parse_args() -- cgit v1.2.3 From 9471a9bac452c2c5600ef5d91dd842c8e084e8d6 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Thu, 26 Aug 2021 18:17:31 +0200 Subject: ipsec: T1210: support road-warrior IP assignment via RADIUS Framed-IP-Address Extended CLI command: "set vpn ipsec remote-access connection rw pool" with a "radius" option. --- interface-definitions/vpn_ipsec.xml.in | 12 ++++++++++-- src/conf_mode/vpn_ipsec.py | 24 +++++++++++++++++++----- 2 files changed, 29 insertions(+), 7 deletions(-) diff --git a/interface-definitions/vpn_ipsec.xml.in b/interface-definitions/vpn_ipsec.xml.in index b28c86ae6..b0dba4bce 100644 --- a/interface-definitions/vpn_ipsec.xml.in +++ b/interface-definitions/vpn_ipsec.xml.in @@ -771,11 +771,19 @@ Pool name used for IP address assignments vpn ipsec remote-access pool - dhcp + dhcp radius txt - Pool name + Name of predefined IP pool + + + dhcp + Forward requests for virtual IP addresses to a DHCP server + + + radius + Forward requests for virtual IP addresses to a RADIUS server diff --git a/src/conf_mode/vpn_ipsec.py b/src/conf_mode/vpn_ipsec.py index d3065fc47..ff6090e22 100755 --- a/src/conf_mode/vpn_ipsec.py +++ b/src/conf_mode/vpn_ipsec.py @@ -286,20 +286,34 @@ def verify(ipsec): if 'pre_shared_secret' not in ra_conf['authentication']: raise ConfigError(f"Missing pre-shared-key on {name} remote-access config") + if 'client_mode' not in ra_conf['authentication']: + raise ConfigError('Client authentication method is required!') - if 'client_mode' in ra_conf['authentication']: - if ra_conf['authentication']['client_mode'] == 'eap-radius': - if 'radius' not in ipsec['remote_access'] or 'server' not in ipsec['remote_access']['radius'] or len(ipsec['remote_access']['radius']['server']) == 0: - raise ConfigError('RADIUS authentication requires at least one server') + if dict_search('authentication.client_mode', ra_conf) == 'eap-radius': + if dict_search('remote_access.radius.server', ipsec) == None: + raise ConfigError('RADIUS authentication requires at least one server') if 'pool' in ra_conf: + if {'dhcp', 'radius'} <= set(ra_conf['pool']): + raise ConfigError(f'Can not use both DHCP and RADIUS for address allocation '\ + f'at the same time for "{name}"!') + if 'dhcp' in ra_conf['pool'] and len(ra_conf['pool']) > 1: - raise ConfigError(f'Can not use both DHCP and a predefined address pool for "{name}"!') + raise ConfigError(f'Can not use DHCP and a predefined address pool for "{name}"!') + + if 'radius' in ra_conf['pool'] and len(ra_conf['pool']) > 1: + raise ConfigError(f'Can not use RADIUS and a predefined address pool for "{name}"!') for pool in ra_conf['pool']: if pool == 'dhcp': if dict_search('remote_access.dhcp.server', ipsec) == None: raise ConfigError('IPSec DHCP server is not configured!') + elif pool == 'radius': + if dict_search('remote_access.radius.server', ipsec) == None: + raise ConfigError('IPSec RADIUS server is not configured!') + + if dict_search('authentication.client_mode', ra_conf) != 'eap-radius': + raise ConfigError('RADIUS IP pool requires eap-radius client authentication!') elif 'pool' not in ipsec['remote_access'] or pool not in ipsec['remote_access']['pool']: raise ConfigError(f'Requested pool "{pool}" does not exist!') -- cgit v1.2.3 From eb11d4b688d883d0c1d150b00eee40b54df42b32 Mon Sep 17 00:00:00 2001 From: zsdc Date: Thu, 26 Aug 2021 23:18:29 +0300 Subject: vyos.util: T3763: Optimized the check_port_availability function `print` was removed or replaced to `ValueError`, where possible. --- python/vyos/util.py | 12 +++--------- src/conf_mode/interfaces-wireguard.py | 9 ++++++--- 2 files changed, 9 insertions(+), 12 deletions(-) diff --git a/python/vyos/util.py b/python/vyos/util.py index fc2834a97..93a2f6640 100644 --- a/python/vyos/util.py +++ b/python/vyos/util.py @@ -834,16 +834,13 @@ def check_port_availability(ipaddress, port, protocol): try: ipaddress = ip_address(ipaddress).compressed except: - print(f'The {ipaddress} is not a valid IPv4 or IPv6 address') - return + raise ValueError(f'The {ipaddress} is not a valid IPv4 or IPv6 address') if port not in range(1, 65536): - print(f'The port number {port} is not in the 1-65535 range') - return + raise ValueError(f'The port number {port} is not in the 1-65535 range') if protocol not in ['tcp', 'udp']: - print( + raise ValueError( f'The protocol {protocol} is not supported. Only tcp and udp are allowed' ) - return # check port availability try: @@ -854,7 +851,4 @@ def check_port_availability(ipaddress, port, protocol): server.server_close() return True except: - print( - f'The {protocol} port {port} on the {ipaddress} is busy or unavailable' - ) return False diff --git a/src/conf_mode/interfaces-wireguard.py b/src/conf_mode/interfaces-wireguard.py index ad3ddcba2..68181465e 100755 --- a/src/conf_mode/interfaces-wireguard.py +++ b/src/conf_mode/interfaces-wireguard.py @@ -74,9 +74,12 @@ def verify(wireguard): if 'peer' not in wireguard: raise ConfigError('At least one Wireguard peer is required!') - if 'port' in wireguard and check_port_availability( - '0.0.0.0', int(wireguard['port']), 'udp') is not True: - raise ConfigError('The port cannot be used for the interface') + listen_port = int(wireguard['port']) + if 'port' in wireguard and check_port_availability('0.0.0.0', listen_port, + 'udp') is not True: + raise ConfigError( + f'The UDP port {listen_port} is busy or unavailable and cannot be used for the interface' + ) # run checks on individual configured WireGuard peer for tmp in wireguard['peer']: -- cgit v1.2.3 From cfd4d283ff0297372248b4ec57f67fd36dd33fc2 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Fri, 27 Aug 2021 11:04:36 +0200 Subject: ipsec: T1210: Jinj2 template did not honor inactivity/timeout setting --- data/templates/ipsec/swanctl/remote_access.tmpl | 3 ++- interface-definitions/vpn_ipsec.xml.in | 8 ++++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/data/templates/ipsec/swanctl/remote_access.tmpl b/data/templates/ipsec/swanctl/remote_access.tmpl index f906836c6..4fdc2a276 100644 --- a/data/templates/ipsec/swanctl/remote_access.tmpl +++ b/data/templates/ipsec/swanctl/remote_access.tmpl @@ -10,7 +10,7 @@ send_certreq = no rekey_time = {{ ike.lifetime }}s keyingtries = 0 -{% if rw_conf.unique is defined and rw_conf.unique is not none %} +{% if rw_conf.unique is defined and rw_conf.unique is not none %} unique = {{ rw_conf.unique }} {% endif %} {% if rw_conf.pool is defined and rw_conf.pool is not none %} @@ -39,6 +39,7 @@ rekey_time = {{ esp.lifetime }}s rand_time = 540s dpd_action = clear + inactivity = {{ rw_conf.timeout }} {% set local_prefix = rw_conf.local.prefix if rw_conf.local is defined and rw_conf.local.prefix is defined else ['0.0.0.0/0', '::/0'] %} {% set local_port = rw_conf.local.port if rw_conf.local is defined and rw_conf.local.port is defined else '' %} {% set local_suffix = '[%any/{1}]'.format(local_port) if local_port else '' %} diff --git a/interface-definitions/vpn_ipsec.xml.in b/interface-definitions/vpn_ipsec.xml.in index b0dba4bce..ed4941efc 100644 --- a/interface-definitions/vpn_ipsec.xml.in +++ b/interface-definitions/vpn_ipsec.xml.in @@ -757,11 +757,15 @@ Timeout to close connection if no data is transmitted - u32:10-86400 + u32:0 + Disable inactivity checks + + + u32:1-86400 Timeout in seconds (default 28800) - + 28800 -- cgit v1.2.3 From da29092d3d40a3b140fcb500bb8ae275cbf367fa Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Fri, 27 Aug 2021 19:44:04 +0200 Subject: vyos.ifconfig: pppoe: T3778: bugfix assignemnt of cached config We need to copy the configuration before this is done in super().update() as we utilize self.set_dhcpv6() before this is done by the base class. --- python/vyos/ifconfig/pppoe.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/python/vyos/ifconfig/pppoe.py b/python/vyos/ifconfig/pppoe.py index 9153863de..1d13264bf 100644 --- a/python/vyos/ifconfig/pppoe.py +++ b/python/vyos/ifconfig/pppoe.py @@ -75,6 +75,14 @@ class PPPoEIf(Interface): interface setup code and provide a single point of entry when workin on any interface. """ + # Cache the configuration - it will be reused inside e.g. DHCP handler + # XXX: maybe pass the option via __init__ in the future and rename this + # method to apply()? + # + # We need to copy this from super().update() as we utilize self.set_dhcpv6() + # before this is done by the base class. + self._config = config + # remove old routes from an e.g. old VRF assignment vrf = '' if 'vrf_old' in config: -- cgit v1.2.3 From d22f97af23abb5c12f8ea79c50fdda7ee0a3832d Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sat, 28 Aug 2021 17:41:34 +0200 Subject: vyos.ethtool: T3163: rename unused methods for offload validation --- python/vyos/ethtool.py | 75 +++++++++++++++++++++++++++++--------------------- 1 file changed, 43 insertions(+), 32 deletions(-) diff --git a/python/vyos/ethtool.py b/python/vyos/ethtool.py index bc103959a..0ae526346 100644 --- a/python/vyos/ethtool.py +++ b/python/vyos/ethtool.py @@ -23,14 +23,14 @@ class Ethtool: # dictionary containing driver featurs, it will be populated on demand and # the content will look like: # { - # 'tls-hw-tx-offload': {'fixed': True, 'on': False}, - # 'tx-checksum-fcoe-crc': {'fixed': True, 'on': False}, - # 'tx-checksum-ip-generic': {'fixed': False, 'on': True}, - # 'tx-checksum-ipv4': {'fixed': True, 'on': False}, - # 'tx-checksum-ipv6': {'fixed': True, 'on': False}, - # 'tx-checksum-sctp': {'fixed': True, 'on': False}, - # 'tx-checksumming': {'fixed': False, 'on': True}, - # 'tx-esp-segmentation': {'fixed': True, 'on': False}, + # 'tls-hw-tx-offload': {'fixed': True, 'enabled': False}, + # 'tx-checksum-fcoe-crc': {'fixed': True, 'enabled': False}, + # 'tx-checksum-ip-generic': {'fixed': False, 'enabled': True}, + # 'tx-checksum-ipv4': {'fixed': True, 'enabled': False}, + # 'tx-checksum-ipv6': {'fixed': True, 'enabled': False}, + # 'tx-checksum-sctp': {'fixed': True, 'enabled': False}, + # 'tx-checksumming': {'fixed': False, 'enabled': True}, + # 'tx-esp-segmentation': {'fixed': True, 'enabled': False}, # } features = { } ring_buffers = { } @@ -42,12 +42,12 @@ class Ethtool: for line in out.splitlines()[1:]: if ":" in line: key, value = [s.strip() for s in line.strip().split(":", 1)] - fixed = "fixed" in value + fixed = bool('fixed' in value) if fixed: value = value.split()[0].strip() self.features[key.strip()] = { - "on": value == "on", - "fixed": fixed + 'enabled' : bool(value == 'on'), + 'fixed' : fixed } out, err = popen(f'ethtool -g {ifname}') @@ -63,36 +63,47 @@ class Ethtool: if value.isdigit(): self.ring_buffers[key] = int(value) - def is_fixed_lro(self): # in case of a missing configuration, rather return "fixed". In Ethtool # terminology "fixed" means the setting can not be changed by the user. return self.features.get('large-receive-offload', True).get('fixed', True) - def is_fixed_gro(self): - # in case of a missing configuration, rather return "fixed". In Ethtool - # terminology "fixed" means the setting can not be changed by the user. - return self.features.get('generic-receive-offload', True).get('fixed', True) + def _get_generic(self, feature): + """ + Generic method to read self.features and return a tuple for feature + enabled and feature is fixed. - def is_fixed_gso(self): - # in case of a missing configuration, rather return "fixed". In Ethtool - # terminology "fixed" means the setting can not be changed by the user. - return self.features.get('generic-segmentation-offload', True).get('fixed', True) + In case of a missing key, return "fixed = True and enabled = False" + """ + fixed = True + enabled = False + if feature in self.features: + if 'enabled' in self.features[feature]: + enabled = self.features[feature]['enabled'] + if 'fixed' in self.features[feature]: + fixed = self.features[feature]['fixed'] + return enabled, fixed - def is_fixed_sg(self): - # in case of a missing configuration, rather return "fixed". In Ethtool - # terminology "fixed" means the setting can not be changed by the user. - return self.features.get('scatter-gather', True).get('fixed', True) + def get_generic_receive_offload(self): + return self._get_generic('generic-receive-offload') - def is_fixed_tso(self): - # in case of a missing configuration, rather return "fixed". In Ethtool - # terminology "fixed" means the setting can not be changed by the user. - return self.features.get('tcp-segmentation-offload', True).get('fixed', True) + def get_generic_segmentation_offload(self): + return self._get_generic('generic-segmentation-offload') - def is_fixed_ufo(self): - # in case of a missing configuration, rather return "fixed". In Ethtool - # terminology "fixed" means the setting can not be changed by the user. - return self.features.get('udp-fragmentation-offload', True).get('fixed', True) + def get_large_receive_offload(self): + return self._get_generic('large-receive-offload') + + def get_scatter_gather(self): + return self._get_generic('scatter-gather') + + def get_tcp_segmentation_offload(self): + return self._get_generic('tcp-segmentation-offload') + + def get_udp_fragmentation_offload(self): + return self._get_generic('udp-fragmentation-offload') + + def get_rx_vlan_offload(self): + return self._get_generic('rx-vlan-offload') def get_rx_buffer(self): # Configuration of RX ring-buffers is not supported on every device, -- cgit v1.2.3 From 31169fa8a763e36f6276632139da46b1aca3a7af Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sat, 28 Aug 2021 17:42:33 +0200 Subject: vyos.ifconfig: T3619: only set offloading options if supported by NIC In the past we always told ethtool to change the offloading settings, even if this was not supported by the underlaying driver. This commit will only change the offloading options if they differ from the current state of the NIC and only if it's supported by the NIC. If the NIC does not support setting the offloading options, a message will be displayed for the user: vyos@vyos# set interfaces ethernet eth2 offload gro vyos@vyos# commit [ interfaces ethernet eth2 ] Adapter does not support changing large-receive-offload settings! --- python/vyos/ifconfig/ethernet.py | 81 +++++++++++++++++++++++++++++++++------- 1 file changed, 67 insertions(+), 14 deletions(-) diff --git a/python/vyos/ifconfig/ethernet.py b/python/vyos/ifconfig/ethernet.py index 07b31a12a..fbff789eb 100644 --- a/python/vyos/ifconfig/ethernet.py +++ b/python/vyos/ifconfig/ethernet.py @@ -16,6 +16,7 @@ import os import re +from vyos.ethtool import Ethtool from vyos.ifconfig.interface import Interface from vyos.util import run from vyos.util import dict_search @@ -41,7 +42,7 @@ class EthernetIf(Interface): @staticmethod def feature(ifname, option, value): - run(f'ethtool -K {ifname} {option} {value}','ifconfig') + run(f'ethtool -K {ifname} {option} {value}') return False _command_set = {**Interface._command_set, **{ @@ -84,6 +85,10 @@ class EthernetIf(Interface): }, }} + def __init__(self, ifname, **kargs): + super().__init__(ifname, **kargs) + self.ethtool = Ethtool(ifname) + def get_driver_name(self): """ Return the driver name used by NIC. Some NICs don't support all @@ -228,8 +233,16 @@ class EthernetIf(Interface): >>> i.set_gro(True) """ if not isinstance(state, bool): - raise ValueError("Value out of range") - return self.set_interface('gro', 'on' if state else 'off') + raise ValueError('Value out of range') + + enabled, fixed = self.ethtool.get_generic_receive_offload() + if not fixed: + enabled = 'on' if enabled else 'off' + if enabled != state: + return self.set_interface('gro', 'on' if state else 'off') + + print('Adapter does not support changing generic-receive-offload settings!') + return False def set_gso(self, state): """ @@ -240,8 +253,16 @@ class EthernetIf(Interface): >>> i.set_gso(True) """ if not isinstance(state, bool): - raise ValueError("Value out of range") - return self.set_interface('gso', 'on' if state else 'off') + raise ValueError('Value out of range') + + enabled, fixed = self.ethtool.get_generic_segmentation_offload() + if not fixed: + enabled = 'on' if enabled else 'off' + if enabled != state: + return self.set_interface('gro', 'on' if state else 'off') + + print('Adapter does not support changing generic-segmentation-offload settings!') + return False def set_lro(self, state): """ @@ -252,12 +273,20 @@ class EthernetIf(Interface): >>> i.set_lro(True) """ if not isinstance(state, bool): - raise ValueError("Value out of range") - return self.set_interface('lro', 'on' if state else 'off') + raise ValueError('Value out of range') + + enabled, fixed = self.ethtool.get_large_receive_offload() + if not fixed: + enabled = 'on' if enabled else 'off' + if enabled != state: + return self.set_interface('gro', 'on' if state else 'off') + + print('Adapter does not support changing large-receive-offload settings!') + return False def set_rps(self, state): if not isinstance(state, bool): - raise ValueError("Value out of range") + raise ValueError('Value out of range') rps_cpus = '0' if state: @@ -282,8 +311,16 @@ class EthernetIf(Interface): >>> i.set_sg(True) """ if not isinstance(state, bool): - raise ValueError("Value out of range") - return self.set_interface('sg', 'on' if state else 'off') + raise ValueError('Value out of range') + + enabled, fixed = self.ethtool.get_scatter_gather() + if not fixed: + enabled = 'on' if enabled else 'off' + if enabled != state: + return self.set_interface('gro', 'on' if state else 'off') + + print('Adapter does not support changing scatter-gather settings!') + return False def set_tso(self, state): """ @@ -295,8 +332,16 @@ class EthernetIf(Interface): >>> i.set_tso(False) """ if not isinstance(state, bool): - raise ValueError("Value out of range") - return self.set_interface('tso', 'on' if state else 'off') + raise ValueError('Value out of range') + + enabled, fixed = self.ethtool.get_tcp_segmentation_offload() + if not fixed: + enabled = 'on' if enabled else 'off' + if enabled != state: + return self.set_interface('gro', 'on' if state else 'off') + + print('Adapter does not support changing tcp-segmentation-offload settings!') + return False def set_ufo(self, state): """ @@ -308,8 +353,16 @@ class EthernetIf(Interface): >>> i.set_udp_offload(True) """ if not isinstance(state, bool): - raise ValueError("Value out of range") - return self.set_interface('ufo', 'on' if state else 'off') + raise ValueError('Value out of range') + + enabled, fixed = self.ethtool.get_udp_fragmentation_offload() + if not fixed: + enabled = 'on' if enabled else 'off' + if enabled != state: + return self.set_interface('gro', 'on' if state else 'off') + + print('Adapter does not support changing udp-fragmentation-offload settings!') + return False def set_ring_buffer(self, b_type, b_size): """ -- cgit v1.2.3 From 91a4d57d429719cbb35fe38f31e1889645a5579a Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sun, 29 Aug 2021 10:55:46 +0200 Subject: isis: T3783: bugfix configuring spf-delay-ietf Mandatory FRR options for spf-delay-ietf did not get rendered in the Jinja2 template. --- data/templates/frr/isisd.frr.tmpl | 2 +- smoketest/scripts/cli/test_protocols_isis.py | 53 ++++++++++++++++++++++++++++ src/conf_mode/containers.py | 16 +++++++++ src/conf_mode/protocols_isis.py | 2 +- 4 files changed, 71 insertions(+), 2 deletions(-) diff --git a/data/templates/frr/isisd.frr.tmpl b/data/templates/frr/isisd.frr.tmpl index 6cfa076d0..51ac40060 100644 --- a/data/templates/frr/isisd.frr.tmpl +++ b/data/templates/frr/isisd.frr.tmpl @@ -100,7 +100,7 @@ router isis VyOS {{ 'vrf ' + vrf if vrf is defined and vrf is not none }} {% endif %} {% endif %} {% if spf_delay_ietf is defined and spf_delay_ietf.init_delay is defined and spf_delay_ietf.init_delay is not none %} - spf-delay-ietf init-delay {{ spf_delay_ietf.init_delay }} + spf-delay-ietf init-delay {{ spf_delay_ietf.init_delay }} short-delay {{ spf_delay_ietf.short_delay }} long-delay {{ spf_delay_ietf.long_delay }} holddown {{ spf_delay_ietf.holddown }} time-to-learn {{ spf_delay_ietf.time_to_learn }} {% endif %} {% if area_password is defined and area_password is not none %} {% if area_password.md5 is defined and area_password.md5 is not none %} diff --git a/smoketest/scripts/cli/test_protocols_isis.py b/smoketest/scripts/cli/test_protocols_isis.py index 9b6d4a4ec..8170f2b56 100755 --- a/smoketest/scripts/cli/test_protocols_isis.py +++ b/smoketest/scripts/cli/test_protocols_isis.py @@ -199,5 +199,58 @@ class TestProtocolsISIS(VyOSUnitTestSHIM.TestCase): self.assertIn(f' area-password clear {password}', tmp) + def test_isis_06_spf_delay(self): + self.isis_base_config() + + network = 'point-to-point' + holddown = '10' + init_delay = '50' + long_delay = '200' + short_delay = '100' + time_to_learn = '75' + + for interface in self._interfaces: + self.cli_set(base_path + ['interface', interface, 'network', network]) + + self.cli_set(base_path + ['spf-delay-ietf', 'holddown', holddown]) + # verify() - All types of spf-delay must be configured + with self.assertRaises(ConfigSessionError): + self.cli_commit() + + self.cli_set(base_path + ['spf-delay-ietf', 'init-delay', init_delay]) + # verify() - All types of spf-delay must be configured + with self.assertRaises(ConfigSessionError): + self.cli_commit() + + self.cli_set(base_path + ['spf-delay-ietf', 'long-delay', long_delay]) + # verify() - All types of spf-delay must be configured + with self.assertRaises(ConfigSessionError): + self.cli_commit() + + self.cli_set(base_path + ['spf-delay-ietf', 'long-delay', long_delay]) + # verify() - All types of spf-delay must be configured + with self.assertRaises(ConfigSessionError): + self.cli_commit() + + self.cli_set(base_path + ['spf-delay-ietf', 'short-delay', short_delay]) + # verify() - All types of spf-delay must be configured + with self.assertRaises(ConfigSessionError): + self.cli_commit() + self.cli_set(base_path + ['spf-delay-ietf', 'time-to-learn', time_to_learn]) + + # Commit all changes + self.cli_commit() + + # Verify all changes + tmp = self.getFRRconfig(f'router isis {domain}') + self.assertIn(f' net {net}', tmp) + self.assertIn(f' spf-delay-ietf init-delay {init_delay} short-delay {short_delay} long-delay {long_delay} holddown {holddown} time-to-learn {time_to_learn}', tmp) + + for interface in self._interfaces: + tmp = self.getFRRconfig(f'interface {interface}') + self.assertIn(f' ip router isis {domain}', tmp) + self.assertIn(f' ipv6 router isis {domain}', tmp) + self.assertIn(f' isis network {network}', tmp) + if __name__ == '__main__': unittest.main(verbosity=2) diff --git a/src/conf_mode/containers.py b/src/conf_mode/containers.py index 32320a4b2..3a93bf062 100755 --- a/src/conf_mode/containers.py +++ b/src/conf_mode/containers.py @@ -19,6 +19,7 @@ import json from ipaddress import ip_address from ipaddress import ip_network +from time import sleep from vyos.config import Config from vyos.configdict import dict_merge @@ -28,6 +29,9 @@ from vyos.util import cmd from vyos.util import run from vyos.util import read_file from vyos.util import write_file +from vyos.util import is_systemd_service_active +from vyos.util import is_systemd_service_running + from vyos.template import render from vyos.template import is_ipv4 from vyos.template import is_ipv6 @@ -199,6 +203,18 @@ def apply(container): for network in container['network_remove']: call(f'podman network rm --force {network}') + service_name = 'podman.service' + if 'network' in container or 'name' in container: + # Start podman if it's required and not yet running + if not is_systemd_service_active(service_name): + _cmd(f'systemctl start {service_name}') + # Wait for podman to be running + while not is_systemd_service_running(service_name): + sleep(0.250) + else: + _cmd(f'systemctl stop {service_name}') + + # Add network if 'network' in container: for network, network_config in container['network'].items(): diff --git a/src/conf_mode/protocols_isis.py b/src/conf_mode/protocols_isis.py index 4cf0312e9..4505e2496 100755 --- a/src/conf_mode/protocols_isis.py +++ b/src/conf_mode/protocols_isis.py @@ -148,7 +148,7 @@ def verify(isis): exist_timers = set(required_timers).difference(set(exist_timers)) if len(exist_timers) > 0: - raise ConfigError('All types of delay must be specified: ' + ', '.join(exist_timers).replace('_', '-')) + raise ConfigError('All types of spf-delay must be configured. Missing: ' + ', '.join(exist_timers).replace('_', '-')) # If Redistribute set, but level don't set if 'redistribute' in isis: -- cgit v1.2.3 From 33ccbfdd0b367a2416c3a8b41a73d7477123d276 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sun, 29 Aug 2021 12:07:53 +0200 Subject: ospf: xml: T3236: update help strings --- interface-definitions/include/ospf/protocol-common-config.xml.i | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/interface-definitions/include/ospf/protocol-common-config.xml.i b/interface-definitions/include/ospf/protocol-common-config.xml.i index c4ca613a4..546516a80 100644 --- a/interface-definitions/include/ospf/protocol-common-config.xml.i +++ b/interface-definitions/include/ospf/protocol-common-config.xml.i @@ -665,7 +665,7 @@ - Redistribute kernel routes + Redistribute Kernel routes #include @@ -685,7 +685,7 @@ - Redistribute static routes + Redistribute statically configured routes #include -- cgit v1.2.3 From 40bfaed4d1d427c33157136026944df80e02a5b6 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sun, 29 Aug 2021 12:08:22 +0200 Subject: ospf: T3236: add possibility to redistribute "table" Add new CLI command: * "set protocols ospf redistribute table " --- data/templates/frr/ospfd.frr.tmpl | 10 ++++++++-- interface-definitions/include/ospf/metric-type.xml.i | 2 +- .../include/ospf/protocol-common-config.xml.i | 17 +++++++++++++++++ src/conf_mode/protocols_ospf.py | 14 +++++++++++++- 4 files changed, 39 insertions(+), 4 deletions(-) diff --git a/data/templates/frr/ospfd.frr.tmpl b/data/templates/frr/ospfd.frr.tmpl index be39519c3..90a6bbd56 100644 --- a/data/templates/frr/ospfd.frr.tmpl +++ b/data/templates/frr/ospfd.frr.tmpl @@ -172,8 +172,14 @@ router ospf {{ 'vrf ' + vrf if vrf is defined and vrf is not none }} {% endfor %} {% endif %} {% if redistribute is defined and redistribute is not none %} -{% for protocol, options in redistribute.items() %} - redistribute {{ protocol }} {{ 'metric ' + options.metric if options.metric is defined }} {{ 'metric-type ' + options.metric_type if options.metric_type is defined }} {{ 'route-map ' + options.route_map if options.route_map is defined }} +{% for protocol, protocols_options in redistribute.items() %} +{% if protocol == 'table' %} +{% for table, table_options in protocols_options.items() %} + redistribute {{ protocol }} {{ table }} {{ 'metric ' + table_options.metric if table_options.metric is defined }} {{ 'metric-type ' + table_options.metric_type if table_options.metric_type is defined }} {{ 'route-map ' + table_options.route_map if table_options.route_map is defined }} +{% endfor %} +{% else %} + redistribute {{ protocol }} {{ 'metric ' + protocols_options.metric if protocols_options.metric is defined }} {{ 'metric-type ' + protocols_options.metric_type if protocols_options.metric_type is defined }} {{ 'route-map ' + protocols_options.route_map if protocols_options.route_map is defined }} +{% endif %} {% endfor %} {% endif %} {% if refresh is defined and refresh.timers is defined and refresh.timers is not none %} diff --git a/interface-definitions/include/ospf/metric-type.xml.i b/interface-definitions/include/ospf/metric-type.xml.i index 83dc24909..ef9fd8ac0 100644 --- a/interface-definitions/include/ospf/metric-type.xml.i +++ b/interface-definitions/include/ospf/metric-type.xml.i @@ -4,7 +4,7 @@ OSPF metric type for default routes (default: 2) u32:1-2 - Metric type for default routes + Set OSPF External Type 1/2 metrics diff --git a/interface-definitions/include/ospf/protocol-common-config.xml.i b/interface-definitions/include/ospf/protocol-common-config.xml.i index 546516a80..d8556ebf5 100644 --- a/interface-definitions/include/ospf/protocol-common-config.xml.i +++ b/interface-definitions/include/ospf/protocol-common-config.xml.i @@ -693,6 +693,23 @@ #include + + + Redistribute non-main Kernel Routing Table + + protocols static table + + + u32:1-200 + Policy route table number + + + + #include + #include + #include + + diff --git a/src/conf_mode/protocols_ospf.py b/src/conf_mode/protocols_ospf.py index 06a29106d..92532fcb5 100755 --- a/src/conf_mode/protocols_ospf.py +++ b/src/conf_mode/protocols_ospf.py @@ -87,7 +87,13 @@ def get_config(config=None): del default_values['area']['area_type']['nssa'] if 'mpls_te' not in ospf: del default_values['mpls_te'] - for protocol in ['bgp', 'connected', 'isis', 'kernel', 'rip', 'static']: + + for protocol in ['bgp', 'connected', 'isis', 'kernel', 'rip', 'static', 'table']: + # table is a tagNode thus we need to clean out all occurances for the + # default values and load them in later individually + if protocol == 'table': + del default_values['redistribute']['table'] + continue if dict_search(f'redistribute.{protocol}', ospf) is None: del default_values['redistribute'][protocol] @@ -127,6 +133,12 @@ def get_config(config=None): ospf['interface'][interface] = dict_merge(default_values, ospf['interface'][interface]) + if 'redistribute' in ospf and 'table' in ospf['redistribute']: + default_values = defaults(base + ['redistribute', 'table']) + for table in ospf['redistribute']['table']: + ospf['redistribute']['table'][table] = dict_merge(default_values, + ospf['redistribute']['table'][table]) + # We also need some additional information from the config, prefix-lists # and route-maps for instance. They will be used in verify(). # -- cgit v1.2.3 From 26f6168626f79ebc3e027ecf5f77d79a6337d97c Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sun, 29 Aug 2021 12:09:12 +0200 Subject: ospf: T3236: remove debug print() statement --- src/conf_mode/protocols_ospf.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/conf_mode/protocols_ospf.py b/src/conf_mode/protocols_ospf.py index 92532fcb5..6ccda2e5a 100755 --- a/src/conf_mode/protocols_ospf.py +++ b/src/conf_mode/protocols_ospf.py @@ -115,7 +115,6 @@ def get_config(config=None): default_values = defaults(base + ['area', 'virtual-link']) for area, area_config in ospf['area'].items(): if 'virtual_link' in area_config: - print(default_values) for virtual_link in area_config['virtual_link']: ospf['area'][area]['virtual_link'][virtual_link] = dict_merge( default_values, ospf['area'][area]['virtual_link'][virtual_link]) -- cgit v1.2.3 From 794f193d11c8c1b5fed78f4e40280480446ab593 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sun, 29 Aug 2021 14:29:19 +0200 Subject: xml: add missing "u32:" value declarator on integer ranges --- interface-definitions/bcast-relay.xml.in | 5 ++- interface-definitions/dhcp-relay.xml.in | 4 +-- interface-definitions/dhcp-server.xml.in | 2 +- interface-definitions/dhcpv6-relay.xml.in | 2 +- interface-definitions/dhcpv6-server.xml.in | 13 ++++---- interface-definitions/dns-dynamic.xml.in | 2 +- interface-definitions/dns-forwarding.xml.in | 4 +-- interface-definitions/flow-accounting-conf.xml.in | 16 +++++----- interface-definitions/https.xml.in | 9 +++--- interface-definitions/igmp-proxy.xml.in | 2 +- .../include/accel-ppp/ppp-interface-cache.xml.i | 2 +- .../include/accel-ppp/radius-additions.xml.i | 12 ++++---- interface-definitions/include/bfd-common.xml.i | 8 ++--- .../include/firewall/common-rule.xml.i | 2 +- .../include/interface/arp-cache-timeout.xml.i | 2 +- .../include/interface/dhcpv6-options.xml.i | 4 +-- .../interface/ipv6-dup-addr-detect-transmits.xml.i | 8 ++--- .../include/interface/mtu-1200-16000.xml.i | 2 +- .../include/interface/mtu-1450-16000.xml.i | 2 +- .../include/interface/mtu-64-8024.xml.i | 2 +- .../include/interface/mtu-68-1500.xml.i | 2 +- .../include/interface/mtu-68-16000.xml.i | 2 +- .../include/interface/parameters-flowlabel.xml.i | 11 +++++-- .../include/interface/parameters-tos.xml.i | 2 +- .../include/interface/parameters-ttl.xml.i | 4 +-- .../include/interface/vif-s.xml.i | 4 +++ interface-definitions/include/interface/vif.xml.i | 2 +- interface-definitions/include/nat-rule.xml.i | 2 +- interface-definitions/include/vni.xml.i | 2 +- interface-definitions/interfaces-bridge.xml.in | 18 +++++------ interface-definitions/interfaces-l2tpv3.xml.in | 12 ++++---- interface-definitions/interfaces-macsec.xml.in | 6 ++-- interface-definitions/interfaces-openvpn.xml.in | 12 ++++---- interface-definitions/interfaces-tunnel.xml.in | 13 +++++--- interface-definitions/interfaces-wireguard.xml.in | 2 +- interface-definitions/interfaces-wireless.xml.in | 2 +- interface-definitions/lldp.xml.in | 2 +- interface-definitions/nat66.xml.in | 4 +-- interface-definitions/protocols-igmp.xml.in | 15 ++++++--- interface-definitions/protocols-multicast.xml.in | 4 +-- interface-definitions/protocols-pim.xml.in | 6 ++-- interface-definitions/service_pppoe-server.xml.in | 4 +-- interface-definitions/service_router-advert.xml.in | 36 +++++++++++----------- interface-definitions/ssh.xml.in | 2 +- interface-definitions/vpn_l2tp.xml.in | 4 +-- interface-definitions/vrf.xml.in | 4 +-- 46 files changed, 149 insertions(+), 131 deletions(-) diff --git a/interface-definitions/bcast-relay.xml.in b/interface-definitions/bcast-relay.xml.in index c7948ded1..3f781f07f 100644 --- a/interface-definitions/bcast-relay.xml.in +++ b/interface-definitions/bcast-relay.xml.in @@ -1,5 +1,4 @@ - @@ -14,8 +13,8 @@ Unique ID for each UDP port to forward - 1-99 - Numerical ID # + u32:1-99 + Broadcast relay instance ID diff --git a/interface-definitions/dhcp-relay.xml.in b/interface-definitions/dhcp-relay.xml.in index 8c95239d9..0d485ef80 100644 --- a/interface-definitions/dhcp-relay.xml.in +++ b/interface-definitions/dhcp-relay.xml.in @@ -27,7 +27,7 @@ Policy to discard packets that have reached specified hop-count - 1-255 + u32:1-255 Hop count (default: 10) @@ -41,7 +41,7 @@ Maximum packet size to send to a DHCPv4/BOOTP server - 64-1400 + u32:64-1400 Maximum packet size (default: 576) diff --git a/interface-definitions/dhcp-server.xml.in b/interface-definitions/dhcp-server.xml.in index 015500043..bafd6f6a2 100644 --- a/interface-definitions/dhcp-server.xml.in +++ b/interface-definitions/dhcp-server.xml.in @@ -96,7 +96,7 @@ Specifies the clients subnet mask as per RFC 950. If unset, subnet declaration is used. - 0-32 + u32:0-32 DHCP client prefix length must be 0 to 32 diff --git a/interface-definitions/dhcpv6-relay.xml.in b/interface-definitions/dhcpv6-relay.xml.in index 308f94a01..7162cf353 100644 --- a/interface-definitions/dhcpv6-relay.xml.in +++ b/interface-definitions/dhcpv6-relay.xml.in @@ -35,7 +35,7 @@ Maximum hop count for which requests will be processed - 1-255 + u32:1-255 Hop count (default: 10) diff --git a/interface-definitions/dhcpv6-server.xml.in b/interface-definitions/dhcpv6-server.xml.in index 5d6c64685..95b1e5602 100644 --- a/interface-definitions/dhcpv6-server.xml.in +++ b/interface-definitions/dhcpv6-server.xml.in @@ -1,5 +1,4 @@ - @@ -34,7 +33,7 @@ Preference of this DHCPv6 server compared with others - 0-255 + u32:0-255 DHCPv6 server preference (0-255) @@ -62,7 +61,7 @@ Time (in seconds) that stateless clients should wait between refreshing the information they were given - 1-4294967295 + u32:1-4294967295 DHCPv6 information refresh time @@ -161,7 +160,7 @@ Default time (in seconds) that will be assigned to a lease - 1-4294967295 + u32:1-4294967295 DHCPv6 valid lifetime @@ -173,7 +172,7 @@ Maximum time (in seconds) that will be assigned to a lease - 1-4294967295 + u32:1-4294967295 Maximum lease time in seconds @@ -185,7 +184,7 @@ Minimum time (in seconds) that will be assigned to a lease - 1-4294967295 + u32:1-4294967295 Minimum lease time in seconds @@ -273,7 +272,7 @@ Length in bits of prefixes to be delegated - 32-64 + u32:32-64 Delagated prefix length (32-64) diff --git a/interface-definitions/dns-dynamic.xml.in b/interface-definitions/dns-dynamic.xml.in index b0b9158c8..250642691 100644 --- a/interface-definitions/dns-dynamic.xml.in +++ b/interface-definitions/dns-dynamic.xml.in @@ -49,7 +49,7 @@ Time To Live (default: 600) - 1-86400 + u32:1-86400 DNS forwarding cache size diff --git a/interface-definitions/dns-forwarding.xml.in b/interface-definitions/dns-forwarding.xml.in index c420e9b8b..06e45ce1e 100644 --- a/interface-definitions/dns-forwarding.xml.in +++ b/interface-definitions/dns-forwarding.xml.in @@ -18,7 +18,7 @@ DNS forwarding cache size (default: 10000) - 0-10000 + u32:0-10000 DNS forwarding cache size @@ -139,7 +139,7 @@ Maximum amount of time negative entries are cached (default: 3600) - 0-7200 + u32:0-7200 Seconds to cache NXDOMAIN entries diff --git a/interface-definitions/flow-accounting-conf.xml.in b/interface-definitions/flow-accounting-conf.xml.in index b3980d9e2..b0f308afd 100644 --- a/interface-definitions/flow-accounting-conf.xml.in +++ b/interface-definitions/flow-accounting-conf.xml.in @@ -267,7 +267,7 @@ Expiry scan interval - 0-2147483647 + u32:0-2147483647 Expiry scan interval (default 60) @@ -279,7 +279,7 @@ Generic flow timeout value - 0-2147483647 + u32:0-2147483647 Generic flow timeout in seconds (default 3600) @@ -291,7 +291,7 @@ ICMP timeout value - 0-2147483647 + u32:0-2147483647 ICMP timeout in seconds (default 300) @@ -303,7 +303,7 @@ Max active timeout value - 0-2147483647 + u32:0-2147483647 Max active timeout in seconds (default 604800) @@ -315,7 +315,7 @@ TCP finish timeout value - 0-2147483647 + u32:0-2147483647 TCP FIN timeout in seconds (default 300) @@ -327,7 +327,7 @@ TCP generic timeout value - 0-2147483647 + u32:0-2147483647 TCP generic timeout in seconds (default 3600) @@ -339,7 +339,7 @@ TCP reset timeout value - 0-2147483647 + u32:0-2147483647 TCP RST timeout in seconds (default 120) @@ -351,7 +351,7 @@ UDP timeout value - 0-2147483647 + u32:0-2147483647 UDP timeout in seconds (default 300) diff --git a/interface-definitions/https.xml.in b/interface-definitions/https.xml.in index b0532e249..bb6f71744 100644 --- a/interface-definitions/https.xml.in +++ b/interface-definitions/https.xml.in @@ -1,5 +1,4 @@ - @@ -47,7 +46,7 @@ Port to listen for HTTPS requests; default 443 - 1-65535 + u32:1-65535 Numeric IP port @@ -135,9 +134,9 @@ - - Email address to associate with certificate - + + Email address to associate with certificate + diff --git a/interface-definitions/igmp-proxy.xml.in b/interface-definitions/igmp-proxy.xml.in index d0f44eada..91c912d8b 100644 --- a/interface-definitions/igmp-proxy.xml.in +++ b/interface-definitions/igmp-proxy.xml.in @@ -65,7 +65,7 @@ TTL threshold (default: 1) - 1-255 + u32:1-255 TTL threshold for the interfaces (default: 1) diff --git a/interface-definitions/include/accel-ppp/ppp-interface-cache.xml.i b/interface-definitions/include/accel-ppp/ppp-interface-cache.xml.i index 9f223d7ed..019601c85 100644 --- a/interface-definitions/include/accel-ppp/ppp-interface-cache.xml.i +++ b/interface-definitions/include/accel-ppp/ppp-interface-cache.xml.i @@ -3,7 +3,7 @@ PPP interface cache - 1-256000 + u32:1-256000 Count of interfaces to keep in cache diff --git a/interface-definitions/include/accel-ppp/radius-additions.xml.i b/interface-definitions/include/accel-ppp/radius-additions.xml.i index fdcff36bf..258ece2b5 100644 --- a/interface-definitions/include/accel-ppp/radius-additions.xml.i +++ b/interface-definitions/include/accel-ppp/radius-additions.xml.i @@ -5,7 +5,7 @@ Maximum jitter value in seconds to be applied to accounting information interval - 1-60 + u32:1-60 Maximum jitter value in seconds @@ -20,7 +20,7 @@ Accounting port - 1-65535 + u32:1-65535 Numeric IP port (default: 1813) @@ -34,7 +34,7 @@ Mark server unavailable for <n> seconds on failure - 0-600 + u32:0-600 Fail time penalty @@ -50,7 +50,7 @@ Timeout in seconds to wait response from RADIUS server - 1-60 + u32:1-60 Timeout in seconds @@ -64,7 +64,7 @@ Timeout for Interim-Update packets, terminate session afterwards (default 3 seconds) - 0-60 + u32:0-60 Timeout in seconds, 0 to keep active @@ -78,7 +78,7 @@ Number of tries to send Access-Request/Accounting-Request queries - 1-20 + u32:1-20 Maximum tries diff --git a/interface-definitions/include/bfd-common.xml.i b/interface-definitions/include/bfd-common.xml.i index b47b47612..1d6ab5d55 100644 --- a/interface-definitions/include/bfd-common.xml.i +++ b/interface-definitions/include/bfd-common.xml.i @@ -14,7 +14,7 @@ Minimum interval of receiving control packets - 10-60000 + u32:10-60000 Interval in milliseconds @@ -27,7 +27,7 @@ Minimum interval of transmitting control packets - 10-60000 + u32:10-60000 Interval in milliseconds @@ -40,7 +40,7 @@ Multiplier to determine packet loss - 2-255 + u32:2-255 Remote transmission interval will be multiplied by this value @@ -53,7 +53,7 @@ Echo receive transmission interval - 10-60000 + u32:10-60000 The minimal echo receive transmission interval that this system is capable of handling diff --git a/interface-definitions/include/firewall/common-rule.xml.i b/interface-definitions/include/firewall/common-rule.xml.i index 1ee8da73d..a59c0b390 100644 --- a/interface-definitions/include/firewall/common-rule.xml.i +++ b/interface-definitions/include/firewall/common-rule.xml.i @@ -110,7 +110,7 @@ Both TCP and UDP - 0-255 + u32:0-255 IP protocol number diff --git a/interface-definitions/include/interface/arp-cache-timeout.xml.i b/interface-definitions/include/interface/arp-cache-timeout.xml.i index 3fb64f1ff..cb01d0525 100644 --- a/interface-definitions/include/interface/arp-cache-timeout.xml.i +++ b/interface-definitions/include/interface/arp-cache-timeout.xml.i @@ -3,7 +3,7 @@ ARP cache entry timeout in seconds - 1-86400 + u32:1-86400 ARP cache entry timout in seconds (default 30) diff --git a/interface-definitions/include/interface/dhcpv6-options.xml.i b/interface-definitions/include/interface/dhcpv6-options.xml.i index ca478a3eb..d1abf4a90 100644 --- a/interface-definitions/include/interface/dhcpv6-options.xml.i +++ b/interface-definitions/include/interface/dhcpv6-options.xml.i @@ -38,7 +38,7 @@ Request IPv6 prefix length from peer - 32-64 + u32:32-64 Length of delegated prefix @@ -71,7 +71,7 @@ Interface site-Level aggregator (SLA) - 0-128 + u32:0-128 Decimal integer which fits in the length of SLA IDs diff --git a/interface-definitions/include/interface/ipv6-dup-addr-detect-transmits.xml.i b/interface-definitions/include/interface/ipv6-dup-addr-detect-transmits.xml.i index 2b5ec0281..babe6d20f 100644 --- a/interface-definitions/include/interface/ipv6-dup-addr-detect-transmits.xml.i +++ b/interface-definitions/include/interface/ipv6-dup-addr-detect-transmits.xml.i @@ -3,12 +3,12 @@ Number of NS messages to send while performing DAD (default: 1) - 1-n - Number of NS messages to send while performing DAD + u32:0 + Disable Duplicate Address Dectection (DAD) - 0 - Disable Duplicate Address Dectection (DAD) + u32:1-n + Number of NS messages to send while performing DAD diff --git a/interface-definitions/include/interface/mtu-1200-16000.xml.i b/interface-definitions/include/interface/mtu-1200-16000.xml.i index ccd986d55..fab053fc1 100644 --- a/interface-definitions/include/interface/mtu-1200-16000.xml.i +++ b/interface-definitions/include/interface/mtu-1200-16000.xml.i @@ -3,7 +3,7 @@ Maximum Transmission Unit (MTU) - 1200-16000 + u32:1200-16000 Maximum Transmission Unit in byte diff --git a/interface-definitions/include/interface/mtu-1450-16000.xml.i b/interface-definitions/include/interface/mtu-1450-16000.xml.i index 2dc3a2029..1e71eab01 100644 --- a/interface-definitions/include/interface/mtu-1450-16000.xml.i +++ b/interface-definitions/include/interface/mtu-1450-16000.xml.i @@ -3,7 +3,7 @@ Maximum Transmission Unit (MTU) - 1450-16000 + u32:1450-16000 Maximum Transmission Unit in byte diff --git a/interface-definitions/include/interface/mtu-64-8024.xml.i b/interface-definitions/include/interface/mtu-64-8024.xml.i index 9b8bc4697..30c77f768 100644 --- a/interface-definitions/include/interface/mtu-64-8024.xml.i +++ b/interface-definitions/include/interface/mtu-64-8024.xml.i @@ -3,7 +3,7 @@ Maximum Transmission Unit (MTU) - 64-8024 + u32:64-8024 Maximum Transmission Unit in byte diff --git a/interface-definitions/include/interface/mtu-68-1500.xml.i b/interface-definitions/include/interface/mtu-68-1500.xml.i index e3b70302f..693e0be7e 100644 --- a/interface-definitions/include/interface/mtu-68-1500.xml.i +++ b/interface-definitions/include/interface/mtu-68-1500.xml.i @@ -3,7 +3,7 @@ Maximum Transmission Unit (MTU) - 68-1500 + u32:68-1500 Maximum Transmission Unit in byte diff --git a/interface-definitions/include/interface/mtu-68-16000.xml.i b/interface-definitions/include/interface/mtu-68-16000.xml.i index b610ab3e2..cb666f470 100644 --- a/interface-definitions/include/interface/mtu-68-16000.xml.i +++ b/interface-definitions/include/interface/mtu-68-16000.xml.i @@ -3,7 +3,7 @@ Maximum Transmission Unit (MTU) - 68-16000 + u32:68-16000 Maximum Transmission Unit in byte diff --git a/interface-definitions/include/interface/parameters-flowlabel.xml.i b/interface-definitions/include/interface/parameters-flowlabel.xml.i index 7fa571634..bd0d1e070 100644 --- a/interface-definitions/include/interface/parameters-flowlabel.xml.i +++ b/interface-definitions/include/interface/parameters-flowlabel.xml.i @@ -2,9 +2,16 @@ Specifies the flow label to use in outgoing packets + + inherit + - 0x0-0x0FFFFF - Tunnel key, 'inherit' or hex value + inherit + Copy field from original header + + + 0x0-0x0fffff + Tunnel key, or hex value ^((0x){0,1}(0?[0-9A-Fa-f]{1,5})|inherit)$ diff --git a/interface-definitions/include/interface/parameters-tos.xml.i b/interface-definitions/include/interface/parameters-tos.xml.i index 83b4e0671..1b342a43e 100644 --- a/interface-definitions/include/interface/parameters-tos.xml.i +++ b/interface-definitions/include/interface/parameters-tos.xml.i @@ -3,7 +3,7 @@ Specifies TOS value to use in outgoing packets - 0-99 + u32:0-99 Type of Service (TOS) diff --git a/interface-definitions/include/interface/parameters-ttl.xml.i b/interface-definitions/include/interface/parameters-ttl.xml.i index da5ce69c2..ade33b4a4 100644 --- a/interface-definitions/include/interface/parameters-ttl.xml.i +++ b/interface-definitions/include/interface/parameters-ttl.xml.i @@ -3,11 +3,11 @@ Specifies TTL value to use in outgoing packets - 0 + u32:0 Inherit - copy value from original IP header - 1-255 + u32:1-255 Time to Live diff --git a/interface-definitions/include/interface/vif-s.xml.i b/interface-definitions/include/interface/vif-s.xml.i index 3fd69d9d1..e7ba6d193 100644 --- a/interface-definitions/include/interface/vif-s.xml.i +++ b/interface-definitions/include/interface/vif-s.xml.i @@ -2,6 +2,10 @@ QinQ TAG-S Virtual Local Area Network (VLAN) ID + + u32:0-4094 + QinQ Virtual Local Area Network (VLAN) ID + diff --git a/interface-definitions/include/interface/vif.xml.i b/interface-definitions/include/interface/vif.xml.i index 8daafeaf4..5644c554f 100644 --- a/interface-definitions/include/interface/vif.xml.i +++ b/interface-definitions/include/interface/vif.xml.i @@ -3,7 +3,7 @@ Virtual Local Area Network (VLAN) ID - 0-4094 + u32:0-4094 Virtual Local Area Network (VLAN) ID diff --git a/interface-definitions/include/nat-rule.xml.i b/interface-definitions/include/nat-rule.xml.i index 579d19bdd..084f1f722 100644 --- a/interface-definitions/include/nat-rule.xml.i +++ b/interface-definitions/include/nat-rule.xml.i @@ -278,7 +278,7 @@ Robust Header Compression - 0-255 + u32:0-255 IP protocol number diff --git a/interface-definitions/include/vni.xml.i b/interface-definitions/include/vni.xml.i index be45c0c97..36176caa3 100644 --- a/interface-definitions/include/vni.xml.i +++ b/interface-definitions/include/vni.xml.i @@ -3,7 +3,7 @@ Virtual Network Identifier - 0-16777214 + u32:0-16777214 VXLAN virtual network identifier diff --git a/interface-definitions/interfaces-bridge.xml.in b/interface-definitions/interfaces-bridge.xml.in index ddfc5ade4..144f43f32 100644 --- a/interface-definitions/interfaces-bridge.xml.in +++ b/interface-definitions/interfaces-bridge.xml.in @@ -21,11 +21,11 @@ MAC address aging interval - 0 + u32:0 Disable MAC address learning (always flood) - 10-1000000 + u32:10-1000000 MAC address aging time in seconds (default: 300) @@ -45,7 +45,7 @@ Forwarding delay - 0-200 + u32:0-200 Spanning Tree Protocol forwarding delay in seconds (default 15) @@ -59,7 +59,7 @@ Hello packet advertisment interval - 1-10 + u32:1-10 Spanning Tree Protocol hello advertisement interval in seconds (default 2) @@ -96,7 +96,7 @@ Interval at which neighbor bridges are removed - 1-40 + u32:1-40 Bridge maximum aging time in seconds (default 20) @@ -123,7 +123,7 @@ Specify VLAN id which should natively be present on the link - 1-4094 + u32:1-4094 Virtual Local Area Network (VLAN) ID @@ -154,7 +154,7 @@ Bridge port cost - 1-65535 + u32:1-65535 Path cost value for Spanning Tree Protocol @@ -168,7 +168,7 @@ Bridge port priority - 0-63 + u32:0-63 Bridge port priority @@ -192,7 +192,7 @@ Priority for this bridge - 0-65535 + u32:0-65535 Bridge priority (default 32768) diff --git a/interface-definitions/interfaces-l2tpv3.xml.in b/interface-definitions/interfaces-l2tpv3.xml.in index 9edc98ef6..9364c85cd 100644 --- a/interface-definitions/interfaces-l2tpv3.xml.in +++ b/interface-definitions/interfaces-l2tpv3.xml.in @@ -22,7 +22,7 @@ UDP destination port for L2TPv3 tunnel (default: 5000) - 1-65535 + u32:1-65535 Numeric IP port @@ -64,7 +64,7 @@ Peer session identifier - 1-429496729 + u32:1-429496729 L2TPv3 peer session identifier @@ -76,7 +76,7 @@ Peer tunnel identifier - 1-429496729 + u32:1-429496729 L2TPv3 peer tunnel identifier @@ -90,7 +90,7 @@ Session identifier - 1-429496729 + u32:1-429496729 L2TPv3 session identifier @@ -102,7 +102,7 @@ UDP source port for L2TPv3 tunnel (default: 5000) - 1-65535 + u32:1-65535 Numeric IP port @@ -115,7 +115,7 @@ Local tunnel identifier - 1-429496729 + u32:1-429496729 L2TPv3 local tunnel identifier diff --git a/interface-definitions/interfaces-macsec.xml.in b/interface-definitions/interfaces-macsec.xml.in index e88cb4794..4a566ef8b 100644 --- a/interface-definitions/interfaces-macsec.xml.in +++ b/interface-definitions/interfaces-macsec.xml.in @@ -82,7 +82,7 @@ Priority of MACsec Key Agreement protocol (MKA) actor (default: 255) - 0-255 + u32:0-255 MACsec Key Agreement protocol (MKA) priority @@ -97,11 +97,11 @@ IEEE 802.1X/MACsec replay protection window - 0 + u32:0 No replay window, strict check - 1-4294967295 + u32:1-4294967295 Number of packets that could be misordered diff --git a/interface-definitions/interfaces-openvpn.xml.in b/interface-definitions/interfaces-openvpn.xml.in index 01e6bf2fb..3ad367900 100644 --- a/interface-definitions/interfaces-openvpn.xml.in +++ b/interface-definitions/interfaces-openvpn.xml.in @@ -206,7 +206,7 @@ Maximum number of keepalive packet failures (default: 60) - 0-1000 + u32:0-1000 Maximum number of keepalive packet failures @@ -219,7 +219,7 @@ Keepalive packet interval in seconds (default: 10) - 0-600 + u32:0-600 Keepalive packet interval (seconds) @@ -268,7 +268,7 @@ Local port number to accept connections - 1-65535 + u32:1-65535 Numeric IP port @@ -378,7 +378,7 @@ Remote port number to connect to - 1-65535 + u32:1-65535 Numeric IP port @@ -546,7 +546,7 @@ Number of maximum client connections - 1-4096 + u32:1-4096 Number of concurrent clients @@ -591,7 +591,7 @@ Set metric for this route - 0-4294967295 + u32:0-4294967295 Metric for this route diff --git a/interface-definitions/interfaces-tunnel.xml.in b/interface-definitions/interfaces-tunnel.xml.in index c059ef624..7450ef2af 100644 --- a/interface-definitions/interfaces-tunnel.xml.in +++ b/interface-definitions/interfaces-tunnel.xml.in @@ -160,7 +160,7 @@ Unique identifier of ERSPAN engine within a system - 0-1048575 + u32:0-1048575 Unique identifier of ERSPAN engine @@ -172,7 +172,7 @@ Specifify ERSPAN version 1 index field - 0-63 + u32:0-63 Platform-depedent field for specifying port number and direction @@ -183,6 +183,9 @@ Protocol version + + 1 2 + 1 ERSPAN Type II @@ -236,7 +239,7 @@ none - 0-255 + u32:0-255 Encaplimit (default: 4) @@ -256,7 +259,7 @@ Hoplimit - 0-255 + u32:0-255 Hoplimit (default 64) @@ -270,7 +273,7 @@ Traffic class (Tclass) - 0x0-0x0FFFFF + 0x0-0x0fffff Traffic class, 'inherit' or hex value diff --git a/interface-definitions/interfaces-wireguard.xml.in b/interface-definitions/interfaces-wireguard.xml.in index ecb4cf331..403282e5c 100644 --- a/interface-definitions/interfaces-wireguard.xml.in +++ b/interface-definitions/interfaces-wireguard.xml.in @@ -107,7 +107,7 @@ Interval to send keepalive messages - 1-65535 + u32:1-65535 Interval in seconds diff --git a/interface-definitions/interfaces-wireless.xml.in b/interface-definitions/interfaces-wireless.xml.in index c96d9b78d..048c7b475 100644 --- a/interface-definitions/interfaces-wireless.xml.in +++ b/interface-definitions/interfaces-wireless.xml.in @@ -206,7 +206,7 @@ Number of antennas on this card - 1-8 + u32:1-8 Number of antennas for this card diff --git a/interface-definitions/lldp.xml.in b/interface-definitions/lldp.xml.in index e14abae14..32ef0ad14 100644 --- a/interface-definitions/lldp.xml.in +++ b/interface-definitions/lldp.xml.in @@ -105,7 +105,7 @@ ECS ELIN (Emergency location identifier number) - 0-9999999999 + u32:0-9999999999 Emergency Call Service ELIN number (between 10-25 numbers) diff --git a/interface-definitions/nat66.xml.in b/interface-definitions/nat66.xml.in index 7b1ec3706..077f0d5cf 100644 --- a/interface-definitions/nat66.xml.in +++ b/interface-definitions/nat66.xml.in @@ -15,7 +15,7 @@ Source NAT66 rule number - 1-999999 + u32:1-999999 Number for this rule @@ -113,7 +113,7 @@ Destination NAT66 rule number - 1-999999 + u32:1-999999 Number for this rule diff --git a/interface-definitions/protocols-igmp.xml.in b/interface-definitions/protocols-igmp.xml.in index a9b11e1a3..e10340512 100644 --- a/interface-definitions/protocols-igmp.xml.in +++ b/interface-definitions/protocols-igmp.xml.in @@ -46,9 +46,16 @@ IGMP version + + 2 3 + - 2-3 - IGMP version + 2 + IGMP version 2 + + + 3 + IGMP version 3 @@ -59,7 +66,7 @@ IGMP host query interval - 1-1800 + u32:1-1800 Query interval in seconds @@ -71,7 +78,7 @@ IGMP max query response time - 10-250 + u32:10-250 Query response value in deci-seconds diff --git a/interface-definitions/protocols-multicast.xml.in b/interface-definitions/protocols-multicast.xml.in index bf0ead78f..b1791c471 100644 --- a/interface-definitions/protocols-multicast.xml.in +++ b/interface-definitions/protocols-multicast.xml.in @@ -37,7 +37,7 @@ Distance value for this route - 1-255 + u32:1-255 Distance for this route @@ -73,7 +73,7 @@ Distance value for this route - 1-255 + u32:1-255 Distance for this route diff --git a/interface-definitions/protocols-pim.xml.in b/interface-definitions/protocols-pim.xml.in index 6152045a7..bb5cc797b 100644 --- a/interface-definitions/protocols-pim.xml.in +++ b/interface-definitions/protocols-pim.xml.in @@ -21,7 +21,7 @@ Designated Router Election Priority - 1-4294967295 + u32:1-4294967295 Value of the new DR Priority @@ -33,7 +33,7 @@ Hello Interval - 1-180 + u32:1-180 Hello Interval in seconds @@ -79,7 +79,7 @@ Keep alive Timer - 31-60000 + u32:31-60000 Keep alive Timer in seconds diff --git a/interface-definitions/service_pppoe-server.xml.in b/interface-definitions/service_pppoe-server.xml.in index 79042e0f3..6fb0bf9f4 100644 --- a/interface-definitions/service_pppoe-server.xml.in +++ b/interface-definitions/service_pppoe-server.xml.in @@ -240,7 +240,7 @@ PADO delays - 1-999999 + u32:1-999999 Number in ms @@ -253,7 +253,7 @@ Number of sessions - 1-999999 + u32:1-999999 Number of sessions diff --git a/interface-definitions/service_router-advert.xml.in b/interface-definitions/service_router-advert.xml.in index 750ae314c..e18b27f1b 100644 --- a/interface-definitions/service_router-advert.xml.in +++ b/interface-definitions/service_router-advert.xml.in @@ -20,12 +20,12 @@ Set Hop Count field of the IP header for outgoing packets (default: 64) - 1-255 - Value should represent current diameter of the Internet + u32:0 + Unspecified (by this router) - 0 - Unspecified (by this router) + u32:1-255 + Value should represent current diameter of the Internet @@ -38,7 +38,7 @@ Lifetime associated with the default router in units of seconds - 4-9000 + u32:4-9000 Router Lifetime in seconds @@ -86,7 +86,7 @@ Link MTU value placed in RAs, exluded in RAs if unset - 1280-9000 + u32:1280-9000 Link MTU value in RAs @@ -110,7 +110,7 @@ Maximum interval between unsolicited multicast RAs (default: 600) - 4-1800 + u32:4-1800 Maximum interval in seconds @@ -124,7 +124,7 @@ Minimum interval between unsolicited multicast RAs - 3-1350 + u32:3-1350 Minimum interval in seconds @@ -173,7 +173,7 @@ infinity - 1-4294967295 + u32:1-4294967295 Time in seconds that the route will remain valid @@ -272,7 +272,7 @@ infinity - 1-4294967295 + u32:1-4294967295 Time in seconds that the prefix will remain valid @@ -292,12 +292,12 @@ Time, in milliseconds, that a node assumes a neighbor is reachable after having received a reachability confirmation - 1-3600000 - Reachable Time value in RAs (in milliseconds) + u32:0 + Reachable Time unspecified by this router - 0 - Reachable Time unspecified by this router + u32:1-3600000 + Reachable Time value in RAs (in milliseconds) @@ -310,12 +310,12 @@ Time in milliseconds between retransmitted Neighbor Solicitation messages - 1-4294967295 - Minimum interval in milliseconds + u32:0 + Time, in milliseconds, between retransmitted Neighbor Solicitation messages - 0 - Time, in milliseconds, between retransmitted Neighbor Solicitation messages + u32:1-4294967295 + Minimum interval in milliseconds diff --git a/interface-definitions/ssh.xml.in b/interface-definitions/ssh.xml.in index c447f144d..e3b9d16e1 100644 --- a/interface-definitions/ssh.xml.in +++ b/interface-definitions/ssh.xml.in @@ -138,7 +138,7 @@ Enable transmission of keepalives from server to client - 1-65535 + u32:1-65535 Time interval in seconds for keepalive message diff --git a/interface-definitions/vpn_l2tp.xml.in b/interface-definitions/vpn_l2tp.xml.in index 907bcaadb..6d556d0bb 100644 --- a/interface-definitions/vpn_l2tp.xml.in +++ b/interface-definitions/vpn_l2tp.xml.in @@ -178,9 +178,9 @@ #include - Mark server unavailable for <n> seconds on failure + Mark server unavailable for N seconds on failure - 0-600 + u32:0-600 Fail time penalty diff --git a/interface-definitions/vrf.xml.in b/interface-definitions/vrf.xml.in index 2ed50ec5c..a82c0b2a6 100644 --- a/interface-definitions/vrf.xml.in +++ b/interface-definitions/vrf.xml.in @@ -76,7 +76,7 @@ Routing table associated with this instance - 100-65535 + u32:100-65535 Routing table ID @@ -91,7 +91,7 @@ 822 - 0-16777214 + u32:0-16777214 VXLAN virtual network identifier -- cgit v1.2.3 From c78daaf0f93937a7ecac139c45c5c81f7fcee81f Mon Sep 17 00:00:00 2001 From: zsdc Date: Sun, 29 Aug 2021 15:50:24 +0300 Subject: wireguard: T3763: Fixed uninitialized port issue The commit fixes the problem, when port availability check is triggered even if a port for WireGuard interface is not defined (randomized port, default behavior). --- src/conf_mode/interfaces-wireguard.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/conf_mode/interfaces-wireguard.py b/src/conf_mode/interfaces-wireguard.py index 68181465e..9baf5b6e9 100755 --- a/src/conf_mode/interfaces-wireguard.py +++ b/src/conf_mode/interfaces-wireguard.py @@ -74,12 +74,12 @@ def verify(wireguard): if 'peer' not in wireguard: raise ConfigError('At least one Wireguard peer is required!') - listen_port = int(wireguard['port']) - if 'port' in wireguard and check_port_availability('0.0.0.0', listen_port, - 'udp') is not True: - raise ConfigError( - f'The UDP port {listen_port} is busy or unavailable and cannot be used for the interface' - ) + if 'port' in wireguard: + listen_port = int(wireguard['port']) + if check_port_availability('0.0.0.0', listen_port, 'udp') is not True: + raise ConfigError( + f'The UDP port {listen_port} is busy or unavailable and cannot be used for the interface' + ) # run checks on individual configured WireGuard peer for tmp in wireguard['peer']: -- cgit v1.2.3 From 8d0207f87cf692458b688527022c8d841ec72904 Mon Sep 17 00:00:00 2001 From: zsdc Date: Sun, 29 Aug 2021 16:13:45 +0300 Subject: wireguard: T3763: The port availability check fix Check a port availability only if it was changed in current commit. This should protect from fail-positive errors when other parameters change for an interface. --- src/conf_mode/interfaces-wireguard.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/conf_mode/interfaces-wireguard.py b/src/conf_mode/interfaces-wireguard.py index 9baf5b6e9..da64dd076 100755 --- a/src/conf_mode/interfaces-wireguard.py +++ b/src/conf_mode/interfaces-wireguard.py @@ -47,6 +47,9 @@ def get_config(config=None): base = ['interfaces', 'wireguard'] wireguard = get_interface_dict(conf, base) + # Check if a port was changed + wireguard['port_changed'] = leaf_node_changed(conf, ['port']) + # Determine which Wireguard peer has been removed. # Peers can only be removed with their public key! dict = {} @@ -74,7 +77,7 @@ def verify(wireguard): if 'peer' not in wireguard: raise ConfigError('At least one Wireguard peer is required!') - if 'port' in wireguard: + if 'port' in wireguard and wireguard['port_changed']: listen_port = int(wireguard['port']) if check_port_availability('0.0.0.0', listen_port, 'udp') is not True: raise ConfigError( -- cgit v1.2.3 From e5796497d5585aac85590e3aa9a80ae67160505a Mon Sep 17 00:00:00 2001 From: Viacheslav Date: Sun, 29 Aug 2021 11:12:01 +0000 Subject: interfaces: T3777: Does not delete empty eui64 address Check eui64_old value before deleting It can be empty or not ipv6 address. (cherry picked from commit 0de23064b9d575ce0569839e3b4453a0c2e9dc1c) --- python/vyos/ifconfig/interface.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/python/vyos/ifconfig/interface.py b/python/vyos/ifconfig/interface.py index 45a220e22..2a7b1eadc 100755 --- a/python/vyos/ifconfig/interface.py +++ b/python/vyos/ifconfig/interface.py @@ -38,6 +38,7 @@ from vyos.util import dict_search from vyos.util import read_file from vyos.util import get_interface_config from vyos.template import is_ipv4 +from vyos.template import is_ipv6 from vyos.validate import is_intf_addr_assigned from vyos.validate import is_ipv6_link_local from vyos.validate import assert_boolean @@ -667,9 +668,10 @@ class Interface(Control): Delete the address based on the interface's MAC-based EUI64 combined with the prefix address. """ - eui64 = mac2eui64(self.get_mac(), prefix) - prefixlen = prefix.split('/')[1] - self.del_addr(f'{eui64}/{prefixlen}') + if is_ipv6(prefix): + eui64 = mac2eui64(self.get_mac(), prefix) + prefixlen = prefix.split('/')[1] + self.del_addr(f'{eui64}/{prefixlen}') def set_ipv6_forwarding(self, forwarding): """ -- cgit v1.2.3 From eac8915413cedce089234fdbef57ad25da208eec Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sun, 29 Aug 2021 22:12:32 +0200 Subject: vyos.ethtool: T3163: drop obsoleted is_fixed_lro() method Commit d22f97af ("vyos.ethtool: T3163: rename unused methods for offload validation") reworked the entire class on how data should be presented to the user, but forgot to drop the is_fixed_lro() method. --- python/vyos/ethtool.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/python/vyos/ethtool.py b/python/vyos/ethtool.py index 0ae526346..e2cd37726 100644 --- a/python/vyos/ethtool.py +++ b/python/vyos/ethtool.py @@ -63,11 +63,6 @@ class Ethtool: if value.isdigit(): self.ring_buffers[key] = int(value) - def is_fixed_lro(self): - # in case of a missing configuration, rather return "fixed". In Ethtool - # terminology "fixed" means the setting can not be changed by the user. - return self.features.get('large-receive-offload', True).get('fixed', True) - def _get_generic(self, feature): """ Generic method to read self.features and return a tuple for feature -- cgit v1.2.3 From 324aa9598c7d90efc917a00447380f985553b657 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sun, 29 Aug 2021 23:10:31 +0200 Subject: vyos.ethtool: T3163: prefix class internal data structures with _ --- python/vyos/ethtool.py | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/python/vyos/ethtool.py b/python/vyos/ethtool.py index e2cd37726..25a116d09 100644 --- a/python/vyos/ethtool.py +++ b/python/vyos/ethtool.py @@ -19,7 +19,6 @@ class Ethtool: """ Class is used to retrive and cache information about an ethernet adapter """ - # dictionary containing driver featurs, it will be populated on demand and # the content will look like: # { @@ -32,8 +31,8 @@ class Ethtool: # 'tx-checksumming': {'fixed': False, 'enabled': True}, # 'tx-esp-segmentation': {'fixed': True, 'enabled': False}, # } - features = { } - ring_buffers = { } + _features = { } + _ring_buffers = { } def __init__(self, ifname): # Now populate features dictionaty @@ -45,7 +44,7 @@ class Ethtool: fixed = bool('fixed' in value) if fixed: value = value.split()[0].strip() - self.features[key.strip()] = { + self._features[key.strip()] = { 'enabled' : bool(value == 'on'), 'fixed' : fixed } @@ -61,22 +60,22 @@ class Ethtool: # output format from 0 -> n/a. As we are only interested in the # tx/rx keys we do not care about RX Mini/Jumbo. if value.isdigit(): - self.ring_buffers[key] = int(value) + self._ring_buffers[key] = int(value) def _get_generic(self, feature): """ - Generic method to read self.features and return a tuple for feature + Generic method to read self._features and return a tuple for feature enabled and feature is fixed. In case of a missing key, return "fixed = True and enabled = False" """ fixed = True enabled = False - if feature in self.features: - if 'enabled' in self.features[feature]: - enabled = self.features[feature]['enabled'] - if 'fixed' in self.features[feature]: - fixed = self.features[feature]['fixed'] + if feature in self._features: + if 'enabled' in self._features[feature]: + enabled = self._features[feature]['enabled'] + if 'fixed' in self._features[feature]: + fixed = self._features[feature]['fixed'] return enabled, fixed def get_generic_receive_offload(self): @@ -103,9 +102,9 @@ class Ethtool: def get_rx_buffer(self): # Configuration of RX ring-buffers is not supported on every device, # thus when it's impossible return None - return self.ring_buffers.get('rx', None) + return self._ring_buffers.get('rx', None) def get_tx_buffer(self): # Configuration of TX ring-buffers is not supported on every device, # thus when it's impossible return None - return self.ring_buffers.get('tx', None) + return self._ring_buffers.get('tx', None) -- cgit v1.2.3 From 147f655a69cd9526cd23f51ab18027cb5abc95b2 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sun, 29 Aug 2021 23:18:06 +0200 Subject: vyos.ethtool: T3163: add check_speed_duplex() method Add a new method which supports checking if the desired speed and duplex setting is actually supported by the underlaying network interface card. >>> from vyos.ethtool import Ethtool >>> tmp = Ethtool('eth0') >>> tmp.check_speed_duplex('100', 'full') False >>> tmp.check_speed_duplex('1000', 'full') True --- python/vyos/ethtool.py | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/python/vyos/ethtool.py b/python/vyos/ethtool.py index 25a116d09..8add1a327 100644 --- a/python/vyos/ethtool.py +++ b/python/vyos/ethtool.py @@ -13,6 +13,7 @@ # You should have received a copy of the GNU Lesser General Public # License along with this library. If not, see . +import re from vyos.util import popen class Ethtool: @@ -32,9 +33,37 @@ class Ethtool: # 'tx-esp-segmentation': {'fixed': True, 'enabled': False}, # } _features = { } + # dictionary containing available interface speed and duplex settings + # { + # '10' : {'full': '', 'half': ''}, + # '100' : {'full': '', 'half': ''}, + # '1000': {'full': ''} + # } + _speed_duplex = { } _ring_buffers = { } def __init__(self, ifname): + # Build a dictinary of supported link-speed and dupley settings. + out, err = popen(f'ethtool {ifname}') + reading = False + pattern = re.compile(r'\d+base.*') + for line in out.splitlines()[1:]: + line = line.lstrip() + if 'Supported link modes:' in line: + reading = True + if 'Supported pause frame use:' in line: + reading = False + break + if reading: + for block in line.split(): + if pattern.search(block): + speed = block.split('base')[0] + duplex = block.split('/')[-1].lower() + if speed not in self._speed_duplex: + self._speed_duplex.update({ speed : {}}) + if duplex not in self._speed_duplex[speed]: + self._speed_duplex[speed].update({ duplex : ''}) + # Now populate features dictionaty out, err = popen(f'ethtool -k {ifname}') # skip the first line, it only says: "Features for eth0": @@ -108,3 +137,19 @@ class Ethtool: # Configuration of TX ring-buffers is not supported on every device, # thus when it's impossible return None return self._ring_buffers.get('tx', None) + + def check_speed_duplex(self, speed, duplex): + """ Check if the passed speed and duplex combination is supported by + the underlaying network adapter. """ + if isinstance(speed, int): + speed = str(speed) + if not speed.isdigit(): + raise ValueError(f'Value "{speed}" for speed is invalid!') + if duplex not in ['full', 'half']: + raise ValueError(f'Value "{duplex}" for duplex is invalid!') + + if speed in self._speed_duplex: + if duplex in self._speed_duplex[speed]: + return True + return False + -- cgit v1.2.3 From 0aed0434cb118d3de068d3e3ab4dfb23abbf26a3 Mon Sep 17 00:00:00 2001 From: Viacheslav Date: Mon, 30 Aug 2021 06:56:12 +0000 Subject: interface: T3782: Fix unexpected delete qdisc rule Some tc qdisc rules are generated by old perl code It prevent to unexpected override this code by python. --- python/vyos/ifconfig/interface.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/python/vyos/ifconfig/interface.py b/python/vyos/ifconfig/interface.py index 2a7b1eadc..04e139805 100755 --- a/python/vyos/ifconfig/interface.py +++ b/python/vyos/ifconfig/interface.py @@ -1156,12 +1156,14 @@ class Interface(Control): source_if = next(iter(self._config['is_mirror_intf'])) config = self._config['is_mirror_intf'][source_if].get('mirror', None) - # Please do not clear the 'set $? = 0 '. It's meant to force a return of 0 - # Remove existing mirroring rules - delete_tc_cmd = f'tc qdisc del dev {source_if} handle ffff: ingress 2> /dev/null;' - delete_tc_cmd += f'tc qdisc del dev {source_if} handle 1: root prio 2> /dev/null;' - delete_tc_cmd += 'set $?=0' - self._popen(delete_tc_cmd) + # Check configuration stored by old perl code before delete T3782 + if not 'redirect' in self._config: + # Please do not clear the 'set $? = 0 '. It's meant to force a return of 0 + # Remove existing mirroring rules + delete_tc_cmd = f'tc qdisc del dev {source_if} handle ffff: ingress 2> /dev/null;' + delete_tc_cmd += f'tc qdisc del dev {source_if} handle 1: root prio 2> /dev/null;' + delete_tc_cmd += 'set $?=0' + self._popen(delete_tc_cmd) # Bail out early if nothing needs to be configured if not config: -- cgit v1.2.3 From 80ee5233aa8245ded09d04f2618a580d5dcc6b46 Mon Sep 17 00:00:00 2001 From: John Estabrook Date: Mon, 30 Aug 2021 08:29:28 -0500 Subject: config: T2941: ignore unicode characters, e.g., in description field --- python/vyos/configsource.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/vyos/configsource.py b/python/vyos/configsource.py index 50222e385..b0981d25e 100644 --- a/python/vyos/configsource.py +++ b/python/vyos/configsource.py @@ -161,7 +161,7 @@ class ConfigSourceSession(ConfigSource): if p.returncode != 0: raise VyOSError() else: - return out.decode('ascii') + return out.decode('ascii', 'ignore') def set_level(self, path): """ -- cgit v1.2.3 From 5c29377fa91595088118419275f6d05b1fbfbd1d Mon Sep 17 00:00:00 2001 From: Viacheslav Date: Mon, 30 Aug 2021 15:43:02 +0000 Subject: tunnel: T3786: Add checks for source any and not key --- src/conf_mode/interfaces-tunnel.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/conf_mode/interfaces-tunnel.py b/src/conf_mode/interfaces-tunnel.py index 294da8ef9..616a2e23c 100755 --- a/src/conf_mode/interfaces-tunnel.py +++ b/src/conf_mode/interfaces-tunnel.py @@ -94,6 +94,12 @@ def verify(tunnel): if 'direction' not in tunnel['parameters']['erspan']: raise ConfigError('ERSPAN version 2 requires direction to be set!') + # If tunnel source address any and key not set + if tunnel['encapsulation'] in ['gre'] and \ + tunnel['source_address'] == '0.0.0.0' and \ + dict_search('parameters.ip.key', tunnel) == None: + raise ConfigError('Tunnel parameters ip key must be set!') + verify_mtu_ipv6(tunnel) verify_address(tunnel) verify_vrf(tunnel) -- cgit v1.2.3 From 50364a4b7a9de85fe59a6a4fb611bafb64c9f7f0 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Mon, 30 Aug 2021 21:25:20 +0200 Subject: vyos.ethtool: T3163: remove test and debug method get_rx_vlan_offload() --- python/vyos/ethtool.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/python/vyos/ethtool.py b/python/vyos/ethtool.py index 8add1a327..98985e972 100644 --- a/python/vyos/ethtool.py +++ b/python/vyos/ethtool.py @@ -125,9 +125,6 @@ class Ethtool: def get_udp_fragmentation_offload(self): return self._get_generic('udp-fragmentation-offload') - def get_rx_vlan_offload(self): - return self._get_generic('rx-vlan-offload') - def get_rx_buffer(self): # Configuration of RX ring-buffers is not supported on every device, # thus when it's impossible return None -- cgit v1.2.3 From ce784a9fcb7199f87949f17777b7b736227c85b3 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Mon, 30 Aug 2021 21:25:38 +0200 Subject: vyos.ifconfig: T3619: only inform user about real offload change for invalid option Commit 31169fa8 ("vyos.ifconfig: T3619: only set offloading options if supported by NIC") added a warning for the user if an offload option was about to change that was not possible at all (harware limit). Unfortunately the warning was even displayed if nothing was done at all. This got corrected. --- python/vyos/ifconfig/ethernet.py | 57 ++++++++++++++++++---------------------- 1 file changed, 25 insertions(+), 32 deletions(-) diff --git a/python/vyos/ifconfig/ethernet.py b/python/vyos/ifconfig/ethernet.py index fbff789eb..696fec03b 100644 --- a/python/vyos/ifconfig/ethernet.py +++ b/python/vyos/ifconfig/ethernet.py @@ -236,12 +236,11 @@ class EthernetIf(Interface): raise ValueError('Value out of range') enabled, fixed = self.ethtool.get_generic_receive_offload() - if not fixed: - enabled = 'on' if enabled else 'off' - if enabled != state: + if enabled != state: + if not fixed: return self.set_interface('gro', 'on' if state else 'off') - - print('Adapter does not support changing generic-receive-offload settings!') + else: + print('Adapter does not support changing generic-receive-offload settings!') return False def set_gso(self, state): @@ -256,12 +255,11 @@ class EthernetIf(Interface): raise ValueError('Value out of range') enabled, fixed = self.ethtool.get_generic_segmentation_offload() - if not fixed: - enabled = 'on' if enabled else 'off' - if enabled != state: - return self.set_interface('gro', 'on' if state else 'off') - - print('Adapter does not support changing generic-segmentation-offload settings!') + if enabled != state: + if not fixed: + return self.set_interface('gso', 'on' if state else 'off') + else: + print('Adapter does not support changing generic-segmentation-offload settings!') return False def set_lro(self, state): @@ -276,12 +274,11 @@ class EthernetIf(Interface): raise ValueError('Value out of range') enabled, fixed = self.ethtool.get_large_receive_offload() - if not fixed: - enabled = 'on' if enabled else 'off' - if enabled != state: + if enabled != state: + if not fixed: return self.set_interface('gro', 'on' if state else 'off') - - print('Adapter does not support changing large-receive-offload settings!') + else: + print('Adapter does not support changing large-receive-offload settings!') return False def set_rps(self, state): @@ -314,12 +311,11 @@ class EthernetIf(Interface): raise ValueError('Value out of range') enabled, fixed = self.ethtool.get_scatter_gather() - if not fixed: - enabled = 'on' if enabled else 'off' - if enabled != state: + if enabled != state: + if not fixed: return self.set_interface('gro', 'on' if state else 'off') - - print('Adapter does not support changing scatter-gather settings!') + else: + print('Adapter does not support changing scatter-gather settings!') return False def set_tso(self, state): @@ -335,12 +331,11 @@ class EthernetIf(Interface): raise ValueError('Value out of range') enabled, fixed = self.ethtool.get_tcp_segmentation_offload() - if not fixed: - enabled = 'on' if enabled else 'off' - if enabled != state: + if enabled != state: + if not fixed: return self.set_interface('gro', 'on' if state else 'off') - - print('Adapter does not support changing tcp-segmentation-offload settings!') + else: + print('Adapter does not support changing tcp-segmentation-offload settings!') return False def set_ufo(self, state): @@ -356,12 +351,11 @@ class EthernetIf(Interface): raise ValueError('Value out of range') enabled, fixed = self.ethtool.get_udp_fragmentation_offload() - if not fixed: - enabled = 'on' if enabled else 'off' - if enabled != state: + if enabled != state: + if not fixed: return self.set_interface('gro', 'on' if state else 'off') - - print('Adapter does not support changing udp-fragmentation-offload settings!') + else: + print('Adapter does not support changing udp-fragmentation-offload settings!') return False def set_ring_buffer(self, b_type, b_size): @@ -381,7 +375,6 @@ class EthernetIf(Interface): print(f'could not set "{b_type}" ring-buffer for {ifname}') return output - def update(self, config): """ General helper function which works on a dictionary retrived by get_config_dict(). It's main intention is to consolidate the scattered -- cgit v1.2.3 From f731a710813d354d570494227a2c2eaa7c9caa88 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Mon, 30 Aug 2021 21:43:06 +0200 Subject: ethernet: T3619: rename interfaces migration scripts VyOS 1.4 already had a migrator for interfaces 20 -> 21, but this is a different one compared to the one in VyOS 1.3 - thus we bump every migration scripts version by one to have the same 20-to-21 converter in both VyOS 1.3 and 1.4. This is possible as VyOS 1.4 (sagitta) is still a highly experimental version and expected to break from time to time :(. --- src/migration-scripts/interfaces/20-to-21 | 145 ------------ src/migration-scripts/interfaces/21-to-22 | 143 ++++++++--- src/migration-scripts/interfaces/22-to-23 | 379 +++--------------------------- src/migration-scripts/interfaces/23-to-24 | 369 +++++++++++++++++++++++++++++ 4 files changed, 518 insertions(+), 518 deletions(-) delete mode 100755 src/migration-scripts/interfaces/20-to-21 create mode 100755 src/migration-scripts/interfaces/23-to-24 diff --git a/src/migration-scripts/interfaces/20-to-21 b/src/migration-scripts/interfaces/20-to-21 deleted file mode 100755 index 06e07572f..000000000 --- a/src/migration-scripts/interfaces/20-to-21 +++ /dev/null @@ -1,145 +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 sys import argv -from sys import exit -from vyos.configtree import ConfigTree - -def migrate_ospf(config, path, interface): - path = path + ['ospf'] - if config.exists(path): - new_base = ['protocols', 'ospf', 'interface'] - config.set(new_base) - config.set_tag(new_base) - config.copy(path, new_base + [interface]) - config.delete(path) - - # if "ip ospf" was the only setting, we can clean out the empty - # ip node afterwards - if len(config.list_nodes(path[:-1])) == 0: - config.delete(path[:-1]) - -def migrate_ospfv3(config, path, interface): - path = path + ['ospfv3'] - if config.exists(path): - new_base = ['protocols', 'ospfv3', 'interface'] - config.set(new_base) - config.set_tag(new_base) - config.copy(path, new_base + [interface]) - config.delete(path) - - # if "ipv6 ospfv3" was the only setting, we can clean out the empty - # ip node afterwards - if len(config.list_nodes(path[:-1])) == 0: - config.delete(path[:-1]) - -def migrate_rip(config, path, interface): - path = path + ['rip'] - if config.exists(path): - new_base = ['protocols', 'rip', 'interface'] - config.set(new_base) - config.set_tag(new_base) - config.copy(path, new_base + [interface]) - config.delete(path) - - # if "ip rip" was the only setting, we can clean out the empty - # ip node afterwards - if len(config.list_nodes(path[:-1])) == 0: - config.delete(path[:-1]) - -def migrate_ripng(config, path, interface): - path = path + ['ripng'] - if config.exists(path): - new_base = ['protocols', 'ripng', 'interface'] - config.set(new_base) - config.set_tag(new_base) - config.copy(path, new_base + [interface]) - config.delete(path) - - # if "ipv6 ripng" was the only setting, we can clean out the empty - # ip node afterwards - if len(config.list_nodes(path[:-1])) == 0: - config.delete(path[:-1]) - -if __name__ == '__main__': - if (len(argv) < 1): - print("Must specify file name!") - exit(1) - - file_name = argv[1] - with open(file_name, 'r') as f: - config_file = f.read() - - config = ConfigTree(config_file) - - # - # Migrate "interface ethernet eth0 ip ospf" to "protocols ospf interface eth0" - # - for type in config.list_nodes(['interfaces']): - for interface in config.list_nodes(['interfaces', type]): - ip_base = ['interfaces', type, interface, 'ip'] - ipv6_base = ['interfaces', type, interface, 'ipv6'] - migrate_rip(config, ip_base, interface) - migrate_ripng(config, ipv6_base, interface) - migrate_ospf(config, ip_base, interface) - migrate_ospfv3(config, ipv6_base, interface) - - vif_path = ['interfaces', type, interface, 'vif'] - if config.exists(vif_path): - for vif in config.list_nodes(vif_path): - vif_ip_base = vif_path + [vif, 'ip'] - vif_ipv6_base = vif_path + [vif, 'ipv6'] - ifname = f'{interface}.{vif}' - - migrate_rip(config, vif_ip_base, ifname) - migrate_ripng(config, vif_ipv6_base, ifname) - migrate_ospf(config, vif_ip_base, ifname) - migrate_ospfv3(config, vif_ipv6_base, ifname) - - - vif_s_path = ['interfaces', type, interface, 'vif-s'] - if config.exists(vif_s_path): - for vif_s in config.list_nodes(vif_s_path): - vif_s_ip_base = vif_s_path + [vif_s, 'ip'] - vif_s_ipv6_base = vif_s_path + [vif_s, 'ipv6'] - - # vif-c interfaces MUST be migrated before their parent vif-s - # interface as the migrate_*() functions delete the path! - vif_c_path = ['interfaces', type, interface, 'vif-s', vif_s, 'vif-c'] - if config.exists(vif_c_path): - for vif_c in config.list_nodes(vif_c_path): - vif_c_ip_base = vif_c_path + [vif_c, 'ip'] - vif_c_ipv6_base = vif_c_path + [vif_c, 'ipv6'] - ifname = f'{interface}.{vif_s}.{vif_c}' - - migrate_rip(config, vif_c_ip_base, ifname) - migrate_ripng(config, vif_c_ipv6_base, ifname) - migrate_ospf(config, vif_c_ip_base, ifname) - migrate_ospfv3(config, vif_c_ipv6_base, ifname) - - - ifname = f'{interface}.{vif_s}' - migrate_rip(config, vif_s_ip_base, ifname) - migrate_ripng(config, vif_s_ipv6_base, ifname) - migrate_ospf(config, vif_s_ip_base, ifname) - migrate_ospfv3(config, vif_s_ipv6_base, ifname) - - try: - with open(file_name, 'w') as f: - f.write(config.to_string()) - except OSError as e: - print("Failed to save the modified config: {}".format(e)) - exit(1) diff --git a/src/migration-scripts/interfaces/21-to-22 b/src/migration-scripts/interfaces/21-to-22 index d1ec2ad3e..06e07572f 100755 --- a/src/migration-scripts/interfaces/21-to-22 +++ b/src/migration-scripts/interfaces/21-to-22 @@ -14,47 +14,132 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -# A VTI interface also requires an IPSec configuration - VyOS 1.2 supported -# having a VTI interface in the CLI but no IPSec configuration - drop VTI -# configuration if this is the case for VyOS 1.4 - -import sys +from sys import argv +from sys import exit from vyos.configtree import ConfigTree +def migrate_ospf(config, path, interface): + path = path + ['ospf'] + if config.exists(path): + new_base = ['protocols', 'ospf', 'interface'] + config.set(new_base) + config.set_tag(new_base) + config.copy(path, new_base + [interface]) + config.delete(path) + + # if "ip ospf" was the only setting, we can clean out the empty + # ip node afterwards + if len(config.list_nodes(path[:-1])) == 0: + config.delete(path[:-1]) + +def migrate_ospfv3(config, path, interface): + path = path + ['ospfv3'] + if config.exists(path): + new_base = ['protocols', 'ospfv3', 'interface'] + config.set(new_base) + config.set_tag(new_base) + config.copy(path, new_base + [interface]) + config.delete(path) + + # if "ipv6 ospfv3" was the only setting, we can clean out the empty + # ip node afterwards + if len(config.list_nodes(path[:-1])) == 0: + config.delete(path[:-1]) + +def migrate_rip(config, path, interface): + path = path + ['rip'] + if config.exists(path): + new_base = ['protocols', 'rip', 'interface'] + config.set(new_base) + config.set_tag(new_base) + config.copy(path, new_base + [interface]) + config.delete(path) + + # if "ip rip" was the only setting, we can clean out the empty + # ip node afterwards + if len(config.list_nodes(path[:-1])) == 0: + config.delete(path[:-1]) + +def migrate_ripng(config, path, interface): + path = path + ['ripng'] + if config.exists(path): + new_base = ['protocols', 'ripng', 'interface'] + config.set(new_base) + config.set_tag(new_base) + config.copy(path, new_base + [interface]) + config.delete(path) + + # if "ipv6 ripng" was the only setting, we can clean out the empty + # ip node afterwards + if len(config.list_nodes(path[:-1])) == 0: + config.delete(path[:-1]) + if __name__ == '__main__': - if (len(sys.argv) < 1): + if (len(argv) < 1): print("Must specify file name!") - sys.exit(1) - - file_name = sys.argv[1] + exit(1) + file_name = argv[1] with open(file_name, 'r') as f: config_file = f.read() config = ConfigTree(config_file) - base = ['interfaces', 'vti'] - if not config.exists(base): - # Nothing to do - sys.exit(0) - - ipsec_base = ['vpn', 'ipsec', 'site-to-site', 'peer'] - for interface in config.list_nodes(base): - found = False - if config.exists(ipsec_base): - for peer in config.list_nodes(ipsec_base): - if config.exists(ipsec_base + [peer, 'vti', 'bind']): - tmp = config.return_value(ipsec_base + [peer, 'vti', 'bind']) - if tmp == interface: - # Interface was found and we no longer need to search - # for it in our IPSec peers - found = True - break - if not found: - config.delete(base + [interface]) + + # + # Migrate "interface ethernet eth0 ip ospf" to "protocols ospf interface eth0" + # + for type in config.list_nodes(['interfaces']): + for interface in config.list_nodes(['interfaces', type]): + ip_base = ['interfaces', type, interface, 'ip'] + ipv6_base = ['interfaces', type, interface, 'ipv6'] + migrate_rip(config, ip_base, interface) + migrate_ripng(config, ipv6_base, interface) + migrate_ospf(config, ip_base, interface) + migrate_ospfv3(config, ipv6_base, interface) + + vif_path = ['interfaces', type, interface, 'vif'] + if config.exists(vif_path): + for vif in config.list_nodes(vif_path): + vif_ip_base = vif_path + [vif, 'ip'] + vif_ipv6_base = vif_path + [vif, 'ipv6'] + ifname = f'{interface}.{vif}' + + migrate_rip(config, vif_ip_base, ifname) + migrate_ripng(config, vif_ipv6_base, ifname) + migrate_ospf(config, vif_ip_base, ifname) + migrate_ospfv3(config, vif_ipv6_base, ifname) + + + vif_s_path = ['interfaces', type, interface, 'vif-s'] + if config.exists(vif_s_path): + for vif_s in config.list_nodes(vif_s_path): + vif_s_ip_base = vif_s_path + [vif_s, 'ip'] + vif_s_ipv6_base = vif_s_path + [vif_s, 'ipv6'] + + # vif-c interfaces MUST be migrated before their parent vif-s + # interface as the migrate_*() functions delete the path! + vif_c_path = ['interfaces', type, interface, 'vif-s', vif_s, 'vif-c'] + if config.exists(vif_c_path): + for vif_c in config.list_nodes(vif_c_path): + vif_c_ip_base = vif_c_path + [vif_c, 'ip'] + vif_c_ipv6_base = vif_c_path + [vif_c, 'ipv6'] + ifname = f'{interface}.{vif_s}.{vif_c}' + + migrate_rip(config, vif_c_ip_base, ifname) + migrate_ripng(config, vif_c_ipv6_base, ifname) + migrate_ospf(config, vif_c_ip_base, ifname) + migrate_ospfv3(config, vif_c_ipv6_base, ifname) + + + ifname = f'{interface}.{vif_s}' + migrate_rip(config, vif_s_ip_base, ifname) + migrate_ripng(config, vif_s_ipv6_base, ifname) + migrate_ospf(config, vif_s_ip_base, ifname) + migrate_ospfv3(config, vif_s_ipv6_base, ifname) try: with open(file_name, 'w') as f: f.write(config.to_string()) except OSError as e: print("Failed to save the modified config: {}".format(e)) - sys.exit(1) + exit(1) diff --git a/src/migration-scripts/interfaces/22-to-23 b/src/migration-scripts/interfaces/22-to-23 index 93ce9215f..d1ec2ad3e 100755 --- a/src/migration-scripts/interfaces/22-to-23 +++ b/src/migration-scripts/interfaces/22-to-23 @@ -14,356 +14,47 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -# Migrate Wireguard to store keys in CLI -# Migrate EAPoL to PKI configuration +# A VTI interface also requires an IPSec configuration - VyOS 1.2 supported +# having a VTI interface in the CLI but no IPSec configuration - drop VTI +# configuration if this is the case for VyOS 1.4 -import os import sys from vyos.configtree import ConfigTree -from vyos.pki import load_certificate -from vyos.pki import load_crl -from vyos.pki import load_dh_parameters -from vyos.pki import load_private_key -from vyos.pki import encode_certificate -from vyos.pki import encode_dh_parameters -from vyos.pki import encode_private_key -from vyos.util import run -def wrapped_pem_to_config_value(pem): - out = [] - for line in pem.strip().split("\n"): - if not line or line.startswith("-----") or line[0] == '#': - continue - out.append(line) - return "".join(out) +if __name__ == '__main__': + if (len(sys.argv) < 1): + print("Must specify file name!") + sys.exit(1) -def read_file_for_pki(config_auth_path): - full_path = os.path.join(AUTH_DIR, config_auth_path) - output = None + file_name = sys.argv[1] - if os.path.isfile(full_path): - if not os.access(full_path, os.R_OK): - run(f'sudo chmod 644 {full_path}') + with open(file_name, 'r') as f: + config_file = f.read() - with open(full_path, 'r') as f: - output = f.read() + config = ConfigTree(config_file) + base = ['interfaces', 'vti'] + if not config.exists(base): + # Nothing to do + sys.exit(0) - return output - -if (len(sys.argv) < 1): - print("Must specify file name!") - sys.exit(1) - -file_name = sys.argv[1] - -with open(file_name, 'r') as f: - config_file = f.read() - -config = ConfigTree(config_file) - -AUTH_DIR = '/config/auth' -pki_base = ['pki'] - -# OpenVPN -base = ['interfaces', 'openvpn'] - -if config.exists(base): - for interface in config.list_nodes(base): - x509_base = base + [interface, 'tls'] - pki_name = f'openvpn_{interface}' - - if config.exists(base + [interface, 'shared-secret-key-file']): - if not config.exists(pki_base + ['openvpn', 'shared-secret']): - config.set(pki_base + ['openvpn', 'shared-secret']) - config.set_tag(pki_base + ['openvpn', 'shared-secret']) - - key_file = config.return_value(base + [interface, 'shared-secret-key-file']) - key = read_file_for_pki(key_file) - key_pki_name = f'{pki_name}_shared' - - if key: - config.set(pki_base + ['openvpn', 'shared-secret', key_pki_name, 'key'], value=wrapped_pem_to_config_value(key)) - config.set(pki_base + ['openvpn', 'shared-secret', key_pki_name, 'version'], value='1') - config.set(base + [interface, 'shared-secret-key'], value=key_pki_name) - else: - print(f'Failed to migrate shared-secret-key on openvpn interface {interface}') - - config.delete(base + [interface, 'shared-secret-key-file']) - - if not config.exists(base + [interface, 'tls']): - continue - - if config.exists(base + [interface, 'tls', 'auth-file']): - if not config.exists(pki_base + ['openvpn', 'shared-secret']): - config.set(pki_base + ['openvpn', 'shared-secret']) - config.set_tag(pki_base + ['openvpn', 'shared-secret']) - - key_file = config.return_value(base + [interface, 'tls', 'auth-file']) - key = read_file_for_pki(key_file) - key_pki_name = f'{pki_name}_auth' - - if key: - config.set(pki_base + ['openvpn', 'shared-secret', key_pki_name, 'key'], value=wrapped_pem_to_config_value(key)) - config.set(pki_base + ['openvpn', 'shared-secret', key_pki_name, 'version'], value='1') - config.set(base + [interface, 'tls', 'auth-key'], value=key_pki_name) - else: - print(f'Failed to migrate auth-key on openvpn interface {interface}') - - config.delete(base + [interface, 'tls', 'auth-file']) - - if config.exists(base + [interface, 'tls', 'crypt-file']): - if not config.exists(pki_base + ['openvpn', 'shared-secret']): - config.set(pki_base + ['openvpn', 'shared-secret']) - config.set_tag(pki_base + ['openvpn', 'shared-secret']) - - key_file = config.return_value(base + [interface, 'tls', 'crypt-file']) - key = read_file_for_pki(key_file) - key_pki_name = f'{pki_name}_crypt' - - if key: - config.set(pki_base + ['openvpn', 'shared-secret', key_pki_name, 'key'], value=wrapped_pem_to_config_value(key)) - config.set(pki_base + ['openvpn', 'shared-secret', key_pki_name, 'version'], value='1') - config.set(base + [interface, 'tls', 'crypt-key'], value=key_pki_name) - else: - print(f'Failed to migrate crypt-key on openvpn interface {interface}') - - config.delete(base + [interface, 'tls', 'crypt-file']) - - if config.exists(x509_base + ['ca-cert-file']): - if not config.exists(pki_base + ['ca']): - config.set(pki_base + ['ca']) - config.set_tag(pki_base + ['ca']) - - cert_file = config.return_value(x509_base + ['ca-cert-file']) - cert_path = os.path.join(AUTH_DIR, cert_file) - cert = None - - if os.path.isfile(cert_path): - if not os.access(cert_path, os.R_OK): - run(f'sudo chmod 644 {cert_path}') - - with open(cert_path, 'r') as f: - cert_data = f.read() - cert = load_certificate(cert_data, wrap_tags=False) - - if cert: - cert_pem = encode_certificate(cert) - config.set(pki_base + ['ca', pki_name, 'certificate'], value=wrapped_pem_to_config_value(cert_pem)) - config.set(x509_base + ['ca-certificate'], value=pki_name) - else: - print(f'Failed to migrate CA certificate on openvpn interface {interface}') - - config.delete(x509_base + ['ca-cert-file']) - - if config.exists(x509_base + ['crl-file']): - if not config.exists(pki_base + ['ca']): - config.set(pki_base + ['ca']) - config.set_tag(pki_base + ['ca']) - - crl_file = config.return_value(x509_base + ['crl-file']) - crl_path = os.path.join(AUTH_DIR, crl_file) - crl = None - - if os.path.isfile(crl_path): - if not os.access(crl_path, os.R_OK): - run(f'sudo chmod 644 {crl_path}') - - with open(crl_path, 'r') as f: - crl_data = f.read() - crl = load_crl(crl_data, wrap_tags=False) - - if crl: - crl_pem = encode_certificate(crl) - config.set(pki_base + ['ca', pki_name, 'crl'], value=wrapped_pem_to_config_value(crl_pem)) - else: - print(f'Failed to migrate CRL on openvpn interface {interface}') - - config.delete(x509_base + ['crl-file']) - - if config.exists(x509_base + ['cert-file']): - if not config.exists(pki_base + ['certificate']): - config.set(pki_base + ['certificate']) - config.set_tag(pki_base + ['certificate']) - - cert_file = config.return_value(x509_base + ['cert-file']) - cert_path = os.path.join(AUTH_DIR, cert_file) - cert = None - - if os.path.isfile(cert_path): - if not os.access(cert_path, os.R_OK): - run(f'sudo chmod 644 {cert_path}') - - with open(cert_path, 'r') as f: - cert_data = f.read() - cert = load_certificate(cert_data, wrap_tags=False) - - if cert: - cert_pem = encode_certificate(cert) - config.set(pki_base + ['certificate', pki_name, 'certificate'], value=wrapped_pem_to_config_value(cert_pem)) - config.set(x509_base + ['certificate'], value=pki_name) - else: - print(f'Failed to migrate certificate on openvpn interface {interface}') - - config.delete(x509_base + ['cert-file']) - - if config.exists(x509_base + ['key-file']): - key_file = config.return_value(x509_base + ['key-file']) - key_path = os.path.join(AUTH_DIR, key_file) - key = None - - if os.path.isfile(key_path): - if not os.access(key_path, os.R_OK): - run(f'sudo chmod 644 {key_path}') - - with open(key_path, 'r') as f: - key_data = f.read() - key = load_private_key(key_data, passphrase=None, wrap_tags=False) - - if key: - key_pem = encode_private_key(key, passphrase=None) - config.set(pki_base + ['certificate', pki_name, 'private', 'key'], value=wrapped_pem_to_config_value(key_pem)) - else: - print(f'Failed to migrate private key on openvpn interface {interface}') - - config.delete(x509_base + ['key-file']) - - if config.exists(x509_base + ['dh-file']): - if not config.exists(pki_base + ['dh']): - config.set(pki_base + ['dh']) - config.set_tag(pki_base + ['dh']) - - dh_file = config.return_value(x509_base + ['dh-file']) - dh_path = os.path.join(AUTH_DIR, dh_file) - dh = None - - if os.path.isfile(dh_path): - if not os.access(dh_path, os.R_OK): - run(f'sudo chmod 644 {dh_path}') - - with open(dh_path, 'r') as f: - dh_data = f.read() - dh = load_dh_parameters(dh_data, wrap_tags=False) - - if dh: - dh_pem = encode_dh_parameters(dh) - config.set(pki_base + ['dh', pki_name, 'parameters'], value=wrapped_pem_to_config_value(dh_pem)) - config.set(x509_base + ['dh-params'], value=pki_name) - else: - print(f'Failed to migrate DH parameters on openvpn interface {interface}') - - config.delete(x509_base + ['dh-file']) - -# Wireguard -base = ['interfaces', 'wireguard'] - -if config.exists(base): - for interface in config.list_nodes(base): - private_key_path = base + [interface, 'private-key'] - - key_file = 'default' - if config.exists(private_key_path): - key_file = config.return_value(private_key_path) - - full_key_path = f'/config/auth/wireguard/{key_file}/private.key' - - if not os.path.exists(full_key_path): - print(f'Could not find wireguard private key for migration on interface "{interface}"') - continue - - with open(full_key_path, 'r') as f: - key_data = f.read().strip() - config.set(private_key_path, value=key_data) - - for peer in config.list_nodes(base + [interface, 'peer']): - config.rename(base + [interface, 'peer', peer, 'pubkey'], 'public-key') - -# Ethernet EAPoL -base = ['interfaces', 'ethernet'] - -if config.exists(base): + ipsec_base = ['vpn', 'ipsec', 'site-to-site', 'peer'] for interface in config.list_nodes(base): - if not config.exists(base + [interface, 'eapol']): - continue - - x509_base = base + [interface, 'eapol'] - pki_name = f'eapol_{interface}' - - if config.exists(x509_base + ['ca-cert-file']): - if not config.exists(pki_base + ['ca']): - config.set(pki_base + ['ca']) - config.set_tag(pki_base + ['ca']) - - cert_file = config.return_value(x509_base + ['ca-cert-file']) - cert_path = os.path.join(AUTH_DIR, cert_file) - cert = None - - if os.path.isfile(cert_path): - if not os.access(cert_path, os.R_OK): - run(f'sudo chmod 644 {cert_path}') - - with open(cert_path, 'r') as f: - cert_data = f.read() - cert = load_certificate(cert_data, wrap_tags=False) - - if cert: - cert_pem = encode_certificate(cert) - config.set(pki_base + ['ca', pki_name, 'certificate'], value=wrapped_pem_to_config_value(cert_pem)) - config.set(x509_base + ['ca-certificate'], value=pki_name) - else: - print(f'Failed to migrate CA certificate on eapol config for interface {interface}') - - config.delete(x509_base + ['ca-cert-file']) - - if config.exists(x509_base + ['cert-file']): - if not config.exists(pki_base + ['certificate']): - config.set(pki_base + ['certificate']) - config.set_tag(pki_base + ['certificate']) - - cert_file = config.return_value(x509_base + ['cert-file']) - cert_path = os.path.join(AUTH_DIR, cert_file) - cert = None - - if os.path.isfile(cert_path): - if not os.access(cert_path, os.R_OK): - run(f'sudo chmod 644 {cert_path}') - - with open(cert_path, 'r') as f: - cert_data = f.read() - cert = load_certificate(cert_data, wrap_tags=False) - - if cert: - cert_pem = encode_certificate(cert) - config.set(pki_base + ['certificate', pki_name, 'certificate'], value=wrapped_pem_to_config_value(cert_pem)) - config.set(x509_base + ['certificate'], value=pki_name) - else: - print(f'Failed to migrate certificate on eapol config for interface {interface}') - - config.delete(x509_base + ['cert-file']) - - if config.exists(x509_base + ['key-file']): - key_file = config.return_value(x509_base + ['key-file']) - key_path = os.path.join(AUTH_DIR, key_file) - key = None - - if os.path.isfile(key_path): - if not os.access(key_path, os.R_OK): - run(f'sudo chmod 644 {key_path}') - - with open(key_path, 'r') as f: - key_data = f.read() - key = load_private_key(key_data, passphrase=None, wrap_tags=False) - - if key: - key_pem = encode_private_key(key, passphrase=None) - config.set(pki_base + ['certificate', pki_name, 'private', 'key'], value=wrapped_pem_to_config_value(key_pem)) - else: - print(f'Failed to migrate private key on eapol config for interface {interface}') - - config.delete(x509_base + ['key-file']) - -try: - with open(file_name, 'w') as f: - f.write(config.to_string()) -except OSError as e: - print("Failed to save the modified config: {}".format(e)) - sys.exit(1) + found = False + if config.exists(ipsec_base): + for peer in config.list_nodes(ipsec_base): + if config.exists(ipsec_base + [peer, 'vti', 'bind']): + tmp = config.return_value(ipsec_base + [peer, 'vti', 'bind']) + if tmp == interface: + # Interface was found and we no longer need to search + # for it in our IPSec peers + found = True + break + if not found: + config.delete(base + [interface]) + + try: + with open(file_name, 'w') as f: + f.write(config.to_string()) + except OSError as e: + print("Failed to save the modified config: {}".format(e)) + sys.exit(1) diff --git a/src/migration-scripts/interfaces/23-to-24 b/src/migration-scripts/interfaces/23-to-24 new file mode 100755 index 000000000..93ce9215f --- /dev/null +++ b/src/migration-scripts/interfaces/23-to-24 @@ -0,0 +1,369 @@ +#!/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 . + +# Migrate Wireguard to store keys in CLI +# Migrate EAPoL to PKI configuration + +import os +import sys +from vyos.configtree import ConfigTree +from vyos.pki import load_certificate +from vyos.pki import load_crl +from vyos.pki import load_dh_parameters +from vyos.pki import load_private_key +from vyos.pki import encode_certificate +from vyos.pki import encode_dh_parameters +from vyos.pki import encode_private_key +from vyos.util import run + +def wrapped_pem_to_config_value(pem): + out = [] + for line in pem.strip().split("\n"): + if not line or line.startswith("-----") or line[0] == '#': + continue + out.append(line) + return "".join(out) + +def read_file_for_pki(config_auth_path): + full_path = os.path.join(AUTH_DIR, config_auth_path) + output = None + + if os.path.isfile(full_path): + if not os.access(full_path, os.R_OK): + run(f'sudo chmod 644 {full_path}') + + with open(full_path, 'r') as f: + output = f.read() + + return output + +if (len(sys.argv) < 1): + print("Must specify file name!") + sys.exit(1) + +file_name = sys.argv[1] + +with open(file_name, 'r') as f: + config_file = f.read() + +config = ConfigTree(config_file) + +AUTH_DIR = '/config/auth' +pki_base = ['pki'] + +# OpenVPN +base = ['interfaces', 'openvpn'] + +if config.exists(base): + for interface in config.list_nodes(base): + x509_base = base + [interface, 'tls'] + pki_name = f'openvpn_{interface}' + + if config.exists(base + [interface, 'shared-secret-key-file']): + if not config.exists(pki_base + ['openvpn', 'shared-secret']): + config.set(pki_base + ['openvpn', 'shared-secret']) + config.set_tag(pki_base + ['openvpn', 'shared-secret']) + + key_file = config.return_value(base + [interface, 'shared-secret-key-file']) + key = read_file_for_pki(key_file) + key_pki_name = f'{pki_name}_shared' + + if key: + config.set(pki_base + ['openvpn', 'shared-secret', key_pki_name, 'key'], value=wrapped_pem_to_config_value(key)) + config.set(pki_base + ['openvpn', 'shared-secret', key_pki_name, 'version'], value='1') + config.set(base + [interface, 'shared-secret-key'], value=key_pki_name) + else: + print(f'Failed to migrate shared-secret-key on openvpn interface {interface}') + + config.delete(base + [interface, 'shared-secret-key-file']) + + if not config.exists(base + [interface, 'tls']): + continue + + if config.exists(base + [interface, 'tls', 'auth-file']): + if not config.exists(pki_base + ['openvpn', 'shared-secret']): + config.set(pki_base + ['openvpn', 'shared-secret']) + config.set_tag(pki_base + ['openvpn', 'shared-secret']) + + key_file = config.return_value(base + [interface, 'tls', 'auth-file']) + key = read_file_for_pki(key_file) + key_pki_name = f'{pki_name}_auth' + + if key: + config.set(pki_base + ['openvpn', 'shared-secret', key_pki_name, 'key'], value=wrapped_pem_to_config_value(key)) + config.set(pki_base + ['openvpn', 'shared-secret', key_pki_name, 'version'], value='1') + config.set(base + [interface, 'tls', 'auth-key'], value=key_pki_name) + else: + print(f'Failed to migrate auth-key on openvpn interface {interface}') + + config.delete(base + [interface, 'tls', 'auth-file']) + + if config.exists(base + [interface, 'tls', 'crypt-file']): + if not config.exists(pki_base + ['openvpn', 'shared-secret']): + config.set(pki_base + ['openvpn', 'shared-secret']) + config.set_tag(pki_base + ['openvpn', 'shared-secret']) + + key_file = config.return_value(base + [interface, 'tls', 'crypt-file']) + key = read_file_for_pki(key_file) + key_pki_name = f'{pki_name}_crypt' + + if key: + config.set(pki_base + ['openvpn', 'shared-secret', key_pki_name, 'key'], value=wrapped_pem_to_config_value(key)) + config.set(pki_base + ['openvpn', 'shared-secret', key_pki_name, 'version'], value='1') + config.set(base + [interface, 'tls', 'crypt-key'], value=key_pki_name) + else: + print(f'Failed to migrate crypt-key on openvpn interface {interface}') + + config.delete(base + [interface, 'tls', 'crypt-file']) + + if config.exists(x509_base + ['ca-cert-file']): + if not config.exists(pki_base + ['ca']): + config.set(pki_base + ['ca']) + config.set_tag(pki_base + ['ca']) + + cert_file = config.return_value(x509_base + ['ca-cert-file']) + cert_path = os.path.join(AUTH_DIR, cert_file) + cert = None + + if os.path.isfile(cert_path): + if not os.access(cert_path, os.R_OK): + run(f'sudo chmod 644 {cert_path}') + + with open(cert_path, 'r') as f: + cert_data = f.read() + cert = load_certificate(cert_data, wrap_tags=False) + + if cert: + cert_pem = encode_certificate(cert) + config.set(pki_base + ['ca', pki_name, 'certificate'], value=wrapped_pem_to_config_value(cert_pem)) + config.set(x509_base + ['ca-certificate'], value=pki_name) + else: + print(f'Failed to migrate CA certificate on openvpn interface {interface}') + + config.delete(x509_base + ['ca-cert-file']) + + if config.exists(x509_base + ['crl-file']): + if not config.exists(pki_base + ['ca']): + config.set(pki_base + ['ca']) + config.set_tag(pki_base + ['ca']) + + crl_file = config.return_value(x509_base + ['crl-file']) + crl_path = os.path.join(AUTH_DIR, crl_file) + crl = None + + if os.path.isfile(crl_path): + if not os.access(crl_path, os.R_OK): + run(f'sudo chmod 644 {crl_path}') + + with open(crl_path, 'r') as f: + crl_data = f.read() + crl = load_crl(crl_data, wrap_tags=False) + + if crl: + crl_pem = encode_certificate(crl) + config.set(pki_base + ['ca', pki_name, 'crl'], value=wrapped_pem_to_config_value(crl_pem)) + else: + print(f'Failed to migrate CRL on openvpn interface {interface}') + + config.delete(x509_base + ['crl-file']) + + if config.exists(x509_base + ['cert-file']): + if not config.exists(pki_base + ['certificate']): + config.set(pki_base + ['certificate']) + config.set_tag(pki_base + ['certificate']) + + cert_file = config.return_value(x509_base + ['cert-file']) + cert_path = os.path.join(AUTH_DIR, cert_file) + cert = None + + if os.path.isfile(cert_path): + if not os.access(cert_path, os.R_OK): + run(f'sudo chmod 644 {cert_path}') + + with open(cert_path, 'r') as f: + cert_data = f.read() + cert = load_certificate(cert_data, wrap_tags=False) + + if cert: + cert_pem = encode_certificate(cert) + config.set(pki_base + ['certificate', pki_name, 'certificate'], value=wrapped_pem_to_config_value(cert_pem)) + config.set(x509_base + ['certificate'], value=pki_name) + else: + print(f'Failed to migrate certificate on openvpn interface {interface}') + + config.delete(x509_base + ['cert-file']) + + if config.exists(x509_base + ['key-file']): + key_file = config.return_value(x509_base + ['key-file']) + key_path = os.path.join(AUTH_DIR, key_file) + key = None + + if os.path.isfile(key_path): + if not os.access(key_path, os.R_OK): + run(f'sudo chmod 644 {key_path}') + + with open(key_path, 'r') as f: + key_data = f.read() + key = load_private_key(key_data, passphrase=None, wrap_tags=False) + + if key: + key_pem = encode_private_key(key, passphrase=None) + config.set(pki_base + ['certificate', pki_name, 'private', 'key'], value=wrapped_pem_to_config_value(key_pem)) + else: + print(f'Failed to migrate private key on openvpn interface {interface}') + + config.delete(x509_base + ['key-file']) + + if config.exists(x509_base + ['dh-file']): + if not config.exists(pki_base + ['dh']): + config.set(pki_base + ['dh']) + config.set_tag(pki_base + ['dh']) + + dh_file = config.return_value(x509_base + ['dh-file']) + dh_path = os.path.join(AUTH_DIR, dh_file) + dh = None + + if os.path.isfile(dh_path): + if not os.access(dh_path, os.R_OK): + run(f'sudo chmod 644 {dh_path}') + + with open(dh_path, 'r') as f: + dh_data = f.read() + dh = load_dh_parameters(dh_data, wrap_tags=False) + + if dh: + dh_pem = encode_dh_parameters(dh) + config.set(pki_base + ['dh', pki_name, 'parameters'], value=wrapped_pem_to_config_value(dh_pem)) + config.set(x509_base + ['dh-params'], value=pki_name) + else: + print(f'Failed to migrate DH parameters on openvpn interface {interface}') + + config.delete(x509_base + ['dh-file']) + +# Wireguard +base = ['interfaces', 'wireguard'] + +if config.exists(base): + for interface in config.list_nodes(base): + private_key_path = base + [interface, 'private-key'] + + key_file = 'default' + if config.exists(private_key_path): + key_file = config.return_value(private_key_path) + + full_key_path = f'/config/auth/wireguard/{key_file}/private.key' + + if not os.path.exists(full_key_path): + print(f'Could not find wireguard private key for migration on interface "{interface}"') + continue + + with open(full_key_path, 'r') as f: + key_data = f.read().strip() + config.set(private_key_path, value=key_data) + + for peer in config.list_nodes(base + [interface, 'peer']): + config.rename(base + [interface, 'peer', peer, 'pubkey'], 'public-key') + +# Ethernet EAPoL +base = ['interfaces', 'ethernet'] + +if config.exists(base): + for interface in config.list_nodes(base): + if not config.exists(base + [interface, 'eapol']): + continue + + x509_base = base + [interface, 'eapol'] + pki_name = f'eapol_{interface}' + + if config.exists(x509_base + ['ca-cert-file']): + if not config.exists(pki_base + ['ca']): + config.set(pki_base + ['ca']) + config.set_tag(pki_base + ['ca']) + + cert_file = config.return_value(x509_base + ['ca-cert-file']) + cert_path = os.path.join(AUTH_DIR, cert_file) + cert = None + + if os.path.isfile(cert_path): + if not os.access(cert_path, os.R_OK): + run(f'sudo chmod 644 {cert_path}') + + with open(cert_path, 'r') as f: + cert_data = f.read() + cert = load_certificate(cert_data, wrap_tags=False) + + if cert: + cert_pem = encode_certificate(cert) + config.set(pki_base + ['ca', pki_name, 'certificate'], value=wrapped_pem_to_config_value(cert_pem)) + config.set(x509_base + ['ca-certificate'], value=pki_name) + else: + print(f'Failed to migrate CA certificate on eapol config for interface {interface}') + + config.delete(x509_base + ['ca-cert-file']) + + if config.exists(x509_base + ['cert-file']): + if not config.exists(pki_base + ['certificate']): + config.set(pki_base + ['certificate']) + config.set_tag(pki_base + ['certificate']) + + cert_file = config.return_value(x509_base + ['cert-file']) + cert_path = os.path.join(AUTH_DIR, cert_file) + cert = None + + if os.path.isfile(cert_path): + if not os.access(cert_path, os.R_OK): + run(f'sudo chmod 644 {cert_path}') + + with open(cert_path, 'r') as f: + cert_data = f.read() + cert = load_certificate(cert_data, wrap_tags=False) + + if cert: + cert_pem = encode_certificate(cert) + config.set(pki_base + ['certificate', pki_name, 'certificate'], value=wrapped_pem_to_config_value(cert_pem)) + config.set(x509_base + ['certificate'], value=pki_name) + else: + print(f'Failed to migrate certificate on eapol config for interface {interface}') + + config.delete(x509_base + ['cert-file']) + + if config.exists(x509_base + ['key-file']): + key_file = config.return_value(x509_base + ['key-file']) + key_path = os.path.join(AUTH_DIR, key_file) + key = None + + if os.path.isfile(key_path): + if not os.access(key_path, os.R_OK): + run(f'sudo chmod 644 {key_path}') + + with open(key_path, 'r') as f: + key_data = f.read() + key = load_private_key(key_data, passphrase=None, wrap_tags=False) + + if key: + key_pem = encode_private_key(key, passphrase=None) + config.set(pki_base + ['certificate', pki_name, 'private', 'key'], value=wrapped_pem_to_config_value(key_pem)) + else: + print(f'Failed to migrate private key on eapol config for interface {interface}') + + config.delete(x509_base + ['key-file']) + +try: + with open(file_name, 'w') as f: + f.write(config.to_string()) +except OSError as e: + print("Failed to save the modified config: {}".format(e)) + sys.exit(1) -- cgit v1.2.3 From c1b298e5ec11313156eb3a9f871fe4f3cd4cbd64 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Mon, 30 Aug 2021 21:29:22 +0200 Subject: ethernet: T3619: fix VyOS 1.2 -> 1.3 performance degradation An analysis of the code base from VyOS 1.2 -> 1.3 -> 1.4 revealed the following "root-cause" VyOS 1.2 uses the "old" node.def file format for: * Generic Segmentation Offloading * Generic Receive Offloading So if any of the above settings is available on the configuration CLI, the node.def file will be executed - this is how it works. By default, this CLI option is not enabled in VyOS 1.2 - but the Linux Kernel enables offloading "under the hood" by default for GRO, GSO... which will boost the performance for users magically. With the rewrite in VyOS 1.3 of all the interface related code T1579, and especially T1637 this was moved to a new approach. There is now only one handler script which is called whenever a user changes something under the interfaces ethernet tree. The Full CLI configuration is assembled by get_interface_dict() - a wrapper for get_config_dict() which abstracts and works for all of our interface types - single source design. The problem now comes into play when the gathered configuration is actually written to the hardware, as there is no GSO, GRO or foo-offloading setting defined - we behave as instructed and disable the offloading. So the real bug originates from VyOS 1.2 and the old Vyatta codebase, but the recent XML Python rewrites brought that one up to light. Solution: A configuration migration script will be provided starting with VyOS 1.3 which will read in the CLI configuration of the ethernet interfaces and if not enabled, will query the adapter if offloading is supported at all, and if so, will enable the CLI nodes. One might say that this will "blow" the CLI configuration but it only represents the truth - which was masked in VyOS 1.2. (cherry picked from commit a515212f4efb08846df04405f31a828edcd63552) --- src/migration-scripts/interfaces/20-to-21 | 101 ++++++++++++++++++++++++++++++ 1 file changed, 101 insertions(+) create mode 100755 src/migration-scripts/interfaces/20-to-21 diff --git a/src/migration-scripts/interfaces/20-to-21 b/src/migration-scripts/interfaces/20-to-21 new file mode 100755 index 000000000..9210330d6 --- /dev/null +++ b/src/migration-scripts/interfaces/20-to-21 @@ -0,0 +1,101 @@ +#!/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 . + +# T3619: mirror Linux Kernel defaults for ethernet offloading options into VyOS +# CLI. See https://phabricator.vyos.net/T3619#102254 for all the details. + +from sys import argv + +from vyos.ethtool import Ethtool +from vyos.configtree import ConfigTree + +if (len(argv) < 1): + print("Must specify file name!") + exit(1) + +file_name = argv[1] +with open(file_name, 'r') as f: + config_file = f.read() + +base = ['interfaces', 'ethernet'] +config = ConfigTree(config_file) + +if not config.exists(base): + exit(0) + +for ifname in config.list_nodes(base): + eth = Ethtool(ifname) + + # If GRO is enabled by the Kernel - we reflect this on the CLI. If GRO is + # enabled via CLI but not supported by the NIC - we remove it from the CLI + configured = config.exists(base + [ifname, 'offload', 'gro']) + enabled, fixed = eth.get_generic_receive_offload() + if configured and fixed: + config.delete(base + [ifname, 'offload', 'gro']) + elif enabled and not fixed: + config.set(base + [ifname, 'offload', 'gro']) + + # If GSO is enabled by the Kernel - we reflect this on the CLI. If GSO is + # enabled via CLI but not supported by the NIC - we remove it from the CLI + configured = config.exists(base + [ifname, 'offload', 'gso']) + enabled, fixed = eth.get_generic_segmentation_offload() + if configured and fixed: + config.delete(base + [ifname, 'offload', 'gso']) + elif enabled and not fixed: + config.set(base + [ifname, 'offload', 'gso']) + + # If LRO is enabled by the Kernel - we reflect this on the CLI. If LRO is + # enabled via CLI but not supported by the NIC - we remove it from the CLI + configured = config.exists(base + [ifname, 'offload', 'lro']) + enabled, fixed = eth.get_large_receive_offload() + if configured and fixed: + config.delete(base + [ifname, 'offload', 'lro']) + elif enabled and not fixed: + config.set(base + [ifname, 'offload', 'lro']) + + # If SG is enabled by the Kernel - we reflect this on the CLI. If SG is + # enabled via CLI but not supported by the NIC - we remove it from the CLI + configured = config.exists(base + [ifname, 'offload', 'sg']) + enabled, fixed = eth.get_scatter_gather() + if configured and fixed: + config.delete(base + [ifname, 'offload', 'sg']) + elif enabled and not fixed: + config.set(base + [ifname, 'offload', 'sg']) + + # If TSO is enabled by the Kernel - we reflect this on the CLI. If TSO is + # enabled via CLI but not supported by the NIC - we remove it from the CLI + configured = config.exists(base + [ifname, 'offload', 'tso']) + enabled, fixed = eth.get_tcp_segmentation_offload() + if configured and fixed: + config.delete(base + [ifname, 'offload', 'tso']) + elif enabled and not fixed: + config.set(base + [ifname, 'offload', 'tso']) + + # If UFO is enabled by the Kernel - we reflect this on the CLI. If UFO is + # enabled via CLI but not supported by the NIC - we remove it from the CLI + configured = config.exists(base + [ifname, 'offload', 'ufo']) + enabled, fixed = eth.get_udp_fragmentation_offload() + if configured and fixed: + config.delete(base + [ifname, 'offload', 'ufo']) + elif enabled and not fixed: + config.set(base + [ifname, 'offload', 'ufo']) + +try: + with open(file_name, 'w') as f: + f.write(config.to_string()) +except OSError as e: + print("Failed to save the modified config: {}".format(e)) + exit(1) -- cgit v1.2.3 From 705022319916222d78082114245c7639c073bd32 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Mon, 30 Aug 2021 21:36:52 +0200 Subject: ethernet: T3787: remove deprecated UDP fragmentation offloading option Deprecated in the Linux Kernel by commit 08a00fea6de277df12ccfadc21 ("net: Remove references to NETIF_F_UFO from ethtool."). (cherry picked from commit f5e46ee6cc2b6c1c1869e26beca4ccd5bf52b62f) --- interface-definitions/interfaces-ethernet.xml.in | 6 ----- python/vyos/ethtool.py | 3 --- python/vyos/ifconfig/ethernet.py | 28 ------------------------ src/migration-scripts/interfaces/20-to-21 | 12 ++++------ 4 files changed, 4 insertions(+), 45 deletions(-) diff --git a/interface-definitions/interfaces-ethernet.xml.in b/interface-definitions/interfaces-ethernet.xml.in index ca076e3fa..ceeda12a0 100644 --- a/interface-definitions/interfaces-ethernet.xml.in +++ b/interface-definitions/interfaces-ethernet.xml.in @@ -104,12 +104,6 @@ - - - Enable UDP Fragmentation Offloading - - - diff --git a/python/vyos/ethtool.py b/python/vyos/ethtool.py index 98985e972..5a5d84bed 100644 --- a/python/vyos/ethtool.py +++ b/python/vyos/ethtool.py @@ -122,9 +122,6 @@ class Ethtool: def get_tcp_segmentation_offload(self): return self._get_generic('tcp-segmentation-offload') - def get_udp_fragmentation_offload(self): - return self._get_generic('udp-fragmentation-offload') - def get_rx_buffer(self): # Configuration of RX ring-buffers is not supported on every device, # thus when it's impossible return None diff --git a/python/vyos/ifconfig/ethernet.py b/python/vyos/ifconfig/ethernet.py index 696fec03b..7d29da8fc 100644 --- a/python/vyos/ifconfig/ethernet.py +++ b/python/vyos/ifconfig/ethernet.py @@ -71,11 +71,6 @@ class EthernetIf(Interface): 'possible': lambda i, v: EthernetIf.feature(i, 'tso', v), # 'shellcmd': 'ethtool -K {ifname} tso {value}', }, - 'ufo': { - 'validate': lambda v: assert_list(v, ['on', 'off']), - 'possible': lambda i, v: EthernetIf.feature(i, 'ufo', v), - # 'shellcmd': 'ethtool -K {ifname} ufo {value}', - }, }} _sysfs_set = {**Interface._sysfs_set, **{ @@ -338,26 +333,6 @@ class EthernetIf(Interface): print('Adapter does not support changing tcp-segmentation-offload settings!') return False - def set_ufo(self, state): - """ - Enable UDP fragmentation offloading. State can be either True or False. - - Example: - >>> from vyos.ifconfig import EthernetIf - >>> i = EthernetIf('eth0') - >>> i.set_udp_offload(True) - """ - if not isinstance(state, bool): - raise ValueError('Value out of range') - - enabled, fixed = self.ethtool.get_udp_fragmentation_offload() - if enabled != state: - if not fixed: - return self.set_interface('gro', 'on' if state else 'off') - else: - print('Adapter does not support changing udp-fragmentation-offload settings!') - return False - def set_ring_buffer(self, b_type, b_size): """ Example: @@ -403,9 +378,6 @@ class EthernetIf(Interface): # TSO (TCP segmentation offloading) self.set_tso(dict_search('offload.tso', config) != None) - # UDP fragmentation offloading - self.set_ufo(dict_search('offload.ufo', config) != None) - # Set physical interface speed and duplex if {'speed', 'duplex'} <= set(config): speed = config.get('speed') diff --git a/src/migration-scripts/interfaces/20-to-21 b/src/migration-scripts/interfaces/20-to-21 index 9210330d6..4b0e70d35 100755 --- a/src/migration-scripts/interfaces/20-to-21 +++ b/src/migration-scripts/interfaces/20-to-21 @@ -15,7 +15,8 @@ # along with this program. If not, see . # T3619: mirror Linux Kernel defaults for ethernet offloading options into VyOS -# CLI. See https://phabricator.vyos.net/T3619#102254 for all the details. +# CLI. See https://phabricator.vyos.net/T3619#102254 for all the details. +# T3787: Remove deprecated UDP fragmentation offloading option from sys import argv @@ -84,14 +85,9 @@ for ifname in config.list_nodes(base): elif enabled and not fixed: config.set(base + [ifname, 'offload', 'tso']) - # If UFO is enabled by the Kernel - we reflect this on the CLI. If UFO is - # enabled via CLI but not supported by the NIC - we remove it from the CLI - configured = config.exists(base + [ifname, 'offload', 'ufo']) - enabled, fixed = eth.get_udp_fragmentation_offload() - if configured and fixed: + # Remove deprecated UDP fragmentation offloading option + if config.exists(base + [ifname, 'offload', 'ufo']): config.delete(base + [ifname, 'offload', 'ufo']) - elif enabled and not fixed: - config.set(base + [ifname, 'offload', 'ufo']) try: with open(file_name, 'w') as f: -- cgit v1.2.3 From 91892e431349ca0edb5e3e3023e4f340ab9b777f Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Tue, 31 Aug 2021 11:58:46 +0200 Subject: ethernet: T3514: bail out early on invalid adapter speed/duplex setting Ethernet adapters have a discrete set of available speed and duplex settings. Instead of passing every value down to ethtool and let it decide, we can do this early in the VyOS verify() function for ethernet interfaces. --- src/conf_mode/interfaces-ethernet.py | 50 +++++++++++++++++++++--------------- 1 file changed, 29 insertions(+), 21 deletions(-) diff --git a/src/conf_mode/interfaces-ethernet.py b/src/conf_mode/interfaces-ethernet.py index 78c24952b..27c4a7c38 100755 --- a/src/conf_mode/interfaces-ethernet.py +++ b/src/conf_mode/interfaces-ethernet.py @@ -73,32 +73,20 @@ def verify(ethernet): ifname = ethernet['ifname'] verify_interface_exists(ifname) + ethtool = Ethtool(ifname) # No need to check speed and duplex keys as both have default values. if ((ethernet['speed'] == 'auto' and ethernet['duplex'] != 'auto') or (ethernet['speed'] != 'auto' and ethernet['duplex'] == 'auto')): raise ConfigError('Speed/Duplex missmatch. Must be both auto or manually configured') - verify_mtu(ethernet) - verify_mtu_ipv6(ethernet) - verify_dhcpv6(ethernet) - verify_address(ethernet) - verify_vrf(ethernet) - verify_eapol(ethernet) - verify_mirror(ethernet) + if ethernet['speed'] != 'auto' and ethernet['duplex'] != 'auto': + # We need to verify if the requested speed and duplex setting is + # supported by the underlaying NIC. + speed = ethernet['speed'] + duplex = ethernet['duplex'] + if not ethtool.check_speed_duplex(speed, duplex): + raise ConfigError(f'Adapter does not support speed "{speed}" and duplex "{duplex}"!') - # verify offloading capabilities - if dict_search('offload.rps', ethernet) != None: - if not os.path.exists(f'/sys/class/net/{ifname}/queues/rx-0/rps_cpus'): - raise ConfigError('Interface does not suport RPS!') - - driver = EthernetIf(ifname).get_driver_name() - # T3342 - Xen driver requires special treatment - if driver == 'vif': - if int(ethernet['mtu']) > 1500 and dict_search('offload.sg', ethernet) == None: - raise ConfigError('Xen netback drivers requires scatter-gatter offloading '\ - 'for MTU size larger then 1500 bytes') - - ethtool = Ethtool(ifname) if 'ring_buffer' in ethernet: max_rx = ethtool.get_rx_buffer() if not max_rx: @@ -118,6 +106,26 @@ def verify(ethernet): raise ConfigError(f'Driver only supports a maximum TX ring-buffer '\ f'size of "{max_tx}" bytes!') + verify_mtu(ethernet) + verify_mtu_ipv6(ethernet) + verify_dhcpv6(ethernet) + verify_address(ethernet) + verify_vrf(ethernet) + verify_eapol(ethernet) + verify_mirror(ethernet) + + # verify offloading capabilities + if dict_search('offload.rps', ethernet) != None: + if not os.path.exists(f'/sys/class/net/{ifname}/queues/rx-0/rps_cpus'): + raise ConfigError('Interface does not suport RPS!') + + driver = EthernetIf(ifname).get_driver_name() + # T3342 - Xen driver requires special treatment + if driver == 'vif': + if int(ethernet['mtu']) > 1500 and dict_search('offload.sg', ethernet) == None: + raise ConfigError('Xen netback drivers requires scatter-gatter offloading '\ + 'for MTU size larger then 1500 bytes') + # XDP requires multiple TX queues if 'xdp' in ethernet: queues = glob(f'/sys/class/net/{ifname}/queues/tx-*') @@ -136,7 +144,7 @@ def generate(ethernet): if 'eapol' in ethernet: render(wpa_suppl_conf.format(**ethernet), 'ethernet/wpa_supplicant.conf.tmpl', ethernet) - + ifname = ethernet['ifname'] cert_file_path = os.path.join(cfg_dir, f'{ifname}_cert.pem') cert_key_path = os.path.join(cfg_dir, f'{ifname}_cert.key') -- cgit v1.2.3 From 00efce716912680354d47a2dca9769cd8c5c89ae Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Tue, 31 Aug 2021 12:20:05 +0200 Subject: ssh: T3789: add custom validator for base64 encoded CLI data SSH keys used for remote login are supplied as base64 encoded data on the CLI. The key is not validated, thus an invalid copy/pasted key will render the login useless. This commit adds a custom and re-usable validator which check if the data is properly base64 encoded. --- interface-definitions/system-login.xml.in | 5 ++++- src/validators/base64 | 27 +++++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) create mode 100755 src/validators/base64 diff --git a/interface-definitions/system-login.xml.in b/interface-definitions/system-login.xml.in index fb34b7199..3c2c7dfa5 100644 --- a/interface-definitions/system-login.xml.in +++ b/interface-definitions/system-login.xml.in @@ -52,7 +52,10 @@ - Public key value (base64-encoded) + Public key value (Base64 encoded) + + + diff --git a/src/validators/base64 b/src/validators/base64 new file mode 100755 index 000000000..e2b1e730d --- /dev/null +++ b/src/validators/base64 @@ -0,0 +1,27 @@ +#!/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 base64 +from sys import argv + +if __name__ == '__main__': + if len(argv) != 2: + exit(1) + try: + base64.b64decode(argv[1]) + except: + exit(1) + exit(0) -- cgit v1.2.3 From a086dc2c429aea9614ac7a9c735c6475c2d6da59 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Tue, 31 Aug 2021 12:22:36 +0200 Subject: vyos.ethtool: T3163: use long option names when calling the ethtool binray This makes understanding the code easier what is "really" called without opening the man page. --- op-mode-definitions/show-interfaces-ethernet.xml.in | 10 +++++----- python/vyos/ethtool.py | 2 +- python/vyos/ifconfig/ethernet.py | 7 +------ 3 files changed, 7 insertions(+), 12 deletions(-) diff --git a/op-mode-definitions/show-interfaces-ethernet.xml.in b/op-mode-definitions/show-interfaces-ethernet.xml.in index fc79f44bf..6d50d6e90 100644 --- a/op-mode-definitions/show-interfaces-ethernet.xml.in +++ b/op-mode-definitions/show-interfaces-ethernet.xml.in @@ -23,19 +23,19 @@ Visually identify specified ethernet interface - echo "Blinking interface $4 for 30 seconds."; /sbin/ethtool --identify "$4" 30 + echo "Blinking interface $4 for 30 seconds."; ethtool --identify "$4" 30 Show physical device information for specified ethernet interface - /sbin/ethtool "$4"; /sbin/ethtool -i "$4" + ethtool "$4"; ethtool --driver "$4" Show physical device offloading capabilities - /sbin/ethtool -k "$4" | sed -e 1d -e '/fixed/d' -e 's/^\t*//g' -e 's/://' | column -t -s' ' + ethtool --show-features "$4" | sed -e 1d -e '/fixed/d' -e 's/^\t*//g' -e 's/://' | column -t -s' ' @@ -43,13 +43,13 @@ Show physical device statistics for specified ethernet interface - /sbin/ethtool -S "$4" + ethtool --statistics "$4" Show transceiver information from modules (e.g SFP+, QSFP) - /sbin/ethtool -m "$4" + ethtool --module-info "$4" diff --git a/python/vyos/ethtool.py b/python/vyos/ethtool.py index 5a5d84bed..7dcb68346 100644 --- a/python/vyos/ethtool.py +++ b/python/vyos/ethtool.py @@ -65,7 +65,7 @@ class Ethtool: self._speed_duplex[speed].update({ duplex : ''}) # Now populate features dictionaty - out, err = popen(f'ethtool -k {ifname}') + out, err = popen(f'ethtool --show-features {ifname}') # skip the first line, it only says: "Features for eth0": for line in out.splitlines()[1:]: if ":" in line: diff --git a/python/vyos/ifconfig/ethernet.py b/python/vyos/ifconfig/ethernet.py index 7d29da8fc..76ed3fd92 100644 --- a/python/vyos/ifconfig/ethernet.py +++ b/python/vyos/ifconfig/ethernet.py @@ -42,34 +42,29 @@ class EthernetIf(Interface): @staticmethod def feature(ifname, option, value): - run(f'ethtool -K {ifname} {option} {value}') + run(f'ethtool --features {ifname} {option} {value}') return False _command_set = {**Interface._command_set, **{ 'gro': { 'validate': lambda v: assert_list(v, ['on', 'off']), 'possible': lambda i, v: EthernetIf.feature(i, 'gro', v), - # 'shellcmd': 'ethtool -K {ifname} gro {value}', }, 'gso': { 'validate': lambda v: assert_list(v, ['on', 'off']), 'possible': lambda i, v: EthernetIf.feature(i, 'gso', v), - # 'shellcmd': 'ethtool -K {ifname} gso {value}', }, 'lro': { 'validate': lambda v: assert_list(v, ['on', 'off']), 'possible': lambda i, v: EthernetIf.feature(i, 'lro', v), - # 'shellcmd': 'ethtool -K {ifname} lro {value}', }, 'sg': { 'validate': lambda v: assert_list(v, ['on', 'off']), 'possible': lambda i, v: EthernetIf.feature(i, 'sg', v), - # 'shellcmd': 'ethtool -K {ifname} sg {value}', }, 'tso': { 'validate': lambda v: assert_list(v, ['on', 'off']), 'possible': lambda i, v: EthernetIf.feature(i, 'tso', v), - # 'shellcmd': 'ethtool -K {ifname} tso {value}', }, }} -- cgit v1.2.3 From cc742d48579e4f76e5d3230d87e22f71f76f9301 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Tue, 31 Aug 2021 18:15:47 +0200 Subject: ethernet: T2241: check if interface supports changing speed/duplex settings Not all interface drivers have the ability to change the speed and duplex settings. Known drivers with this limitation are vmxnet3, virtio_net and xen_netfront. If this driver is detected, an error will be presented to the user. --- python/vyos/ethtool.py | 15 +++++++++++++++ src/conf_mode/interfaces-ethernet.py | 3 ++- src/migration-scripts/interfaces/20-to-21 | 15 +++++++++++++++ 3 files changed, 32 insertions(+), 1 deletion(-) diff --git a/python/vyos/ethtool.py b/python/vyos/ethtool.py index 7dcb68346..968e2e2a3 100644 --- a/python/vyos/ethtool.py +++ b/python/vyos/ethtool.py @@ -13,7 +13,9 @@ # You should have received a copy of the GNU Lesser General Public # License along with this library. If not, see . +import os import re + from vyos.util import popen class Ethtool: @@ -41,8 +43,18 @@ class Ethtool: # } _speed_duplex = { } _ring_buffers = { } + _driver_name = None def __init__(self, ifname): + # Get driver used for interface + sysfs_file = f'/sys/class/net/{ifname}/device/driver/module' + if os.path.exists(sysfs_file): + link = os.readlink(sysfs_file) + self._driver_name = os.path.basename(link) + + if not self._driver_name: + raise ValueError(f'Could not determine driver for interface {ifname}!') + # Build a dictinary of supported link-speed and dupley settings. out, err = popen(f'ethtool {ifname}') reading = False @@ -142,6 +154,9 @@ class Ethtool: if duplex not in ['full', 'half']: raise ValueError(f'Value "{duplex}" for duplex is invalid!') + if self._driver_name in ['vmxnet3', 'virtio_net', 'xen_netfront']: + return False + if speed in self._speed_duplex: if duplex in self._speed_duplex[speed]: return True diff --git a/src/conf_mode/interfaces-ethernet.py b/src/conf_mode/interfaces-ethernet.py index 27c4a7c38..889f4856f 100755 --- a/src/conf_mode/interfaces-ethernet.py +++ b/src/conf_mode/interfaces-ethernet.py @@ -85,7 +85,8 @@ def verify(ethernet): speed = ethernet['speed'] duplex = ethernet['duplex'] if not ethtool.check_speed_duplex(speed, duplex): - raise ConfigError(f'Adapter does not support speed "{speed}" and duplex "{duplex}"!') + raise ConfigError(f'Adapter does not support changing speed and duplex '\ + f'settings to: {speed}/{duplex}!') if 'ring_buffer' in ethernet: max_rx = ethtool.get_rx_buffer() diff --git a/src/migration-scripts/interfaces/20-to-21 b/src/migration-scripts/interfaces/20-to-21 index 4b0e70d35..bd89dcdb4 100755 --- a/src/migration-scripts/interfaces/20-to-21 +++ b/src/migration-scripts/interfaces/20-to-21 @@ -89,6 +89,21 @@ for ifname in config.list_nodes(base): if config.exists(base + [ifname, 'offload', 'ufo']): config.delete(base + [ifname, 'offload', 'ufo']) + # Also while processing the interface configuration, not all adapters support + # changing the speed and duplex settings. If the desired speed and duplex + # values do not work for the NIC driver, we change them back to the default + # value of "auto" - which will be applied if the CLI node is deleted. + speed_path = base + [ifname, 'speed'] + duplex_path = base + [ifname, 'duplex'] + # speed and duplex must always be set at the same time if not set to "auto" + if config.exists(speed_path) and config.exists(duplex_path): + speed = config.return_value(speed_path) + duplex = config.return_value(duplex_path) + if speed != 'auto' and duplex != 'auto': + if not eth.check_speed_duplex(speed, duplex): + config.delete(speed_path) + config.delete(duplex_path) + try: with open(file_name, 'w') as f: f.write(config.to_string()) -- cgit v1.2.3 From 6f5fb5c503b5df96d0686002355da3633b1fc597 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Tue, 31 Aug 2021 21:28:08 +0200 Subject: vyos.ethtool: T3163: purify code to read current speed and duplex settings It makes no sense to have a parser for the ethtool value sin ethtool.py and ethernet.py - one instance ios more then enough! --- python/vyos/ethtool.py | 13 ++++++++++++- python/vyos/ifconfig/ethernet.py | 22 +++++----------------- 2 files changed, 17 insertions(+), 18 deletions(-) diff --git a/python/vyos/ethtool.py b/python/vyos/ethtool.py index 968e2e2a3..e803e28a1 100644 --- a/python/vyos/ethtool.py +++ b/python/vyos/ethtool.py @@ -44,6 +44,7 @@ class Ethtool: _speed_duplex = { } _ring_buffers = { } _driver_name = None + _auto_negotiation = None def __init__(self, ifname): # Get driver used for interface @@ -65,7 +66,6 @@ class Ethtool: reading = True if 'Supported pause frame use:' in line: reading = False - break if reading: for block in line.split(): if pattern.search(block): @@ -75,6 +75,15 @@ class Ethtool: self._speed_duplex.update({ speed : {}}) if duplex not in self._speed_duplex[speed]: self._speed_duplex[speed].update({ duplex : ''}) + if 'Auto-negotiation:' in line: + # Split the following string: Auto-negotiation: off + # we are only interested in off or on + tmp = line.split()[-1] + self._auto_negotiation = bool(tmp == 'on') + + if self._auto_negotiation == None: + raise ValueError(f'Could not determine auto-negotiation settings '\ + f'for interface {ifname}!') # Now populate features dictionaty out, err = popen(f'ethtool --show-features {ifname}') @@ -162,3 +171,5 @@ class Ethtool: return True return False + def get_auto_negotiation(self): + return self._auto_negotiation diff --git a/python/vyos/ifconfig/ethernet.py b/python/vyos/ifconfig/ethernet.py index 76ed3fd92..d4fa3f655 100644 --- a/python/vyos/ifconfig/ethernet.py +++ b/python/vyos/ifconfig/ethernet.py @@ -20,6 +20,7 @@ from vyos.ethtool import Ethtool from vyos.ifconfig.interface import Interface from vyos.util import run from vyos.util import dict_search +from vyos.util import read_file from vyos.validate import assert_list @Interface.register @@ -181,32 +182,19 @@ class EthernetIf(Interface): # Get current speed and duplex settings: ifname = self.config['ifname'] - cmd = f'ethtool {ifname}' - tmp = self._cmd(cmd) - - if re.search("\tAuto-negotiation: on", tmp): + if self.ethtool.get_auto_negotiation(): if speed == 'auto' and duplex == 'auto': # bail out early as nothing is to change return else: # read in current speed and duplex settings - cur_speed = 0 - cur_duplex = '' - for line in tmp.splitlines(): - if line.lstrip().startswith("Speed:"): - non_decimal = re.compile(r'[^\d.]+') - cur_speed = non_decimal.sub('', line) - continue - - if line.lstrip().startswith("Duplex:"): - cur_duplex = line.split()[-1].lower() - break - + cur_speed = read_file(f'/sys/class/net/{ifname}/speed') + cur_duplex = read_file(f'/sys/class/net/{ifname}/duplex') if (cur_speed == speed) and (cur_duplex == duplex): # bail out early as nothing is to change return - cmd = f'ethtool -s {ifname}' + cmd = f'ethtool --change {ifname}' if speed == 'auto' or duplex == 'auto': cmd += ' autoneg on' else: -- cgit v1.2.3 From 29082959e0efc02462fba8560d6726096e8743e9 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Tue, 31 Aug 2021 21:50:05 +0200 Subject: ethernet: T3163: only change ring-buffer settings if required Only update the RX/TX ring-buffer settings if they are different from the ones currently programmed to the hardware. There is no need to write the same value to the hardware again - this could cause traffic disruption on some NICs. --- python/vyos/ethtool.py | 29 ++++++++++++++++++++++------- python/vyos/ifconfig/ethernet.py | 15 ++++++++++----- src/conf_mode/interfaces-ethernet.py | 4 ++-- 3 files changed, 34 insertions(+), 14 deletions(-) diff --git a/python/vyos/ethtool.py b/python/vyos/ethtool.py index e803e28a1..f5796358d 100644 --- a/python/vyos/ethtool.py +++ b/python/vyos/ethtool.py @@ -43,6 +43,7 @@ class Ethtool: # } _speed_duplex = { } _ring_buffers = { } + _ring_buffers_max = { } _driver_name = None _auto_negotiation = None @@ -99,10 +100,20 @@ class Ethtool: 'fixed' : fixed } - out, err = popen(f'ethtool -g {ifname}') + out, err = popen(f'ethtool --show-ring {ifname}') # We are only interested in line 2-5 which contains the device maximum # ringbuffers for line in out.splitlines()[2:6]: + if ':' in line: + key, value = [s.strip() for s in line.strip().split(":", 1)] + key = key.lower().replace(' ', '_') + # T3645: ethtool version used on Debian Bullseye changed the + # output format from 0 -> n/a. As we are only interested in the + # tx/rx keys we do not care about RX Mini/Jumbo. + if value.isdigit(): + self._ring_buffers_max[key] = int(value) + # Now we wan't to get the current RX/TX ringbuffer values - used for + for line in out.splitlines()[7:11]: if ':' in line: key, value = [s.strip() for s in line.strip().split(":", 1)] key = key.lower().replace(' ', '_') @@ -143,15 +154,19 @@ class Ethtool: def get_tcp_segmentation_offload(self): return self._get_generic('tcp-segmentation-offload') - def get_rx_buffer(self): - # Configuration of RX ring-buffers is not supported on every device, + def get_ring_buffer_max(self, rx_tx): + # Configuration of RX/TX ring-buffers is not supported on every device, # thus when it's impossible return None - return self._ring_buffers.get('rx', None) + if rx_tx not in ['rx', 'tx']: + ValueError('Ring-buffer type must be either "rx" or "tx"') + return self._ring_buffers_max.get(rx_tx, None) - def get_tx_buffer(self): - # Configuration of TX ring-buffers is not supported on every device, + def get_ring_buffer(self, rx_tx): + # Configuration of RX/TX ring-buffers is not supported on every device, # thus when it's impossible return None - return self._ring_buffers.get('tx', None) + if rx_tx not in ['rx', 'tx']: + ValueError('Ring-buffer type must be either "rx" or "tx"') + return self._ring_buffers.get(rx_tx, None) def check_speed_duplex(self, speed, duplex): """ Check if the passed speed and duplex combination is supported by diff --git a/python/vyos/ifconfig/ethernet.py b/python/vyos/ifconfig/ethernet.py index d4fa3f655..e5da3ac25 100644 --- a/python/vyos/ifconfig/ethernet.py +++ b/python/vyos/ifconfig/ethernet.py @@ -316,21 +316,26 @@ class EthernetIf(Interface): print('Adapter does not support changing tcp-segmentation-offload settings!') return False - def set_ring_buffer(self, b_type, b_size): + def set_ring_buffer(self, rx_tx, size): """ Example: >>> from vyos.ifconfig import EthernetIf >>> i = EthernetIf('eth0') >>> i.set_ring_buffer('rx', '4096') """ + current_size = self.ethtool.get_ring_buffer(rx_tx) + if current_size == size: + # bail out early if nothing is about to change + return None + ifname = self.config['ifname'] - cmd = f'ethtool -G {ifname} {b_type} {b_size}' + cmd = f'ethtool --set-ring {ifname} {rx_tx} {size}' output, code = self._popen(cmd) # ethtool error codes: # 80 - value already setted # 81 - does not possible to set value if code and code != 80: - print(f'could not set "{b_type}" ring-buffer for {ifname}') + print(f'could not set "{rx_tx}" ring-buffer for {ifname}') return output def update(self, config): @@ -369,8 +374,8 @@ class EthernetIf(Interface): # Set interface ring buffer if 'ring_buffer' in config: - for b_type in config['ring_buffer']: - self.set_ring_buffer(b_type, config['ring_buffer'][b_type]) + for rx_tx, size in config['ring_buffer'].items(): + self.set_ring_buffer(rx_tx, size) # call base class first super().update(config) diff --git a/src/conf_mode/interfaces-ethernet.py b/src/conf_mode/interfaces-ethernet.py index 889f4856f..f604f787c 100755 --- a/src/conf_mode/interfaces-ethernet.py +++ b/src/conf_mode/interfaces-ethernet.py @@ -89,11 +89,11 @@ def verify(ethernet): f'settings to: {speed}/{duplex}!') if 'ring_buffer' in ethernet: - max_rx = ethtool.get_rx_buffer() + max_rx = ethtool.get_ring_buffer_max('rx') if not max_rx: raise ConfigError('Driver does not support RX ring-buffer configuration!') - max_tx = ethtool.get_tx_buffer() + max_tx = ethtool.get_ring_buffer_max('tx') if not max_tx: raise ConfigError('Driver does not support TX ring-buffer configuration!') -- cgit v1.2.3 From 8834c22dc3f5758c1d2364579acc428cfc0fe650 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Tue, 31 Aug 2021 22:00:27 +0200 Subject: op-mode: "show interfaces ethernet eth0 physical" should display ring-buffers --- op-mode-definitions/show-interfaces-ethernet.xml.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/op-mode-definitions/show-interfaces-ethernet.xml.in b/op-mode-definitions/show-interfaces-ethernet.xml.in index 6d50d6e90..c42efd21f 100644 --- a/op-mode-definitions/show-interfaces-ethernet.xml.in +++ b/op-mode-definitions/show-interfaces-ethernet.xml.in @@ -29,7 +29,7 @@ Show physical device information for specified ethernet interface - ethtool "$4"; ethtool --driver "$4" + ethtool "$4"; ethtool --show-ring "$4"; ethtool --driver "$4" -- cgit v1.2.3 From 0229645c8248decb5664056df8aa5cd5dff41802 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Tue, 31 Aug 2021 23:03:01 +0200 Subject: vyos.ethtool: T3163: purify code to read and change flow-control settings It makes no sense to have a parser for the ethtool values in ethtool.py and ethernet.py - one instance ios more then enough! --- python/vyos/ethtool.py | 23 +++++++++++++++++ python/vyos/ifconfig/ethernet.py | 42 ++++++++----------------------- src/conf_mode/interfaces-ethernet.py | 4 +++ src/migration-scripts/interfaces/20-to-21 | 8 ++++++ 4 files changed, 45 insertions(+), 32 deletions(-) diff --git a/python/vyos/ethtool.py b/python/vyos/ethtool.py index f5796358d..87b9d7dd0 100644 --- a/python/vyos/ethtool.py +++ b/python/vyos/ethtool.py @@ -46,6 +46,8 @@ class Ethtool: _ring_buffers_max = { } _driver_name = None _auto_negotiation = None + _flow_control = None + _flow_control_enabled = None def __init__(self, ifname): # Get driver used for interface @@ -123,6 +125,15 @@ class Ethtool: if value.isdigit(): self._ring_buffers[key] = int(value) + # Get current flow control settings, but this is not supported by + # all NICs (e.g. vmxnet3 does not support is) + out, err = popen(f'ethtool --show-pause {ifname}') + if len(out.splitlines()) > 1: + self._flow_control = True + # read current flow control setting, this returns: + # ['Autonegotiate:', 'on'] + self._flow_control_enabled = out.splitlines()[1].split()[-1] + def _get_generic(self, feature): """ Generic method to read self._features and return a tuple for feature @@ -186,5 +197,17 @@ class Ethtool: return True return False + def check_flow_control(self): + """ Check if the NIC supports flow-control """ + if self._driver_name in ['vmxnet3', 'virtio_net', 'xen_netfront']: + return False + return self._flow_control + + def get_flow_control(self): + if self._flow_control_enabled == None: + raise ValueError('Interface does not support changing '\ + 'flow-control settings!') + return self._flow_control_enabled + def get_auto_negotiation(self): return self._auto_negotiation diff --git a/python/vyos/ifconfig/ethernet.py b/python/vyos/ifconfig/ethernet.py index e5da3ac25..7bd269491 100644 --- a/python/vyos/ifconfig/ethernet.py +++ b/python/vyos/ifconfig/ethernet.py @@ -121,38 +121,16 @@ class EthernetIf(Interface): 'flow control settings!') return - # Get current flow control settings: - cmd = f'ethtool --show-pause {ifname}' - output, code = self._popen(cmd) - if code == 76: - # the interface does not support it - return '' - if code: - # never fail here as it prevent vyos to boot - print(f'unexpected return code {code} from {cmd}') - return '' - - # The above command returns - with tabs: - # - # Pause parameters for eth0: - # Autonegotiate: on - # RX: off - # TX: off - if re.search("Autonegotiate:\ton", output): - if enable == "on": - # flowcontrol is already enabled - no need to re-enable it again - # this will prevent the interface from flapping as applying the - # flow-control settings will take the interface down and bring - # it back up every time. - return '' - - # Assemble command executed on system. Unfortunately there is no way - # to change this setting via sysfs - cmd = f'ethtool --pause {ifname} autoneg {enable} tx {enable} rx {enable}' - output, code = self._popen(cmd) - if code: - print(f'could not set flowcontrol for {ifname}') - return output + current = self.ethtool.get_flow_control() + if current != enable: + # Assemble command executed on system. Unfortunately there is no way + # to change this setting via sysfs + cmd = f'ethtool --pause {ifname} autoneg {enable} tx {enable} rx {enable}' + output, code = self._popen(cmd) + if code: + print(f'Could not set flowcontrol for {ifname}') + return output + return None def set_speed_duplex(self, speed, duplex): """ diff --git a/src/conf_mode/interfaces-ethernet.py b/src/conf_mode/interfaces-ethernet.py index f604f787c..81ed36bf2 100755 --- a/src/conf_mode/interfaces-ethernet.py +++ b/src/conf_mode/interfaces-ethernet.py @@ -88,6 +88,10 @@ def verify(ethernet): raise ConfigError(f'Adapter does not support changing speed and duplex '\ f'settings to: {speed}/{duplex}!') + if 'disable_flow_control' in ethernet: + if not ethtool.check_flow_control(): + raise ConfigError('Adapter does not support changing flow-control settings!') + if 'ring_buffer' in ethernet: max_rx = ethtool.get_ring_buffer_max('rx') if not max_rx: diff --git a/src/migration-scripts/interfaces/20-to-21 b/src/migration-scripts/interfaces/20-to-21 index bd89dcdb4..0bd858760 100755 --- a/src/migration-scripts/interfaces/20-to-21 +++ b/src/migration-scripts/interfaces/20-to-21 @@ -104,6 +104,14 @@ for ifname in config.list_nodes(base): config.delete(speed_path) config.delete(duplex_path) + # Also while processing the interface configuration, not all adapters support + # changing disabling flow-control - or change this setting. If disabling + # flow-control is not supported by the NIC, we remove the setting from CLI + flow_control_path = base + [ifname, 'disable-flow-control'] + if config.exists(flow_control_path): + if not eth.check_flow_control(): + config.delete(flow_control_path) + try: with open(file_name, 'w') as f: f.write(config.to_string()) -- cgit v1.2.3 From 6c280b1ca52c8f2a80bbaea52aa3e09060af04b3 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Tue, 31 Aug 2021 23:31:29 +0200 Subject: vyos.ethtool: T3163: ring-buffer values should be stored as string Commit 29082959 ("ethernet: T3163: only change ring-buffer settings if required") added a delta-check code for the ring buffer values, unfortunately this was never properly evaluated as str() and int() got compared resulting always in an unequal result. --- python/vyos/ethtool.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/python/vyos/ethtool.py b/python/vyos/ethtool.py index 87b9d7dd0..609d83b5e 100644 --- a/python/vyos/ethtool.py +++ b/python/vyos/ethtool.py @@ -113,7 +113,7 @@ class Ethtool: # output format from 0 -> n/a. As we are only interested in the # tx/rx keys we do not care about RX Mini/Jumbo. if value.isdigit(): - self._ring_buffers_max[key] = int(value) + self._ring_buffers_max[key] = value # Now we wan't to get the current RX/TX ringbuffer values - used for for line in out.splitlines()[7:11]: if ':' in line: @@ -123,7 +123,7 @@ class Ethtool: # output format from 0 -> n/a. As we are only interested in the # tx/rx keys we do not care about RX Mini/Jumbo. if value.isdigit(): - self._ring_buffers[key] = int(value) + self._ring_buffers[key] = value # Get current flow control settings, but this is not supported by # all NICs (e.g. vmxnet3 does not support is) @@ -177,7 +177,7 @@ class Ethtool: # thus when it's impossible return None if rx_tx not in ['rx', 'tx']: ValueError('Ring-buffer type must be either "rx" or "tx"') - return self._ring_buffers.get(rx_tx, None) + return str(self._ring_buffers.get(rx_tx, None)) def check_speed_duplex(self, speed, duplex): """ Check if the passed speed and duplex combination is supported by -- cgit v1.2.3 From 468ba7b076c7145b7fe62b60b7e81b432bb27d54 Mon Sep 17 00:00:00 2001 From: Viacheslav Date: Tue, 31 Aug 2021 10:48:12 +0000 Subject: tunnel: T2920: Add checks tun with same source addr and keys 2 tunnels with the same local-address should has different keys Check existing tunnels (source-address key) with new tunnel. --- src/conf_mode/interfaces-tunnel.py | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/conf_mode/interfaces-tunnel.py b/src/conf_mode/interfaces-tunnel.py index 616a2e23c..bfd9a8c56 100755 --- a/src/conf_mode/interfaces-tunnel.py +++ b/src/conf_mode/interfaces-tunnel.py @@ -18,6 +18,7 @@ import os from sys import exit from netifaces import interfaces +from ipaddress import IPv4Address from vyos.config import Config from vyos.configdict import dict_merge @@ -31,6 +32,7 @@ from vyos.configverify import verify_mtu_ipv6 from vyos.configverify import verify_vrf from vyos.configverify import verify_tunnel from vyos.ifconfig import Interface +from vyos.ifconfig import Section from vyos.ifconfig import TunnelIf from vyos.template import is_ipv4 from vyos.template import is_ipv6 @@ -100,6 +102,27 @@ def verify(tunnel): dict_search('parameters.ip.key', tunnel) == None: raise ConfigError('Tunnel parameters ip key must be set!') + if tunnel['encapsulation'] in ['gre', 'gretap']: + if dict_search('parameters.ip.key', tunnel) != None: + # Check pairs tunnel source-address/encapsulation/key with exists tunnels. + # Prevent the same key for 2 tunnels with same source-address/encap. T2920 + for tunnel_if in Section.interfaces('tunnel'): + tunnel_cfg = get_interface_config(tunnel_if) + exist_encap = tunnel_cfg['linkinfo']['info_kind'] + exist_source_address = tunnel_cfg['address'] + exist_key = tunnel_cfg['linkinfo']['info_data']['ikey'] + new_source_address = tunnel['source_address'] + # Convert tunnel key to ip key, format "ip -j link show" + # 1 => 0.0.0.1, 999 => 0.0.3.231 + orig_new_key = int(tunnel['parameters']['ip']['key']) + new_key = IPv4Address(orig_new_key) + new_key = str(new_key) + if tunnel['encapsulation'] == exist_encap and \ + new_source_address == exist_source_address and \ + new_key == exist_key: + raise ConfigError(f'Key "{orig_new_key}" for source-address "{new_source_address}" ' \ + f'is already used for tunnel "{tunnel_if}"!') + verify_mtu_ipv6(tunnel) verify_address(tunnel) verify_vrf(tunnel) -- cgit v1.2.3 From bbe0deda9bfcfd4116c44b42156a628de8400b48 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Wed, 1 Sep 2021 21:51:55 +0200 Subject: login: T1948: fix username regex - add missing start ^ and end $ --- interface-definitions/system-login.xml.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface-definitions/system-login.xml.in b/interface-definitions/system-login.xml.in index 3c2c7dfa5..4bfe82268 100644 --- a/interface-definitions/system-login.xml.in +++ b/interface-definitions/system-login.xml.in @@ -12,7 +12,7 @@ Local user account information - [a-zA-Z0-9\-_\.]{1,100} + ^[-_a-zA-Z0-9.]{1,100} Username contains illegal characters or\nexceeds 100 character limitation. -- cgit v1.2.3 From 514da738173696c70440c959b9d7ec9afd77fbae Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Wed, 1 Sep 2021 21:52:20 +0200 Subject: login: T1948: add missing ssh-public key name regex --- interface-definitions/system-login.xml.in | 3 +++ 1 file changed, 3 insertions(+) diff --git a/interface-definitions/system-login.xml.in b/interface-definitions/system-login.xml.in index 4bfe82268..f4613b8a2 100644 --- a/interface-definitions/system-login.xml.in +++ b/interface-definitions/system-login.xml.in @@ -44,6 +44,9 @@ Remote access public keys + + ^[-_a-zA-Z0-9@]+$ + txt Key identifier used by ssh-keygen (usually of form user@host) -- cgit v1.2.3 From 0d1bb97861bc904ba6699b9d031f64adb33d044a Mon Sep 17 00:00:00 2001 From: DmitriyEshenko Date: Wed, 1 Sep 2021 18:53:08 +0000 Subject: pptp-server: T3790: Change ippool priority and define gw-ip-address (cherry picked from commit 23388fe193f04ab05f270098123cbb3e5f0b9f75) --- data/templates/accel-ppp/pptp.config.tmpl | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/data/templates/accel-ppp/pptp.config.tmpl b/data/templates/accel-ppp/pptp.config.tmpl index 5a6cfe749..3cfc4a906 100644 --- a/data/templates/accel-ppp/pptp.config.tmpl +++ b/data/templates/accel-ppp/pptp.config.tmpl @@ -2,12 +2,13 @@ [modules] log_syslog pptp -ippool +shaper {% if auth_mode == 'local' %} chap-secrets {% elif auth_mode == 'radius' %} radius {% endif %} +ippool {% for proto in auth_proto %} {{proto}} {% endfor %} @@ -87,6 +88,10 @@ nas-ip-address={{ radius_nas_ip }} bind={{ radius_source_address }} {% endif %} {% endif %} +{# Both chap-secrets and radius block required the gw-ip-address #} +{% if gw_ip is defined and gw_ip is not none %} +gw-ip-address={{ gw_ip }} +{% endif %} [cli] tcp=127.0.0.1:2003 -- cgit v1.2.3 From b1ff7baaf3c52c8c364955632fcece2da7033b10 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Thu, 2 Sep 2021 14:05:33 +0200 Subject: op-mode: T1376: speed up tab-completion for DHCP pool listing Commit 9f20bee81c ("T1376: improve show_dhcp and show_dhcpv6") added the tab completion helper to list the availbale IP pools to query. This was done by calling a python script which then called cli-shell-api which resulted in a penalty by the Python interpreter startup. This can be solved by directly using the cli-shell-api wrapper available as in op-mode - as also seen for DHCPv6. --- op-mode-definitions/dhcp.xml.in | 4 ++-- src/op_mode/show_dhcp.py | 8 ++------ 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/op-mode-definitions/dhcp.xml.in b/op-mode-definitions/dhcp.xml.in index 1dacbd5ba..6f0c25110 100644 --- a/op-mode-definitions/dhcp.xml.in +++ b/op-mode-definitions/dhcp.xml.in @@ -22,7 +22,7 @@ Show DHCP server leases for a specific pool - + service dhcp-server shared-network-name sudo ${vyos_op_scripts_dir}/show_dhcp.py --leases --pool $6 @@ -57,7 +57,7 @@ Show DHCP server statistics for a specific pool - + service dhcp-server shared-network-name sudo ${vyos_op_scripts_dir}/show_dhcp.py --statistics --pool $6 diff --git a/src/op_mode/show_dhcp.py b/src/op_mode/show_dhcp.py index 4df275e04..cd6e8ed43 100755 --- a/src/op_mode/show_dhcp.py +++ b/src/op_mode/show_dhcp.py @@ -177,7 +177,7 @@ if __name__ == '__main__': group = parser.add_mutually_exclusive_group() group.add_argument("-l", "--leases", action="store_true", help="Show DHCP leases") group.add_argument("-s", "--statistics", action="store_true", help="Show DHCP statistics") - group.add_argument("--allowed", type=str, choices=["pool", "sort", "state"], help="Show allowed values for argument") + group.add_argument("--allowed", type=str, choices=["sort", "state"], help="Show allowed values for argument") parser.add_argument("-p", "--pool", type=str, help="Show lease for specific pool") parser.add_argument("-S", "--sort", type=str, default='ip', help="Sort by") @@ -188,11 +188,7 @@ if __name__ == '__main__': conf = Config() - if args.allowed == 'pool': - if conf.exists_effective('service dhcp-server'): - print(' '.join(conf.list_effective_nodes("service dhcp-server shared-network-name"))) - exit(0) - elif args.allowed == 'sort': + if args.allowed == 'sort': print(' '.join(lease_display_fields.keys())) exit(0) elif args.allowed == 'state': -- cgit v1.2.3 From 8032f4bb72db064d2b3f6f0954f6674091822cc4 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Thu, 2 Sep 2021 16:05:40 +0200 Subject: login: radius: T3192: drop workaround required by get_config_dict() The workaround is no longer required, as the issue was resolved in get_config_dict() so if it is a node, a list is always returned. --- src/conf_mode/system-login.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/conf_mode/system-login.py b/src/conf_mode/system-login.py index f0b92aea8..93696e653 100755 --- a/src/conf_mode/system-login.py +++ b/src/conf_mode/system-login.py @@ -80,12 +80,6 @@ def get_config(config=None): login['radius']['server'][server] = dict_merge(default_values, login['radius']['server'][server]) - # XXX: for a yet unknown reason when we only have one source-address - # get_config_dict() will show a string over a string - if 'radius' in login and 'source_address' in login['radius']: - if isinstance(login['radius']['source_address'], str): - login['radius']['source_address'] = [login['radius']['source_address']] - # create a list of all users, cli and users all_users = list(set(local_users + cli_users)) # We will remove any normal users that dos not exist in the current -- cgit v1.2.3 From 658de9ea0fbe91e593f9cf0a8c434791282af100 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Thu, 2 Sep 2021 16:08:57 +0200 Subject: login: T3792: bugfix for usernames containing a hyphen While migrating to get_config_dict() in commit e8a1c291b1 ("login: radius: T3192: migrate to get_config_dict()") the user-name was not excluded from mangling (no_tag_node_value_mangle=True). This resulted in a username "vyos-user" from CLI to be actually created as "vyos_user" on the system. This commit also adds respective Smoketests to prevent this in the future. --- smoketest/scripts/cli/test_system_login.py | 41 +++++++++++++++++++++++++++--- src/conf_mode/system-login.py | 2 +- 2 files changed, 39 insertions(+), 4 deletions(-) diff --git a/smoketest/scripts/cli/test_system_login.py b/smoketest/scripts/cli/test_system_login.py index 8327235fb..af3a5851c 100755 --- a/smoketest/scripts/cli/test_system_login.py +++ b/smoketest/scripts/cli/test_system_login.py @@ -31,7 +31,19 @@ from vyos.util import read_file from vyos.template import inc_ip base_path = ['system', 'login'] -users = ['vyos1', 'vyos2'] +users = ['vyos1', 'vyos-roxx123', 'VyOS-123_super.Nice'] + +ssh_pubkey = """ +AAAAB3NzaC1yc2EAAAADAQABAAABgQD0NuhUOEtMIKnUVFIHoFatqX/c4mjerXyF +TlXYfVt6Ls2NZZsUSwHbnhK4BKDrPvVZMW/LycjQPzWW6TGtk6UbZP1WqdviQ9hP +jsEeKJSTKciMSvQpjBWyEQQPXSKYQC7ryQQilZDqnJgzqwzejKEe+nhhOdBvjuZc +uukxjT69E0UmWAwLxzvfiurwiQaC7tG+PwqvtfHOPL3i6yRO2C5ORpFarx8PeGDS +IfIXJCr3LoUbLHeuE7T2KaOKQcX0UsWJ4CoCapRLpTVYPDB32BYfgq7cW1Sal1re +EGH2PzuXBklinTBgCHA87lHjpwDIAqdmvMj7SXIW9LxazLtP+e37sexE7xEs0cpN +l68txdDbY2P2Kbz5mqGFfCvBYKv9V2clM5vyWNy/Xp5TsCis89nn83KJmgFS7sMx +pHJz8umqkxy3hfw0K7BRFtjWd63sbOP8Q/SDV7LPaIfIxenA9zv2rY7y+AIqTmSr +TTSb0X1zPGxPIRFy5GoGtO9Mm5h4OZk= +""" class TestSystemLogin(VyOSUnitTestSHIM.TestCase): def tearDown(self): @@ -42,6 +54,8 @@ class TestSystemLogin(VyOSUnitTestSHIM.TestCase): self.cli_commit() def test_add_linux_system_user(self): + # We are not allowed to re-use a username already taken by the Linux + # base system system_user = 'backup' self.cli_set(base_path + ['user', system_user, 'authentication', 'plaintext-password', system_user]) @@ -75,9 +89,30 @@ class TestSystemLogin(VyOSUnitTestSHIM.TestCase): (stdout, stderr) = proc.communicate() # stdout is something like this: - # b'Linux vyos 4.19.101-amd64-vyos #1 SMP Sun Feb 2 10:18:07 UTC 2020 x86_64 GNU/Linux\n' + # b'Linux LR1.wue3 5.10.61-amd64-vyos #1 SMP Fri Aug 27 08:55:46 UTC 2021 x86_64 GNU/Linux\n' self.assertTrue(len(stdout) > 40) + def test_system_user_ssh_key(self): + ssh_user = 'ssh-test_user' + public_keys = 'vyos' + type = 'ssh-rsa' + + self.cli_set(base_path + ['user', ssh_user, 'authentication', 'public-keys', public_keys, 'key', ssh_pubkey.replace('\n','')]) + + # check validate() - missing type for public-key + with self.assertRaises(ConfigSessionError): + self.cli_commit() + self.cli_set(base_path + ['user', ssh_user, 'authentication', 'public-keys', public_keys, 'type', type]) + + self.cli_commit() + + # Check that SSH key was written properly + tmp = cmd(f'sudo cat /home/{ssh_user}/.ssh/authorized_keys') + key = f'{type} ' + ssh_pubkey.replace('\n','') + self.assertIn(key, tmp) + + self.cli_delete(base_path + ['user', ssh_user]) + def test_radius_kernel_features(self): # T2886: RADIUS requires some Kernel options to be present kernel = platform.release() @@ -201,4 +236,4 @@ class TestSystemLogin(VyOSUnitTestSHIM.TestCase): self.assertTrue(tmp) if __name__ == '__main__': - unittest.main(verbosity=2) + unittest.main(verbosity=2, failfast=True) diff --git a/src/conf_mode/system-login.py b/src/conf_mode/system-login.py index 93696e653..318ff276d 100755 --- a/src/conf_mode/system-login.py +++ b/src/conf_mode/system-login.py @@ -59,7 +59,7 @@ def get_config(config=None): conf = Config() base = ['system', 'login'] login = conf.get_config_dict(base, key_mangling=('-', '_'), - get_first_key=True) + no_tag_node_value_mangle=True, get_first_key=True) # users no longer existing in the running configuration need to be deleted local_users = get_local_users() -- cgit v1.2.3 From 7e84566dedfdc532ffe05b404005daa6f21df567 Mon Sep 17 00:00:00 2001 From: Viacheslav Date: Thu, 2 Sep 2021 18:58:11 +0000 Subject: tunnel: T3788: Add check keys for ipip and sit Keys are not allowed with ipip and sit tunnels --- src/conf_mode/interfaces-tunnel.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/conf_mode/interfaces-tunnel.py b/src/conf_mode/interfaces-tunnel.py index bfd9a8c56..ef385d2e7 100755 --- a/src/conf_mode/interfaces-tunnel.py +++ b/src/conf_mode/interfaces-tunnel.py @@ -123,6 +123,11 @@ def verify(tunnel): raise ConfigError(f'Key "{orig_new_key}" for source-address "{new_source_address}" ' \ f'is already used for tunnel "{tunnel_if}"!') + # Keys are not allowed with ipip and sit tunnels + if tunnel['encapsulation'] in ['ipip', 'sit']: + if dict_search('parameters.ip.key', tunnel) != None: + raise ConfigError('Keys are not allowed with ipip and sit tunnels!') + verify_mtu_ipv6(tunnel) verify_address(tunnel) verify_vrf(tunnel) -- cgit v1.2.3 From 5f1c1ae4770fe36b5290f34d2f3a248c6b1a0ddb Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Fri, 3 Sep 2021 20:44:42 +0200 Subject: bgp: T3798: add support for neighbor local-as replace-as --- data/templates/frr/bgpd.frr.tmpl | 5 +++-- interface-definitions/include/bgp/neighbor-local-as.xml.i | 12 +++++++++--- smoketest/scripts/cli/test_protocols_bgp.py | 8 +++++--- src/conf_mode/protocols_bgp.py | 2 +- 4 files changed, 18 insertions(+), 9 deletions(-) diff --git a/data/templates/frr/bgpd.frr.tmpl b/data/templates/frr/bgpd.frr.tmpl index 96815836b..4ac2127cb 100644 --- a/data/templates/frr/bgpd.frr.tmpl +++ b/data/templates/frr/bgpd.frr.tmpl @@ -46,8 +46,9 @@ neighbor {{ neighbor }} {{ graceful_restart }} {% endif %} {% if config.local_as is defined and config.local_as is not none %} -{% for local_asn in config.local_as %} - neighbor {{ neighbor }} local-as {{ local_asn }} {{ 'no-prepend' if config.local_as[local_asn].no_prepend is defined }} +{% for local_asn, local_asn_config in config.local_as.items() %} +{# There can be only one local-as value, this is checked in the Python code #} + neighbor {{ neighbor }} local-as {{ local_asn }} {{ 'no-prepend' if local_asn_config.no_prepend is defined }} {{ 'replace-as' if local_asn_config.replace_as is defined }} {% endfor %} {% endif %} {% if config.override_capability is defined %} diff --git a/interface-definitions/include/bgp/neighbor-local-as.xml.i b/interface-definitions/include/bgp/neighbor-local-as.xml.i index 28c6b72b6..8cf0167fd 100644 --- a/interface-definitions/include/bgp/neighbor-local-as.xml.i +++ b/interface-definitions/include/bgp/neighbor-local-as.xml.i @@ -1,10 +1,10 @@ - Local AS number [REQUIRED] + Specify alternate ASN for this BGP process u32:1-4294967294 - Local AS number + Autonomous System Number (ASN) @@ -13,7 +13,13 @@ - Disable prepending local-as to updates from EBGP peers + Disable prepending local-as from/to updates for eBGP peers + + + + + + Prepend only local-as from/to updates for eBGP peers diff --git a/smoketest/scripts/cli/test_protocols_bgp.py b/smoketest/scripts/cli/test_protocols_bgp.py index df9dc342b..05919abbc 100755 --- a/smoketest/scripts/cli/test_protocols_bgp.py +++ b/smoketest/scripts/cli/test_protocols_bgp.py @@ -165,7 +165,7 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase): if 'multi_hop' in peer_config: self.assertIn(f' neighbor {peer} ebgp-multihop {peer_config["multi_hop"]}', frrconfig) if 'local_as' in peer_config: - self.assertIn(f' neighbor {peer} local-as {peer_config["local_as"]}', frrconfig) + self.assertIn(f' neighbor {peer} local-as {peer_config["local_as"]} no-prepend replace-as', frrconfig) if 'cap_over' in peer_config: self.assertIn(f' neighbor {peer} override-capability', frrconfig) if 'passive' in peer_config: @@ -284,7 +284,8 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase): if 'multi_hop' in peer_config: self.cli_set(base_path + ['neighbor', peer, 'ebgp-multihop', peer_config["multi_hop"]]) if 'local_as' in peer_config: - self.cli_set(base_path + ['neighbor', peer, 'local-as', peer_config["local_as"]]) + self.cli_set(base_path + ['neighbor', peer, 'local-as', peer_config["local_as"], 'no-prepend']) + self.cli_set(base_path + ['neighbor', peer, 'local-as', peer_config["local_as"], 'replace-as']) if 'cap_over' in peer_config: self.cli_set(base_path + ['neighbor', peer, 'override-capability']) if 'passive' in peer_config: @@ -353,7 +354,8 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase): if 'multi_hop' in config: self.cli_set(base_path + ['peer-group', peer_group, 'ebgp-multihop', config["multi_hop"]]) if 'local_as' in config: - self.cli_set(base_path + ['peer-group', peer_group, 'local-as', config["local_as"]]) + self.cli_set(base_path + ['peer-group', peer_group, 'local-as', config["local_as"], 'no-prepend']) + self.cli_set(base_path + ['peer-group', peer_group, 'local-as', config["local_as"], 'replace-as']) if 'cap_over' in config: self.cli_set(base_path + ['peer-group', peer_group, 'override-capability']) if 'passive' in config: diff --git a/src/conf_mode/protocols_bgp.py b/src/conf_mode/protocols_bgp.py index 7d05eed9f..e24fcef14 100755 --- a/src/conf_mode/protocols_bgp.py +++ b/src/conf_mode/protocols_bgp.py @@ -130,7 +130,7 @@ def verify(bgp): if 'local_as' in peer_config: if len(peer_config['local_as']) > 1: - raise ConfigError('Only one local-as number may be specified!') + raise ConfigError(f'Only one local-as number can be specified for peer "{peer}"!') # Neighbor local-as override can not be the same as the local-as # we use for this BGP instane! -- cgit v1.2.3 From 6b52387190f8213e7e02060e894c6ddd4fb7cb3d Mon Sep 17 00:00:00 2001 From: Paul Lettington Date: Fri, 3 Sep 2021 23:39:22 +0100 Subject: login: T971 allow quoting in public-keys options This patch allows the use of `"` in ssh public-key options which unlocks the ability to set the `from` option in a way that sshd will accept to limit what hosts a user can connect from. --- src/conf_mode/system-login.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/conf_mode/system-login.py b/src/conf_mode/system-login.py index 318ff276d..4dd7f936d 100755 --- a/src/conf_mode/system-login.py +++ b/src/conf_mode/system-login.py @@ -240,7 +240,9 @@ def apply(login): # XXX: Should we deny using root at all? home_dir = getpwnam(user).pw_dir render(f'{home_dir}/.ssh/authorized_keys', 'login/authorized_keys.tmpl', - user_config, permission=0o600, user=user, group='users') + user_config, permission=0o600, + formater=lambda _: _.replace(""", '"'), + user=user, group='users') except Exception as e: raise ConfigError(f'Adding user "{user}" raised exception: "{e}"') -- cgit v1.2.3 From dd210c92beeffae07f26dc72ab51d9a93219b582 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sat, 4 Sep 2021 10:18:19 +0200 Subject: bgp: T3798: "replace-as" option can only be used when "no-prepend" is defined Commit 5f1c1ae4 ("bgp: T3798: add support for neighbor local-as replace-as") added support for a new CLI option when the local-as is changed for a specified neighbor or peer-group. There was an error in the CLI / design as the "replace-as" option can only be used when "no-prepend" is defined. Thus "no-prepend" became a and the new "replace-as" leafNode is now a child of "no-prepend". --- data/templates/frr/bgpd.frr.tmpl | 4 ++-- .../include/bgp/neighbor-local-as.xml.i | 19 ++++++++++--------- smoketest/scripts/cli/test_protocols_bgp.py | 6 ++---- src/conf_mode/protocols_bgp.py | 4 ++-- 4 files changed, 16 insertions(+), 17 deletions(-) diff --git a/data/templates/frr/bgpd.frr.tmpl b/data/templates/frr/bgpd.frr.tmpl index 4ac2127cb..987b922da 100644 --- a/data/templates/frr/bgpd.frr.tmpl +++ b/data/templates/frr/bgpd.frr.tmpl @@ -46,9 +46,9 @@ neighbor {{ neighbor }} {{ graceful_restart }} {% endif %} {% if config.local_as is defined and config.local_as is not none %} -{% for local_asn, local_asn_config in config.local_as.items() %} +{% for local_as, local_as_config in config.local_as.items() %} {# There can be only one local-as value, this is checked in the Python code #} - neighbor {{ neighbor }} local-as {{ local_asn }} {{ 'no-prepend' if local_asn_config.no_prepend is defined }} {{ 'replace-as' if local_asn_config.replace_as is defined }} + neighbor {{ neighbor }} local-as {{ local_as }} {{ 'no-prepend' if local_as_config.no_prepend is defined }} {{ 'replace-as' if local_as_config.no_prepend is defined and local_as_config.no_prepend.replace_as is defined }} {% endfor %} {% endif %} {% if config.override_capability is defined %} diff --git a/interface-definitions/include/bgp/neighbor-local-as.xml.i b/interface-definitions/include/bgp/neighbor-local-as.xml.i index 8cf0167fd..8868e3093 100644 --- a/interface-definitions/include/bgp/neighbor-local-as.xml.i +++ b/interface-definitions/include/bgp/neighbor-local-as.xml.i @@ -11,18 +11,19 @@ - + Disable prepending local-as from/to updates for eBGP peers - - - - - Prepend only local-as from/to updates for eBGP peers - - - + + + + Prepend only local-as from/to updates for eBGP peers + + + + + diff --git a/smoketest/scripts/cli/test_protocols_bgp.py b/smoketest/scripts/cli/test_protocols_bgp.py index 05919abbc..29b5aa9d1 100755 --- a/smoketest/scripts/cli/test_protocols_bgp.py +++ b/smoketest/scripts/cli/test_protocols_bgp.py @@ -284,8 +284,7 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase): if 'multi_hop' in peer_config: self.cli_set(base_path + ['neighbor', peer, 'ebgp-multihop', peer_config["multi_hop"]]) if 'local_as' in peer_config: - self.cli_set(base_path + ['neighbor', peer, 'local-as', peer_config["local_as"], 'no-prepend']) - self.cli_set(base_path + ['neighbor', peer, 'local-as', peer_config["local_as"], 'replace-as']) + self.cli_set(base_path + ['neighbor', peer, 'local-as', peer_config["local_as"], 'no-prepend', 'replace-as']) if 'cap_over' in peer_config: self.cli_set(base_path + ['neighbor', peer, 'override-capability']) if 'passive' in peer_config: @@ -354,8 +353,7 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase): if 'multi_hop' in config: self.cli_set(base_path + ['peer-group', peer_group, 'ebgp-multihop', config["multi_hop"]]) if 'local_as' in config: - self.cli_set(base_path + ['peer-group', peer_group, 'local-as', config["local_as"], 'no-prepend']) - self.cli_set(base_path + ['peer-group', peer_group, 'local-as', config["local_as"], 'replace-as']) + self.cli_set(base_path + ['peer-group', peer_group, 'local-as', config["local_as"], 'no-prepend', 'replace-as']) if 'cap_over' in config: self.cli_set(base_path + ['peer-group', peer_group, 'override-capability']) if 'passive' in config: diff --git a/src/conf_mode/protocols_bgp.py b/src/conf_mode/protocols_bgp.py index e24fcef14..68284e0f9 100755 --- a/src/conf_mode/protocols_bgp.py +++ b/src/conf_mode/protocols_bgp.py @@ -140,7 +140,7 @@ def verify(bgp): # ttl-security and ebgp-multihop can't be used in the same configration if 'ebgp_multihop' in peer_config and 'ttl_security' in peer_config: - raise ConfigError('You can\'t set both ebgp-multihop and ttl-security hops') + raise ConfigError('You can not set both ebgp-multihop and ttl-security hops') # Check if neighbor has both override capability and strict capability match configured at the same time. if 'override_capability' in peer_config and 'strict_capability_match' in peer_config: @@ -148,7 +148,7 @@ def verify(bgp): # Check spaces in the password if 'password' in peer_config and ' ' in peer_config['password']: - raise ConfigError('You can\'t use spaces in the password') + raise ConfigError('Whitespace is not allowed in passwords!') # Some checks can/must only be done on a neighbor and not a peer-group if neighbor == 'neighbor': -- cgit v1.2.3 From 5bde11aceffd3d7fca99e582b16555fc0c584410 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sat, 4 Sep 2021 11:38:19 +0200 Subject: op-mode: import cleanup in "show interfaces" script --- src/op_mode/show_interfaces.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/op_mode/show_interfaces.py b/src/op_mode/show_interfaces.py index 241fba4f4..2eaaa34f8 100755 --- a/src/op_mode/show_interfaces.py +++ b/src/op_mode/show_interfaces.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 -# Copyright 2017, 2019 VyOS maintainers and contributors +# Copyright 2017-2021 VyOS maintainers and contributors # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public @@ -19,9 +19,7 @@ import os import re import sys import glob -import datetime import argparse -import netifaces from vyos.ifconfig import Section from vyos.ifconfig import Interface -- cgit v1.2.3 From 27e53fbcd843c3aad27db9e97f9060ae6dfcc5ee Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sat, 4 Sep 2021 11:39:01 +0200 Subject: op-mode: T3619: bugfix "show interfaces" for VLANs Commit 31169fa8a7 ("vyos.ifconfig: T3619: only set offloading options if supported by NIC") always instantiated an object of the Ethtool class for an ethernet object - this is right as a real ethernet interface is managed by Ethtool. Unfortunately the script used for "show interface" determindes the "base class" for an interface by its name, so eth0 -> Ethernet, eth0.10 -> Ethernet. This assumption is incorrect as a VLAN interface can not have the physical parameters changed of its underlaying interface. This can only be done for eth0. There is no need for the op-mode script to determine the implementation class for an interface at this level, as we are only interested in the state of the interface and it's IP addresses - which is a common operation valid for every interface on VyOS. --- src/op_mode/show_interfaces.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/op_mode/show_interfaces.py b/src/op_mode/show_interfaces.py index 2eaaa34f8..1707d8669 100755 --- a/src/op_mode/show_interfaces.py +++ b/src/op_mode/show_interfaces.py @@ -69,10 +69,9 @@ def filtered_interfaces(ifnames, iftypes, vif, vrrp): if ifnames and ifname not in ifnames: continue - # return the class which can handle this interface name - klass = Section.klass(ifname) - # connect to the interface - interface = klass(ifname, create=False, debug=False) + # As we are only "reading" from the interface - we must use the + # generic base class which exposes all the data via a common API + interface = Interface(ifname, create=False, debug=False) if iftypes and interface.definition['section'] not in iftypes: continue -- cgit v1.2.3 From 5e1f76d16332a917bfd99c6f2bffcd73e61d934d Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sat, 4 Sep 2021 12:37:23 +0200 Subject: op-mode: T3619: bugfix "show interfaces X detail" Commit 27e53fbc ("op-mode: T3619: bugfix "show interfaces" for VLANs") fixed the op-mode command for the "show interfaces" operation, but if a user was interested in all the ethernet or bridge interfaces, the command "show interfaces detail" did not yield any output. The filtered_interfaces() function was further generalized to only operate on base components and call itself recusively if required. --- src/op_mode/show_interfaces.py | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/op_mode/show_interfaces.py b/src/op_mode/show_interfaces.py index 1707d8669..3d50eb938 100755 --- a/src/op_mode/show_interfaces.py +++ b/src/op_mode/show_interfaces.py @@ -61,11 +61,12 @@ def filtered_interfaces(ifnames, iftypes, vif, vrrp): ifnames: a list of interfaces names to consider, empty do not filter return an instance of the interface class """ - allnames = Section.interfaces() + if isinstance(iftypes, list): + for iftype in iftypes: + yield from filtered_interfaces(ifnames, iftype, vif, vrrp) - vrrp_interfaces = VRRP.active_interfaces() if vrrp else [] - - for ifname in allnames: + for ifname in Section.interfaces(iftypes): + # Bail out early if interface name not part of our search list if ifnames and ifname not in ifnames: continue @@ -73,14 +74,14 @@ def filtered_interfaces(ifnames, iftypes, vif, vrrp): # generic base class which exposes all the data via a common API interface = Interface(ifname, create=False, debug=False) - if iftypes and interface.definition['section'] not in iftypes: - continue - + # VLAN interfaces have a '.' in their name by convention if vif and not '.' in ifname: continue - if vrrp and ifname not in vrrp_interfaces: - continue + if vrrp: + vrrp_interfaces = VRRP.active_interfaces() + if ifname not in vrrp_interfaces: + continue yield interface -- cgit v1.2.3 From e211cdbb375dba13af33d6ad6c3addab707f2870 Mon Sep 17 00:00:00 2001 From: Daniil Baturin Date: Sun, 5 Sep 2021 04:43:29 -0500 Subject: T3803: add source-address option to the op mode ping CLI. --- src/op_mode/ping.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/op_mode/ping.py b/src/op_mode/ping.py index 2144ab53c..60bbc0c78 100755 --- a/src/op_mode/ping.py +++ b/src/op_mode/ping.py @@ -62,8 +62,8 @@ options = { }, 'interface': { 'ping': '{command} -I {value}', - 'type': ' ', - 'help': 'Interface to use as source for ping' + 'type': '', + 'help': 'Source interface' }, 'interval': { 'ping': '{command} -i {value}', @@ -115,6 +115,10 @@ options = { 'type': '', 'help': 'Number of bytes to send' }, + 'source-address': { + 'ping': '{command} -I {value}', + 'type': ' ', + }, 'ttl': { 'ping': '{command} -t {value}', 'type': '', @@ -234,4 +238,4 @@ if __name__ == '__main__': # print(f'{command} {host}') os.system(f'{command} {host}') - \ No newline at end of file + -- cgit v1.2.3 From e48d9fbd35619cd4c06b1eb5aa66eb5889e3f0c0 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sun, 5 Sep 2021 18:03:11 +0200 Subject: system: T3804: rename migration script 20-to-21 -> 21-to-22 VyOS 1.3 equuleus now uses version 21 so we have to bump this by one. --- src/migration-scripts/system/20-to-21 | 57 ----------------------------------- src/migration-scripts/system/21-to-22 | 57 +++++++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+), 57 deletions(-) delete mode 100755 src/migration-scripts/system/20-to-21 create mode 100755 src/migration-scripts/system/21-to-22 diff --git a/src/migration-scripts/system/20-to-21 b/src/migration-scripts/system/20-to-21 deleted file mode 100755 index ad41be646..000000000 --- a/src/migration-scripts/system/20-to-21 +++ /dev/null @@ -1,57 +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 . - -import os - -from sys import exit, argv -from vyos.configtree import ConfigTree - -if (len(argv) < 1): - print("Must specify file name!") - exit(1) - -file_name = argv[1] -with open(file_name, 'r') as f: - config_file = f.read() - -base = ['system', 'sysctl'] -config = ConfigTree(config_file) - -if not config.exists(base): - # Nothing to do - exit(0) - -for all_custom in ['all', 'custom']: - if config.exists(base + [all_custom]): - for key in config.list_nodes(base + [all_custom]): - tmp = config.return_value(base + [all_custom, key, 'value']) - config.set(base + ['parameter', key, 'value'], value=tmp) - config.set_tag(base + ['parameter']) - config.delete(base + [all_custom]) - -for ipv4_param in ['net.ipv4.igmp_max_memberships', 'net.ipv4.ipfrag_time']: - if config.exists(base + [ipv4_param]): - tmp = config.return_value(base + [ipv4_param]) - config.set(base + ['parameter', ipv4_param, 'value'], value=tmp) - config.set_tag(base + ['parameter']) - config.delete(base + [ipv4_param]) - -try: - with open(file_name, 'w') as f: - f.write(config.to_string()) -except OSError as e: - print("Failed to save the modified config: {}".format(e)) - exit(1) diff --git a/src/migration-scripts/system/21-to-22 b/src/migration-scripts/system/21-to-22 new file mode 100755 index 000000000..ad41be646 --- /dev/null +++ b/src/migration-scripts/system/21-to-22 @@ -0,0 +1,57 @@ +#!/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, argv +from vyos.configtree import ConfigTree + +if (len(argv) < 1): + print("Must specify file name!") + exit(1) + +file_name = argv[1] +with open(file_name, 'r') as f: + config_file = f.read() + +base = ['system', 'sysctl'] +config = ConfigTree(config_file) + +if not config.exists(base): + # Nothing to do + exit(0) + +for all_custom in ['all', 'custom']: + if config.exists(base + [all_custom]): + for key in config.list_nodes(base + [all_custom]): + tmp = config.return_value(base + [all_custom, key, 'value']) + config.set(base + ['parameter', key, 'value'], value=tmp) + config.set_tag(base + ['parameter']) + config.delete(base + [all_custom]) + +for ipv4_param in ['net.ipv4.igmp_max_memberships', 'net.ipv4.ipfrag_time']: + if config.exists(base + [ipv4_param]): + tmp = config.return_value(base + [ipv4_param]) + config.set(base + ['parameter', ipv4_param, 'value'], value=tmp) + config.set_tag(base + ['parameter']) + config.delete(base + [ipv4_param]) + +try: + with open(file_name, 'w') as f: + f.write(config.to_string()) +except OSError as e: + print("Failed to save the modified config: {}".format(e)) + exit(1) -- cgit v1.2.3 From f7b1017559f4a511b116a5acd4705a15caf97865 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sun, 5 Sep 2021 17:56:28 +0200 Subject: name-server: T3804: merge "system name-servers-dhcp" into "system name-server" We have "set system name-server " to specify a name-server IP address we wan't to use. We also have "set system name-servers-dhcp " which does the same, but the name-server in question is retrieved via DHCP. Both CLI nodes are combined under "set system name-server " to keep things as they are in real life - we need a name-server. (cherry picked from commit 2ecf7a9f9cbe9359457bd23b4a0c45f3763123c7) --- interface-definitions/dns-domain-name.xml.in | 25 +++++++------- src/conf_mode/host_name.py | 49 ++++++++++++++++------------ src/migration-scripts/system/20-to-21 | 48 +++++++++++++++++++++++++++ 3 files changed, 88 insertions(+), 34 deletions(-) create mode 100755 src/migration-scripts/system/20-to-21 diff --git a/interface-definitions/dns-domain-name.xml.in b/interface-definitions/dns-domain-name.xml.in index ff632e1d1..2b1644609 100644 --- a/interface-definitions/dns-domain-name.xml.in +++ b/interface-definitions/dns-domain-name.xml.in @@ -1,37 +1,34 @@ - - Domain Name Servers (DNS) used by the system (resolv.conf) + System Domain Name Servers (DNS) 400 + + + ipv4 - Domain Name Server (DNS) address + Domain Name Server IPv4 address ipv6 - Domain Name Server (DNS) address + Domain Name Server IPv6 address + + + txt + Use Domain Name Server from DHCP interface + - - - Interfaces whose DHCP client nameservers will be used by the system (resolv.conf) - 400 - - - - - - System host name (default: vyos) diff --git a/src/conf_mode/host_name.py b/src/conf_mode/host_name.py index f4c75c257..a7135911d 100755 --- a/src/conf_mode/host_name.py +++ b/src/conf_mode/host_name.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2018-2020 VyOS maintainers and contributors +# Copyright (C) 2018-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 @@ -14,10 +14,6 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -""" -conf-mode script for 'system host-name' and 'system domain-name'. -""" - import re import sys import copy @@ -25,10 +21,13 @@ import copy import vyos.util import vyos.hostsd_client -from vyos.config import Config from vyos import ConfigError -from vyos.util import cmd, call, process_named_running - +from vyos.config import Config +from vyos.ifconfig import Section +from vyos.template import is_ip +from vyos.util import cmd +from vyos.util import call +from vyos.util import process_named_running from vyos import airbag airbag.enable() @@ -37,7 +36,7 @@ default_config_data = { 'domain_name': '', 'domain_search': [], 'nameserver': [], - 'nameservers_dhcp_interfaces': [], + 'nameservers_dhcp_interfaces': {}, 'static_host_mapping': {} } @@ -51,29 +50,37 @@ def get_config(config=None): hosts = copy.deepcopy(default_config_data) - hosts['hostname'] = conf.return_value("system host-name") + hosts['hostname'] = conf.return_value(['system', 'host-name']) # This may happen if the config is not loaded yet, # e.g. if run by cloud-init if not hosts['hostname']: hosts['hostname'] = default_config_data['hostname'] - if conf.exists("system domain-name"): - hosts['domain_name'] = conf.return_value("system domain-name") + if conf.exists(['system', 'domain-name']): + hosts['domain_name'] = conf.return_value(['system', 'domain-name']) hosts['domain_search'].append(hosts['domain_name']) - for search in conf.return_values("system domain-search domain"): + for search in conf.return_values(['system', 'domain-search', 'domain']): hosts['domain_search'].append(search) - hosts['nameserver'] = conf.return_values("system name-server") + if conf.exists(['system', 'name-server']): + for ns in conf.return_values(['system', 'name-server']): + if is_ip(ns): + hosts['nameserver'].append(ns) + else: + tmp = '' + if_type = Section.section(ns) + if conf.exists(['interfaces', if_type, ns, 'address']): + tmp = conf.return_values(['interfaces', if_type, ns, 'address']) - hosts['nameservers_dhcp_interfaces'] = conf.return_values("system name-servers-dhcp") + hosts['nameservers_dhcp_interfaces'].update({ ns : tmp }) # system static-host-mapping - for hn in conf.list_nodes('system static-host-mapping host-name'): + for hn in conf.list_nodes(['system', 'static-host-mapping', 'host-name']): hosts['static_host_mapping'][hn] = {} - hosts['static_host_mapping'][hn]['address'] = conf.return_value(f'system static-host-mapping host-name {hn} inet') - hosts['static_host_mapping'][hn]['aliases'] = conf.return_values(f'system static-host-mapping host-name {hn} alias') + hosts['static_host_mapping'][hn]['address'] = conf.return_value(['system', 'static-host-mapping', 'host-name', hn, 'inet']) + hosts['static_host_mapping'][hn]['aliases'] = conf.return_values(['system', 'static-host-mapping', 'host-name', hn, 'alias']) return hosts @@ -103,8 +110,10 @@ def verify(hosts): if not hostname_regex.match(a) and len(a) != 0: raise ConfigError(f'Invalid alias "{a}" in static-host-mapping "{host}"') - # TODO: add warnings for nameservers_dhcp_interfaces if interface doesn't - # exist or doesn't have address dhcp(v6) + for interface, interface_config in hosts['nameservers_dhcp_interfaces'].items(): + # Warnin user if interface does not have DHCP or DHCPv6 configured + if not set(interface_config).intersection(['dhcp', 'dhcpv6']): + print(f'WARNING: "{interface}" is not a DHCP interface but uses DHCP name-server option!') return None diff --git a/src/migration-scripts/system/20-to-21 b/src/migration-scripts/system/20-to-21 new file mode 100755 index 000000000..1728995de --- /dev/null +++ b/src/migration-scripts/system/20-to-21 @@ -0,0 +1,48 @@ +#!/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 . + +# T3795: merge "system name-servers-dhcp" into "system name-server" + +import os + +from sys import argv +from vyos.configtree import ConfigTree + +if (len(argv) < 1): + print("Must specify file name!") + exit(1) + +file_name = argv[1] +with open(file_name, 'r') as f: + config_file = f.read() + +base = ['system', 'name-servers-dhcp'] +config = ConfigTree(config_file) +if not config.exists(base): + # Nothing to do + exit(0) + +for interface in config.return_values(base): + config.set(['system', 'name-server'], value=interface, replace=False) + +config.delete(base) + +try: + with open(file_name, 'w') as f: + f.write(config.to_string()) +except OSError as e: + print("Failed to save the modified config: {}".format(e)) + exit(1) -- cgit v1.2.3 From 0a82936aff8bbfcc6253f4767ea82805525c47ec Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Mon, 6 Sep 2021 07:57:16 +0200 Subject: smoketest: config: add DHCP name-server to dialup-router-medium-vpn config T3804 changed how DHCP servers from DHCP interfaces are read in and passed to the system. The config migrator is tested with this addition. --- smoketest/configs/dialup-router-medium-vpn | 1 + 1 file changed, 1 insertion(+) diff --git a/smoketest/configs/dialup-router-medium-vpn b/smoketest/configs/dialup-router-medium-vpn index dfb3d9621..af7c075e4 100644 --- a/smoketest/configs/dialup-router-medium-vpn +++ b/smoketest/configs/dialup-router-medium-vpn @@ -624,6 +624,7 @@ system { } } name-server 192.168.0.1 + name-servers-dhcp pppoe0 ntp { allow-clients { address 192.168.0.0/16 -- cgit v1.2.3 From 10814c4d3360598262e991e4b20768dfcde91d75 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Mon, 6 Sep 2021 08:23:24 +0200 Subject: wwan: T3620: op-mode: not all commands supported by all modems - add info message --- op-mode-definitions/show-interfaces-wwan.xml.in | 4 ++-- src/op_mode/show_wwan.py | 18 +++++++++++------- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/op-mode-definitions/show-interfaces-wwan.xml.in b/op-mode-definitions/show-interfaces-wwan.xml.in index d57e17a13..7e5f49ba6 100644 --- a/op-mode-definitions/show-interfaces-wwan.xml.in +++ b/op-mode-definitions/show-interfaces-wwan.xml.in @@ -68,9 +68,9 @@ sudo ${vyos_op_scripts_dir}/show_wwan.py --interface=$4 --sim - + - Show WWAN module information summary + Show WWAN module detailed information summary mmcli --modem ${4#wwan} diff --git a/src/op_mode/show_wwan.py b/src/op_mode/show_wwan.py index 249dda2a5..529b5bd0f 100755 --- a/src/op_mode/show_wwan.py +++ b/src/op_mode/show_wwan.py @@ -34,13 +34,17 @@ required = parser.add_argument_group('Required arguments') required.add_argument("--interface", help="WWAN interface name, e.g. wwan0", required=True) def qmi_cmd(device, command, silent=False): - tmp = cmd(f'qmicli --device={device} --device-open-proxy {command}') - tmp = tmp.replace(f'[{cdc}] ', '') - if not silent: - # skip first line as this only holds the info headline - for line in tmp.splitlines()[1:]: - print(line.lstrip()) - return tmp + try: + tmp = cmd(f'qmicli --device={device} --device-open-proxy {command}') + tmp = tmp.replace(f'[{cdc}] ', '') + if not silent: + # skip first line as this only holds the info headline + for line in tmp.splitlines()[1:]: + print(line.lstrip()) + return tmp + except: + print('Command not supported by Modem') + exit(1) if __name__ == '__main__': args = parser.parse_args() -- cgit v1.2.3 From 7546e249708de3e0b4bf8f89912caf73265edd60 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Mon, 6 Sep 2021 09:56:06 +0200 Subject: https: T2230: only support TLS1.2 and TLS1.3 --- data/templates/https/nginx.default.tmpl | 1 + 1 file changed, 1 insertion(+) diff --git a/data/templates/https/nginx.default.tmpl b/data/templates/https/nginx.default.tmpl index 2f8aa06c2..9d73baeee 100644 --- a/data/templates/https/nginx.default.tmpl +++ b/data/templates/https/nginx.default.tmpl @@ -39,6 +39,7 @@ server { # include snippets/snakeoil.conf; {% endif %} + ssl_protocols TLSv1.2 TLSv1.3; # proxy settings for HTTP API, if enabled; 503, if not location ~ /(retrieve|configure|config-file|image|generate|show|docs|openapi.json|redoc|graphql) { -- cgit v1.2.3 From b060fb70cdca155027f92222ea0d989f8e9bf21f Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Mon, 6 Sep 2021 12:02:06 +0200 Subject: pki: xml: T3642: use "txt" as format identifier --- interface-definitions/include/pki/ca-certificate.xml.i | 8 ++++---- interface-definitions/include/pki/certificate.xml.i | 8 ++++---- interface-definitions/include/pki/private-key.xml.i | 8 ++++---- interface-definitions/include/pki/public-key.xml.i | 8 ++++---- 4 files changed, 16 insertions(+), 16 deletions(-) diff --git a/interface-definitions/include/pki/ca-certificate.xml.i b/interface-definitions/include/pki/ca-certificate.xml.i index 14295a281..b32bb676a 100644 --- a/interface-definitions/include/pki/ca-certificate.xml.i +++ b/interface-definitions/include/pki/ca-certificate.xml.i @@ -2,13 +2,13 @@ Certificate Authority in PKI configuration - - CA name - Name of CA in PKI configuration - pki ca + + txt + Name of CA in PKI configuration + diff --git a/interface-definitions/include/pki/certificate.xml.i b/interface-definitions/include/pki/certificate.xml.i index 436aa90ba..1ba70e058 100644 --- a/interface-definitions/include/pki/certificate.xml.i +++ b/interface-definitions/include/pki/certificate.xml.i @@ -2,13 +2,13 @@ Certificate in PKI configuration - - cert name - Name of certificate in PKI configuration - pki certificate + + txt + Name of certificate in PKI configuration + diff --git a/interface-definitions/include/pki/private-key.xml.i b/interface-definitions/include/pki/private-key.xml.i index 6099daa89..ae4e9103e 100644 --- a/interface-definitions/include/pki/private-key.xml.i +++ b/interface-definitions/include/pki/private-key.xml.i @@ -7,13 +7,13 @@ Private key in PKI configuration - - key name - Name of private key in PKI configuration - pki key-pair + + txt + Name of private key in PKI configuration + diff --git a/interface-definitions/include/pki/public-key.xml.i b/interface-definitions/include/pki/public-key.xml.i index dfc6979fd..3067bff74 100644 --- a/interface-definitions/include/pki/public-key.xml.i +++ b/interface-definitions/include/pki/public-key.xml.i @@ -2,13 +2,13 @@ Public key in PKI configuration - - key name - Name of public key in PKI configuration - pki key-pair + + txt + Name of public key in PKI configuration + -- cgit v1.2.3 From 9d0c37fbbc91acc9f2c0f2abaab360479e451f0f Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Mon, 6 Sep 2021 12:02:56 +0200 Subject: vyos.util: T2755: rename dict_search() function args to match other implementations --- python/vyos/util.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/python/vyos/util.py b/python/vyos/util.py index 93a2f6640..18b7f5fcb 100644 --- a/python/vyos/util.py +++ b/python/vyos/util.py @@ -692,21 +692,21 @@ def find_device_file(device): return None -def dict_search(path, my_dict): - """ Traverse Python dictionary (my_dict) delimited by dot (.). +def dict_search(path, dict_object): + """ Traverse Python dictionary (dict_object) delimited by dot (.). Return value of key if found, None otherwise. - This is faster implementation then jmespath.search('foo.bar', my_dict)""" - if not isinstance(my_dict, dict) or not path: + This is faster implementation then jmespath.search('foo.bar', dict_object)""" + if not isinstance(dict_object, dict) or not path: return None parts = path.split('.') inside = parts[:-1] if not inside: - if path not in my_dict: + if path not in dict_object: return None - return my_dict[path] - c = my_dict + return dict_object[path] + c = dict_object for p in parts[:-1]: c = c.get(p, {}) return c.get(parts[-1], None) -- cgit v1.2.3 From acc6e461a92b14091fef9f49514f26364579391d Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Mon, 6 Sep 2021 12:09:57 +0200 Subject: vyos.util: add function to search a key recursively in a dictionary data = { 'interfaces': {'dummy': {'dum0': {'address': ['192.0.2.17/29']}}, 'ethernet': {'eth0': {'address': ['2001:db8::1/64', '192.0.2.1/29'], 'description': 'Test123', 'duplex': 'auto', 'hw_id': '00:00:00:00:00:01', 'speed': 'auto'}, 'eth1': {'address': ['192.0.2.9/29'], 'description': 'Test456', 'duplex': 'auto', 'hw_id': '00:00:00:00:00:02', 'speed': 'auto'}}} } dict_search_recursive(data, 'hw_id') will yield both '00:00:00:00:00:01' and '00:00:00:00:00:02' as generator object. --- python/vyos/util.py | 17 +++++++++++++++++ src/tests/test_dict_search.py | 21 ++++++++++++++++++++- 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/python/vyos/util.py b/python/vyos/util.py index 18b7f5fcb..b41c5b346 100644 --- a/python/vyos/util.py +++ b/python/vyos/util.py @@ -724,6 +724,23 @@ def dict_search_args(dict_object, *path): dict_object = dict_object[item] return dict_object +def dict_search_recursive(dict_object, key): + """ Traverse a dictionary recurisvely and return the value of the key + we are looking for. + + Thankfully copied from https://stackoverflow.com/a/19871956 + """ + if isinstance(dict_object, list): + for i in dict_object: + for x in dict_search_recursive(i, key): + yield x + elif isinstance(dict_object, dict): + if key in dict_object: + yield dict_object[key] + for j in dict_object.values(): + for x in dict_search_recursive(j, key): + yield x + def get_interface_config(interface): """ Returns the used encapsulation protocol for given interface. If interface does not exist, None is returned. diff --git a/src/tests/test_dict_search.py b/src/tests/test_dict_search.py index 991722f0f..1028437b2 100644 --- a/src/tests/test_dict_search.py +++ b/src/tests/test_dict_search.py @@ -16,13 +16,25 @@ from unittest import TestCase from vyos.util import dict_search +from vyos.util import dict_search_recursive data = { 'string': 'fooo', 'nested': {'string': 'bar', 'empty': '', 'list': ['foo', 'bar']}, 'non': {}, 'list': ['bar', 'baz'], - 'dict': {'key_1': {}, 'key_2': 'vyos'} + 'dict': {'key_1': {}, 'key_2': 'vyos'}, + 'interfaces': {'dummy': {'dum0': {'address': ['192.0.2.17/29']}}, + 'ethernet': {'eth0': {'address': ['2001:db8::1/64', '192.0.2.1/29'], + 'description': 'Test123', + 'duplex': 'auto', + 'hw_id': '00:00:00:00:00:01', + 'speed': 'auto'}, + 'eth1': {'address': ['192.0.2.9/29'], + 'description': 'Test456', + 'duplex': 'auto', + 'hw_id': '00:00:00:00:00:02', + 'speed': 'auto'}}} } class TestDictSearch(TestCase): @@ -63,3 +75,10 @@ class TestDictSearch(TestCase): # TestDictSearch: Return list items when querying nested list self.assertEqual(dict_search('nested.list', None), None) self.assertEqual(dict_search(None, data), None) + + def test_dict_search_recursive(self): + # Test nested search in dictionary + tmp = list(dict_search_recursive(data, 'hw_id')) + self.assertEqual(len(tmp), 2) + tmp = list(dict_search_recursive(data, 'address')) + self.assertEqual(len(tmp), 3) -- cgit v1.2.3 From 1fc69810450a306310fc6bf82cd0b2406d029fb2 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Mon, 6 Sep 2021 12:12:40 +0200 Subject: pki: T3642: verify() that we can not delete certificates still referenced in CLI --- src/conf_mode/pki.py | 46 +++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 41 insertions(+), 5 deletions(-) diff --git a/src/conf_mode/pki.py b/src/conf_mode/pki.py index ef1b57650..efa3578b4 100755 --- a/src/conf_mode/pki.py +++ b/src/conf_mode/pki.py @@ -16,8 +16,11 @@ from sys import exit +import jmespath + from vyos.config import Config from vyos.configdict import dict_merge +from vyos.configdict import node_changed from vyos.pki import is_ca_certificate from vyos.pki import load_certificate from vyos.pki import load_certificate_request @@ -26,6 +29,7 @@ from vyos.pki import load_private_key from vyos.pki import load_crl from vyos.pki import load_dh_parameters from vyos.util import ask_input +from vyos.util import dict_search_recursive from vyos.xml import defaults from vyos import ConfigError from vyos import airbag @@ -37,14 +41,29 @@ def get_config(config=None): else: conf = Config() base = ['pki'] - if not conf.exists(base): - return None pki = conf.get_config_dict(base, key_mangling=('-', '_'), - get_first_key=True, no_tag_node_value_mangle=True) + get_first_key=True, + no_tag_node_value_mangle=True) + + pki['changed'] = {} + tmp = node_changed(conf, base + ['ca'], key_mangling=('-', '_')) + if tmp: pki['changed'].update({'ca' : tmp}) + + tmp = node_changed(conf, base + ['certificate'], key_mangling=('-', '_')) + if tmp: pki['changed'].update({'certificate' : tmp}) + + # We only merge on the defaults of there is a configuration at all + if conf.exists(base): + default_values = defaults(base) + pki = dict_merge(default_values, pki) + + # We need to get the entire system configuration to verify that we are not + # deleting a certificate that is still referenced somewhere! + pki['system'] = conf.get_config_dict([], key_mangling=('-', '_'), + get_first_key=True, + no_tag_node_value_mangle=True) - default_values = defaults(base) - pki = dict_merge(default_values, pki) return pki def is_valid_certificate(raw_data): @@ -142,6 +161,21 @@ def verify(pki): if len(country) != 2 or not country.isalpha(): raise ConfigError(f'Invalid default country value. Value must be 2 alpha characters.') + if 'changed' in pki: + # if the list is getting longer, we can move to a dict() and also embed the + # search key as value from line 173 or 176 + for cert_type in ['ca', 'certificate']: + if not cert_type in pki['changed']: + continue + for certificate in pki['changed'][cert_type]: + if cert_type not in pki or certificate not in pki['changed'][cert_type]: + if cert_type == 'ca': + if certificate in dict_search_recursive(pki['system'], 'ca_certificate'): + raise ConfigError(f'CA certificate "{certificate}" is still in use!') + elif cert_type == 'certificate': + if certificate in dict_search_recursive(pki['system'], 'certificate'): + raise ConfigError(f'Certificate "{certificate}" is still in use!') + return None def generate(pki): @@ -154,6 +188,8 @@ def apply(pki): if not pki: return None + # XXX: restart services if the content of a certificate changes + return None if __name__ == '__main__': -- cgit v1.2.3 From 84a429b41175b95634ec9492e0cf3a564a47abdd Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Mon, 6 Sep 2021 21:17:42 +0200 Subject: ifconfig: T3806: "ipv6 address no_default_link_local" required for MTU < 1280 This commit also extends the smoketest to verify that the exception for this error is raised. --- python/vyos/configverify.py | 24 ++++++++++++------------ smoketest/scripts/cli/base_interfaces_test.py | 10 +++++++++- src/conf_mode/interfaces-ethernet.py | 15 +++++++-------- 3 files changed, 28 insertions(+), 21 deletions(-) diff --git a/python/vyos/configverify.py b/python/vyos/configverify.py index 7f49aa9af..760255d0e 100644 --- a/python/vyos/configverify.py +++ b/python/vyos/configverify.py @@ -67,22 +67,22 @@ def verify_mtu_ipv6(config): min_mtu = 1280 if int(config['mtu']) < min_mtu: interface = config['ifname'] - error_msg = f'IPv6 address will be configured on interface "{interface}" ' \ - f'thus the minimum MTU requirement is {min_mtu}!' + error_msg = f'IPv6 address will be configured on interface "{interface}",\n' \ + f'the required minimum MTU is {min_mtu}!' - for address in (dict_search('address', config) or []): - if address in ['dhcpv6'] or is_ipv6(address): - raise ConfigError(error_msg) + if 'address' in config: + for address in config['address']: + if address in ['dhcpv6'] or is_ipv6(address): + raise ConfigError(error_msg) - tmp = dict_search('ipv6.address', config) - if tmp and 'no_default_link_local' not in tmp: - raise ConfigError('link-local ' + error_msg) + tmp = dict_search('ipv6.address.no_default_link_local', config) + if tmp == None: raise ConfigError('link-local ' + error_msg) - if tmp and 'autoconf' in tmp: - raise ConfigError(error_msg) + tmp = dict_search('ipv6.address.autoconf', config) + if tmp != None: raise ConfigError(error_msg) - if tmp and 'eui64' in tmp: - raise ConfigError(error_msg) + tmp = dict_search('ipv6.address.eui64', config) + if tmp != None: raise ConfigError(error_msg) def verify_vrf(config): """ diff --git a/smoketest/scripts/cli/base_interfaces_test.py b/smoketest/scripts/cli/base_interfaces_test.py index edb604dbf..6a5b9c4ee 100644 --- a/smoketest/scripts/cli/base_interfaces_test.py +++ b/smoketest/scripts/cli/base_interfaces_test.py @@ -246,11 +246,19 @@ class BasicInterfaceTest: for intf in self._interfaces: base = self._base_path + [intf] self.cli_set(base + ['mtu', self._mtu]) - self.cli_set(base + ['ipv6', 'address', 'no-default-link-local']) for option in self._options.get(intf, []): self.cli_set(base + option.split()) + # check validate() - can not set low MTU if 'no-default-link-local' + # is not set on CLI + with self.assertRaises(ConfigSessionError): + self.cli_commit() + + for intf in self._interfaces: + base = self._base_path + [intf] + self.cli_set(base + ['ipv6', 'address', 'no-default-link-local']) + # commit interface changes self.cli_commit() diff --git a/src/conf_mode/interfaces-ethernet.py b/src/conf_mode/interfaces-ethernet.py index 81ed36bf2..2a094af52 100755 --- a/src/conf_mode/interfaces-ethernet.py +++ b/src/conf_mode/interfaces-ethernet.py @@ -72,6 +72,13 @@ def verify(ethernet): ifname = ethernet['ifname'] verify_interface_exists(ifname) + verify_mtu(ethernet) + verify_mtu_ipv6(ethernet) + verify_dhcpv6(ethernet) + verify_address(ethernet) + verify_vrf(ethernet) + verify_eapol(ethernet) + verify_mirror(ethernet) ethtool = Ethtool(ifname) # No need to check speed and duplex keys as both have default values. @@ -111,14 +118,6 @@ def verify(ethernet): raise ConfigError(f'Driver only supports a maximum TX ring-buffer '\ f'size of "{max_tx}" bytes!') - verify_mtu(ethernet) - verify_mtu_ipv6(ethernet) - verify_dhcpv6(ethernet) - verify_address(ethernet) - verify_vrf(ethernet) - verify_eapol(ethernet) - verify_mirror(ethernet) - # verify offloading capabilities if dict_search('offload.rps', ethernet) != None: if not os.path.exists(f'/sys/class/net/{ifname}/queues/rx-0/rps_cpus'): -- cgit v1.2.3 From b45cef9185cc78de7bc4170403e47b8ee1d14e3b Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Mon, 6 Sep 2021 21:22:57 +0200 Subject: pki: eapol: T3642: use write_file() to store certificates --- src/conf_mode/interfaces-ethernet.py | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/conf_mode/interfaces-ethernet.py b/src/conf_mode/interfaces-ethernet.py index 2a094af52..31998a9a8 100755 --- a/src/conf_mode/interfaces-ethernet.py +++ b/src/conf_mode/interfaces-ethernet.py @@ -37,6 +37,7 @@ from vyos.pki import wrap_private_key from vyos.template import render from vyos.util import call from vyos.util import dict_search +from vyos.util import write_file from vyos import ConfigError from vyos import airbag airbag.enable() @@ -156,19 +157,16 @@ def generate(ethernet): cert_name = ethernet['eapol']['certificate'] pki_cert = ethernet['pki']['certificate'][cert_name] - with open(cert_file_path, 'w') as f: - f.write(wrap_certificate(pki_cert['certificate'])) - - with open(cert_key_path, 'w') as f: - f.write(wrap_private_key(pki_cert['private']['key'])) + write_file(cert_file_path, wrap_certificate(pki_cert['certificate'])) + write_file(cert_key_path, wrap_private_key(pki_cert['private']['key'])) if 'ca_certificate' in ethernet['eapol']: ca_cert_file_path = os.path.join(cfg_dir, f'{ifname}_ca.pem') ca_cert_name = ethernet['eapol']['ca_certificate'] pki_ca_cert = ethernet['pki']['ca'][cert_name] - with open(ca_cert_file_path, 'w') as f: - f.write(wrap_certificate(pki_ca_cert['certificate'])) + write_file(ca_cert_file_path, + wrap_certificate(pki_ca_cert['certificate'])) else: # delete configuration on interface removal if os.path.isfile(wpa_suppl_conf.format(**ethernet)): -- cgit v1.2.3 From 3cd59879451504b4da865cc066112b1da882e5af Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Mon, 6 Sep 2021 21:23:44 +0200 Subject: pki: eapol: T3642: only add "pki" key to interface dict if pki is configured --- python/vyos/configverify.py | 3 +- smoketest/scripts/cli/test_interfaces_ethernet.py | 42 +++++++++++++++++------ src/conf_mode/interfaces-ethernet.py | 10 +++--- 3 files changed, 38 insertions(+), 17 deletions(-) diff --git a/python/vyos/configverify.py b/python/vyos/configverify.py index 760255d0e..8aca76568 100644 --- a/python/vyos/configverify.py +++ b/python/vyos/configverify.py @@ -152,11 +152,10 @@ def verify_eapol(config): if 'certificate' not in config['eapol']: raise ConfigError('Certificate must be specified when using EAPoL!') - if 'certificate' not in config['pki']: + if 'pki' not in config or 'certificate' not in config['pki']: raise ConfigError('Invalid certificate specified for EAPoL') cert_name = config['eapol']['certificate'] - if cert_name not in config['pki']['certificate']: raise ConfigError('Invalid certificate specified for EAPoL') diff --git a/smoketest/scripts/cli/test_interfaces_ethernet.py b/smoketest/scripts/cli/test_interfaces_ethernet.py index a9cdab16a..6d80e4c96 100755 --- a/smoketest/scripts/cli/test_interfaces_ethernet.py +++ b/smoketest/scripts/cli/test_interfaces_ethernet.py @@ -25,9 +25,26 @@ from vyos.util import cmd from vyos.util import process_named_running from vyos.util import read_file -pki_path = ['pki'] -cert_data = 'MIICFDCCAbugAwIBAgIUfMbIsB/ozMXijYgUYG80T1ry+mcwCgYIKoZIzj0EAwIwWTELMAkGA1UEBhMCR0IxEzARBgNVBAgMClNvbWUtU3RhdGUxEjAQBgNVBAcMCVNvbWUtQ2l0eTENMAsGA1UECgwEVnlPUzESMBAGA1UEAwwJVnlPUyBUZXN0MB4XDTIxMDcyMDEyNDUxMloXDTI2MDcxOTEyNDUxMlowWTELMAkGA1UEBhMCR0IxEzARBgNVBAgMClNvbWUtU3RhdGUxEjAQBgNVBAcMCVNvbWUtQ2l0eTENMAsGA1UECgwEVnlPUzESMBAGA1UEAwwJVnlPUyBUZXN0MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE01HrLcNttqq4/PtoMua8rMWEkOdBu7vP94xzDO7A8C92ls1v86eePy4QllKCzIw3QxBIoCuH2peGRfWgPRdFsKNhMF8wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAYYwHQYDVR0lBBYwFAYIKwYBBQUHAwIGCCsGAQUFBwMBMB0GA1UdDgQWBBSu+JnU5ZC4mkuEpqg2+Mk4K79oeDAKBggqhkjOPQQDAgNHADBEAiBEFdzQ/Bc3LftzngrY605UhA6UprHhAogKgROv7iR4QgIgEFUxTtW3xXJcnUPWhhUFhyZoqfn8dE93+dm/LDnp7C0=' -key_data = 'MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgPLpD0Ohhoq0g4nhx2KMIuze7ucKUt/lBEB2wc03IxXyhRANCAATTUestw222qrj8+2gy5rysxYSQ50G7u8/3jHMM7sDwL3aWzW/zp54/LhCWUoLMjDdDEEigK4fal4ZF9aA9F0Ww' +cert_data = """ +MIICFDCCAbugAwIBAgIUfMbIsB/ozMXijYgUYG80T1ry+mcwCgYIKoZIzj0EAwIw +WTELMAkGA1UEBhMCR0IxEzARBgNVBAgMClNvbWUtU3RhdGUxEjAQBgNVBAcMCVNv +bWUtQ2l0eTENMAsGA1UECgwEVnlPUzESMBAGA1UEAwwJVnlPUyBUZXN0MB4XDTIx +MDcyMDEyNDUxMloXDTI2MDcxOTEyNDUxMlowWTELMAkGA1UEBhMCR0IxEzARBgNV +BAgMClNvbWUtU3RhdGUxEjAQBgNVBAcMCVNvbWUtQ2l0eTENMAsGA1UECgwEVnlP +UzESMBAGA1UEAwwJVnlPUyBUZXN0MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE +01HrLcNttqq4/PtoMua8rMWEkOdBu7vP94xzDO7A8C92ls1v86eePy4QllKCzIw3 +QxBIoCuH2peGRfWgPRdFsKNhMF8wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8E +BAMCAYYwHQYDVR0lBBYwFAYIKwYBBQUHAwIGCCsGAQUFBwMBMB0GA1UdDgQWBBSu ++JnU5ZC4mkuEpqg2+Mk4K79oeDAKBggqhkjOPQQDAgNHADBEAiBEFdzQ/Bc3Lftz +ngrY605UhA6UprHhAogKgROv7iR4QgIgEFUxTtW3xXJcnUPWhhUFhyZoqfn8dE93 ++dm/LDnp7C0= +""" + +key_data = """ +MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgPLpD0Ohhoq0g4nhx +2KMIuze7ucKUt/lBEB2wc03IxXyhRANCAATTUestw222qrj8+2gy5rysxYSQ50G7 +u8/3jHMM7sDwL3aWzW/zp54/LhCWUoLMjDdDEEigK4fal4ZF9aA9F0Ww +""" def get_wpa_supplicant_value(interface, key): tmp = read_file(f'/run/wpa_supplicant/{interface}.conf') @@ -64,10 +81,7 @@ class EthernetInterfaceTest(BasicInterfaceTest.TestCase): # call base-classes classmethod super(cls, cls).setUpClass() - def tearDown(self): - self.cli_delete(pki_path) - for interface in self._interfaces: # when using a dedicated interface to test via TEST_ETH environment # variable only this one will be cleared in the end - usable to test @@ -151,14 +165,17 @@ class EthernetInterfaceTest(BasicInterfaceTest.TestCase): self.cli_commit() def test_eapol_support(self): - self.cli_set(pki_path + ['ca', 'eapol', 'certificate', cert_data]) - self.cli_set(pki_path + ['certificate', 'eapol', 'certificate', cert_data]) - self.cli_set(pki_path + ['certificate', 'eapol', 'private', 'key', key_data]) + ca_name = 'eapol' + cert_name = 'eapol' + + self.cli_set(['pki', 'ca', ca_name, 'certificate', cert_data.replace('\n','')]) + self.cli_set(['pki', 'certificate', cert_name, 'certificate', cert_data.replace('\n','')]) + self.cli_set(['pki', 'certificate', cert_name, 'private', 'key', key_data.replace('\n','')]) for interface in self._interfaces: # Enable EAPoL - self.cli_set(self._base_path + [interface, 'eapol', 'ca-certificate', 'eapol']) - self.cli_set(self._base_path + [interface, 'eapol', 'certificate', 'eapol']) + self.cli_set(self._base_path + [interface, 'eapol', 'ca-certificate', ca_name]) + self.cli_set(self._base_path + [interface, 'eapol', 'certificate', cert_name]) self.cli_commit() @@ -189,5 +206,8 @@ class EthernetInterfaceTest(BasicInterfaceTest.TestCase): tmp = get_wpa_supplicant_value(interface, 'identity') self.assertEqual(f'"{mac}"', tmp) + self.cli_delete(['pki', 'ca', ca_name]) + self.cli_delete(['pki', 'certificate', cert_name]) + if __name__ == '__main__': unittest.main(verbosity=2) diff --git a/src/conf_mode/interfaces-ethernet.py b/src/conf_mode/interfaces-ethernet.py index 31998a9a8..21a04f954 100755 --- a/src/conf_mode/interfaces-ethernet.py +++ b/src/conf_mode/interfaces-ethernet.py @@ -55,15 +55,17 @@ def get_config(config=None): conf = config else: conf = Config() - base = ['interfaces', 'ethernet'] - tmp_pki = conf.get_config_dict(['pki'], key_mangling=('-', '_'), - get_first_key=True, no_tag_node_value_mangle=True) + # This must be called prior to get_interface_dict(), as this function will + # alter the config level (config.set_level()) + pki = conf.get_config_dict(['pki'], key_mangling=('-', '_'), + get_first_key=True, no_tag_node_value_mangle=True) + base = ['interfaces', 'ethernet'] ethernet = get_interface_dict(conf, base) if 'deleted' not in ethernet: - ethernet['pki'] = tmp_pki + if pki: ethernet['pki'] = pki return ethernet -- cgit v1.2.3 From bd2c79ebb8abbd844f4b0568110c7bcea0d8f4f1 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Tue, 7 Sep 2021 11:26:42 +0200 Subject: op-mode: xml: improve "show interfaces " help text --- op-mode-definitions/show-interfaces-bonding.xml.in | 4 +- op-mode-definitions/show-interfaces-bridge.xml.in | 4 +- op-mode-definitions/show-interfaces-dummy.xml.in | 4 +- .../show-interfaces-ethernet.xml.in | 4 +- op-mode-definitions/show-interfaces-input.xml.in | 4 +- op-mode-definitions/show-interfaces-l2tpv3.xml.in | 2 +- .../show-interfaces-loopback.xml.in | 4 +- op-mode-definitions/show-interfaces-pppoe.xml.in | 2 +- .../show-interfaces-pseudo-ethernet.xml.in | 4 +- op-mode-definitions/show-interfaces-tunnel.xml.in | 4 +- op-mode-definitions/show-interfaces-vti.xml.in | 4 +- op-mode-definitions/show-interfaces-vxlan.xml.in | 2 +- .../show-interfaces-wireguard.xml.in | 4 +- .../show-interfaces-wireless.xml.in | 82 ++++++++++++++++++++++ op-mode-definitions/show-interfaces-wwan.xml.in | 2 +- op-mode-definitions/wireless.xml.in | 79 --------------------- 16 files changed, 106 insertions(+), 103 deletions(-) create mode 100644 op-mode-definitions/show-interfaces-wireless.xml.in diff --git a/op-mode-definitions/show-interfaces-bonding.xml.in b/op-mode-definitions/show-interfaces-bonding.xml.in index 08ce78296..c5f82b70e 100644 --- a/op-mode-definitions/show-interfaces-bonding.xml.in +++ b/op-mode-definitions/show-interfaces-bonding.xml.in @@ -6,7 +6,7 @@ - Show bonding interface information + Show specified Bonding interface information interfaces bonding @@ -58,7 +58,7 @@ - Show bonding interface information + Show Bonding interface information ${vyos_op_scripts_dir}/show_interfaces.py --intf-type=bonding --action=show-brief diff --git a/op-mode-definitions/show-interfaces-bridge.xml.in b/op-mode-definitions/show-interfaces-bridge.xml.in index 85fde95b5..e1444bd84 100644 --- a/op-mode-definitions/show-interfaces-bridge.xml.in +++ b/op-mode-definitions/show-interfaces-bridge.xml.in @@ -6,7 +6,7 @@ - Show bridge interface information + Show specified Bridge interface information interfaces bridge @@ -23,7 +23,7 @@ - Show bridge interface information + Show Bridge interface information ${vyos_op_scripts_dir}/show_interfaces.py --intf-type=bridge --action=show-brief diff --git a/op-mode-definitions/show-interfaces-dummy.xml.in b/op-mode-definitions/show-interfaces-dummy.xml.in index 7c24c6921..52d2cc7ee 100644 --- a/op-mode-definitions/show-interfaces-dummy.xml.in +++ b/op-mode-definitions/show-interfaces-dummy.xml.in @@ -6,7 +6,7 @@ - Show dummy interface information + Show specified Dummy interface information interfaces dummy @@ -23,7 +23,7 @@ - Show dummy interface information + Show Dummy interface information ${vyos_op_scripts_dir}/show_interfaces.py --intf-type=dummy --action=show-brief diff --git a/op-mode-definitions/show-interfaces-ethernet.xml.in b/op-mode-definitions/show-interfaces-ethernet.xml.in index c42efd21f..f8d1c9395 100644 --- a/op-mode-definitions/show-interfaces-ethernet.xml.in +++ b/op-mode-definitions/show-interfaces-ethernet.xml.in @@ -6,7 +6,7 @@ - Show ethernet interface information + Show specified Ethernet interface information interfaces ethernet @@ -78,7 +78,7 @@ - Show ethernet interface information + Show Ethernet interface information ${vyos_op_scripts_dir}/show_interfaces.py --intf-type=ethernet --action=show-brief diff --git a/op-mode-definitions/show-interfaces-input.xml.in b/op-mode-definitions/show-interfaces-input.xml.in index 15e8203e5..9ae3828c8 100644 --- a/op-mode-definitions/show-interfaces-input.xml.in +++ b/op-mode-definitions/show-interfaces-input.xml.in @@ -6,7 +6,7 @@ - Show input interface information + Show specified Input interface information interfaces input @@ -23,7 +23,7 @@ - Show input interface information + Show Input (ifb) interface information ${vyos_op_scripts_dir}/show_interfaces.py --intf-type=input --action=show-brief diff --git a/op-mode-definitions/show-interfaces-l2tpv3.xml.in b/op-mode-definitions/show-interfaces-l2tpv3.xml.in index 60fee34a1..2a1d6a1c6 100644 --- a/op-mode-definitions/show-interfaces-l2tpv3.xml.in +++ b/op-mode-definitions/show-interfaces-l2tpv3.xml.in @@ -6,7 +6,7 @@ - Show L2TPv3 interface information + Show specified L2TPv3 interface information interfaces l2tpv3 diff --git a/op-mode-definitions/show-interfaces-loopback.xml.in b/op-mode-definitions/show-interfaces-loopback.xml.in index b30b57909..25a75ffff 100644 --- a/op-mode-definitions/show-interfaces-loopback.xml.in +++ b/op-mode-definitions/show-interfaces-loopback.xml.in @@ -6,7 +6,7 @@ - Show loopback interface information + Show specified Loopback interface information interfaces loopback @@ -23,7 +23,7 @@ - Show loopback interface information + Show Loopback interface information ${vyos_op_scripts_dir}/show_interfaces.py --intf-type=loopback --action=show-brief diff --git a/op-mode-definitions/show-interfaces-pppoe.xml.in b/op-mode-definitions/show-interfaces-pppoe.xml.in index 18697a275..767836abf 100644 --- a/op-mode-definitions/show-interfaces-pppoe.xml.in +++ b/op-mode-definitions/show-interfaces-pppoe.xml.in @@ -6,7 +6,7 @@ - Show PPPoE interface information + Show specified PPPoE interface information interfaces pppoe diff --git a/op-mode-definitions/show-interfaces-pseudo-ethernet.xml.in b/op-mode-definitions/show-interfaces-pseudo-ethernet.xml.in index 195944745..2ae4b5a9e 100644 --- a/op-mode-definitions/show-interfaces-pseudo-ethernet.xml.in +++ b/op-mode-definitions/show-interfaces-pseudo-ethernet.xml.in @@ -6,7 +6,7 @@ - Show pseudo-ethernet/MACvlan interface information + Show specified Pseudo-Ethernet/MACvlan interface information interfaces pseudo-ethernet @@ -23,7 +23,7 @@ - Show pseudo-ethernet/MACvlan interface information + Show Pseudo-Ethernet/MACvlan interface information ${vyos_op_scripts_dir}/show_interfaces.py --intf-type=pseudo-ethernet --action=show-brief diff --git a/op-mode-definitions/show-interfaces-tunnel.xml.in b/op-mode-definitions/show-interfaces-tunnel.xml.in index 416de0299..51b25efd9 100644 --- a/op-mode-definitions/show-interfaces-tunnel.xml.in +++ b/op-mode-definitions/show-interfaces-tunnel.xml.in @@ -6,7 +6,7 @@ - Show tunnel interface information + Show specified Tunnel interface information interfaces tunnel @@ -23,7 +23,7 @@ - Show tunnel interface information + Show Tunnel interface information ${vyos_op_scripts_dir}/show_interfaces.py --intf-type=tunnel --action=show-brief diff --git a/op-mode-definitions/show-interfaces-vti.xml.in b/op-mode-definitions/show-interfaces-vti.xml.in index f51be2d19..b436b8414 100644 --- a/op-mode-definitions/show-interfaces-vti.xml.in +++ b/op-mode-definitions/show-interfaces-vti.xml.in @@ -6,7 +6,7 @@ - Show vti interface information + Show specified VTI interface information interfaces vti @@ -23,7 +23,7 @@ - Show vti interface information + Show VTI interface information ${vyos_op_scripts_dir}/show_interfaces.py --intf-type=vti --action=show-brief diff --git a/op-mode-definitions/show-interfaces-vxlan.xml.in b/op-mode-definitions/show-interfaces-vxlan.xml.in index 4e3cb93cd..1befd428c 100644 --- a/op-mode-definitions/show-interfaces-vxlan.xml.in +++ b/op-mode-definitions/show-interfaces-vxlan.xml.in @@ -6,7 +6,7 @@ - Show VXLAN interface information + Show specified VXLAN interface information interfaces vxlan diff --git a/op-mode-definitions/show-interfaces-wireguard.xml.in b/op-mode-definitions/show-interfaces-wireguard.xml.in index 863357ef7..c9b754dcd 100644 --- a/op-mode-definitions/show-interfaces-wireguard.xml.in +++ b/op-mode-definitions/show-interfaces-wireguard.xml.in @@ -6,7 +6,7 @@ - Show Wireguard interface information + Show specified WireGuard interface information @@ -47,7 +47,7 @@ - Show Wireguard interface information + Show WireGuard interface information ${vyos_op_scripts_dir}/show_interfaces.py --intf-type=wireguard --action=show-brief diff --git a/op-mode-definitions/show-interfaces-wireless.xml.in b/op-mode-definitions/show-interfaces-wireless.xml.in new file mode 100644 index 000000000..4a37417aa --- /dev/null +++ b/op-mode-definitions/show-interfaces-wireless.xml.in @@ -0,0 +1,82 @@ + + + + + + + + + Show Wireless (WLAN) interface information + + ${vyos_op_scripts_dir}/show_interfaces.py --intf-type=wireless --action=show-brief + + + + Show detailed wireless interface information + + ${vyos_op_scripts_dir}/show_interfaces.py --intf-type=wireless --action=show + + + + Show wireless interface configuration + + ${vyos_op_scripts_dir}/show_wireless.py --brief + + + + + + Show specified wireless interface information + + + + + ${vyos_op_scripts_dir}/show_interfaces.py --intf="$4" + + + + Show summary of the specified wireless interface information + + ${vyos_op_scripts_dir}/show_interfaces.py --intf="$4" --action=show-brief + + + + Show summary of the specified wireless interface information + + sudo ${vyos_op_scripts_dir}/show_wireless.py --scan "$4" + + + + Show detailed scan results + + sudo /sbin/iw dev "$4" scan ap-force + + + + + + Show specified Wireless interface information + + ${vyos_op_scripts_dir}/show_wireless.py --stations "$4" + + + + Show specified virtual network interface (vif) information + + ${vyos_op_scripts_dir}/show_interfaces.py --intf="$4.$6" + + + + Show summary of specified virtual network interface (vif) information + + ${vyos_op_scripts_dir}/show_interfaces.py --intf="$4.$6" --action=show-brief + + + + + + + + + + diff --git a/op-mode-definitions/show-interfaces-wwan.xml.in b/op-mode-definitions/show-interfaces-wwan.xml.in index 7e5f49ba6..3cd29b38a 100644 --- a/op-mode-definitions/show-interfaces-wwan.xml.in +++ b/op-mode-definitions/show-interfaces-wwan.xml.in @@ -6,7 +6,7 @@ - Show Wireless Wire Area Network (WWAN) interface information + Show specified Wireless Wire Area Network (WWAN) interface information interfaces wwan diff --git a/op-mode-definitions/wireless.xml.in b/op-mode-definitions/wireless.xml.in index a3a9d1f55..5d9db1544 100644 --- a/op-mode-definitions/wireless.xml.in +++ b/op-mode-definitions/wireless.xml.in @@ -37,83 +37,4 @@ - - - - - - - Show wireless interface information - - ${vyos_op_scripts_dir}/show_interfaces.py --intf-type=wireless --action=show-brief - - - - Show detailed wireless interface information - - ${vyos_op_scripts_dir}/show_interfaces.py --intf-type=wireless --action=show - - - - Show wireless interface configuration - - ${vyos_op_scripts_dir}/show_wireless.py --brief - - - - - - Show specified wireless interface information - - - - - ${vyos_op_scripts_dir}/show_interfaces.py --intf="$4" - - - - Show summary of the specified wireless interface information - - ${vyos_op_scripts_dir}/show_interfaces.py --intf="$4" --action=show-brief - - - - Show summary of the specified wireless interface information - - sudo ${vyos_op_scripts_dir}/show_wireless.py --scan "$4" - - - - Show detailed scan results - - sudo /sbin/iw dev "$4" scan ap-force - - - - - - Show specified wireless interface information - - ${vyos_op_scripts_dir}/show_wireless.py --stations "$4" - - - - Show specified virtual network interface (vif) information - - ${vyos_op_scripts_dir}/show_interfaces.py --intf="$4.$6" - - - - Show summary of specified virtual network interface (vif) information - - ${vyos_op_scripts_dir}/show_interfaces.py --intf="$4.$6" --action=show-brief - - - - - - - - - -- cgit v1.2.3 From 7623e37c918c65418d8dfc521f976bb91f0594c0 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Tue, 7 Sep 2021 11:41:12 +0200 Subject: scripts: op-mode: T3807: bugfix node.def generator process_node() processes the XML tree in a fixed order, "node" before "tagNode" before "leafNode". If the generator created a "node.def" file, it can no longer be overwritten - else we would have some stale "node.def" files with an empty help string (T2555). Without the fixed order this would resulted in a case where we get a node and a tagNode with the same name, e.g. "show interfaces ethernet" and "show interfaces ethernet eth0" that the node implementation was not callable from the CLI, rendering this command useless (T3807). This can be fixed by forcing the "node", "tagNode", "leafNode" order by sorting the input XML file automatically (sorting from https://stackoverflow.com/a/46128043) thus adding no additional overhead to the user. --- scripts/build-command-op-templates | 57 +++++++++++++++++++++++++++----------- 1 file changed, 41 insertions(+), 16 deletions(-) diff --git a/scripts/build-command-op-templates b/scripts/build-command-op-templates index a4d6d1d08..d4515b8db 100755 --- a/scripts/build-command-op-templates +++ b/scripts/build-command-op-templates @@ -29,13 +29,10 @@ import functools from lxml import etree as ET # Defaults - validator_dir = "/opt/vyatta/libexec/validators" default_constraint_err_msg = "Invalid value" - ## Get arguments - parser = argparse.ArgumentParser(description='Converts new-style XML interface definitions to old-style command templates') parser.add_argument('--debug', help='Enable debug information output', action='store_true') parser.add_argument('INPUT_FILE', type=str, help="XML interface definition file") @@ -50,7 +47,6 @@ output_dir = args.OUTPUT_DIR debug = args.debug ## Load and validate the inputs - try: xml = ET.parse(input_file) except Exception as e: @@ -76,7 +72,6 @@ if not os.access(output_dir, os.W_OK): sys.exit(1) ## If we got this far, everything must be ok and we can convert the file - def make_path(l): path = functools.reduce(os.path.join, l) if debug: @@ -125,21 +120,14 @@ def get_properties(p): def make_node_def(props, command): # XXX: replace with a template processor if it grows # out of control - node_def = "" if "help" in props: node_def += "help: {0}\n".format(props["help"]) - - if "comp_help" in props: node_def += "allowed: {0}\n".format(props["comp_help"]) - - if command is not None: node_def += "run: {0}\n".format(command.text) - - if debug: print("The contents of the node.def file:\n", node_def) @@ -152,7 +140,6 @@ def process_node(n, tmpl_dir): props_elem = n.find("properties") children = n.find("children") command = n.find("command") - name = n.get("name") node_type = n.tag @@ -180,8 +167,7 @@ def process_node(n, tmpl_dir): inner_nodes = children.iterfind("*") for inner_n in inner_nodes: process_node(inner_n, my_tmpl_dir) - - if node_type == "tagNode": + elif node_type == "tagNode": if debug: print(f"Processing tagNode {name}") @@ -211,7 +197,7 @@ def process_node(n, tmpl_dir): inner_nodes = children.iterfind("*") for inner_n in inner_nodes: process_node(inner_n, my_tmpl_dir) - else: + elif node_type == "leafNode": # This is a leaf node if debug: print(f"Processing leaf node {name}") @@ -219,9 +205,48 @@ def process_node(n, tmpl_dir): if not os.path.exists(nodedef_path) or os.path.getsize(nodedef_path) == 0: with open(nodedef_path, "w") as f: f.write(make_node_def(props, command)) + else: + print(f"Unknown node_type: {node_type}") + + +def get_node_key(node, attr=None): + """ Return the sorting key of an xml node using tag and attributes """ + if attr is None: + return '%s' % node.tag + ':'.join([node.get(attr) + for attr in sorted(node.attrib)]) + if attr in node.attrib: + return '%s:%s' % (node.tag, node.get(attr)) + return '%s' % node.tag + + +def sort_children(node, attr=None): + """ Sort children along tag and given attribute. if attr is None, sort + along all attributes """ + if not isinstance(node.tag, str): # PYTHON 2: use basestring instead + # not a TAG, it is comment or DATA + # no need to sort + return + # sort child along attr + node[:] = sorted(node, key=lambda child: get_node_key(child, attr)) + # and recurse + for child in node: + sort_children(child, attr) root = xml.getroot() +# process_node() processes the XML tree in a fixed order, "node" before "tagNode" +# before "leafNode". If the generator created a "node.def" file, it can no longer +# be overwritten - else we would have some stale "node.def" files with an empty +# help string (T2555). Without the fixed order this would resulted in a case +# where we get a node and a tagNode with the same name, e.g. "show interfaces +# ethernet" and "show interfaces ethernet eth0" that the node implementation +# was not callable from the CLI, rendering this command useless (T3807). +# +# This can be fixed by forcing the "node", "tagNode", "leafNode" order by sorting +# the input XML file automatically (sorting from https://stackoverflow.com/a/46128043) +# thus adding no additional overhead to the user. +sort_children(root, 'name') + nodes = root.iterfind("*") for n in nodes: process_node(n, [output_dir]) -- cgit v1.2.3 From 63fbd8c663c8c42ad178d6f0694f20bb98acf01a Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Wed, 8 Sep 2021 14:33:14 +0200 Subject: openvpn: T3805: use vyos.util.write_file() to store certificates --- python/vyos/util.py | 4 +-- src/conf_mode/interfaces-openvpn.py | 58 ++++++++++--------------------------- 2 files changed, 18 insertions(+), 44 deletions(-) diff --git a/python/vyos/util.py b/python/vyos/util.py index b41c5b346..849b27d3b 100644 --- a/python/vyos/util.py +++ b/python/vyos/util.py @@ -197,7 +197,7 @@ def read_file(fname, defaultonfailure=None): return defaultonfailure raise e -def write_file(fname, data, defaultonfailure=None, user=None, group=None): +def write_file(fname, data, defaultonfailure=None, user=None, group=None, mode=None): """ Write content of data to given fname, should defaultonfailure be not None, it is returned on failure to read. @@ -215,6 +215,7 @@ def write_file(fname, data, defaultonfailure=None, user=None, group=None): with open(fname, 'w') as f: bytes = f.write(data) chown(fname, user, group) + chmod(fname, mode) return bytes except Exception as e: if defaultonfailure is not None: @@ -295,7 +296,6 @@ def makedir(path, user=None, group=None): os.makedirs(path, mode=0o755) chown(path, user, group) - def colon_separated_to_dict(data_string, uniquekeys=False): """ Converts a string containing newline-separated entries of colon-separated key-value pairs into a dict. diff --git a/src/conf_mode/interfaces-openvpn.py b/src/conf_mode/interfaces-openvpn.py index 6be4e918b..274bc655e 100755 --- a/src/conf_mode/interfaces-openvpn.py +++ b/src/conf_mode/interfaces-openvpn.py @@ -45,9 +45,9 @@ from vyos.template import is_ipv4 from vyos.template import is_ipv6 from vyos.util import call from vyos.util import chown -from vyos.util import chmod_600 from vyos.util import dict_search from vyos.util import dict_search_args +from vyos.util import write_file from vyos.validate import is_addr_assigned from vyos import ConfigError @@ -449,7 +449,6 @@ def verify(openvpn): def generate_pki_files(openvpn): pki = openvpn['pki'] - if not pki: return None @@ -457,16 +456,11 @@ def generate_pki_files(openvpn): shared_secret_key = dict_search_args(openvpn, 'shared_secret_key') tls = dict_search_args(openvpn, 'tls') - files = [] - if shared_secret_key: pki_key = pki['openvpn']['shared_secret'][shared_secret_key] key_path = os.path.join(cfg_dir, f'{interface}_shared.key') - - with open(key_path, 'w') as f: - f.write(wrap_openvpn_key(pki_key['key'])) - - files.append(key_path) + write_file(key_path, wrap_openvpn_key(pki_key['key']), + user=user, group=group) if tls: if 'ca_certificate' in tls: @@ -475,20 +469,15 @@ def generate_pki_files(openvpn): if 'certificate' in pki_ca: cert_path = os.path.join(cfg_dir, f'{interface}_ca.pem') - - with open(cert_path, 'w') as f: - f.write(wrap_certificate(pki_ca['certificate'])) - - files.append(cert_path) + write_file(cert_path, wrap_certificate(pki_ca['certificate']), + user=user, group=group, mode=0o600) if 'crl' in pki_ca: for crl in pki_ca['crl']: crl_path = os.path.join(cfg_dir, f'{interface}_crl.pem') + write_file(crl_path, wrap_crl(crl), user=user, group=group, + mode=0o600) - with open(crl_path, 'w') as f: - f.write(wrap_crl(crl)) - - files.append(crl_path) openvpn['tls']['crl'] = True if 'certificate' in tls: @@ -497,19 +486,14 @@ def generate_pki_files(openvpn): if 'certificate' in pki_cert: cert_path = os.path.join(cfg_dir, f'{interface}_cert.pem') - - with open(cert_path, 'w') as f: - f.write(wrap_certificate(pki_cert['certificate'])) - - files.append(cert_path) + write_file(cert_path, wrap_certificate(pki_cert['certificate']), + user=user, group=group, mode=0o600) if 'private' in pki_cert and 'key' in pki_cert['private']: key_path = os.path.join(cfg_dir, f'{interface}_cert.key') + write_file(key_path, wrap_private_key(pki_cert['private']['key']), + user=user, group=group, mode=0o600) - with open(key_path, 'w') as f: - f.write(wrap_private_key(pki_cert['private']['key'])) - - files.append(key_path) openvpn['tls']['private_key'] = True if 'dh_params' in tls: @@ -518,11 +502,8 @@ def generate_pki_files(openvpn): if 'parameters' in pki_dh: dh_path = os.path.join(cfg_dir, f'{interface}_dh.pem') - - with open(dh_path, 'w') as f: - f.write(wrap_dh_parameters(pki_dh['parameters'])) - - files.append(dh_path) + write_file(dh_path, wrap_dh_parameters(pki_dh['parameters']), + user=user, group=group, mode=0o600) if 'auth_key' in tls: key_name = tls['auth_key'] @@ -530,11 +511,8 @@ def generate_pki_files(openvpn): if 'key' in pki_key: key_path = os.path.join(cfg_dir, f'{interface}_auth.key') - - with open(key_path, 'w') as f: - f.write(wrap_openvpn_key(pki_key['key'])) - - files.append(key_path) + write_file(key_path, wrap_openvpn_key(pki_key['key']), + user=user, group=group, mode=0o600) if 'crypt_key' in tls: key_name = tls['crypt_key'] @@ -570,7 +548,7 @@ def generate(openvpn): chown(ccd_dir, user, group) # Fix file permissons for keys - fix_permissions = generate_pki_files(openvpn) + generate_pki_files(openvpn) # Generate User/Password authentication file if 'authentication' in openvpn: @@ -598,10 +576,6 @@ def generate(openvpn): render(cfg_file.format(**openvpn), 'openvpn/server.conf.tmpl', openvpn, formater=lambda _: _.replace(""", '"'), user=user, group=group) - # Fixup file permissions - for file in fix_permissions: - chmod_600(file) - return None def apply(openvpn): -- cgit v1.2.3 From 84e912ab2f583864e637c2df137f62f3d4cbeb14 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Wed, 8 Sep 2021 14:34:41 +0200 Subject: openvpn: T3805: use vyos.util.makedir() to create system directories --- src/conf_mode/interfaces-openvpn.py | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/src/conf_mode/interfaces-openvpn.py b/src/conf_mode/interfaces-openvpn.py index 274bc655e..c837328be 100755 --- a/src/conf_mode/interfaces-openvpn.py +++ b/src/conf_mode/interfaces-openvpn.py @@ -47,6 +47,7 @@ from vyos.util import call from vyos.util import chown from vyos.util import dict_search from vyos.util import dict_search_args +from vyos.util import makedir from vyos.util import write_file from vyos.validate import is_addr_assigned @@ -520,18 +521,17 @@ def generate_pki_files(openvpn): if 'key' in pki_key: key_path = os.path.join(cfg_dir, f'{interface}_crypt.key') - - with open(key_path, 'w') as f: - f.write(wrap_openvpn_key(pki_key['key'])) - - files.append(key_path) - - return files + write_file(key_path, wrap_openvpn_key(pki_key['key']), + user=user, group=group, mode=0o600) def generate(openvpn): interface = openvpn['ifname'] directory = os.path.dirname(cfg_file.format(**openvpn)) + # create base config directory on demand + makedir(directory, user, group) + # enforce proper permissions on /run/openvpn + chown(directory, user, group) # we can't know in advance which clients have been removed, # thus all client configs will be removed and re-added on demand @@ -543,9 +543,7 @@ def generate(openvpn): return None # create client config directory on demand - if not os.path.exists(ccd_dir): - os.makedirs(ccd_dir, 0o755) - chown(ccd_dir, user, group) + makedir(ccd_dir, user, group) # Fix file permissons for keys generate_pki_files(openvpn) -- cgit v1.2.3 From 2647edc30f1e02840cae62fde8b44345d35ac720 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Wed, 8 Sep 2021 14:35:20 +0200 Subject: openvpn: T3805: drop privileges using systemd - required for rtnetlink --- data/templates/openvpn/server.conf.tmpl | 2 -- src/conf_mode/interfaces-openvpn.py | 3 --- src/etc/systemd/system/openvpn@.service.d/override.conf | 4 ++++ 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/data/templates/openvpn/server.conf.tmpl b/data/templates/openvpn/server.conf.tmpl index 9b07a9ba2..9e4cc6813 100644 --- a/data/templates/openvpn/server.conf.tmpl +++ b/data/templates/openvpn/server.conf.tmpl @@ -7,8 +7,6 @@ # verb 3 -user {{ daemon_user }} -group {{ daemon_group }} dev-type {{ device_type }} dev {{ ifname }} persist-key diff --git a/src/conf_mode/interfaces-openvpn.py b/src/conf_mode/interfaces-openvpn.py index c837328be..bbf17ed5a 100755 --- a/src/conf_mode/interfaces-openvpn.py +++ b/src/conf_mode/interfaces-openvpn.py @@ -81,9 +81,6 @@ def get_config(config=None): openvpn['pki'] = tmp_pki openvpn['auth_user_pass_file'] = '/run/openvpn/{ifname}.pw'.format(**openvpn) - openvpn['daemon_user'] = user - openvpn['daemon_group'] = group - return openvpn def is_ec_private_key(pki, cert_name): diff --git a/src/etc/systemd/system/openvpn@.service.d/override.conf b/src/etc/systemd/system/openvpn@.service.d/override.conf index 7946484a3..03fe6b587 100644 --- a/src/etc/systemd/system/openvpn@.service.d/override.conf +++ b/src/etc/systemd/system/openvpn@.service.d/override.conf @@ -7,3 +7,7 @@ WorkingDirectory= WorkingDirectory=/run/openvpn ExecStart= ExecStart=/usr/sbin/openvpn --daemon openvpn-%i --config %i.conf --status %i.status 30 --writepid %i.pid +User=openvpn +Group=openvpn +AmbientCapabilities=CAP_IPC_LOCK CAP_NET_ADMIN CAP_NET_BIND_SERVICE CAP_NET_RAW CAP_SETGID CAP_SETUID CAP_SYS_CHROOT CAP_DAC_OVERRIDE CAP_AUDIT_WRITE +CapabilityBoundingSet=CAP_IPC_LOCK CAP_NET_ADMIN CAP_NET_BIND_SERVICE CAP_NET_RAW CAP_SETGID CAP_SETUID CAP_SYS_CHROOT CAP_DAC_OVERRIDE CAP_AUDIT_WRITE -- cgit v1.2.3 From 588cc03a61414e8f9f35285b9b961c2004e24751 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Wed, 8 Sep 2021 14:36:06 +0200 Subject: openvpn: T3805: fix bool logic in verify_pki() for client mode Add support for OpenVPN client mode with only the CA certificate of the server installed. --- src/conf_mode/interfaces-openvpn.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/conf_mode/interfaces-openvpn.py b/src/conf_mode/interfaces-openvpn.py index bbf17ed5a..02b7f83bf 100755 --- a/src/conf_mode/interfaces-openvpn.py +++ b/src/conf_mode/interfaces-openvpn.py @@ -126,7 +126,7 @@ def verify_pki(openvpn): if tls['ca_certificate'] not in pki['ca']: raise ConfigError(f'Invalid CA certificate on openvpn interface {interface}') - if not (mode == 'client' and 'auth_key' in tls): + if mode != 'client' and 'auth_key' not in tls: if 'certificate' not in tls: raise ConfigError(f'Missing "tls certificate" on openvpn interface {interface}') -- cgit v1.2.3 From 4d2201eed00ac4780d0196abf53dd9b7cb943a09 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Thu, 9 Sep 2021 10:42:46 +0200 Subject: vyos.configdict: T3814: use no_tag_node_value_mangle in get_interface_dict() This change is required and currently only impacts WireGuards peer configuration, so that the peers name is not mangled. --- python/vyos/configdict.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/python/vyos/configdict.py b/python/vyos/configdict.py index e15579b95..24b76fb0b 100644 --- a/python/vyos/configdict.py +++ b/python/vyos/configdict.py @@ -347,8 +347,8 @@ def get_interface_dict(config, base, ifname=''): # setup config level which is extracted in get_removed_vlans() config.set_level(base + [ifname]) - dict = config.get_config_dict([], key_mangling=('-', '_'), - get_first_key=True) + dict = config.get_config_dict([], key_mangling=('-', '_'), get_first_key=True, + no_tag_node_value_mangle=True) # Check if interface has been removed. We must use exists() as # get_config_dict() will always return {} - even when an empty interface -- cgit v1.2.3 From bfffe55b2b7c620def4b26fbfc325c2ce3f31c59 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Thu, 9 Sep 2021 16:49:37 +0200 Subject: pki: wireguard: T3815: do not bail out early so keys can be written to file --- src/op_mode/pki.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/op_mode/pki.py b/src/op_mode/pki.py index 36891d080..a023f815a 100755 --- a/src/op_mode/pki.py +++ b/src/op_mode/pki.py @@ -643,7 +643,6 @@ def generate_wireguard_key(name, install=False, file=False): if not install: print("Private key: " + private_key) print("Public key: " + public_key) - return None if install: install_wireguard_key(name, private_key, public_key) -- cgit v1.2.3 From 3a0e586544fbe729f030d1950ea3ebebc454aa87 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Thu, 9 Sep 2021 17:14:05 +0200 Subject: policy: T3812: FRR bgpd also knows about route-maps because of rpki In order to alter rpki configuration we must also process the route-map nodes with/for bgpd. --- src/conf_mode/policy.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/conf_mode/policy.py b/src/conf_mode/policy.py index d56bae9e9..1a03d520b 100755 --- a/src/conf_mode/policy.py +++ b/src/conf_mode/policy.py @@ -190,6 +190,7 @@ def apply(policy): frr_cfg.modify_section(r'^bgp community-list .*') frr_cfg.modify_section(r'^bgp extcommunity-list .*') frr_cfg.modify_section(r'^bgp large-community-list .*') + frr_cfg.modify_section(r'^route-map .*') frr_cfg.add_before('^line vty', policy['new_frr_config']) frr_cfg.commit_configuration(bgp_daemon) -- cgit v1.2.3 From 9f1a737d46c29c715193c4831929700ae37b8f84 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Thu, 9 Sep 2021 23:14:48 +0200 Subject: pki: T3642: use f'ormated strings in print() --- src/op_mode/pki.py | 49 +++++++++++++++++++++++++++++++------------------ 1 file changed, 31 insertions(+), 18 deletions(-) diff --git a/src/op_mode/pki.py b/src/op_mode/pki.py index a023f815a..55330cbc2 100755 --- a/src/op_mode/pki.py +++ b/src/op_mode/pki.py @@ -17,7 +17,6 @@ import argparse import ipaddress import os -import re import sys import tabulate @@ -44,12 +43,14 @@ auth_dir = '/config/auth' conf = ConfigTreeQuery() def get_default_values(): # Fetch default x509 values - base = ['pki', 'x509', 'default'] x509_defaults = conf.get_config_dict(base, key_mangling=('-', '_'), - get_first_key=True, no_tag_node_value_mangle=True) + get_first_key=True, + no_tag_node_value_mangle=True) default_values = defaults(base) - return dict_merge(default_values, x509_defaults) + x509_defaults = dict_merge(default_values, x509_defaults) + + return x509_defaults def get_config_ca_certificate(name=None): # Fetch ca certificates from config @@ -63,7 +64,8 @@ def get_config_ca_certificate(name=None): return False return conf.get_config_dict(base, key_mangling=('-', '_'), - get_first_key=True, no_tag_node_value_mangle=True) + get_first_key=True, + no_tag_node_value_mangle=True) def get_config_certificate(name=None): # Get certificates from config @@ -77,7 +79,8 @@ def get_config_certificate(name=None): return False return conf.get_config_dict(base, key_mangling=('-', '_'), - get_first_key=True, no_tag_node_value_mangle=True) + get_first_key=True, + no_tag_node_value_mangle=True) def get_certificate_ca(cert, ca_certs): # Find CA certificate for given certificate @@ -103,12 +106,14 @@ def get_config_revoked_certificates(): if conf.exists(ca_base): ca_certificates = conf.get_config_dict(ca_base, key_mangling=('-', '_'), - get_first_key=True, no_tag_node_value_mangle=True) + get_first_key=True, + no_tag_node_value_mangle=True) certs.extend(ca_certificates.values()) if conf.exists(cert_base): certificates = conf.get_config_dict(cert_base, key_mangling=('-', '_'), - get_first_key=True, no_tag_node_value_mangle=True) + get_first_key=True, + no_tag_node_value_mangle=True) certs.extend(certificates.values()) return [cert_dict for cert_dict in certs if 'revoke' in cert_dict] @@ -139,39 +144,41 @@ def get_revoked_by_serial_numbers(serial_numbers=[]): def install_certificate(name, cert='', private_key=None, key_type=None, key_passphrase=None, is_ca=False): # Show conf commands for installing certificate prefix = 'ca' if is_ca else 'certificate' - print("Configure mode commands to install:") + print('Configure mode commands to install:') + base = f"set pki {prefix} {name}" if cert: cert_pem = "".join(encode_certificate(cert).strip().split("\n")[1:-1]) - print("set pki %s %s certificate '%s'" % (prefix, name, cert_pem)) + print(f"{base} certificate '{cert_pem}'") if private_key: key_pem = "".join(encode_private_key(private_key, passphrase=key_passphrase).strip().split("\n")[1:-1]) - print("set pki %s %s private key '%s'" % (prefix, name, key_pem)) + print(f"{base} private key '{key_pem}'") if key_passphrase: - print("set pki %s %s private password-protected" % (prefix, name)) + print(f"{base} private password-protected") def install_crl(ca_name, crl): # Show conf commands for installing crl print("Configure mode commands to install CRL:") crl_pem = "".join(encode_certificate(crl).strip().split("\n")[1:-1]) - print("set pki ca %s crl '%s'" % (ca_name, crl_pem)) + print(f"set pki ca {ca_name} crl '{crl_pem}'") def install_dh_parameters(name, params): # Show conf commands for installing dh params print("Configure mode commands to install DH parameters:") dh_pem = "".join(encode_dh_parameters(params).strip().split("\n")[1:-1]) - print("set pki dh %s parameters '%s'" % (name, dh_pem)) + print(f"set pki dh {name} parameters '{dh_pem}'") def install_ssh_key(name, public_key, private_key, passphrase=None): # Show conf commands for installing ssh key key_openssh = encode_public_key(public_key, encoding='OpenSSH', key_format='OpenSSH') username = os.getlogin() type_key_split = key_openssh.split(" ") + + base = f"set system login user {username} authentication public-keys {name}" print("Configure mode commands to install SSH key:") - print("set system login user %s authentication public-keys %s key '%s'" % (username, name, type_key_split[1])) - print("set system login user %s authentication public-keys %s type '%s'" % (username, name, type_key_split[0])) - print("") + print(f"{base} key '{type_key_split[1]}'") + print(f"{base} type '{type_key_split[0]}'", end="\n\n") print(encode_private_key(private_key, encoding='PEM', key_format='OpenSSH', passphrase=passphrase)) def install_keypair(name, key_type, private_key=None, public_key=None, passphrase=None): @@ -184,7 +191,7 @@ def install_keypair(name, key_type, private_key=None, public_key=None, passphras if install_public_key: install_public_pem = "".join(public_key_pem.strip().split("\n")[1:-1]) - print("set pki key-pair %s public key '%s'" % (name, install_public_pem)) + print(f"set pki key-pair {name} public key '{install_public_pem}'") else: print("Public key:") print(public_key_pem) @@ -808,16 +815,22 @@ if __name__ == '__main__': generate_certificate_selfsign(args.certificate, install=args.install, file=args.file) else: generate_certificate_request(name=args.certificate, install=args.install, file=args.file) + elif args.crl: generate_certificate_revocation_list(args.crl, install=args.install, file=args.file) + elif args.ssh: generate_ssh_keypair(args.ssh, install=args.install, file=args.file) + elif args.dh: generate_dh_parameters(args.dh, install=args.install, file=args.file) + elif args.keypair: generate_keypair(args.keypair, install=args.install, file=args.file) + elif args.openvpn: generate_openvpn_key(args.openvpn, install=args.install, file=args.file) + elif args.wireguard: if args.key: generate_wireguard_key(args.key, install=args.install, file=args.file) -- cgit v1.2.3 From a50095408e9e95afebce97bccc62a2d9a2563b3e Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Thu, 9 Sep 2021 23:16:13 +0200 Subject: wireguard: T3642: add deprecation notice to old commands for key generation --- op-mode-definitions/generate-wireguard.xml.in | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/op-mode-definitions/generate-wireguard.xml.in b/op-mode-definitions/generate-wireguard.xml.in index 6557b463b..259c9a898 100644 --- a/op-mode-definitions/generate-wireguard.xml.in +++ b/op-mode-definitions/generate-wireguard.xml.in @@ -4,9 +4,27 @@ - Generate Wireguard keys + Generate WireGuard keys + + + generates the wireguard default-keypair + + echo "This command is deprecated. Please use: \"generate pki wireguard key-pair\"" + + + + generate a wireguard preshared key + + echo "This command is deprecated. Please use: \"generate pki wireguard pre-shared-key\"" + + + + Generates named wireguard keypairs + + echo "This command is deprecated. Please use: \"generate pki wireguard key-pair install wgN\"" + Generate Client config QR code @@ -59,12 +77,6 @@ - - - Generate Wireguard key pair for use with server or peer - - sudo ${vyos_op_scripts_dir}/pki.py --action generate --wireguard --key "noname" - -- cgit v1.2.3 From 310eb1b527047211ae236c6415fee51f15a0fa57 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Thu, 9 Sep 2021 23:16:45 +0200 Subject: wireguard: T3642: improve "set" commands for generated key-pairs --- op-mode-definitions/pki.xml.in | 82 ++++++++++++++++++++----------------- src/op_mode/pki.py | 92 ++++++++++++++++++++---------------------- 2 files changed, 87 insertions(+), 87 deletions(-) diff --git a/op-mode-definitions/pki.xml.in b/op-mode-definitions/pki.xml.in index a11814c8a..6b9b0d3f6 100644 --- a/op-mode-definitions/pki.xml.in +++ b/op-mode-definitions/pki.xml.in @@ -282,60 +282,66 @@ - Generate Wireguard keys + Generate WireGuard keys - Generate Wireguard key pair for use with server or peer + Generate WireGuard public/private key-pair - + - Write generated Wireguard keys into the specified filename - - <filename> - + Generate CLI commands to install WireGuard key to configuration - sudo ${vyos_op_scripts_dir}/pki.py --action generate --wireguard --key "$6" --file - - - - Commands for installing generated Wireguard key into running configuration - - <interface> <peer> - - - sudo ${vyos_op_scripts_dir}/pki.py --action generate --wireguard --key "$6" --install - + + + + WireGuard Interface used in install command + + interfaces wireguard + + + sudo ${vyos_op_scripts_dir}/pki.py --action generate --wireguard --key --interface "$7" --install + + + - sudo ${vyos_op_scripts_dir}/pki.py --action generate --wireguard --key "noname" + sudo ${vyos_op_scripts_dir}/pki.py --action generate --wireguard --key - + - Generate pre-shared key for use with a Wireguard peer + Generate WireGuard pre-shared key - + - Write generated Wireguard PSK into the specified filename - - <filename> - + Generate CLI commands to install WireGuard key to configuration - sudo ${vyos_op_scripts_dir}/pki.py --action generate --wireguard --psk "$6" --file - - - - Commands for installing generated Wireguard PSK on specified peer into running configuration - - <peer> - - - sudo ${vyos_op_scripts_dir}/pki.py --action generate --wireguard --psk "$6" --install - + + + + WireGuard Interface used in install command + + interfaces wireguard + + + + + + Interface used for install command + + interfaces wireguard ${COMP_WORDS[COMP_CWORD-2]} peer + + + sudo ${vyos_op_scripts_dir}/pki.py --action generate --wireguard --psk --interface "$7" --peer "$9" --install + + + + + - sudo ${vyos_op_scripts_dir}/pki.py --action generate --wireguard --psk "noname" + sudo ${vyos_op_scripts_dir}/pki.py --action generate --wireguard --psk diff --git a/src/op_mode/pki.py b/src/op_mode/pki.py index 55330cbc2..d28cee5d0 100755 --- a/src/op_mode/pki.py +++ b/src/op_mode/pki.py @@ -202,30 +202,31 @@ def install_keypair(name, key_type, private_key=None, public_key=None, passphras if install_private_key: install_private_pem = "".join(private_key_pem.strip().split("\n")[1:-1]) - print("set pki key-pair %s private key '%s'" % (name, install_private_pem)) + print(f"set pki key-pair {name} private key '{install_private_pem}'") if passphrase: - print("set pki key-pair %s private password-protected" % (name,)) + print(f"set pki key-pair {name} private password-protected") else: print("Private key:") print(private_key_pem) -def install_wireguard_key(name, private_key, public_key): +def install_wireguard_key(interface, private_key, public_key): # Show conf commands for installing wireguard key pairs - is_interface = re.match(r'^wg[\d]+$', name) - - print("Configure mode commands to install key:") - if is_interface: - print("set interfaces wireguard %s private-key '%s'" % (name, private_key)) - print("") - print("Public key for use on peer configuration: " + public_key) - else: - print("set interfaces wireguard [INTERFACE] peer %s public-key '%s'" % (name, public_key)) - print("") - print("Private key for use on peer configuration: " + private_key) - -def install_wireguard_psk(name, psk): + from vyos.ifconfig import Section + if Section.section(interface) != 'wireguard': + print(f'"{interface}" is not a WireGuard interface name!') + exit(1) + + print("Configure mode commands to install key:", end="\n\n") + print(f"set interfaces wireguard {interface} private-key '{private_key}'", end="\n\n") + print(f"Public key to use on peer system: '{public_key}'") + +def install_wireguard_psk(interface, peer, psk): + from vyos.ifconfig import Section + if Section.section(interface) != 'wireguard': + print(f'"{interface}" is not a WireGuard interface name!') + exit(1) # Show conf commands for installing wireguard psk - print("set interfaces wireguard [INTERFACE] peer %s preshared-key '%s'" % (name, psk)) + print(f"set interfaces wireguard {interface} peer {peer} preshared-key '{psk}'") def ask_passphrase(): passphrase = None @@ -632,48 +633,37 @@ def generate_openvpn_key(name, install=False, file=False): key_data = "".join(key_lines[1:-1]) # Remove wrapper tags and line endings key_version = '1' + import re version_search = re.search(r'BEGIN OpenVPN Static key V(\d+)', result) # Future-proofing (hopefully) if version_search: key_version = version_search[1] + base = f"set pki openvpn shared-secret {name}" print("Configure mode commands to install OpenVPN key:") - print("set pki openvpn shared-secret %s key '%s'" % (name, key_data)) - print("set pki openvpn shared-secret %s version '%s'" % (name, key_version)) + print(f"{base} key '{key_data}'") + print(f"{base} version '{key_version}'") if file: write_file(f'{name}.key', result) -def generate_wireguard_key(name, install=False, file=False): +def generate_wireguard_key(interface=None, install=False): private_key = cmd('wg genkey') public_key = cmd('wg pubkey', input=private_key) - if not install: - print("Private key: " + private_key) - print("Public key: " + public_key) - - if install: - install_wireguard_key(name, private_key, public_key) - - if file: - write_file(f'{name}_public.key', public_key) - write_file(f'{name}_private.key', private_key) + if interface and install: + install_wireguard_key(interface, private_key, public_key) + else: + print(f'Private key: {private_key}') + print(f'Public key: {public_key}', end='\n\n') -def generate_wireguard_psk(name, install=False, file=False): +def generate_wireguard_psk(interface=None, peer=None, install=False): psk = cmd('wg genpsk') - - if not install and not file: - print("Pre-shared key:") - print(psk) - return None - - if install: - install_wireguard_psk(name, psk) - - if file: - write_file(f'{name}.key', psk) + if interface and peer and install: + install_wireguard_psk(interface, peer, psk) + else: + print(f'Pre-shared key: {psk}') # Show functions - def show_certificate_authority(name=None): headers = ['Name', 'Subject', 'Issuer CN', 'Issued', 'Expiry', 'Private Key', 'Parent'] data = [] @@ -790,10 +780,13 @@ if __name__ == '__main__': # OpenVPN parser.add_argument('--openvpn', help='OpenVPN TLS key', required=False) - # Wireguard + # WireGuard parser.add_argument('--wireguard', help='Wireguard', action='store_true') - parser.add_argument('--key', help='Wireguard key pair', required=False) - parser.add_argument('--psk', help='Wireguard pre shared key', required=False) + group = parser.add_mutually_exclusive_group() + group.add_argument('--key', help='Wireguard key pair', action='store_true', required=False) + group.add_argument('--psk', help='Wireguard pre shared key', action='store_true', required=False) + parser.add_argument('--interface', help='Install generated keys into running-config for named interface', action='store') + parser.add_argument('--peer', help='Install generated keys into running-config for peer', action='store') # Global parser.add_argument('--file', help='Write generated keys into specified filename', action='store_true') @@ -833,9 +826,10 @@ if __name__ == '__main__': elif args.wireguard: if args.key: - generate_wireguard_key(args.key, install=args.install, file=args.file) - elif args.psk: - generate_wireguard_psk(args.psk, install=args.install, file=args.file) + generate_wireguard_key(args.interface, install=args.install) + if args.psk: + generate_wireguard_psk(args.interface, peer=args.peer, install=args.install) + elif args.action == 'show': if args.ca: show_certificate_authority(None if args.ca == 'all' else args.ca) -- cgit v1.2.3 From cc313b32ef26141c2c027e8543f78da1231cada2 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Fri, 10 Sep 2021 16:45:30 +0200 Subject: ethernet: T3802: check if driver supports changing flow-control settings (cherry picked from commit 1572edd2cef355710d1129907d3e49451a6c31d4) --- python/vyos/ethtool.py | 2 +- python/vyos/ifconfig/ethernet.py | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/python/vyos/ethtool.py b/python/vyos/ethtool.py index 609d83b5e..7e46969cf 100644 --- a/python/vyos/ethtool.py +++ b/python/vyos/ethtool.py @@ -46,7 +46,7 @@ class Ethtool: _ring_buffers_max = { } _driver_name = None _auto_negotiation = None - _flow_control = None + _flow_control = False _flow_control_enabled = None def __init__(self, ifname): diff --git a/python/vyos/ifconfig/ethernet.py b/python/vyos/ifconfig/ethernet.py index 7bd269491..c4dfc7198 100644 --- a/python/vyos/ifconfig/ethernet.py +++ b/python/vyos/ifconfig/ethernet.py @@ -115,11 +115,10 @@ class EthernetIf(Interface): if enable not in ['on', 'off']: raise ValueError("Value out of range") - driver_name = self.get_driver_name() - if driver_name in ['vmxnet3', 'virtio_net', 'xen_netfront']: + if not self.ethtool.check_flow_control(): self._debug_msg(f'{driver_name} driver does not support changing '\ 'flow control settings!') - return + return False current = self.ethtool.get_flow_control() if current != enable: -- cgit v1.2.3 From 2abf6711e880463b9d9276b2fa9ea7b4ebcdc179 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Fri, 10 Sep 2021 16:46:55 +0200 Subject: ethernet: T3802: use only one implementation for get_driver_name() Move the two implementations to get the driver name of a NIC from ethernet.py and ethtool.py to only ethtool.py. (cherry picked from commit 07840977834816b69fa3b366817d90f44b5dc7a7) --- python/vyos/ethtool.py | 13 ++++++++----- python/vyos/ifconfig/ethernet.py | 28 +++------------------------- src/conf_mode/interfaces-ethernet.py | 2 +- 3 files changed, 12 insertions(+), 31 deletions(-) diff --git a/python/vyos/ethtool.py b/python/vyos/ethtool.py index 7e46969cf..4efc3a234 100644 --- a/python/vyos/ethtool.py +++ b/python/vyos/ethtool.py @@ -134,6 +134,12 @@ class Ethtool: # ['Autonegotiate:', 'on'] self._flow_control_enabled = out.splitlines()[1].split()[-1] + def get_auto_negotiation(self): + return self._auto_negotiation + + def get_driver_name(self): + return self._driver_name + def _get_generic(self, feature): """ Generic method to read self._features and return a tuple for feature @@ -189,7 +195,7 @@ class Ethtool: if duplex not in ['full', 'half']: raise ValueError(f'Value "{duplex}" for duplex is invalid!') - if self._driver_name in ['vmxnet3', 'virtio_net', 'xen_netfront']: + if self.get_driver_name() in ['vmxnet3', 'virtio_net', 'xen_netfront']: return False if speed in self._speed_duplex: @@ -199,7 +205,7 @@ class Ethtool: def check_flow_control(self): """ Check if the NIC supports flow-control """ - if self._driver_name in ['vmxnet3', 'virtio_net', 'xen_netfront']: + if self.get_driver_name() in ['vmxnet3', 'virtio_net', 'xen_netfront']: return False return self._flow_control @@ -208,6 +214,3 @@ class Ethtool: raise ValueError('Interface does not support changing '\ 'flow-control settings!') return self._flow_control_enabled - - def get_auto_negotiation(self): - return self._auto_negotiation diff --git a/python/vyos/ifconfig/ethernet.py b/python/vyos/ifconfig/ethernet.py index c4dfc7198..78fec1fa2 100644 --- a/python/vyos/ifconfig/ethernet.py +++ b/python/vyos/ifconfig/ethernet.py @@ -80,25 +80,6 @@ class EthernetIf(Interface): super().__init__(ifname, **kargs) self.ethtool = Ethtool(ifname) - def get_driver_name(self): - """ - Return the driver name used by NIC. Some NICs don't support all - features e.g. changing link-speed, duplex - - Example: - >>> from vyos.ifconfig import EthernetIf - >>> i = EthernetIf('eth0') - >>> i.get_driver_name() - 'vmxnet3' - """ - ifname = self.config['ifname'] - sysfs_file = f'/sys/class/net/{ifname}/device/driver/module' - if os.path.exists(sysfs_file): - link = os.readlink(sysfs_file) - return os.path.basename(link) - else: - return None - def set_flow_control(self, enable): """ Changes the pause parameters of the specified Ethernet device. @@ -116,8 +97,7 @@ class EthernetIf(Interface): raise ValueError("Value out of range") if not self.ethtool.check_flow_control(): - self._debug_msg(f'{driver_name} driver does not support changing '\ - 'flow control settings!') + self._debug_msg(f'NIC driver does not support changing flow control settings!') return False current = self.ethtool.get_flow_control() @@ -151,10 +131,8 @@ class EthernetIf(Interface): if duplex not in ['auto', 'full', 'half']: raise ValueError("Value out of range (duplex)") - driver_name = self.get_driver_name() - if driver_name in ['vmxnet3', 'virtio_net', 'xen_netfront']: - self._debug_msg(f'{driver_name} driver does not support changing '\ - 'speed/duplex settings!') + if not self.ethtool.check_speed_duplex(speed, duplex): + self._debug_msg(f'NIC driver does not support changing speed/duplex settings!') return # Get current speed and duplex settings: diff --git a/src/conf_mode/interfaces-ethernet.py b/src/conf_mode/interfaces-ethernet.py index 21a04f954..e7250fb49 100755 --- a/src/conf_mode/interfaces-ethernet.py +++ b/src/conf_mode/interfaces-ethernet.py @@ -126,7 +126,7 @@ def verify(ethernet): if not os.path.exists(f'/sys/class/net/{ifname}/queues/rx-0/rps_cpus'): raise ConfigError('Interface does not suport RPS!') - driver = EthernetIf(ifname).get_driver_name() + driver = ethtool.get_driver_name() # T3342 - Xen driver requires special treatment if driver == 'vif': if int(ethernet['mtu']) > 1500 and dict_search('offload.sg', ethernet) == None: -- cgit v1.2.3 From 7a264d9e17bd918c33879ba5cd70b00bb786726a Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Fri, 10 Sep 2021 16:47:42 +0200 Subject: ethernet: T3802: not all NICs support reading speed/duplex settings in all states Turns out an AX88179 USB 3.0 NIC does not support reading back the speed and duplex settings in every operating state. While the NIC is beeing initialized, reading the speed setting will return: $ cat /sys/class/net/eth6/speed cat: /sys/class/net/eth6/speed: Invalid argument Thus if this happens, we simply tell the system that the current NIC speed matches the requested speed and nothing is changed at this point in time. (cherry picked from commit e2b7e1766cc22c5cd718a5001be6336bdca92eec) --- python/vyos/ifconfig/ethernet.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/python/vyos/ifconfig/ethernet.py b/python/vyos/ifconfig/ethernet.py index 78fec1fa2..2e59a7afc 100644 --- a/python/vyos/ifconfig/ethernet.py +++ b/python/vyos/ifconfig/ethernet.py @@ -142,9 +142,12 @@ class EthernetIf(Interface): # bail out early as nothing is to change return else: - # read in current speed and duplex settings - cur_speed = read_file(f'/sys/class/net/{ifname}/speed') - cur_duplex = read_file(f'/sys/class/net/{ifname}/duplex') + # XXX: read in current speed and duplex settings + # There are some "nice" NICs like AX88179 which do not support + # reading the speed thus we simply fallback to the supplied speed + # to not cause any change here and raise an exception. + cur_speed = read_file(f'/sys/class/net/{ifname}/speed', speed) + cur_duplex = read_file(f'/sys/class/net/{ifname}/duplex', duplex) if (cur_speed == speed) and (cur_duplex == duplex): # bail out early as nothing is to change return -- cgit v1.2.3 From e626407f4c246941eb2ca1b167a4b594b7bf6461 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Fri, 10 Sep 2021 16:56:32 +0200 Subject: ethtool: T3802: extend check_speed_duplex() implementation to support 'auto' (cherry picked from commit 3037661951d0e5d1f6264f886781b7ddc019329e) --- python/vyos/ethtool.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/python/vyos/ethtool.py b/python/vyos/ethtool.py index 4efc3a234..bc95767b1 100644 --- a/python/vyos/ethtool.py +++ b/python/vyos/ethtool.py @@ -190,9 +190,9 @@ class Ethtool: the underlaying network adapter. """ if isinstance(speed, int): speed = str(speed) - if not speed.isdigit(): + if speed != 'auto' and not speed.isdigit(): raise ValueError(f'Value "{speed}" for speed is invalid!') - if duplex not in ['full', 'half']: + if duplex not in ['auto', 'full', 'half']: raise ValueError(f'Value "{duplex}" for duplex is invalid!') if self.get_driver_name() in ['vmxnet3', 'virtio_net', 'xen_netfront']: -- cgit v1.2.3 From c608970402e0ebb68673cab4ea31e84fbb6843e5 Mon Sep 17 00:00:00 2001 From: Viacheslav Date: Fri, 10 Sep 2021 15:25:09 +0000 Subject: squid: T3810: Remove build in acl vars localost and to_localhost --- data/templates/squid/squid.conf.tmpl | 2 -- 1 file changed, 2 deletions(-) diff --git a/data/templates/squid/squid.conf.tmpl b/data/templates/squid/squid.conf.tmpl index 8754e762d..8458e9589 100644 --- a/data/templates/squid/squid.conf.tmpl +++ b/data/templates/squid/squid.conf.tmpl @@ -1,7 +1,5 @@ ### generated by service_webproxy.py ### -acl localhost src 127.0.0.1/32 -acl to_localhost dst 127.0.0.0/8 acl net src all acl SSL_ports port 443 acl Safe_ports port 80 # http -- cgit v1.2.3 From 9d209cbf7318e4658b2a8ba39a3dd064ea629109 Mon Sep 17 00:00:00 2001 From: Viacheslav Date: Fri, 10 Sep 2021 15:27:25 +0000 Subject: squid: T3810: Node source-group address should be multi --- interface-definitions/service_webproxy.xml.in | 1 + 1 file changed, 1 insertion(+) diff --git a/interface-definitions/service_webproxy.xml.in b/interface-definitions/service_webproxy.xml.in index 64747420b..d61a95690 100644 --- a/interface-definitions/service_webproxy.xml.in +++ b/interface-definitions/service_webproxy.xml.in @@ -504,6 +504,7 @@ + -- cgit v1.2.3 From c3e585ebd53a9e61303ffd7c13289ce328badb3d Mon Sep 17 00:00:00 2001 From: Viacheslav Date: Fri, 10 Sep 2021 15:28:48 +0000 Subject: squidguard: T3810: Set DB directory rigths 755 --- src/conf_mode/service_webproxy.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/conf_mode/service_webproxy.py b/src/conf_mode/service_webproxy.py index cbbd2e0bc..a16cc4aeb 100755 --- a/src/conf_mode/service_webproxy.py +++ b/src/conf_mode/service_webproxy.py @@ -23,6 +23,7 @@ from vyos.config import Config from vyos.configdict import dict_merge from vyos.template import render from vyos.util import call +from vyos.util import chmod_755 from vyos.util import dict_search from vyos.util import write_file from vyos.validate import is_addr_assigned @@ -192,6 +193,8 @@ def apply(proxy): return None + if os.path.exists(squidguard_db_dir): + chmod_755(squidguard_db_dir) call('systemctl restart squid.service') return None -- cgit v1.2.3 From f36fe0fbd65f4992c867873698b921745bfc8f91 Mon Sep 17 00:00:00 2001 From: Viacheslav Date: Fri, 10 Sep 2021 15:32:35 +0000 Subject: squid: squidguard: T3810: Fix template for sourcre-group and rule Modify template for squid Replace old directives to actual --- data/templates/squid/squid.conf.tmpl | 6 ++-- data/templates/squid/squidGuard.conf.tmpl | 45 ++++++++++++++++++++++---- smoketest/scripts/cli/test_service_webproxy.py | 6 ++-- 3 files changed, 44 insertions(+), 13 deletions(-) diff --git a/data/templates/squid/squid.conf.tmpl b/data/templates/squid/squid.conf.tmpl index 8458e9589..80826fc75 100644 --- a/data/templates/squid/squid.conf.tmpl +++ b/data/templates/squid/squid.conf.tmpl @@ -99,9 +99,9 @@ forwarded_for off {# SquidGuard #} {% if url_filtering is defined and url_filtering.disable is not defined %} {% if url_filtering.squidguard is defined and url_filtering.squidguard is not none %} -redirect_program /usr/bin/squidGuard -c {{ squidguard_conf }} -redirect_children 8 -redirector_bypass on +url_rewrite_program /usr/bin/squidGuard -c {{ squidguard_conf }} +url_rewrite_children 8 +url_rewrite_bypass on {% endif %} {% endif %} diff --git a/data/templates/squid/squidGuard.conf.tmpl b/data/templates/squid/squidGuard.conf.tmpl index f530d1072..c59dc901e 100644 --- a/data/templates/squid/squidGuard.conf.tmpl +++ b/data/templates/squid/squidGuard.conf.tmpl @@ -73,19 +73,52 @@ dest local-block-keyword-default { {% for category in sg_config.allow_category %} {{ sg_rule(category, False, squidguard_db_dir) }} {% set acl.value = acl.value + ' ' + category + '-default' %} +{% endfor %} +{% endif %} +{% if sg_config.source_group is defined and sg_config.source_group is not none %} +{% for sgroup, sg_config in sg_config.source_group.items() %} +{% if sg_config.address is defined and sg_config.address is not none %} +src {{ sgroup }} { +{% for address in sg_config.address %} + ip {{ address }} +{% endfor %} +} + +{% endif %} +{% endfor %} +{% endif %} +{% if sg_config.rule is defined and sg_config.rule is not none %} +{% for rule, rule_config in sg_config.rule.items() %} +{% for b_category in rule_config.block_category%} +dest {{ b_category }} { + domainlist {{ b_category }}/domains + urllist {{ b_category }}/urls +} +{% endfor %} + {% endfor %} {% endif %} acl { - default { +{% if sg_config.rule is defined and sg_config.rule is not none %} +{% for rule, rule_config in sg_config.rule.items() %} + {{ rule_config.source_group }} { +{% for b_category in rule_config.block_category%} + pass local-ok-1 !in-addr !{{ b_category }} all +{% endfor %} + } +{% endfor %} +{% endif %} + + default { {% if sg_config.enable_safe_search is defined %} - rewrite safesearch + rewrite safesearch {% endif %} - pass {{ acl.value }} {{ 'none' if sg_config.default_action is defined and sg_config.default_action == 'block' else 'allow' }} - redirect 302:http://{{ sg_config.redirect_url }} + pass {{ acl.value }} {{ 'none' if sg_config.default_action is defined and sg_config.default_action == 'block' else 'allow' }} + redirect 302:http://{{ sg_config.redirect_url }} {% if sg_config.log is defined and sg_config.log is not none %} - log blacklist.log + log blacklist.log {% endif %} - } + } } {% endif %} {% endif %} diff --git a/smoketest/scripts/cli/test_service_webproxy.py b/smoketest/scripts/cli/test_service_webproxy.py index d47bd452d..6780a93f9 100755 --- a/smoketest/scripts/cli/test_service_webproxy.py +++ b/smoketest/scripts/cli/test_service_webproxy.py @@ -52,8 +52,6 @@ class TestServiceWebProxy(VyOSUnitTestSHIM.TestCase): self.assertIn(f'access_log /var/log/squid/access.log squid', config) # ACL verification - self.assertIn(f'acl localhost src 127.0.0.1/32', config) - self.assertIn(f'acl to_localhost dst 127.0.0.0/8', config) self.assertIn(f'acl net src all', config) self.assertIn(f'acl SSL_ports port 443', config) @@ -234,8 +232,8 @@ class TestServiceWebProxy(VyOSUnitTestSHIM.TestCase): config = read_file(PROXY_CONF) self.assertIn(f'http_port {listen_ip}:3128 intercept', config) - self.assertIn(f'redirect_program /usr/bin/squidGuard -c /etc/squidguard/squidGuard.conf', config) - self.assertIn(f'redirect_children 8', config) + self.assertIn(f'url_rewrite_program /usr/bin/squidGuard -c /etc/squidguard/squidGuard.conf', config) + self.assertIn(f'url_rewrite_children 8', config) # Check SquidGuard config sg_config = read_file('/etc/squidguard/squidGuard.conf') -- cgit v1.2.3 From d39567c977c84f1c16998947e16d397edbb015be Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Fri, 10 Sep 2021 22:50:27 +0200 Subject: frr: T1514: refactor restart script and drop duplicated code --- src/op_mode/restart_frr.py | 131 ++++++++++++++++++++------------------------- 1 file changed, 57 insertions(+), 74 deletions(-) diff --git a/src/op_mode/restart_frr.py b/src/op_mode/restart_frr.py index 0b2322478..109c8dd7b 100755 --- a/src/op_mode/restart_frr.py +++ b/src/op_mode/restart_frr.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2019 VyOS maintainers and contributors +# Copyright (C) 2019-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 @@ -13,16 +13,19 @@ # # You should have received a copy of the GNU General Public License # along with this program. If not, see . -# -import sys +import os import argparse import logging -from logging.handlers import SysLogHandler -from pathlib import Path import psutil +from logging.handlers import SysLogHandler +from shutil import rmtree + from vyos.util import call +from vyos.util import ask_yes_no +from vyos.util import process_named_running +from vyos.util import makedir # some default values watchfrr = '/usr/lib/frr/watchfrr.sh' @@ -40,40 +43,45 @@ logger.setLevel(logging.INFO) def _check_safety(): try: # print warning - answer = input("WARNING: This is a potentially unsafe function! You may lose the connection to the router or active configuration after running this command. Use it at your own risk! Continue? [y/N]: ") - if not answer.lower() == "y": - logger.error("User aborted command") + if not ask_yes_no('WARNING: This is a potentially unsafe function!\n' \ + 'You may lose the connection to the router or active configuration after\n' \ + 'running this command. Use it at your own risk!\n\n' + 'Continue?'): return False # check if another restart process already running if len([process for process in psutil.process_iter(attrs=['pid', 'name', 'cmdline']) if 'python' in process.info['name'] and 'restart_frr.py' in process.info['cmdline'][1]]) > 1: - logger.error("Another restart_frr.py already running") - answer = input("Another restart_frr.py process is already running. It is unsafe to continue. Do you want to process anyway? [y/N]: ") - if not answer.lower() == "y": + message = 'Another restart_frr.py process is already running!' + logger.error(message) + if not ask_yes_no(f'\n{message} It is unsafe to continue.\n\n' \ + 'Do you want to process anyway?'): return False # check if watchfrr.sh is running - for process in psutil.process_iter(attrs=['pid', 'name', 'cmdline']): - if 'bash' in process.info['name'] and watchfrr in process.info['cmdline']: - logger.error("Another {} already running".format(watchfrr)) - answer = input("Another {} process is already running. It is unsafe to continue. Do you want to process anyway? [y/N]: ".format(watchfrr)) - if not answer.lower() == "y": - return False + tmp = os.path.basename(watchfrr) + if process_named_running(tmp): + message = f'Another {tmp} process is already running.' + logger.error(message) + if not ask_yes_no(f'{message} It is unsafe to continue.\n\n' \ + 'Do you want to process anyway?'): + return False # check if vtysh is running - for process in psutil.process_iter(attrs=['pid', 'name', 'cmdline']): - if 'vtysh' in process.info['name']: - logger.error("The vtysh is running by another task") - answer = input("The vtysh is running by another task. It is unsafe to continue. Do you want to process anyway? [y/N]: ") - if not answer.lower() == "y": - return False + if process_named_running('vtysh'): + message = 'vtysh process is executed by another task.' + logger.error(message) + if not ask_yes_no(f'{message} It is unsafe to continue.\n\n' \ + 'Do you want to process anyway?'): + return False # check if temporary directory exists - if Path(frrconfig_tmp).exists(): - logger.error("The temporary directory \"{}\" already exists".format(frrconfig_tmp)) - answer = input("The temporary directory \"{}\" already exists. It is unsafe to continue. Do you want to process anyway? [y/N]: ".format(frrconfig_tmp)) - if not answer.lower() == "y": + if os.path.exists(frrconfig_tmp): + message = f'Temporary directory "{frrconfig_tmp}" already exists!' + logger.error(message) + if not ask_yes_no(f'{message} It is unsafe to continue.\n\n' \ + 'Do you want to process anyway?'): return False + except: logger.error("Something goes wrong in _check_safety()") return False @@ -84,72 +92,47 @@ def _check_safety(): # write active config to file def _write_config(): # create temporary directory - Path(frrconfig_tmp).mkdir(parents=False, exist_ok=True) + makedir(frrconfig_tmp) # save frr.conf to it - command = "{} -n -w --config_dir {} 2> /dev/null".format(vtysh, frrconfig_tmp) + command = f'{vtysh} -n -w --config_dir {frrconfig_tmp} 2> /dev/null' return_code = call(command) - if not return_code == 0: - logger.error("Failed to save active config: \"{}\" returned exit code: {}".format(command, return_code)) + if return_code != 0: + logger.error(f'Failed to save active config: "{command}" returned exit code: {return_code}') return False - logger.info("Active config saved to {}".format(frrconfig_tmp)) + logger.info(f'Active config saved to {frrconfig_tmp}') return True # clear and remove temporary directory def _cleanup(): - tmpdir = Path(frrconfig_tmp) - try: - if tmpdir.exists(): - for file in tmpdir.iterdir(): - file.unlink() - tmpdir.rmdir() - except: - logger.error("Failed to remove temporary directory {}".format(frrconfig_tmp)) - print("Failed to remove temporary directory {}".format(frrconfig_tmp)) - -# check if daemon is running -def _daemon_check(daemon): - command = "{} print_status {}".format(watchfrr, daemon) - return_code = call(command) - if not return_code == 0: - logger.error("Daemon \"{}\" is not running".format(daemon)) - return False - - # return True if all checks were passed - return True + if os.path.isdir(frrconfig_tmp): + rmtree(frrconfig_tmp) # restart daemon def _daemon_restart(daemon): - command = "{} restart {}".format(watchfrr, daemon) + command = f'{watchfrr} restart {daemon}' return_code = call(command) if not return_code == 0: - logger.error("Failed to restart daemon \"{}\"".format(daemon)) + logger.error(f'Failed to restart daemon "{daemon}"!') return False # return True if restarted successfully - logger.info("Daemon \"{}\" restarted".format(daemon)) + logger.info(f'Daemon "{daemon}" restarted!') return True # reload old config def _reload_config(daemon): if daemon != '': - command = "{} -n -b --config_dir {} -d {} 2> /dev/null".format(vtysh, frrconfig_tmp, daemon) + command = f'{vtysh} -n -b --config_dir {frrconfig_tmp} -d {daemon} 2> /dev/null' else: - command = "{} -n -b --config_dir {} 2> /dev/null".format(vtysh, frrconfig_tmp) + command = f'{vtysh} -n -b --config_dir {frrconfig_tmp} 2> /dev/null' return_code = call(command) if not return_code == 0: - logger.error("Failed to reinstall configuration") + logger.error('Failed to re-install configuration!') return False # return True if restarted successfully - logger.info("Configuration reinstalled successfully") - return True - -# check all daemons if they are running -def _check_args_daemon(daemons): - for daemon in daemons: - if not _daemon_check(daemon): - return False + logger.info('Configuration re-installed successfully!') return True # define program arguments @@ -159,19 +142,18 @@ cmd_args_parser.add_argument('--daemon', choices=['bfdd', 'bgpd', 'ospfd', 'ospf # parse arguments cmd_args = cmd_args_parser.parse_args() - # main logic # restart daemon if cmd_args.action == 'restart': # check if it is safe to restart FRR if not _check_safety(): print("\nOne of the safety checks was failed or user aborted command. Exiting.") - sys.exit(1) + exit(1) if not _write_config(): print("Failed to save active config") _cleanup() - sys.exit(1) + exit(1) # a little trick to make further commands more clear if not cmd_args.daemon: @@ -179,19 +161,20 @@ if cmd_args.action == 'restart': # check all daemons if they are running if cmd_args.daemon != ['']: - if not _check_args_daemon(cmd_args.daemon): - print("Warning: some of listed daemons are not running") + for daemon in cmd_args.daemon: + if not process_named_running(daemon): + print('WARNING: some of listed daemons are not running!') # run command to restart daemon for daemon in cmd_args.daemon: if not _daemon_restart(daemon): - print("Failed to restart daemon: {}".format(daemon)) + print('Failed to restart daemon: {daemon}') _cleanup() - sys.exit(1) + exit(1) # reinstall old configuration _reload_config(daemon) # cleanup after all actions _cleanup() -sys.exit(0) +exit(0) -- cgit v1.2.3 From b46c1b0811a6391dd3dbb6c91d079f3fea5081e1 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Fri, 10 Sep 2021 23:11:15 +0200 Subject: wireguard: T3642: directly import keys to CLI when run in config mode vyos@vyos# run generate pki wireguard key-pair install interface wg10 "generate" CLI command executed from config session. Generated private-key was imported to CLI! Use the following command to verify: show interfaces wireguard wg10 Corresponding public-key to use on peer system is: 'hGaWcoG7f+5sPAUY/MNQH1JFhsYdsGTecYA9S2J8xGs=' vyos@vyos# run generate pki wireguard preshared-key install interface wg10 peer vyos "generate" CLI command executed from config session. Generated preshared-key was imported to CLI! Use the following command to verify: show interfaces wireguard wg10 vyos@vyos# show interfaces wireguard wg10 +peer vyos { + preshared-key OwTALZy8w6VIBMxUwbOv6Ys7QMyhrtY4aw+0cUjmmCw= +} +private-key 0Pu95CejvCUCCwrTW39TCYnitESWAdIIFTVJb7UgxVU= [edit] --- src/op_mode/pki.py | 38 ++++++++++++++++++++++++++++++++------ 1 file changed, 32 insertions(+), 6 deletions(-) diff --git a/src/op_mode/pki.py b/src/op_mode/pki.py index d28cee5d0..e1428c581 100755 --- a/src/op_mode/pki.py +++ b/src/op_mode/pki.py @@ -23,6 +23,7 @@ import tabulate from cryptography import x509 from cryptography.x509.oid import ExtendedKeyUsageOID +from vyos.config import Config from vyos.configquery import ConfigTreeQuery from vyos.configdict import dict_merge from vyos.pki import encode_certificate, encode_public_key, encode_private_key, encode_dh_parameters @@ -36,7 +37,6 @@ from vyos.util import ask_input, ask_yes_no from vyos.util import cmd CERT_REQ_END = '-----END CERTIFICATE REQUEST-----' - auth_dir = '/config/auth' # Helper Functions @@ -216,17 +216,39 @@ def install_wireguard_key(interface, private_key, public_key): print(f'"{interface}" is not a WireGuard interface name!') exit(1) - print("Configure mode commands to install key:", end="\n\n") - print(f"set interfaces wireguard {interface} private-key '{private_key}'", end="\n\n") - print(f"Public key to use on peer system: '{public_key}'") + # Check if we are running in a config session - if yes, we can directly write to the CLI + cli_string = f"interfaces wireguard {interface} private-key '{private_key}'" + if Config().in_session(): + cmd(f"/opt/vyatta/sbin/my_set {cli_string}") + + print('"generate" CLI command executed from config session.\nGenerated private-key was imported to CLI!',end='\n\n') + print(f'Use the following command to verify: show interfaces wireguard {interface}') + else: + print('"generate" CLI command executed from operational level.\n' + 'Generated private-key is not stored to CLI, use configure mode commands to install key:', end='\n\n') + print(f"set {cli_string}", end="\n\n") + + print(f"Corresponding public-key to use on peer system is: '{public_key}'") + def install_wireguard_psk(interface, peer, psk): from vyos.ifconfig import Section if Section.section(interface) != 'wireguard': print(f'"{interface}" is not a WireGuard interface name!') exit(1) - # Show conf commands for installing wireguard psk - print(f"set interfaces wireguard {interface} peer {peer} preshared-key '{psk}'") + + # Check if we are running in a config session - if yes, we can directly write to the CLI + cli_string = f"interfaces wireguard {interface} peer {peer} preshared-key '{psk}'" + if Config().in_session(): + cmd(f"/opt/vyatta/sbin/my_set {cli_string}") + + print('"generate" CLI command executed from config session.\nGenerated preshared-key was imported to CLI!',end='\n\n') + print(f'Use the following command to verify: show interfaces wireguard {interface}') + else: + print('"generate" CLI command executed from operational level.\n' + 'Generated preshared-key is not stored to CLI, use configure mode commands to install key:', end='\n\n') + print(f"set {cli_string}", end="\n\n") + def ask_passphrase(): passphrase = None @@ -825,6 +847,10 @@ if __name__ == '__main__': generate_openvpn_key(args.openvpn, install=args.install, file=args.file) elif args.wireguard: + # WireGuard supports writing key directly into the CLI, but this + # requires the vyos_libexec_dir environment variable to be set + os.environ["vyos_libexec_dir"] = "/usr/libexec/vyos" + if args.key: generate_wireguard_key(args.interface, install=args.install) if args.psk: -- cgit v1.2.3 From ed01c03fc32d77ea95b89a40a7478e36abec8c1e Mon Sep 17 00:00:00 2001 From: Daniil Baturin Date: Sat, 11 Sep 2021 21:22:23 -0500 Subject: Fix inconsistent capitalization in the show version output --- python/vyos/airbag.py | 8 ++++---- src/op_mode/show_version.py | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/python/vyos/airbag.py b/python/vyos/airbag.py index a20f44207..3c7a144b7 100644 --- a/python/vyos/airbag.py +++ b/python/vyos/airbag.py @@ -125,14 +125,14 @@ def _intercepting_exceptions(_singleton=[False]): # if the key before the value has not time, syslog takes that as the source of the message FAULT = """\ -Report Time: {date} -Image Version: VyOS {version} -Release Train: {release_train} +Report time: {date} +Image version: VyOS {version} +Release train: {release_train} Built by: {built_by} Built on: {built_on} Build UUID: {build_uuid} -Build Commit ID: {build_git} +Build commit ID: {build_git} Architecture: {system_arch} Boot via: {boot_via} diff --git a/src/op_mode/show_version.py b/src/op_mode/show_version.py index 5bbc2e1f1..7962e1e7b 100755 --- a/src/op_mode/show_version.py +++ b/src/op_mode/show_version.py @@ -32,12 +32,12 @@ parser.add_argument("-j", "--json", action="store_true", help="Produce JSON outp version_output_tmpl = """ Version: VyOS {{version}} -Release Train: {{release_train}} +Release train: {{release_train}} Built by: {{built_by}} Built on: {{built_on}} Build UUID: {{build_uuid}} -Build Commit ID: {{build_git}} +Build commit ID: {{build_git}} Architecture: {{system_arch}} Boot via: {{boot_via}} -- cgit v1.2.3 From ebf89e0535cf8362a077c07a9cfa2dbd8eeecbb4 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sun, 12 Sep 2021 18:30:09 +0200 Subject: pki: T3642: show only requested CA in op-mode --- op-mode-definitions/pki.xml.in | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/op-mode-definitions/pki.xml.in b/op-mode-definitions/pki.xml.in index 6b9b0d3f6..70154f881 100644 --- a/op-mode-definitions/pki.xml.in +++ b/op-mode-definitions/pki.xml.in @@ -356,23 +356,21 @@ Show PKI certificates - + Show CA certificates - - - - Show CA certificate by name - - pki ca - - - sudo ${vyos_op_scripts_dir}/pki.py --action show --ca "$5" - - sudo ${vyos_op_scripts_dir}/pki.py --action show --ca "all" - + + + + Show CA certificate by name + + pki ca + + + sudo ${vyos_op_scripts_dir}/pki.py --action show --ca "$4" + Show certificates -- cgit v1.2.3 From e092ea4321510c690a51b87a9f7b3eff1fc6ed72 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sun, 12 Sep 2021 20:12:58 +0200 Subject: pki: T3642: show only requested certificate in op-mode --- op-mode-definitions/pki.xml.in | 32 +++++++++++++++----------------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/op-mode-definitions/pki.xml.in b/op-mode-definitions/pki.xml.in index 70154f881..75ef92d19 100644 --- a/op-mode-definitions/pki.xml.in +++ b/op-mode-definitions/pki.xml.in @@ -353,41 +353,39 @@ - Show PKI certificates + Show PKI x509 certificates - Show CA certificates + Show x509 CA certificates sudo ${vyos_op_scripts_dir}/pki.py --action show --ca "all" - Show CA certificate by name + Show x509 CA certificate by name pki ca sudo ${vyos_op_scripts_dir}/pki.py --action show --ca "$4" - + - Show certificates + Show x509 certificates - - - - Show certificate by name - - pki certificate - - - sudo ${vyos_op_scripts_dir}/pki.py --action show --certificate "$5" - - sudo ${vyos_op_scripts_dir}/pki.py --action show --certificate "all" - + + + + Show x509 certificate by name + + pki certificate + + + sudo ${vyos_op_scripts_dir}/pki.py --action show --certificate "all" + Show certificate revocation lists -- cgit v1.2.3 From 42f675bb1c160fa6b1115e2b3f26afbdfa26e734 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sun, 12 Sep 2021 20:12:58 +0200 Subject: pki: T3642: show only requested certificate in op-mode --- op-mode-definitions/pki.xml.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/op-mode-definitions/pki.xml.in b/op-mode-definitions/pki.xml.in index 75ef92d19..9aaf2a571 100644 --- a/op-mode-definitions/pki.xml.in +++ b/op-mode-definitions/pki.xml.in @@ -384,7 +384,7 @@ pki certificate - sudo ${vyos_op_scripts_dir}/pki.py --action show --certificate "all" + sudo ${vyos_op_scripts_dir}/pki.py --action show --certificate "$4" -- cgit v1.2.3 From f86b7314d025fd0cf11c2d91638ed3cc7c4fa507 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sun, 12 Sep 2021 20:23:45 +0200 Subject: pki: T3642: listing all CRLs show be doen using a leafNode --- op-mode-definitions/pki.xml.in | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) diff --git a/op-mode-definitions/pki.xml.in b/op-mode-definitions/pki.xml.in index 9aaf2a571..a1c55dcf4 100644 --- a/op-mode-definitions/pki.xml.in +++ b/op-mode-definitions/pki.xml.in @@ -386,23 +386,12 @@ sudo ${vyos_op_scripts_dir}/pki.py --action show --certificate "$4" - + - Show certificate revocation lists + Show x509 certificate revocation lists - - - - Show certificate revocation lists from specified CA - - pki ca - - - sudo ${vyos_op_scripts_dir}/pki.py --action show --crl "$5" - - sudo ${vyos_op_scripts_dir}/pki.py --action show --crl "all" - + sudo ${vyos_op_scripts_dir}/pki.py --action show -- cgit v1.2.3 From 2e68b070f1531c87d5976213565d9b929c5589ab Mon Sep 17 00:00:00 2001 From: sarthurdev <965089+sarthurdev@users.noreply.github.com> Date: Mon, 13 Sep 2021 13:51:49 +0200 Subject: ipsec: T3828: Use IKE dh-group when ESP dh-group is set to `enable` --- data/templates/ipsec/swanctl/l2tp.tmpl | 2 +- data/templates/ipsec/swanctl/peer.tmpl | 4 ++-- data/templates/ipsec/swanctl/profile.tmpl | 2 +- data/templates/ipsec/swanctl/remote_access.tmpl | 2 +- python/vyos/template.py | 11 +++++++++-- 5 files changed, 14 insertions(+), 7 deletions(-) diff --git a/data/templates/ipsec/swanctl/l2tp.tmpl b/data/templates/ipsec/swanctl/l2tp.tmpl index 2df5c2a4d..4cd1b4af3 100644 --- a/data/templates/ipsec/swanctl/l2tp.tmpl +++ b/data/templates/ipsec/swanctl/l2tp.tmpl @@ -20,7 +20,7 @@ children { l2tp_remote_access_esp { mode = transport - esp_proposals = {{ l2tp_esp | get_esp_ike_cipher | join(',') if l2tp_esp else l2tp_esp_default }} + esp_proposals = {{ l2tp_esp | get_esp_ike_cipher(l2tp_ike) | join(',') if l2tp_esp else l2tp_esp_default }} life_time = {{ l2tp_esp.lifetime if l2tp_esp else l2tp.lifetime }}s local_ts = dynamic[/1701] remote_ts = dynamic diff --git a/data/templates/ipsec/swanctl/peer.tmpl b/data/templates/ipsec/swanctl/peer.tmpl index 019f9e0d7..951a1b22c 100644 --- a/data/templates/ipsec/swanctl/peer.tmpl +++ b/data/templates/ipsec/swanctl/peer.tmpl @@ -56,7 +56,7 @@ {% if peer_conf.vti is defined and peer_conf.vti.bind is defined and peer_conf.tunnel is not defined %} {% set vti_esp = esp_group[ peer_conf.vti.esp_group ] if peer_conf.vti.esp_group is defined else esp_group[ peer_conf.default_esp_group ] %} peer_{{ name }}_vti { - esp_proposals = {{ vti_esp | get_esp_ike_cipher | join(',') }} + esp_proposals = {{ vti_esp | get_esp_ike_cipher(ike) | join(',') }} life_time = {{ vti_esp.lifetime }}s local_ts = 0.0.0.0/0,::/0 remote_ts = 0.0.0.0/0,::/0 @@ -87,7 +87,7 @@ {% set remote_port = tunnel_conf.remote.port if tunnel_conf.remote is defined and tunnel_conf.remote.port is defined else '' %} {% set remote_suffix = '[{0}/{1}]'.format(proto, remote_port) if proto or remote_port else '' %} peer_{{ name }}_tunnel_{{ tunnel_id }} { - esp_proposals = {{ tunnel_esp | get_esp_ike_cipher | join(',') }} + esp_proposals = {{ tunnel_esp | get_esp_ike_cipher(ike) | join(',') }} life_time = {{ tunnel_esp.lifetime }}s {% if tunnel_esp.mode is not defined or tunnel_esp.mode == 'tunnel' %} {% if tunnel_conf.local is defined and tunnel_conf.local.prefix is defined %} diff --git a/data/templates/ipsec/swanctl/profile.tmpl b/data/templates/ipsec/swanctl/profile.tmpl index 66bcaa776..948dd8f87 100644 --- a/data/templates/ipsec/swanctl/profile.tmpl +++ b/data/templates/ipsec/swanctl/profile.tmpl @@ -19,7 +19,7 @@ {% endif %} children { dmvpn { - esp_proposals = {{ esp | get_esp_ike_cipher | join(',') }} + esp_proposals = {{ esp | get_esp_ike_cipher(ike) | join(',') }} rekey_time = {{ esp.lifetime }}s rand_time = 540s local_ts = dynamic[gre] diff --git a/data/templates/ipsec/swanctl/remote_access.tmpl b/data/templates/ipsec/swanctl/remote_access.tmpl index 4fdc2a276..6354c60b1 100644 --- a/data/templates/ipsec/swanctl/remote_access.tmpl +++ b/data/templates/ipsec/swanctl/remote_access.tmpl @@ -35,7 +35,7 @@ } children { ikev2-vpn { - esp_proposals = {{ esp | get_esp_ike_cipher | join(',') }} + esp_proposals = {{ esp | get_esp_ike_cipher(ike) | join(',') }} rekey_time = {{ esp.lifetime }}s rand_time = 540s dpd_action = clear diff --git a/python/vyos/template.py b/python/vyos/template.py index ee6e52e1d..d13915766 100644 --- a/python/vyos/template.py +++ b/python/vyos/template.py @@ -393,8 +393,15 @@ def get_ip(interface): from vyos.ifconfig import Interface return Interface(interface).get_addr() +def get_first_ike_dh_group(ike_group): + if ike_group and 'proposal' in ike_group: + for priority, proposal in ike_group['proposal'].items(): + if 'dh_group' in proposal: + return 'dh-group' + proposal['dh_group'] + return 'dh-group2' # Fallback on dh-group2 + @register_filter('get_esp_ike_cipher') -def get_esp_ike_cipher(group_config): +def get_esp_ike_cipher(group_config, ike_group=None): pfs_lut = { 'dh-group1' : 'modp768', 'dh-group2' : 'modp1024', @@ -433,7 +440,7 @@ def get_esp_ike_cipher(group_config): elif 'pfs' in group_config and group_config['pfs'] != 'disable': group = group_config['pfs'] if group_config['pfs'] == 'enable': - group = 'dh-group2' + group = get_first_ike_dh_group(ike_group) tmp += '-' + pfs_lut[group] ciphers.append(tmp) -- cgit v1.2.3 From b60069c4d8d2c9cf156bf5877beb4f2601774171 Mon Sep 17 00:00:00 2001 From: sarthurdev <965089+sarthurdev@users.noreply.github.com> Date: Tue, 14 Sep 2021 11:01:08 +0200 Subject: ipsec: T3828: Update interface definitions for new behaviour --- interface-definitions/vpn_ipsec.xml.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface-definitions/vpn_ipsec.xml.in b/interface-definitions/vpn_ipsec.xml.in index ed4941efc..4120232ea 100644 --- a/interface-definitions/vpn_ipsec.xml.in +++ b/interface-definitions/vpn_ipsec.xml.in @@ -83,7 +83,7 @@ enable - Use Diffie-Hellman group 2 (modp1024) - default + Inherit Diffie-Hellman group from IKE group - default dh-group1 -- cgit v1.2.3 From 6b48900358ce9b01eaa78e3a086e95a26064f0df Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Tue, 14 Sep 2021 19:50:52 +0200 Subject: dhcpv6-pd: T421: disable wide dhcpv6 client debug messages --- src/systemd/dhcp6c@.service | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/systemd/dhcp6c@.service b/src/systemd/dhcp6c@.service index 9a97ee261..fdd6d7d88 100644 --- a/src/systemd/dhcp6c@.service +++ b/src/systemd/dhcp6c@.service @@ -9,7 +9,7 @@ StartLimitIntervalSec=0 WorkingDirectory=/run/dhcp6c Type=forking PIDFile=/run/dhcp6c/dhcp6c.%i.pid -ExecStart=/usr/sbin/dhcp6c -D -k /run/dhcp6c/dhcp6c.%i.sock -c /run/dhcp6c/dhcp6c.%i.conf -p /run/dhcp6c/dhcp6c.%i.pid %i +ExecStart=/usr/sbin/dhcp6c -k /run/dhcp6c/dhcp6c.%i.sock -c /run/dhcp6c/dhcp6c.%i.conf -p /run/dhcp6c/dhcp6c.%i.pid %i Restart=on-failure RestartSec=20 -- cgit v1.2.3 From ec9503a9ec487ec7aa3524cb9847357f0631ca25 Mon Sep 17 00:00:00 2001 From: Javinator9889 Date: Wed, 15 Sep 2021 19:11:49 +0200 Subject: typo: remove unnecessary 'i' from help --- interface-definitions/include/source-address-ipv4.xml.i | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface-definitions/include/source-address-ipv4.xml.i b/interface-definitions/include/source-address-ipv4.xml.i index 86235df61..052678113 100644 --- a/interface-definitions/include/source-address-ipv4.xml.i +++ b/interface-definitions/include/source-address-ipv4.xml.i @@ -1,7 +1,7 @@ - IPv4 source address used to initiiate connection + IPv4 source address used to initiate connection -- cgit v1.2.3 From 74878d14157408ef3a4c42f32608dcd5bbf812a1 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Wed, 15 Sep 2021 19:13:43 +0200 Subject: ipsec: T3830: set connections..remote.id to "peer" if undefined Restore "default" behavior from ipsec.conf --- data/templates/ipsec/swanctl/peer.tmpl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/data/templates/ipsec/swanctl/peer.tmpl b/data/templates/ipsec/swanctl/peer.tmpl index 951a1b22c..5d69b3d66 100644 --- a/data/templates/ipsec/swanctl/peer.tmpl +++ b/data/templates/ipsec/swanctl/peer.tmpl @@ -42,9 +42,9 @@ {% endif %} } remote { -{% if peer_conf.authentication.remote_id is defined %} +{% if peer_conf.authentication is defined and peer_conf.authentication.remote_id is defined and peer_conf.authentication.remote_id is not none %} id = "{{ peer_conf.authentication.remote_id }}" -{% elif peer[0:1] == '@' %} +{% else %} id = "{{ peer }}" {% endif %} auth = {{ 'psk' if peer_conf.authentication.mode == 'pre-shared-secret' else 'pubkey' }} -- cgit v1.2.3 From 3e85333ae7c53fc8b2ceae1d1788e795fd92c939 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Wed, 15 Sep 2021 19:14:37 +0200 Subject: ipsec: T3830: "authentication id|use-x509-id" are mutually exclusive Manually set peer id and use-x509-id are mutually exclusive! --- data/templates/ipsec/swanctl/peer.tmpl | 2 +- src/conf_mode/vpn_ipsec.py | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/data/templates/ipsec/swanctl/peer.tmpl b/data/templates/ipsec/swanctl/peer.tmpl index 5d69b3d66..98c09436c 100644 --- a/data/templates/ipsec/swanctl/peer.tmpl +++ b/data/templates/ipsec/swanctl/peer.tmpl @@ -31,7 +31,7 @@ encap = yes {% endif %} local { -{% if peer_conf.authentication is defined and peer_conf.authentication.id is defined and peer_conf.authentication.use_x509_id is not defined %} +{% if peer_conf.authentication is defined and peer_conf.authentication.id is defined and peer_conf.authentication.id is not none %} id = "{{ peer_conf.authentication.id }}" {% endif %} auth = {{ 'psk' if peer_conf.authentication.mode == 'pre-shared-secret' else 'pubkey' }} diff --git a/src/conf_mode/vpn_ipsec.py b/src/conf_mode/vpn_ipsec.py index ff6090e22..99b82ca2d 100755 --- a/src/conf_mode/vpn_ipsec.py +++ b/src/conf_mode/vpn_ipsec.py @@ -362,6 +362,9 @@ def verify(ipsec): if 'authentication' not in peer_conf or 'mode' not in peer_conf['authentication']: raise ConfigError(f"Missing authentication on site-to-site peer {peer}") + if {'id', 'use_x509_id'} <= set(peer_conf['authentication']): + raise ConfigError(f"Manually set peer id and use-x509-id are mutually exclusive!") + if peer_conf['authentication']['mode'] == 'x509': if 'x509' not in peer_conf['authentication']: raise ConfigError(f"Missing x509 settings on site-to-site peer {peer}") -- cgit v1.2.3 From c1e0a1148c867c1202a0f69ac83f77e14272effd Mon Sep 17 00:00:00 2001 From: erkin Date: Fri, 17 Sep 2021 12:03:15 +0300 Subject: T3823: Stop strip-private regexp from swallowing quotes --- src/helpers/strip-private.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/helpers/strip-private.py b/src/helpers/strip-private.py index c165d2cba..c74a379aa 100755 --- a/src/helpers/strip-private.py +++ b/src/helpers/strip-private.py @@ -47,7 +47,7 @@ ipv4_re = re.compile(r'(\d{1,3}\.){2}(\d{1,3}\.\d{1,3})') ipv4_subst = r'xxx.xxx.\2' # Censor all but the first two fields. -ipv6_re = re.compile(r'([0-9a-fA-F]{1,4}\:){2}(\S+)') +ipv6_re = re.compile(r'([0-9a-fA-F]{1,4}\:){2}([0-9a-fA-F:]+)') ipv6_subst = r'xxxx:xxxx:\2' def ip_match(match: re.Match, subst: str) -> str: @@ -96,12 +96,12 @@ if __name__ == "__main__": args = parser.parse_args() # Strict mode is the default and the absence of loose mode implies presence of strict mode. if not args.loose: - for arg in [args.mac, args.domain, args.hostname, args.username, args.dhcp, args.asn, args.snmp, args.lldp]: - arg = True + args.mac = args.domain = args.hostname = args.username = args.dhcp = args.asn = args.snmp = args.lldp = True if not args.public_address and not args.keep_address: args.address = True elif not args.address and not args.public_address: args.keep_address = True + # (condition, precompiled regexp, substitution string) stripping_rules = [ # Strip passwords @@ -120,7 +120,7 @@ if __name__ == "__main__": (True, re.compile(r'private-key \S+'), 'private-key xxxxxx'), # Strip MAC addresses - (args.mac, re.compile(r'([0-9a-fA-F]{2}\:){5}([0-9a-fA-F]{2}((\:{0,1})){3})'), r'XX:XX:XX:XX:XX:\2'), + (args.mac, re.compile(r'([0-9a-fA-F]{2}\:){5}([0-9a-fA-F]{2}((\:{0,1})){3})'), r'xx:xx:xx:xx:xx:\2'), # Strip host-name, domain-name, and domain-search (args.hostname, re.compile(r'(host-name|domain-name|domain-search) \S+'), r'\1 xxxxxx'), -- cgit v1.2.3 From dda9f655f94968b07043887a03e3bba176eb94d5 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sat, 18 Sep 2021 11:26:14 +0200 Subject: validator: T2417: bugfix on Python3 f'ormat strings Commit 3639a5610b590a ("validator: T2417: try to make the code clearer") introduced Python3 f'ormatted strings but missed the "f" keyword. --- src/validators/script | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/validators/script b/src/validators/script index 2665ec1f6..1d8a27e5c 100755 --- a/src/validators/script +++ b/src/validators/script @@ -1,8 +1,6 @@ #!/usr/bin/env python3 # -# numeric value validator -# -# Copyright (C) 2018 VyOS maintainers and contributors +# Copyright (C) 2018-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 @@ -23,7 +21,6 @@ import shlex import vyos.util - if __name__ == '__main__': if len(sys.argv) < 2: sys.exit('Please specify script file to check') @@ -35,11 +32,11 @@ if __name__ == '__main__': sys.exit(f'File {script} does not exist') if not (os.path.isfile(script) and os.access(script, os.X_OK)): - sys.exit('File {script} is not an executable file') + sys.exit(f'File {script} is not an executable file') # File outside the config dir is just a warning if not vyos.util.file_is_persistent(script): sys.exit( - 'Warning: file {path} is outside the / config directory\n' + f'Warning: file {path} is outside the / config directory\n' 'It will not be automatically migrated to a new image on system update' ) -- cgit v1.2.3 From 6f3130ea5c8c3043e4a5377c972b96233f22a5fc Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sat, 18 Sep 2021 15:18:21 +0200 Subject: ipsec: vti: T3831: avoid usinf xfrm if_id 0 - implement shift by one The key defaults to 0 and will match any policies which similarly do not have a lookup key configuration. This means that a vti0 named interface will pull in all traffic and others will stop working. Thus we simply shift the key by one to also support a vti0 interface. --- data/templates/ipsec/swanctl/peer.tmpl | 14 ++++++++++---- python/vyos/ifconfig/vti.py | 5 ++++- smoketest/scripts/cli/test_vpn_ipsec.py | 22 +++++++++++++++++----- 3 files changed, 31 insertions(+), 10 deletions(-) diff --git a/data/templates/ipsec/swanctl/peer.tmpl b/data/templates/ipsec/swanctl/peer.tmpl index 98c09436c..e039e98aa 100644 --- a/data/templates/ipsec/swanctl/peer.tmpl +++ b/data/templates/ipsec/swanctl/peer.tmpl @@ -61,8 +61,11 @@ local_ts = 0.0.0.0/0,::/0 remote_ts = 0.0.0.0/0,::/0 updown = "/etc/ipsec.d/vti-up-down {{ peer_conf.vti.bind }} {{ peer_conf.dhcp_interface if peer_conf.dhcp_interface is defined else 'no' }}" - if_id_in = {{ peer_conf.vti.bind | replace('vti', '') }} - if_id_out = {{ peer_conf.vti.bind | replace('vti', '') }} + {# The key defaults to 0 and will match any policies which similarly do not have a lookup key configuration. #} + {# Thus we simply shift the key by one to also support a vti0 interface #} +{% set if_id = peer_conf.vti.bind | replace('vti', '') | int +1 %} + if_id_in = {{ if_id }} + if_id_out = {{ if_id }} ipcomp = {{ 'yes' if vti_esp.compression is defined and vti_esp.compression == 'enable' else 'no' }} mode = {{ vti_esp.mode }} {% if peer[0:1] == '@' %} @@ -117,8 +120,11 @@ {% endif %} {% if peer_conf.vti is defined and peer_conf.vti.bind is defined %} updown = "/etc/ipsec.d/vti-up-down {{ peer_conf.vti.bind }} {{ peer_conf.dhcp_interface if peer_conf.dhcp_interface is defined else 'no' }}" - if_id_in = {{ peer_conf.vti.bind | replace('vti', '') }} - if_id_out = {{ peer_conf.vti.bind | replace('vti', '') }} + {# The key defaults to 0 and will match any policies which similarly do not have a lookup key configuration. #} + {# Thus we simply shift the key by one to also support a vti0 interface #} +{% set if_id = peer_conf.vti.bind | replace('vti', '') | int +1 %} + if_id_in = {{ if_id }} + if_id_out = {{ if_id }} {% endif %} } {% if tunnel_conf.passthrough is defined and tunnel_conf.passthrough %} diff --git a/python/vyos/ifconfig/vti.py b/python/vyos/ifconfig/vti.py index 470ebbff3..c50cd5ce9 100644 --- a/python/vyos/ifconfig/vti.py +++ b/python/vyos/ifconfig/vti.py @@ -35,8 +35,11 @@ class VTIIf(Interface): mapping = { 'source_interface' : 'dev', } - if_id = self.ifname.lstrip('vti') + # The key defaults to 0 and will match any policies which similarly do + # not have a lookup key configuration - thus we shift the key by one + # to also support a vti0 interface + if_id = str(int(if_id) +1) cmd = f'ip link add {self.ifname} type xfrm if_id {if_id}' for vyos_key, iproute2_key in mapping.items(): # dict_search will return an empty dict "{}" for valueless nodes like diff --git a/smoketest/scripts/cli/test_vpn_ipsec.py b/smoketest/scripts/cli/test_vpn_ipsec.py index f33268083..71a9d5137 100755 --- a/smoketest/scripts/cli/test_vpn_ipsec.py +++ b/smoketest/scripts/cli/test_vpn_ipsec.py @@ -128,7 +128,6 @@ class TestVPNIPsec(VyOSUnitTestSHIM.TestCase): self.cli_delete(base_path) self.cli_delete(nhrp_path) self.cli_delete(tunnel_path) - self.cli_delete(vti_path) self.cli_delete(ethernet_path) self.cli_commit() @@ -228,6 +227,11 @@ class TestVPNIPsec(VyOSUnitTestSHIM.TestCase): self.cli_commit() swanctl_conf = read_file(swanctl_file) + if_id = vti.lstrip('vti') + # The key defaults to 0 and will match any policies which similarly do + # not have a lookup key configuration - thus we shift the key by one + # to also support a vti0 interface + if_id = str(int(if_id) +1) swanctl_conf_lines = [ f'version = 2', f'auth = psk', @@ -238,8 +242,8 @@ class TestVPNIPsec(VyOSUnitTestSHIM.TestCase): f'mode = tunnel', f'local_ts = 172.16.10.0/24,172.16.11.0/24', f'remote_ts = 172.17.10.0/24,172.17.11.0/24', - f'if_id_in = {vti.lstrip("vti")}', # will be 10 for vti10 - f'if_id_out = {vti.lstrip("vti")}', + f'if_id_in = {if_id}', # will be 11 for vti10 - shifted by one + f'if_id_out = {if_id}', f'updown = "/etc/ipsec.d/vti-up-down {vti} no"' ] for line in swanctl_conf_lines: @@ -346,6 +350,11 @@ class TestVPNIPsec(VyOSUnitTestSHIM.TestCase): swanctl_conf = read_file(swanctl_file) tmp = peer_ip.replace('.', '-') + if_id = vti.lstrip('vti') + # The key defaults to 0 and will match any policies which similarly do + # not have a lookup key configuration - thus we shift the key by one + # to also support a vti0 interface + if_id = str(int(if_id) +1) swanctl_lines = [ f'peer_{tmp}', f'version = 0', # key-exchange not set - defaulting to 0 for ikev1 and ikev2 @@ -362,8 +371,8 @@ class TestVPNIPsec(VyOSUnitTestSHIM.TestCase): f'local_ts = 0.0.0.0/0,::/0', f'remote_ts = 0.0.0.0/0,::/0', f'updown = "/etc/ipsec.d/vti-up-down {vti} no"', - f'if_id_in = {vti.lstrip("vti")}', # will be 10 for vti10 - f'if_id_out = {vti.lstrip("vti")}', + f'if_id_in = {if_id}', # will be 11 for vti10 + f'if_id_out = {if_id}', f'ipcomp = no', f'mode = tunnel', f'start_action = start', @@ -378,5 +387,8 @@ class TestVPNIPsec(VyOSUnitTestSHIM.TestCase): for line in swanctl_secrets_lines: self.assertIn(line, swanctl_conf) + # There is only one VTI test so no need to delete this globally in tearDown() + self.cli_delete(vti_path) + if __name__ == '__main__': unittest.main(verbosity=2) -- cgit v1.2.3 From 0ef9c351598d4182da267e802010aa6ce0d9c6aa Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sat, 18 Sep 2021 15:20:16 +0200 Subject: smoketest: ipsec: only delete nhrp path where it is used --- smoketest/scripts/cli/test_vpn_ipsec.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/smoketest/scripts/cli/test_vpn_ipsec.py b/smoketest/scripts/cli/test_vpn_ipsec.py index 71a9d5137..1747c1cfc 100755 --- a/smoketest/scripts/cli/test_vpn_ipsec.py +++ b/smoketest/scripts/cli/test_vpn_ipsec.py @@ -126,7 +126,6 @@ class TestVPNIPsec(VyOSUnitTestSHIM.TestCase): self.assertTrue(process_named_running('charon')) self.cli_delete(base_path) - self.cli_delete(nhrp_path) self.cli_delete(tunnel_path) self.cli_delete(ethernet_path) self.cli_commit() @@ -319,6 +318,9 @@ class TestVPNIPsec(VyOSUnitTestSHIM.TestCase): for line in swanctl_lines: self.assertIn(line, swanctl_conf) + # There is only one NHRP test so no need to delete this globally in tearDown() + self.cli_delete(nhrp_path) + def test_05_x509_site2site(self): # Enable PKI peer_name = 'peer1' -- cgit v1.2.3 From a4440bd589db645eb99f343a8163e188a700774c Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sat, 18 Sep 2021 21:27:47 +0200 Subject: dhcp-server: T1968: allow multiple static-routes to be configured vyos@vyos# show service dhcp-server shared-network-name LAN { subnet 10.0.0.0/24 { default-router 10.0.0.1 dns-server 194.145.150.1 lease 88 range 0 { start 10.0.0.100 stop 10.0.0.200 } static-route 192.168.10.0/24 { next-hop 10.0.0.2 } static-route 192.168.20.0/24 { router 10.0.0.2 } } } --- data/templates/dhcp-server/dhcpd.conf.tmpl | 10 ++-- interface-definitions/dhcp-server.xml.in | 29 +++++------ smoketest/scripts/cli/test_service_dhcp-server.py | 3 +- src/conf_mode/dhcp_server.py | 7 +-- src/migration-scripts/dhcp-server/5-to-6 | 61 +++++++++++++++++++++++ 5 files changed, 85 insertions(+), 25 deletions(-) create mode 100755 src/migration-scripts/dhcp-server/5-to-6 diff --git a/data/templates/dhcp-server/dhcpd.conf.tmpl b/data/templates/dhcp-server/dhcpd.conf.tmpl index f0bfa468c..3ac92d3c9 100644 --- a/data/templates/dhcp-server/dhcpd.conf.tmpl +++ b/data/templates/dhcp-server/dhcpd.conf.tmpl @@ -110,9 +110,13 @@ shared-network {{ network | replace('_','-') }} { {% if subnet_config.default_router and subnet_config.default_router is not none %} {% set static_default_route = ', ' + '0.0.0.0/0' | isc_static_route(subnet_config.default_router) %} {% endif %} -{% if subnet_config.static_route.router is defined and subnet_config.static_route.router is not none and subnet_config.static_route.destination_subnet is defined and subnet_config.static_route.destination_subnet is not none %} - option rfc3442-static-route {{ subnet_config.static_route.destination_subnet | isc_static_route(subnet_config.static_route.router) }}{{ static_default_route }}; - option windows-static-route {{ subnet_config.static_route.destination_subnet | isc_static_route(subnet_config.static_route.router) }}; +{% if subnet_config.static_route is defined and subnet_config.static_route is not none %} +{% set rfc3442_routes = [] %} +{% for route, route_options in subnet_config.static_route.items() %} +{% set rfc3442_routes = rfc3442_routes.append(route | isc_static_route(route_options.next_hop)) %} +{% endfor %} + option rfc3442-static-route {{ rfc3442_routes | join(', ') }}{{ static_default_route }}; + option windows-static-route {{ rfc3442_routes | join(', ') }}; {% endif %} {% endif %} {% if subnet_config.ip_forwarding is defined %} diff --git a/interface-definitions/dhcp-server.xml.in b/interface-definitions/dhcp-server.xml.in index bafd6f6a2..c0f72dd86 100644 --- a/interface-definitions/dhcp-server.xml.in +++ b/interface-definitions/dhcp-server.xml.in @@ -357,26 +357,21 @@ - + - Classless static route + Classless static route destination subnet [REQUIRED] + + ipv4net + IPv4 address and prefix length + + + + - - - Destination subnet [REQUIRED] - - ipv4net - IPv4 address and prefix length - - - - - - - + - IP address of router to be used to reach the destination subnet [REQUIRED] + IP address of router to be used to reach the destination subnet ipv4 IPv4 address of router @@ -387,7 +382,7 @@ - + Additional subnet parameters for DHCP server. You must diff --git a/smoketest/scripts/cli/test_service_dhcp-server.py b/smoketest/scripts/cli/test_service_dhcp-server.py index 815bd333a..40977bb04 100755 --- a/smoketest/scripts/cli/test_service_dhcp-server.py +++ b/smoketest/scripts/cli/test_service_dhcp-server.py @@ -123,8 +123,7 @@ class TestServiceDHCPServer(VyOSUnitTestSHIM.TestCase): self.cli_set(pool + ['wpad-url', wpad]) self.cli_set(pool + ['server-identifier', server_identifier]) - self.cli_set(pool + ['static-route', 'destination-subnet', '10.0.0.0/24']) - self.cli_set(pool + ['static-route', 'router', '192.0.2.1']) + self.cli_set(pool + ['static-route', '10.0.0.0/24', 'next-hop', '192.0.2.1']) # check validate() - No DHCP address range or active static-mapping set with self.assertRaises(ConfigSessionError): diff --git a/src/conf_mode/dhcp_server.py b/src/conf_mode/dhcp_server.py index cdee72e09..8d6cef8b7 100755 --- a/src/conf_mode/dhcp_server.py +++ b/src/conf_mode/dhcp_server.py @@ -159,9 +159,10 @@ def verify(dhcp): 'lease subnet must be configured.') for subnet, subnet_config in network_config['subnet'].items(): - if 'static_route' in subnet_config and len(subnet_config['static_route']) != 2: - raise ConfigError('Missing DHCP static-route parameter(s):\n' \ - 'destination-subnet | router must be defined!') + if 'static_route' in subnet_config: + for route, route_option in subnet_config['static_route'].items(): + if 'next_hop' not in route_option: + raise ConfigError(f'DHCP static-route "{route}" requires router to be defined!') # Check if DHCP address range is inside configured subnet declaration if 'range' in subnet_config: diff --git a/src/migration-scripts/dhcp-server/5-to-6 b/src/migration-scripts/dhcp-server/5-to-6 new file mode 100755 index 000000000..4cd2ec07a --- /dev/null +++ b/src/migration-scripts/dhcp-server/5-to-6 @@ -0,0 +1,61 @@ +#!/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 sys +from vyos.configtree import ConfigTree + +if (len(sys.argv) < 1): + print("Must specify file name!") + sys.exit(1) + +file_name = sys.argv[1] + +with open(file_name, 'r') as f: + config_file = f.read() + +base = ['service', 'dhcp-server', 'shared-network-name'] +config = ConfigTree(config_file) + +if not config.exists(base): + # Nothing to do + exit(0) + +# Run this for every instance if 'shared-network-name' +for network in config.list_nodes(base): + base_network = base + [network] + + if not config.exists(base_network + ['subnet']): + continue + + # Run this for every specified 'subnet' + for subnet in config.list_nodes(base_network + ['subnet']): + base_subnet = base_network + ['subnet', subnet] + + if config.exists(base_subnet + ['static-route']): + prefix = config.return_value(base_subnet + ['static-route', 'destination-subnet']) + router = config.return_value(base_subnet + ['static-route', 'router']) + config.delete(base_subnet + ['static-route']) + + config.set(base_subnet + ['static-route', prefix, 'next-hop'], value=router) + config.set_tag(base_subnet + ['static-route']) + +try: + with open(file_name, 'w') as f: + f.write(config.to_string()) +except OSError as e: + print("Failed to save the modified config: {}".format(e)) + exit(1) -- cgit v1.2.3 From e2f9f4f4e8b2e961a58d935d09798ddb4e1e0460 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sat, 18 Sep 2021 21:48:53 +0200 Subject: dhcp-server: T3838: rename dns-server to name-server node IPv4 DHCP uses "dns-server" to specify one or more name-servers for a given pool. In order to use the same CLI syntax this should be renamed to name-server, which is already the case for DHCPv6. --- data/templates/dhcp-server/dhcpd.conf.tmpl | 4 +-- interface-definitions/dhcp-server.xml.in | 14 +------- interface-definitions/dhcpv6-server.xml.in | 42 ++-------------------- interface-definitions/dns-forwarding.xml.in | 19 +--------- .../include/accel-ppp/name-server.xml.i | 20 ----------- .../include/name-server-ipv4-ipv6.xml.i | 20 +++++++++++ .../include/name-server-ipv4.xml.i | 15 ++++++++ .../include/name-server-ipv6.xml.i | 15 ++++++++ interface-definitions/interfaces-openvpn.xml.in | 18 +--------- interface-definitions/service_ipoe-server.xml.in | 2 +- interface-definitions/service_pppoe-server.xml.in | 2 +- interface-definitions/service_router-advert.xml.in | 14 +------- interface-definitions/vpn_ipsec.xml.in | 3 +- interface-definitions/vpn_l2tp.xml.in | 2 +- interface-definitions/vpn_openconnect.xml.in | 2 +- interface-definitions/vpn_pptp.xml.in | 14 +------- interface-definitions/vpn_sstp.xml.in | 2 +- smoketest/scripts/cli/test_service_dhcp-server.py | 16 ++++----- src/migration-scripts/dhcp-server/5-to-6 | 7 ++++ 19 files changed, 81 insertions(+), 150 deletions(-) delete mode 100644 interface-definitions/include/accel-ppp/name-server.xml.i create mode 100644 interface-definitions/include/name-server-ipv4-ipv6.xml.i create mode 100644 interface-definitions/include/name-server-ipv4.xml.i create mode 100644 interface-definitions/include/name-server-ipv6.xml.i diff --git a/data/templates/dhcp-server/dhcpd.conf.tmpl b/data/templates/dhcp-server/dhcpd.conf.tmpl index 3ac92d3c9..ed39ff4da 100644 --- a/data/templates/dhcp-server/dhcpd.conf.tmpl +++ b/data/templates/dhcp-server/dhcpd.conf.tmpl @@ -84,8 +84,8 @@ shared-network {{ network | replace('_','-') }} { {% if network_config.subnet is defined and network_config.subnet is not none %} {% for subnet, subnet_config in network_config.subnet.items() %} subnet {{ subnet | address_from_cidr }} netmask {{ subnet | netmask_from_cidr }} { -{% if subnet_config.dns_server is defined and subnet_config.dns_server is not none %} - option domain-name-servers {{ subnet_config.dns_server | join(', ') }}; +{% if subnet_config.name_server is defined and subnet_config.name_server is not none %} + option domain-name-servers {{ subnet_config.name_server | join(', ') }}; {% endif %} {% if subnet_config.domain_search is defined and subnet_config.domain_search is not none %} option domain-search "{{ subnet_config.domain_search | join('", "') }}"; diff --git a/interface-definitions/dhcp-server.xml.in b/interface-definitions/dhcp-server.xml.in index c0f72dd86..3a1eee60e 100644 --- a/interface-definitions/dhcp-server.xml.in +++ b/interface-definitions/dhcp-server.xml.in @@ -117,19 +117,7 @@ - - - DNS server IPv4 address - - ipv4 - DNS server IPv4 address - - - - - - - + #include Client Domain Name diff --git a/interface-definitions/dhcpv6-server.xml.in b/interface-definitions/dhcpv6-server.xml.in index 95b1e5602..58181872b 100644 --- a/interface-definitions/dhcpv6-server.xml.in +++ b/interface-definitions/dhcpv6-server.xml.in @@ -14,19 +14,7 @@ Additional global parameters for DHCPv6 server - - - IPv6 address of a Recursive DNS Server - - ipv6 - IPv6 address of DNS name server - - - - - - - + #include @@ -70,19 +58,7 @@ #include - - - IPv6 address of a Recursive DNS Server - - ipv6 - IPv6 address of DNS name server - - - - - - - + #include @@ -194,19 +170,7 @@ - - - IPv6 address of a Recursive DNS Server - - ipv6 - IPv6 address of DNS name server - - - - - - - + #include NIS domain name for client to use diff --git a/interface-definitions/dns-forwarding.xml.in b/interface-definitions/dns-forwarding.xml.in index 06e45ce1e..33cb6223f 100644 --- a/interface-definitions/dns-forwarding.xml.in +++ b/interface-definitions/dns-forwarding.xml.in @@ -148,24 +148,7 @@ 3600 - - - Domain Name Servers (DNS) addresses [OPTIONAL] - - ipv4 - Domain Name Server (DNS) IPv4 address - - - ipv6 - Domain Name Server (DNS) IPv6 address - - - - - - - - + #include Local addresses from which to send DNS queries diff --git a/interface-definitions/include/accel-ppp/name-server.xml.i b/interface-definitions/include/accel-ppp/name-server.xml.i deleted file mode 100644 index e744b384f..000000000 --- a/interface-definitions/include/accel-ppp/name-server.xml.i +++ /dev/null @@ -1,20 +0,0 @@ - - - - Domain Name Server (DNS) propagated to client - - ipv4 - Domain Name Server (DNS) IPv4 address - - - ipv6 - Domain Name Server (DNS) IPv6 address - - - - - - - - - diff --git a/interface-definitions/include/name-server-ipv4-ipv6.xml.i b/interface-definitions/include/name-server-ipv4-ipv6.xml.i new file mode 100644 index 000000000..14973234b --- /dev/null +++ b/interface-definitions/include/name-server-ipv4-ipv6.xml.i @@ -0,0 +1,20 @@ + + + + Domain Name Servers (DNS) addresses + + ipv4 + Domain Name Server (DNS) IPv4 address + + + ipv6 + Domain Name Server (DNS) IPv6 address + + + + + + + + + diff --git a/interface-definitions/include/name-server-ipv4.xml.i b/interface-definitions/include/name-server-ipv4.xml.i new file mode 100644 index 000000000..0cf884e03 --- /dev/null +++ b/interface-definitions/include/name-server-ipv4.xml.i @@ -0,0 +1,15 @@ + + + + Domain Name Servers (DNS) addresses + + ipv4 + Domain Name Server (DNS) IPv4 address + + + + + + + + diff --git a/interface-definitions/include/name-server-ipv6.xml.i b/interface-definitions/include/name-server-ipv6.xml.i new file mode 100644 index 000000000..d4517c4c6 --- /dev/null +++ b/interface-definitions/include/name-server-ipv6.xml.i @@ -0,0 +1,15 @@ + + + + Domain Name Servers (DNS) addresses + + ipv6 + Domain Name Server (DNS) IPv6 address + + + + + + + + diff --git a/interface-definitions/interfaces-openvpn.xml.in b/interface-definitions/interfaces-openvpn.xml.in index 3ad367900..2ecac78e2 100644 --- a/interface-definitions/interfaces-openvpn.xml.in +++ b/interface-definitions/interfaces-openvpn.xml.in @@ -554,23 +554,7 @@ - - - Domain Name Server (DNS) - - ipv4 - DNS server IPv4 address - - - ipv6 - DNS server IPv6 address - - - - - - - + #include Route to be pushed to all clients diff --git a/interface-definitions/service_ipoe-server.xml.in b/interface-definitions/service_ipoe-server.xml.in index 7c575ba77..b19acab56 100644 --- a/interface-definitions/service_ipoe-server.xml.in +++ b/interface-definitions/service_ipoe-server.xml.in @@ -111,7 +111,7 @@ - #include + #include #include diff --git a/interface-definitions/service_pppoe-server.xml.in b/interface-definitions/service_pppoe-server.xml.in index 6fb0bf9f4..188aed6c4 100644 --- a/interface-definitions/service_pppoe-server.xml.in +++ b/interface-definitions/service_pppoe-server.xml.in @@ -59,7 +59,7 @@ #include - #include + #include interface(s) to listen on diff --git a/interface-definitions/service_router-advert.xml.in b/interface-definitions/service_router-advert.xml.in index e18b27f1b..0f4009f5c 100644 --- a/interface-definitions/service_router-advert.xml.in +++ b/interface-definitions/service_router-advert.xml.in @@ -135,19 +135,7 @@ - - - IPv6 address of recursive DNS server - - ipv6 - IPv6 address of DNS name server - - - - - - - + #include Hosts use the administered (stateful) protocol for autoconfiguration of other (non-address) information diff --git a/interface-definitions/vpn_ipsec.xml.in b/interface-definitions/vpn_ipsec.xml.in index 4120232ea..164ba6618 100644 --- a/interface-definitions/vpn_ipsec.xml.in +++ b/interface-definitions/vpn_ipsec.xml.in @@ -884,8 +884,7 @@ - - #include + #include #include diff --git a/interface-definitions/vpn_l2tp.xml.in b/interface-definitions/vpn_l2tp.xml.in index 6d556d0bb..cbd5e38e7 100644 --- a/interface-definitions/vpn_l2tp.xml.in +++ b/interface-definitions/vpn_l2tp.xml.in @@ -23,7 +23,7 @@ #include - #include + #include L2TP Network Server (LNS) diff --git a/interface-definitions/vpn_openconnect.xml.in b/interface-definitions/vpn_openconnect.xml.in index a33ff67ea..0db5e79d0 100644 --- a/interface-definitions/vpn_openconnect.xml.in +++ b/interface-definitions/vpn_openconnect.xml.in @@ -170,7 +170,7 @@ - #include + #include diff --git a/interface-definitions/vpn_pptp.xml.in b/interface-definitions/vpn_pptp.xml.in index dab317f68..0d1690013 100644 --- a/interface-definitions/vpn_pptp.xml.in +++ b/interface-definitions/vpn_pptp.xml.in @@ -22,19 +22,7 @@ - - - Domain Name Server (DNS) propagated to client - - ipv4 - Domain Name Server (DNS) IPv4 address - - - - - - - + #include #include diff --git a/interface-definitions/vpn_sstp.xml.in b/interface-definitions/vpn_sstp.xml.in index 5cd331d7f..9901a0cdf 100644 --- a/interface-definitions/vpn_sstp.xml.in +++ b/interface-definitions/vpn_sstp.xml.in @@ -27,7 +27,7 @@ #include #include - #include + #include Client IP pools and gateway setting diff --git a/smoketest/scripts/cli/test_service_dhcp-server.py b/smoketest/scripts/cli/test_service_dhcp-server.py index 40977bb04..37e016778 100755 --- a/smoketest/scripts/cli/test_service_dhcp-server.py +++ b/smoketest/scripts/cli/test_service_dhcp-server.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2020 VyOS maintainers and contributors +# Copyright (C) 2020-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 @@ -59,8 +59,8 @@ class TestServiceDHCPServer(VyOSUnitTestSHIM.TestCase): pool = base_path + ['shared-network-name', shared_net_name, 'subnet', subnet] # we use the first subnet IP address as default gateway self.cli_set(pool + ['default-router', router]) - self.cli_set(pool + ['dns-server', dns_1]) - self.cli_set(pool + ['dns-server', dns_2]) + self.cli_set(pool + ['name-server', dns_1]) + self.cli_set(pool + ['name-server', dns_2]) self.cli_set(pool + ['domain-name', domain_name]) # check validate() - No DHCP address range or active static-mapping set @@ -108,8 +108,8 @@ class TestServiceDHCPServer(VyOSUnitTestSHIM.TestCase): pool = base_path + ['shared-network-name', shared_net_name, 'subnet', subnet] # we use the first subnet IP address as default gateway self.cli_set(pool + ['default-router', router]) - self.cli_set(pool + ['dns-server', dns_1]) - self.cli_set(pool + ['dns-server', dns_2]) + self.cli_set(pool + ['name-server', dns_1]) + self.cli_set(pool + ['name-server', dns_2]) self.cli_set(pool + ['domain-name', domain_name]) self.cli_set(pool + ['ip-forwarding']) self.cli_set(pool + ['smtp-server', smtp_server]) @@ -201,8 +201,8 @@ class TestServiceDHCPServer(VyOSUnitTestSHIM.TestCase): pool = base_path + ['shared-network-name', shared_net_name, 'subnet', subnet] # we use the first subnet IP address as default gateway self.cli_set(pool + ['default-router', router]) - self.cli_set(pool + ['dns-server', dns_1]) - self.cli_set(pool + ['dns-server', dns_2]) + self.cli_set(pool + ['name-server', dns_1]) + self.cli_set(pool + ['name-server', dns_2]) self.cli_set(pool + ['domain-name', domain_name]) # check validate() - No DHCP address range or active static-mapping set @@ -261,7 +261,7 @@ class TestServiceDHCPServer(VyOSUnitTestSHIM.TestCase): pool = base_path + ['shared-network-name', shared_net_name, 'subnet', subnet] # we use the first subnet IP address as default gateway self.cli_set(pool + ['default-router', router]) - self.cli_set(pool + ['dns-server', dns_1]) + self.cli_set(pool + ['name-server', dns_1]) self.cli_set(pool + ['domain-name', domain_name]) self.cli_set(pool + ['lease', lease_time]) diff --git a/src/migration-scripts/dhcp-server/5-to-6 b/src/migration-scripts/dhcp-server/5-to-6 index 4cd2ec07a..7f447ac17 100755 --- a/src/migration-scripts/dhcp-server/5-to-6 +++ b/src/migration-scripts/dhcp-server/5-to-6 @@ -14,6 +14,8 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . +# T1968: allow multiple static-routes to be configured +# T3838: rename dns-server -> name-server import sys from vyos.configtree import ConfigTree @@ -45,6 +47,7 @@ for network in config.list_nodes(base): for subnet in config.list_nodes(base_network + ['subnet']): base_subnet = base_network + ['subnet', subnet] + # T1968: allow multiple static-routes to be configured if config.exists(base_subnet + ['static-route']): prefix = config.return_value(base_subnet + ['static-route', 'destination-subnet']) router = config.return_value(base_subnet + ['static-route', 'router']) @@ -53,6 +56,10 @@ for network in config.list_nodes(base): config.set(base_subnet + ['static-route', prefix, 'next-hop'], value=router) config.set_tag(base_subnet + ['static-route']) + # T3838: rename dns-server -> name-server + if config.exists(base_subnet + ['dns-server']): + config.rename(base_subnet + ['dns-server'], 'name-server') + try: with open(file_name, 'w') as f: f.write(config.to_string()) -- cgit v1.2.3 From 90dffcb3c14ec976fecae32d19a979f05a40d9c8 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sat, 18 Sep 2021 22:00:56 +0200 Subject: dhcpv6-server: xml: add description CLI node --- interface-definitions/dhcpv6-server.xml.in | 1 + 1 file changed, 1 insertion(+) diff --git a/interface-definitions/dhcpv6-server.xml.in b/interface-definitions/dhcpv6-server.xml.in index 58181872b..18aa53fb8 100644 --- a/interface-definitions/dhcpv6-server.xml.in +++ b/interface-definitions/dhcpv6-server.xml.in @@ -40,6 +40,7 @@ #include + #include Common options to distribute to all clients, including stateless clients -- cgit v1.2.3 From 564f05614b6e8650185c46b9625f6a0cd9661639 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sat, 18 Sep 2021 22:01:57 +0200 Subject: dhcp-server: xml: use description building block --- interface-definitions/dhcp-server.xml.in | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/interface-definitions/dhcp-server.xml.in b/interface-definitions/dhcp-server.xml.in index 3a1eee60e..5db46a0c4 100644 --- a/interface-definitions/dhcp-server.xml.in +++ b/interface-definitions/dhcp-server.xml.in @@ -53,11 +53,7 @@ - - - Shared-network-name description - - + #include #include -- cgit v1.2.3 From d411a40a3598c55fae7abd8bc5f1876007aa704b Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sat, 18 Sep 2021 22:09:05 +0200 Subject: dhcp-server: T3839: support name-servers and domain config per shared-network DHCP servers "shared-network" level only makes sense if one can specify configuration items that can be inherited by individual subnets. This is now possible for name-servers and the domain-name. set service dhcp-server shared-network-name LAN domain-name 'vyos.net' set service dhcp-server shared-network-name LAN name-server '192.0.2.1' --- data/templates/dhcp-server/dhcpd.conf.tmpl | 6 ++++++ interface-definitions/dhcp-server.xml.in | 12 +++--------- interface-definitions/include/dhcp-domain-name.xml.i | 11 +++++++++++ 3 files changed, 20 insertions(+), 9 deletions(-) create mode 100644 interface-definitions/include/dhcp-domain-name.xml.i diff --git a/data/templates/dhcp-server/dhcpd.conf.tmpl b/data/templates/dhcp-server/dhcpd.conf.tmpl index ed39ff4da..108c9cc85 100644 --- a/data/templates/dhcp-server/dhcpd.conf.tmpl +++ b/data/templates/dhcp-server/dhcpd.conf.tmpl @@ -74,6 +74,12 @@ shared-network {{ network | replace('_','-') }} { {% if network_config.authoritative is defined %} authoritative; {% endif %} +{% if network_config.name_server is defined and network_config.name_server is not none %} + option domain-name-servers {{ network_config.name_server | join(', ') }}; +{% endif %} +{% if network_config.domain_name is defined and network_config.domain_name is not none %} + option domain-name "{{ network_config.domain_name }}"; +{% endif %} {% if network_config.shared_network_parameters is defined and network_config.shared_network_parameters is not none %} # The following {{ network_config.shared_network_parameters | length }} line(s) # were added as shared-network-parameters in the CLI and have not been validated diff --git a/interface-definitions/dhcp-server.xml.in b/interface-definitions/dhcp-server.xml.in index 5db46a0c4..e629d96ab 100644 --- a/interface-definitions/dhcp-server.xml.in +++ b/interface-definitions/dhcp-server.xml.in @@ -53,6 +53,8 @@ + #include + #include #include #include @@ -114,15 +116,7 @@ #include - - - Client Domain Name - - - - Invalid domain name (RFC 1123 section 2).\nMay only contain letters, numbers and .-_ - - + #include #include diff --git a/interface-definitions/include/dhcp-domain-name.xml.i b/interface-definitions/include/dhcp-domain-name.xml.i new file mode 100644 index 000000000..eb95596da --- /dev/null +++ b/interface-definitions/include/dhcp-domain-name.xml.i @@ -0,0 +1,11 @@ + + + + Client Domain Name + + + + Invalid domain name (RFC 1123 section 2).\nMay only contain letters, numbers and .-_ + + + -- cgit v1.2.3 From ae2dc55aa68679e828d4bb133fc515172c081d0f Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sat, 18 Sep 2021 22:12:09 +0200 Subject: container: T2216: add IPv6 support to container networks --- interface-definitions/containers.xml.in | 8 +++- src/conf_mode/containers.py | 72 ++++++++++++++++++--------------- 2 files changed, 46 insertions(+), 34 deletions(-) diff --git a/interface-definitions/containers.xml.in b/interface-definitions/containers.xml.in index d990e41a3..bf672307c 100644 --- a/interface-definitions/containers.xml.in +++ b/interface-definitions/containers.xml.in @@ -76,7 +76,8 @@ - Set IPv4 static address to container (optional) + + Assign static IP address to container ipv4 IPv4 address @@ -206,8 +207,13 @@ ipv4net IPv4 network prefix + + ipv6net + IPv6 network prefix + + diff --git a/src/conf_mode/containers.py b/src/conf_mode/containers.py index 3a93bf062..1e0197a13 100755 --- a/src/conf_mode/containers.py +++ b/src/conf_mode/containers.py @@ -20,6 +20,7 @@ import json from ipaddress import ip_address from ipaddress import ip_network from time import sleep +from json import dumps as json_write from vyos.config import Config from vyos.configdict import dict_merge @@ -31,10 +32,10 @@ from vyos.util import read_file from vyos.util import write_file from vyos.util import is_systemd_service_active from vyos.util import is_systemd_service_running - -from vyos.template import render +from vyos.template import inc_ip from vyos.template import is_ipv4 from vyos.template import is_ipv6 +from vyos.template import render from vyos.xml import defaults from vyos import ConfigError from vyos import airbag @@ -185,6 +186,37 @@ def generate(container): if not container: return None + if 'network' in container: + for network, network_config in container['network'].items(): + tmp = { + 'cniVersion' : '0.4.0', + 'name' : network, + 'plugins' : [{ + 'type': 'bridge', + 'bridge': f'cni-{network}', + 'isGateway': True, + 'ipMasq': False, + 'hairpinMode': False, + 'ipam' : { + 'type': 'host-local', + 'routes': [], + 'ranges' : [], + }, + }] + } + + for prefix in network_config['prefix']: + net = [{'gateway' : inc_ip(prefix, 1), 'subnet' : prefix}] + tmp['plugins'][0]['ipam']['ranges'].append(net) + + # install per address-family default orutes + default_route = '0.0.0.0/0' + if is_ipv6(prefix): + default_route = '::/0' + tmp['plugins'][0]['ipam']['routes'].append({'dst': default_route}) + + write_file(f'/etc/cni/net.d/{network}.conflist', json_write(tmp, indent=2)) + render(config_containers_registry, 'containers/registry.tmpl', container) render(config_containers_storage, 'containers/storage.tmpl', container) @@ -201,7 +233,9 @@ def apply(container): # Delete old networks if needed if 'network_remove' in container: for network in container['network_remove']: - call(f'podman network rm --force {network}') + tmp = f'/etc/cni/net.d/{network}.conflist' + if os.path.exists(tmp): + os.unlink(tmp) service_name = 'podman.service' if 'network' in container or 'name' in container: @@ -214,35 +248,6 @@ def apply(container): else: _cmd(f'systemctl stop {service_name}') - - # Add network - if 'network' in container: - for network, network_config in container['network'].items(): - # Check if the network has already been created - if not network_exists(network) and 'prefix' in network_config: - tmp = f'podman network create {network}' - # we can not use list comprehension here as the --ipv6 option - # must immediately follow the specified subnet!!! - for prefix in sorted(network_config['prefix']): - tmp += f' --subnet={prefix}' - if is_ipv6(prefix): - tmp += ' --ipv6' - _cmd(tmp) - - # Disable masquerading and use traditional bridging so VyOS - # can control firewalling/NAT by the real VyOS CLI - cni_network_config = f'/etc/cni/net.d/{network}.conflist' - tmp = read_file(cni_network_config) - config = json.loads(tmp) - if 'plugins' in config: - for count in range(0, len(config['plugins'])): - if 'ipMasq' in config['plugins'][count]: - config['plugins'][count]['ipMasq'] = False - if 'hairpinMode' in config['plugins'][count]: - config['plugins'][count]['hairpinMode'] = False - - write_file(cni_network_config, json.dumps(config, indent=4)) - # Add container if 'name' in container: for name, container_config in container['name'].items(): @@ -303,7 +308,8 @@ def apply(container): for network in container_config['network']: ipparam = '' if 'address' in container_config['network'][network]: - ipparam = '--ip ' + container_config['network'][network]['address'] + address = container_config['network'][network]['address'] + ipparam = f'--ip {address}' _cmd(f'{container_base_cmd} --net {network} {ipparam} {image}') return None -- cgit v1.2.3 From 60f34805d72973e510d1381e4b67a73d0a0952f4 Mon Sep 17 00:00:00 2001 From: Lucas Christian Date: Sat, 18 Sep 2021 21:21:56 -0700 Subject: T3840: Allow larger DNS forwarding cache sizes --- interface-definitions/dns-forwarding.xml.in | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/interface-definitions/dns-forwarding.xml.in b/interface-definitions/dns-forwarding.xml.in index 33cb6223f..5b0c87597 100644 --- a/interface-definitions/dns-forwarding.xml.in +++ b/interface-definitions/dns-forwarding.xml.in @@ -18,11 +18,11 @@ DNS forwarding cache size (default: 10000) - u32:0-10000 + u32:0-2147483647 DNS forwarding cache size - + 10000 -- cgit v1.2.3 From 425eabea7c82992ab1319b9adb0e9d21172b255d Mon Sep 17 00:00:00 2001 From: jack9603301 Date: Sun, 19 Sep 2021 13:34:41 +0800 Subject: op-mode: nat: T3648: Fix NAT script errors --- src/op_mode/show_nat_rules.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/op_mode/show_nat_rules.py b/src/op_mode/show_nat_rules.py index 4a059c848..d68def26a 100755 --- a/src/op_mode/show_nat_rules.py +++ b/src/op_mode/show_nat_rules.py @@ -69,7 +69,7 @@ if args.source or args.destination: srcdest = '' srcdests = [] tran_addr = '' - for i in range(1,len(data['expr']) + 1): + for i in range(1,len(data['expr']) ): srcdest_json = dict_search('match.right', data['expr'][i]) if srcdest_json: if isinstance(srcdest_json,str): @@ -113,7 +113,7 @@ if args.source or args.destination: srcdest = '' print(format_nat_rule.format(rule, srcdests[0], tran_addr, interface)) - for i in range(1, list(srcdest)): + for i in range(1, len(srcdests)): print(format_nat_rule.format(' ', srcdests[i], ' ', ' ')) exit(0) -- cgit v1.2.3 From 41807725cad3266bf426a9de411bb693b914395e Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sun, 19 Sep 2021 09:17:00 +0200 Subject: dhcp: op-mode: xml: fix duplicate help string --- op-mode-definitions/dhcp.xml.in | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/op-mode-definitions/dhcp.xml.in b/op-mode-definitions/dhcp.xml.in index 6f0c25110..9b21cb9af 100644 --- a/op-mode-definitions/dhcp.xml.in +++ b/op-mode-definitions/dhcp.xml.in @@ -128,13 +128,13 @@ - Restart the DHCP server process + Restart DHCP server process sudo systemctl restart isc-dhcp-server.service - Restart the DHCP server process + Restart DHCP relay agent sudo ${vyos_op_scripts_dir}/restart_dhcp_relay.py --ipv4 -- cgit v1.2.3 From 83f9d4b8bac6b5d401dfea19d4f9d61b7e638a47 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sun, 19 Sep 2021 09:17:36 +0200 Subject: dhcp: op-mode: xml: error out if DHCP service not configured --- op-mode-definitions/dhcp.xml.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/op-mode-definitions/dhcp.xml.in b/op-mode-definitions/dhcp.xml.in index 9b21cb9af..4e7568ab6 100644 --- a/op-mode-definitions/dhcp.xml.in +++ b/op-mode-definitions/dhcp.xml.in @@ -130,7 +130,7 @@ Restart DHCP server process - sudo systemctl restart isc-dhcp-server.service + if cli-shell-api existsActive service dhcp-server; then sudo systemctl restart isc-dhcp-server.service; else echo "DHCP server not configured"; fi -- cgit v1.2.3 From 5d4c75d7fdecc56a8864a3ef76ead3f6121a0cff Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sun, 19 Sep 2021 09:21:00 +0200 Subject: dhcpv6: op-mode: xml: fix duplicate help strings --- op-mode-definitions/dhcp.xml.in | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/op-mode-definitions/dhcp.xml.in b/op-mode-definitions/dhcp.xml.in index 4e7568ab6..7f4bdb990 100644 --- a/op-mode-definitions/dhcp.xml.in +++ b/op-mode-definitions/dhcp.xml.in @@ -123,18 +123,18 @@ - Restart DHCP server processes + Restart DHCP processes - Restart DHCP server process + Restart DHCP server if cli-shell-api existsActive service dhcp-server; then sudo systemctl restart isc-dhcp-server.service; else echo "DHCP server not configured"; fi - Restart DHCP relay agent + Restart DHCP relay-agent sudo ${vyos_op_scripts_dir}/restart_dhcp_relay.py --ipv4 @@ -142,18 +142,18 @@ - Restart DHCPv6 server processes + Restart DHCPv6 processes - Restart the DHCPv6 server process + Restart DHCPv6 server sudo systemctl restart isc-dhcp-server6.service - Restart the DHCP server process + Restart DHCPv6 relay-agent sudo ${vyos_op_scripts_dir}/restart_dhcp_relay.py --ipv6 -- cgit v1.2.3 From 224f0b8042f439b8dcc0eb32730da669e8b163af Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sun, 19 Sep 2021 09:21:25 +0200 Subject: dhcpv6: op-mode: xml: error out if DHCPv6 service not configured Restarting DHCPv6 server should not be possible when service is not configured on the CLI. --- op-mode-definitions/dhcp.xml.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/op-mode-definitions/dhcp.xml.in b/op-mode-definitions/dhcp.xml.in index 7f4bdb990..241cca0ce 100644 --- a/op-mode-definitions/dhcp.xml.in +++ b/op-mode-definitions/dhcp.xml.in @@ -149,7 +149,7 @@ Restart DHCPv6 server - sudo systemctl restart isc-dhcp-server6.service + if cli-shell-api existsActive service dhcpv6-server; then sudo systemctl restart isc-dhcp-server6.service; else echo "DHCPv6 server not configured"; fi -- cgit v1.2.3 From a8ccf72c222caad8cd7aaca9bca773be39e87f5c Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sun, 19 Sep 2021 10:51:15 +0200 Subject: dhcp-server: T3672: only one failover peer is supported --- data/templates/dhcp-server/dhcpd.conf.tmpl | 37 ++++------- interface-definitions/dhcp-server.xml.in | 98 ++++++++++++++---------------- src/conf_mode/dhcp_server.py | 36 +++++------ src/migration-scripts/dhcp-server/5-to-6 | 25 ++++++-- 4 files changed, 97 insertions(+), 99 deletions(-) diff --git a/data/templates/dhcp-server/dhcpd.conf.tmpl b/data/templates/dhcp-server/dhcpd.conf.tmpl index 108c9cc85..54fff3ded 100644 --- a/data/templates/dhcp-server/dhcpd.conf.tmpl +++ b/data/templates/dhcp-server/dhcpd.conf.tmpl @@ -31,32 +31,25 @@ option wpad-url code 252 = text; {% endfor %} {% endif %} -{% if shared_network_name is defined and shared_network_name is not none %} -{% for network, network_config in shared_network_name.items() if network_config.disable is not defined %} -{% if network_config.subnet is defined and network_config.subnet is not none %} -{% for subnet, subnet_config in network_config.subnet.items() %} -{% if subnet_config.failover is defined and subnet_config.failover is defined and subnet_config.failover.name is defined and subnet_config.failover.name is not none %} -# Failover configuration for {{ subnet }} -failover peer "{{ subnet_config.failover.name }}" { -{% if subnet_config.failover.status == 'primary' %} +{% if failover is defined and failover is not none %} +{% set dhcp_failover_name = 'VyOS-DHCP-failover-peer' %} +# DHCP failover configuration +failover peer "{{ dhcp_failover_name }}" { +{% if failover.status == 'primary' %} primary; mclt 1800; split 128; -{% elif subnet_config.failover.status == 'secondary' %} +{% elif failover.status == 'secondary' %} secondary; -{% endif %} - address {{ subnet_config.failover.local_address }}; +{% endif %} + address {{ failover.source_address }}; port 520; - peer address {{ subnet_config.failover.peer_address }}; + peer address {{ failover.remote }}; peer port 520; max-response-delay 30; max-unacked-updates 10; load balance max seconds 3; } -{% endif %} -{% endfor %} -{% endif %} -{% endfor %} {% endif %} {% if listen_address is defined and listen_address is not none %} @@ -184,23 +177,17 @@ shared-network {{ network | replace('_','-') }} { } {% endfor %} {% endif %} -{% if subnet_config.failover is defined and subnet_config.failover.name is defined and subnet_config.failover.name is not none %} pool { - failover peer "{{ subnet_config.failover.name }}"; +{% if subnet_config.enable_failover is defined %} + failover peer "{{ dhcp_failover_name }}"; deny dynamic bootp clients; +{% endif %} {% if subnet_config.range is defined and subnet_config.range is not none %} {% for range, range_options in subnet_config.range.items() %} range {{ range_options.start }} {{ range_options.stop }}; {% endfor %} {% endif %} } -{% else %} -{% if subnet_config.range is defined and subnet_config.range is not none %} -{% for range, range_options in subnet_config.range.items() %} - range {{ range_options.start }} {{ range_options.stop }}; -{% endfor %} -{% endif %} -{% endif %} } {% endfor %} {% endif %} diff --git a/interface-definitions/dhcp-server.xml.in b/interface-definitions/dhcp-server.xml.in index e629d96ab..960b8a4f0 100644 --- a/interface-definitions/dhcp-server.xml.in +++ b/interface-definitions/dhcp-server.xml.in @@ -16,6 +16,46 @@ + + + DHCP failover configuration + + + #include + + + IPv4 remote address used for connectio + + ipv4 + IPv4 address of failover peer + + + + + + + + + Failover hierarchy + + primary secondary + + + primary + Configure this server to be the primary node + + + secondary + Configure this server to be the secondary node + + + ^(primary|secondary)$ + + Invalid DHCP failover peer status + + + + Additional global parameters for DHCP server. You must @@ -118,6 +158,12 @@ #include #include #include + + + Enable DHCP failover support for this subnet + + + IP address to exclude from DHCP lease range @@ -131,58 +177,6 @@ - - - DHCP failover parameters - - - - - IP address for failover peer to connect [REQUIRED] - - ipv4 - IPv4 address to exclude from lease range - - - - - - - - - DHCP failover peer name [REQUIRED] - - [-_a-zA-Z0-9.]+ - - Invalid failover peer name. May only contain letters, numbers and .-_ - - - - - IP address of failover peer [REQUIRED] - - ipv4 - IPv4 address of failover peer - - - - - - - - - DHCP failover peer status (primary|secondary) [REQUIRED] - - primary secondary - - - ^(primary|secondary)$ - - Invalid DHCP failover peer status - - - - Enable IP forwarding on client diff --git a/src/conf_mode/dhcp_server.py b/src/conf_mode/dhcp_server.py index 8d6cef8b7..5b3809017 100755 --- a/src/conf_mode/dhcp_server.py +++ b/src/conf_mode/dhcp_server.py @@ -148,9 +148,9 @@ def verify(dhcp): 'At least one DHCP shared network must be configured.') # Inspect shared-network/subnet - failover_names = [] listen_ok = False subnets = [] + failover_ok = False # A shared-network requires a subnet definition for network, network_config in dhcp['shared_network_name'].items(): @@ -159,11 +159,19 @@ def verify(dhcp): 'lease subnet must be configured.') for subnet, subnet_config in network_config['subnet'].items(): + # All delivered static routes require a next-hop to be set if 'static_route' in subnet_config: for route, route_option in subnet_config['static_route'].items(): if 'next_hop' not in route_option: raise ConfigError(f'DHCP static-route "{route}" requires router to be defined!') + # DHCP failover needs at least one subnet that uses it + if 'enable_failover' in subnet_config: + if 'failover' not in dhcp: + raise ConfigError(f'Can not enable failover for "{subnet}" in "{network}".\n' \ + 'Failover is not configured globally!') + failover_ok = True + # Check if DHCP address range is inside configured subnet declaration if 'range' in subnet_config: networks = [] @@ -192,23 +200,6 @@ def verify(dhcp): tmp = IPRange(range_config['start'], range_config['stop']) networks.append(tmp) - if 'failover' in subnet_config: - for key in ['local_address', 'peer_address', 'name', 'status']: - if key not in subnet_config['failover']: - raise ConfigError(f'Missing DHCP failover parameter "{key}"!') - - # Failover names must be uniquie - if subnet_config['failover']['name'] in failover_names: - name = subnet_config['failover']['name'] - raise ConfigError(f'DHCP failover names must be unique:\n' \ - f'{name} has already been configured!') - failover_names.append(subnet_config['failover']['name']) - - # Failover requires start/stop ranges for pool - if 'range' not in subnet_config: - raise ConfigError(f'DHCP failover requires at least one start-stop range to be configured\n'\ - f'within shared-network "{network}, {subnet}" for using failover!') - # Exclude addresses must be in bound if 'exclude' in subnet_config: for exclude in subnet_config['exclude']: @@ -252,6 +243,15 @@ def verify(dhcp): if net.overlaps(net2): raise ConfigError('Conflicting subnet ranges: "{net}" overlaps "{net2}"!') + if 'failover' in dhcp: + if not failover_ok: + raise ConfigError('DHCP failover must be enabled for at least one subnet!') + + for key in ['source_address', 'remote', 'status']: + if key not in dhcp['failover']: + tmp = key.replace('_', '-') + raise ConfigError(f'DHCP failover requires "{tmp}" to be specified!') + for address in (dict_search('listen_address', dhcp) or []): if is_addr_assigned(address): listen_ok = True diff --git a/src/migration-scripts/dhcp-server/5-to-6 b/src/migration-scripts/dhcp-server/5-to-6 index 7f447ac17..39bbb9f50 100755 --- a/src/migration-scripts/dhcp-server/5-to-6 +++ b/src/migration-scripts/dhcp-server/5-to-6 @@ -29,16 +29,16 @@ file_name = sys.argv[1] with open(file_name, 'r') as f: config_file = f.read() -base = ['service', 'dhcp-server', 'shared-network-name'] +base = ['service', 'dhcp-server'] config = ConfigTree(config_file) -if not config.exists(base): +if not config.exists(base + ['shared-network-name']): # Nothing to do exit(0) # Run this for every instance if 'shared-network-name' -for network in config.list_nodes(base): - base_network = base + [network] +for network in config.list_nodes(base + ['shared-network-name']): + base_network = base + ['shared-network-name', network] if not config.exists(base_network + ['subnet']): continue @@ -60,6 +60,23 @@ for network in config.list_nodes(base): if config.exists(base_subnet + ['dns-server']): config.rename(base_subnet + ['dns-server'], 'name-server') + + # T3672: ISC DHCP server only supports one failover peer + if config.exists(base_subnet + ['failover']): + # There can only be one failover configuration, if none is present + # we add the first one + if not config.exists(base + ['failover']): + local = config.return_value(base_subnet + ['failover', 'local-address']) + remote = config.return_value(base_subnet + ['failover', 'peer-address']) + status = config.return_value(base_subnet + ['failover', 'status']) + + config.set(base + ['failover', 'remote'], value=remote) + config.set(base + ['failover', 'source-address'], value=local) + config.set(base + ['failover', 'status'], value=status) + + config.delete(base_subnet + ['failover']) + config.set(base_subnet + ['enable-failover']) + try: with open(file_name, 'w') as f: f.write(config.to_string()) -- cgit v1.2.3 From 83ea0cb273e29db22062cc133b6eabd4ba2761c7 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sun, 19 Sep 2021 11:14:43 +0200 Subject: dhcp-server: T3841: add option to perform ICMP check before address assignment --- data/templates/dhcp-server/dhcpd.conf.tmpl | 6 ++++++ interface-definitions/dhcp-server.xml.in | 2 ++ interface-definitions/include/dhcp-ping-check.xml.i | 8 ++++++++ 3 files changed, 16 insertions(+) create mode 100644 interface-definitions/include/dhcp-ping-check.xml.i diff --git a/data/templates/dhcp-server/dhcpd.conf.tmpl b/data/templates/dhcp-server/dhcpd.conf.tmpl index 54fff3ded..c71934426 100644 --- a/data/templates/dhcp-server/dhcpd.conf.tmpl +++ b/data/templates/dhcp-server/dhcpd.conf.tmpl @@ -73,6 +73,9 @@ shared-network {{ network | replace('_','-') }} { {% if network_config.domain_name is defined and network_config.domain_name is not none %} option domain-name "{{ network_config.domain_name }}"; {% endif %} +{% if network_config.ping_check is defined %} + ping-check true; +{% endif %} {% if network_config.shared_network_parameters is defined and network_config.shared_network_parameters is not none %} # The following {{ network_config.shared_network_parameters | length }} line(s) # were added as shared-network-parameters in the CLI and have not been validated @@ -160,6 +163,9 @@ shared-network {{ network | replace('_','-') }} { default-lease-time {{ subnet_config.lease }}; max-lease-time {{ subnet_config.lease }}; {% endif %} +{% if network_config.ping_check is not defined and subnet_config.ping_check is defined %} + ping-check true; +{% endif %} {% if subnet_config.static_mapping is defined and subnet_config.static_mapping is not none %} {% for host, host_config in subnet_config.static_mapping.items() if host_config.disable is not defined %} host {{ host | replace('_','-') if host_decl_name is defined else network | replace('_','-') + '_' + host | replace('_','-') }} { diff --git a/interface-definitions/dhcp-server.xml.in b/interface-definitions/dhcp-server.xml.in index 960b8a4f0..7ff777c43 100644 --- a/interface-definitions/dhcp-server.xml.in +++ b/interface-definitions/dhcp-server.xml.in @@ -94,6 +94,7 @@ #include + #include #include #include #include @@ -210,6 +211,7 @@ + #include IP address of POP3 server diff --git a/interface-definitions/include/dhcp-ping-check.xml.i b/interface-definitions/include/dhcp-ping-check.xml.i new file mode 100644 index 000000000..0b2a1214a --- /dev/null +++ b/interface-definitions/include/dhcp-ping-check.xml.i @@ -0,0 +1,8 @@ + + + + Sends ICMP Echo request to the address being assigned + + + + -- cgit v1.2.3 From 59e5b5eb4c0507f9d3831483152a748b58560bfd Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sun, 19 Sep 2021 11:31:01 +0200 Subject: xml: dhcp-server: move building blocks to dhcp subdirectory --- interface-definitions/dhcp-server.xml.in | 10 +++++----- interface-definitions/dhcpv6-server.xml.in | 4 ++-- interface-definitions/include/dhcp-domain-name.xml.i | 11 ----------- interface-definitions/include/dhcp-ping-check.xml.i | 8 -------- .../include/dhcp-server-domain-search.xml.i | 12 ------------ interface-definitions/include/dhcp/domain-name.xml.i | 11 +++++++++++ interface-definitions/include/dhcp/domain-search.xml.i | 12 ++++++++++++ interface-definitions/include/dhcp/ping-check.xml.i | 8 ++++++++ 8 files changed, 38 insertions(+), 38 deletions(-) delete mode 100644 interface-definitions/include/dhcp-domain-name.xml.i delete mode 100644 interface-definitions/include/dhcp-ping-check.xml.i delete mode 100644 interface-definitions/include/dhcp-server-domain-search.xml.i create mode 100644 interface-definitions/include/dhcp/domain-name.xml.i create mode 100644 interface-definitions/include/dhcp/domain-search.xml.i create mode 100644 interface-definitions/include/dhcp/ping-check.xml.i diff --git a/interface-definitions/dhcp-server.xml.in b/interface-definitions/dhcp-server.xml.in index 7ff777c43..4551d75a9 100644 --- a/interface-definitions/dhcp-server.xml.in +++ b/interface-definitions/dhcp-server.xml.in @@ -93,8 +93,8 @@ - #include - #include + #include + #include #include #include #include @@ -157,8 +157,8 @@ #include - #include - #include + #include + #include Enable DHCP failover support for this subnet @@ -211,7 +211,7 @@ - #include + #include IP address of POP3 server diff --git a/interface-definitions/dhcpv6-server.xml.in b/interface-definitions/dhcpv6-server.xml.in index 18aa53fb8..fb96571f5 100644 --- a/interface-definitions/dhcpv6-server.xml.in +++ b/interface-definitions/dhcpv6-server.xml.in @@ -58,7 +58,7 @@ - #include + #include #include @@ -127,7 +127,7 @@ - #include + #include Parameters relating to the lease time diff --git a/interface-definitions/include/dhcp-domain-name.xml.i b/interface-definitions/include/dhcp-domain-name.xml.i deleted file mode 100644 index eb95596da..000000000 --- a/interface-definitions/include/dhcp-domain-name.xml.i +++ /dev/null @@ -1,11 +0,0 @@ - - - - Client Domain Name - - - - Invalid domain name (RFC 1123 section 2).\nMay only contain letters, numbers and .-_ - - - diff --git a/interface-definitions/include/dhcp-ping-check.xml.i b/interface-definitions/include/dhcp-ping-check.xml.i deleted file mode 100644 index 0b2a1214a..000000000 --- a/interface-definitions/include/dhcp-ping-check.xml.i +++ /dev/null @@ -1,8 +0,0 @@ - - - - Sends ICMP Echo request to the address being assigned - - - - diff --git a/interface-definitions/include/dhcp-server-domain-search.xml.i b/interface-definitions/include/dhcp-server-domain-search.xml.i deleted file mode 100644 index 4fc55097b..000000000 --- a/interface-definitions/include/dhcp-server-domain-search.xml.i +++ /dev/null @@ -1,12 +0,0 @@ - - - - Client Domain Name search list - - - - Invalid domain name (RFC 1123 section 2).\nMay only contain letters, numbers, period, and underscore. - - - - diff --git a/interface-definitions/include/dhcp/domain-name.xml.i b/interface-definitions/include/dhcp/domain-name.xml.i new file mode 100644 index 000000000..410e27d29 --- /dev/null +++ b/interface-definitions/include/dhcp/domain-name.xml.i @@ -0,0 +1,11 @@ + + + + Client Domain Name + + + + Invalid domain name (RFC 1123 section 2).\nMay only contain letters, numbers and .-_ + + + diff --git a/interface-definitions/include/dhcp/domain-search.xml.i b/interface-definitions/include/dhcp/domain-search.xml.i new file mode 100644 index 000000000..bcc8fcd12 --- /dev/null +++ b/interface-definitions/include/dhcp/domain-search.xml.i @@ -0,0 +1,12 @@ + + + + Client Domain Name search list + + + + Invalid domain name (RFC 1123 section 2).\nMay only contain letters, numbers, period, and underscore. + + + + diff --git a/interface-definitions/include/dhcp/ping-check.xml.i b/interface-definitions/include/dhcp/ping-check.xml.i new file mode 100644 index 000000000..a506f68e4 --- /dev/null +++ b/interface-definitions/include/dhcp/ping-check.xml.i @@ -0,0 +1,8 @@ + + + + Sends ICMP Echo request to the address being assigned + + + + -- cgit v1.2.3 From 2985035bcb2f3732e15a41e3c2ee6c6c93a6836e Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sun, 19 Sep 2021 11:32:04 +0200 Subject: dhcp-server: T3672: re-add missing "name" CLI option This option is mandatory and must be user configurable as it needs to match on both sides. --- data/templates/dhcp-server/dhcpd.conf.tmpl | 5 ++--- interface-definitions/dhcp-server.xml.in | 9 +++++++++ src/conf_mode/dhcp_server.py | 2 +- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/data/templates/dhcp-server/dhcpd.conf.tmpl b/data/templates/dhcp-server/dhcpd.conf.tmpl index c71934426..790f57bbb 100644 --- a/data/templates/dhcp-server/dhcpd.conf.tmpl +++ b/data/templates/dhcp-server/dhcpd.conf.tmpl @@ -32,9 +32,8 @@ option wpad-url code 252 = text; {% endif %} {% if failover is defined and failover is not none %} -{% set dhcp_failover_name = 'VyOS-DHCP-failover-peer' %} # DHCP failover configuration -failover peer "{{ dhcp_failover_name }}" { +failover peer "{{ failover.name }}" { {% if failover.status == 'primary' %} primary; mclt 1800; @@ -185,7 +184,7 @@ shared-network {{ network | replace('_','-') }} { {% endif %} pool { {% if subnet_config.enable_failover is defined %} - failover peer "{{ dhcp_failover_name }}"; + failover peer "{{ failover.name }}"; deny dynamic bootp clients; {% endif %} {% if subnet_config.range is defined and subnet_config.range is not none %} diff --git a/interface-definitions/dhcp-server.xml.in b/interface-definitions/dhcp-server.xml.in index 4551d75a9..2707ce96d 100644 --- a/interface-definitions/dhcp-server.xml.in +++ b/interface-definitions/dhcp-server.xml.in @@ -34,6 +34,15 @@ + + + Peer name used to identify connection + + [-_a-zA-Z0-9.]+ + + Invalid failover peer name. May only contain letters, numbers and .-_ + + Failover hierarchy diff --git a/src/conf_mode/dhcp_server.py b/src/conf_mode/dhcp_server.py index 5b3809017..28f2a4ca5 100755 --- a/src/conf_mode/dhcp_server.py +++ b/src/conf_mode/dhcp_server.py @@ -247,7 +247,7 @@ def verify(dhcp): if not failover_ok: raise ConfigError('DHCP failover must be enabled for at least one subnet!') - for key in ['source_address', 'remote', 'status']: + for key in ['name', 'remote', 'source_address', 'status']: if key not in dhcp['failover']: tmp = key.replace('_', '-') raise ConfigError(f'DHCP failover requires "{tmp}" to be specified!') -- cgit v1.2.3 From 6c2c089c26f1652644c9ded7d5cfd8a0497f148e Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sun, 19 Sep 2021 11:58:19 +0200 Subject: dhcp-server: T3672: bugfix Jinja2 template The DHCP servers pool {} option can only be used when there follows a range statement. This is invalid for a network with only "static" leases. --- data/templates/dhcp-server/dhcpd.conf.tmpl | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/data/templates/dhcp-server/dhcpd.conf.tmpl b/data/templates/dhcp-server/dhcpd.conf.tmpl index 790f57bbb..d774b4827 100644 --- a/data/templates/dhcp-server/dhcpd.conf.tmpl +++ b/data/templates/dhcp-server/dhcpd.conf.tmpl @@ -182,7 +182,10 @@ shared-network {{ network | replace('_','-') }} { } {% endfor %} {% endif %} +{% if subnet_config.range is defined and subnet_config.range is not none %} +{# pool configuration can only be used if there follows a range option #} pool { +{% endif %} {% if subnet_config.enable_failover is defined %} failover peer "{{ failover.name }}"; deny dynamic bootp clients; @@ -192,7 +195,10 @@ shared-network {{ network | replace('_','-') }} { range {{ range_options.start }} {{ range_options.stop }}; {% endfor %} {% endif %} +{% if subnet_config.range is defined and subnet_config.range is not none %} +{# pool configuration can only be used if there follows a range option #} } +{% endif %} } {% endfor %} {% endif %} -- cgit v1.2.3 From a85ff749bb84d562b711171768cdc92498ad7406 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sun, 19 Sep 2021 11:59:36 +0200 Subject: smoketest: dhcp: T3672: adjust to new CLI syntax --- smoketest/scripts/cli/test_service_dhcp-server.py | 101 ++++++++++++++++------ 1 file changed, 73 insertions(+), 28 deletions(-) diff --git a/smoketest/scripts/cli/test_service_dhcp-server.py b/smoketest/scripts/cli/test_service_dhcp-server.py index 37e016778..2aa7b43c8 100755 --- a/smoketest/scripts/cli/test_service_dhcp-server.py +++ b/smoketest/scripts/cli/test_service_dhcp-server.py @@ -37,12 +37,19 @@ dns_2 = inc_ip(subnet, 3) domain_name = 'vyos.net' class TestServiceDHCPServer(VyOSUnitTestSHIM.TestCase): - def setUp(self): + @classmethod + def setUpClass(cls): + super(cls, cls).setUpClass() + cidr_mask = subnet.split('/')[-1] - self.cli_set(['interfaces', 'dummy', 'dum8765', 'address', f'{router}/{cidr_mask}']) + cls.cli_set(cls, ['interfaces', 'dummy', 'dum8765', 'address', f'{router}/{cidr_mask}']) + + @classmethod + def tearDownClass(cls): + cls.cli_delete(cls, ['interfaces', 'dummy', 'dum8765']) + super(cls, cls).tearDownClass() def tearDown(self): - self.cli_delete(['interfaces', 'dummy', 'dum8765']) self.cli_delete(base_path) self.cli_commit() @@ -131,34 +138,11 @@ class TestServiceDHCPServer(VyOSUnitTestSHIM.TestCase): self.cli_set(pool + ['range', '0', 'start', range_0_start]) self.cli_set(pool + ['range', '0', 'stop', range_0_stop]) - # failover - failover_local = router - failover_remote = inc_ip(router, 1) - - self.cli_set(pool + ['failover', 'local-address', failover_local]) - self.cli_set(pool + ['failover', 'name', shared_net_name]) - self.cli_set(pool + ['failover', 'peer-address', failover_remote]) - self.cli_set(pool + ['failover', 'status', 'primary']) - # commit changes self.cli_commit() config = read_file(DHCPD_CONF) - self.assertIn(f'failover peer "{shared_net_name}"' + r' {', config) - self.assertIn(f'primary;', config) - self.assertIn(f'mclt 1800;', config) - self.assertIn(f'mclt 1800;', config) - self.assertIn(f'split 128;', config) - self.assertIn(f'port 520;', config) - self.assertIn(f'peer port 520;', config) - self.assertIn(f'max-response-delay 30;', config) - self.assertIn(f'max-unacked-updates 10;', config) - self.assertIn(f'load balance max seconds 3;', config) - self.assertIn(f'peer port 520;', config) - self.assertIn(f'address {failover_local};', config) - self.assertIn(f'peer address {failover_remote};', config) - network = address_from_cidr(subnet) netmask = netmask_from_cidr(subnet) self.assertIn(f'ddns-update-style none;', config) @@ -184,8 +168,6 @@ class TestServiceDHCPServer(VyOSUnitTestSHIM.TestCase): self.assertIn(f'max-lease-time 86400;', config) self.assertIn(f'range {range_0_start} {range_0_stop};', config) self.assertIn(f'set shared-networkname = "{shared_net_name}";', config) - self.assertIn(f'failover peer "{shared_net_name}";', config) - self.assertIn(f'deny dynamic bootp clients;', config) # weird syntax for those static routes self.assertIn(f'option rfc3442-static-route 24,10,0,0,192,0,2,1, 0,192,0,2,1;', config) @@ -437,5 +419,68 @@ class TestServiceDHCPServer(VyOSUnitTestSHIM.TestCase): # Check for running process self.assertTrue(process_named_running(PROCESS_NAME)) + def test_dhcp_failover(self): + shared_net_name = 'FAILOVER' + failover_name = 'VyOS-Failover' + + range_0_start = inc_ip(subnet, 10) + range_0_stop = inc_ip(subnet, 20) + + pool = base_path + ['shared-network-name', shared_net_name, 'subnet', subnet] + # we use the first subnet IP address as default gateway + self.cli_set(pool + ['default-router', router]) + + # check validate() - No DHCP address range or active static-mapping set + with self.assertRaises(ConfigSessionError): + self.cli_commit() + self.cli_set(pool + ['range', '0', 'start', range_0_start]) + self.cli_set(pool + ['range', '0', 'stop', range_0_stop]) + + # failover + failover_local = router + failover_remote = inc_ip(router, 1) + + self.cli_set(base_path + ['failover', 'source-address', failover_local]) + self.cli_set(base_path + ['failover', 'name', failover_name]) + self.cli_set(base_path + ['failover', 'remote', failover_remote]) + self.cli_set(base_path + ['failover', 'status', 'primary']) + + # check validate() - failover needs to be enabled for at least one subnet + with self.assertRaises(ConfigSessionError): + self.cli_commit() + self.cli_set(pool + ['enable-failover']) + + # commit changes + self.cli_commit() + + config = read_file(DHCPD_CONF) + + self.assertIn(f'failover peer "{failover_name}"' + r' {', config) + self.assertIn(f'primary;', config) + self.assertIn(f'mclt 1800;', config) + self.assertIn(f'mclt 1800;', config) + self.assertIn(f'split 128;', config) + self.assertIn(f'port 520;', config) + self.assertIn(f'peer port 520;', config) + self.assertIn(f'max-response-delay 30;', config) + self.assertIn(f'max-unacked-updates 10;', config) + self.assertIn(f'load balance max seconds 3;', config) + self.assertIn(f'peer port 520;', config) + self.assertIn(f'address {failover_local};', config) + self.assertIn(f'peer address {failover_remote};', config) + + network = address_from_cidr(subnet) + netmask = netmask_from_cidr(subnet) + self.assertIn(f'ddns-update-style none;', config) + self.assertIn(f'subnet {network} netmask {netmask}' + r' {', config) + self.assertIn(f'option routers {router};', config) + self.assertIn(f'range {range_0_start} {range_0_stop};', config) + self.assertIn(f'set shared-networkname = "{shared_net_name}";', config) + self.assertIn(f'failover peer "{failover_name}";', config) + self.assertIn(f'deny dynamic bootp clients;', config) + + # Check for running process + self.assertTrue(process_named_running(PROCESS_NAME)) + if __name__ == '__main__': unittest.main(verbosity=2) -- cgit v1.2.3 From 88f71fc6b5b3bd89623c3d5f0af1c7dbd19f0996 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sun, 19 Sep 2021 12:01:50 +0200 Subject: smoketest: dhcp: T3841: validate ping-check option --- smoketest/scripts/cli/test_service_dhcp-server.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/smoketest/scripts/cli/test_service_dhcp-server.py b/smoketest/scripts/cli/test_service_dhcp-server.py index 2aa7b43c8..301f8fa31 100755 --- a/smoketest/scripts/cli/test_service_dhcp-server.py +++ b/smoketest/scripts/cli/test_service_dhcp-server.py @@ -69,6 +69,7 @@ class TestServiceDHCPServer(VyOSUnitTestSHIM.TestCase): self.cli_set(pool + ['name-server', dns_1]) self.cli_set(pool + ['name-server', dns_2]) self.cli_set(pool + ['domain-name', domain_name]) + self.cli_set(pool + ['ping-check']) # check validate() - No DHCP address range or active static-mapping set with self.assertRaises(ConfigSessionError): @@ -91,6 +92,7 @@ class TestServiceDHCPServer(VyOSUnitTestSHIM.TestCase): self.assertIn(f'option domain-name "{domain_name}";', config) self.assertIn(f'default-lease-time 86400;', config) self.assertIn(f'max-lease-time 86400;', config) + self.assertIn(f'ping-check true;', config) self.assertIn(f'range {range_0_start} {range_0_stop};', config) self.assertIn(f'range {range_1_start} {range_1_stop};', config) self.assertIn(f'set shared-networkname = "{shared_net_name}";', config) -- cgit v1.2.3 From e83a113360ba18043edcf7f70689c7042dee2b37 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sun, 19 Sep 2021 17:04:49 +0200 Subject: dhcp-server: T3672: migrate failover name option Commit 2985035b (dhcp-server: T3672: re-add missing "name" CLI option) unfortunately did not add the name option to the migration script. --- src/migration-scripts/dhcp-server/5-to-6 | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/migration-scripts/dhcp-server/5-to-6 b/src/migration-scripts/dhcp-server/5-to-6 index 39bbb9f50..aefe84737 100755 --- a/src/migration-scripts/dhcp-server/5-to-6 +++ b/src/migration-scripts/dhcp-server/5-to-6 @@ -69,10 +69,12 @@ for network in config.list_nodes(base + ['shared-network-name']): local = config.return_value(base_subnet + ['failover', 'local-address']) remote = config.return_value(base_subnet + ['failover', 'peer-address']) status = config.return_value(base_subnet + ['failover', 'status']) + name = config.return_value(base_subnet + ['failover', 'name']) config.set(base + ['failover', 'remote'], value=remote) config.set(base + ['failover', 'source-address'], value=local) config.set(base + ['failover', 'status'], value=status) + config.set(base + ['failover', 'name'], value=name) config.delete(base_subnet + ['failover']) config.set(base_subnet + ['enable-failover']) -- cgit v1.2.3 From b4c58c5aefaca4fce817b58327b9c7c3e8145d6d Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sun, 19 Sep 2021 21:41:56 +0200 Subject: vyos.ifconfig: T2738: add delta check when changing interface parameters There is no need to alter interface parameters if they have not changed at all. --- python/vyos/ifconfig/interface.py | 127 ++++++++++++++++++++++++++++++++++---- 1 file changed, 114 insertions(+), 13 deletions(-) diff --git a/python/vyos/ifconfig/interface.py b/python/vyos/ifconfig/interface.py index 04e139805..14b782db4 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 is_systemd_service_active from vyos.template import is_ipv4 from vyos.template import is_ipv6 from vyos.validate import is_intf_addr_assigned @@ -108,6 +109,10 @@ class Interface(Control): 'shellcmd': 'ip -json -detail link list dev {ifname}', 'format': lambda j: jmespath.search('[*].operstate | [0]', json.loads(j)), }, + 'vrf': { + 'shellcmd': 'ip -json -detail link list dev {ifname}', + 'format': lambda j: jmespath.search('[*].master | [0]', json.loads(j)), + }, } _command_set = { @@ -139,7 +144,6 @@ class Interface(Control): _sysfs_set = { 'arp_cache_tmo': { - 'convert': lambda tmo: (int(tmo) * 1000), 'location': '/proc/sys/net/ipv4/neigh/{ifname}/base_reachable_time_ms', }, 'arp_filter': { @@ -209,6 +213,51 @@ class Interface(Control): }, } + _sysfs_get = { + 'arp_cache_tmo': { + 'location': '/proc/sys/net/ipv4/neigh/{ifname}/base_reachable_time_ms', + }, + 'arp_filter': { + 'location': '/proc/sys/net/ipv4/conf/{ifname}/arp_filter', + }, + 'arp_accept': { + 'location': '/proc/sys/net/ipv4/conf/{ifname}/arp_accept', + }, + 'arp_announce': { + 'location': '/proc/sys/net/ipv4/conf/{ifname}/arp_announce', + }, + 'arp_ignore': { + 'location': '/proc/sys/net/ipv4/conf/{ifname}/arp_ignore', + }, + 'ipv4_forwarding': { + 'location': '/proc/sys/net/ipv4/conf/{ifname}/forwarding', + }, + 'rp_filter': { + 'location': '/proc/sys/net/ipv4/conf/{ifname}/rp_filter', + }, + 'ipv6_accept_ra': { + 'location': '/proc/sys/net/ipv6/conf/{ifname}/accept_ra', + }, + 'ipv6_autoconf': { + 'location': '/proc/sys/net/ipv6/conf/{ifname}/autoconf', + }, + 'ipv6_forwarding': { + 'location': '/proc/sys/net/ipv6/conf/{ifname}/forwarding', + }, + 'ipv6_dad_transmits': { + 'location': '/proc/sys/net/ipv6/conf/{ifname}/dad_transmits', + }, + 'proxy_arp': { + 'location': '/proc/sys/net/ipv4/conf/{ifname}/proxy_arp', + }, + 'proxy_arp_pvlan': { + 'location': '/proc/sys/net/ipv4/conf/{ifname}/proxy_arp_pvlan', + }, + 'link_detect': { + 'location': '/proc/sys/net/ipv4/conf/{ifname}/link_filter', + }, + } + @classmethod def exists(cls, ifname): return os.path.exists(f'/sys/class/net/{ifname}') @@ -327,9 +376,7 @@ class Interface(Control): 'info_data', {}).get('table') # Add map element with interface and zone ID if vrf_table_id: - self._cmd( - f'nft add element inet vrf_zones ct_iface_map {{ "{self.ifname}" : {vrf_table_id} }}' - ) + self._cmd(f'nft add element inet vrf_zones ct_iface_map {{ "{self.ifname}" : {vrf_table_id} }}') else: nft_del_element = f'delete element inet vrf_zones ct_iface_map {{ "{self.ifname}" }}' # Check if deleting is possible first to avoid raising errors @@ -381,6 +428,9 @@ class Interface(Control): >>> Interface('eth0').get_mtu() '1400' """ + tmp = self.get_interface('mtu') + if str(tmp) == mtu: + return None return self.set_interface('mtu', mtu) def get_mac(self): @@ -443,7 +493,7 @@ class Interface(Control): if prev_state == 'up': self.set_admin_state('up') - def set_vrf(self, vrf=''): + def set_vrf(self, vrf): """ Add/Remove interface from given VRF instance. @@ -452,6 +502,11 @@ class Interface(Control): >>> Interface('eth0').set_vrf('foo') >>> Interface('eth0').set_vrf() """ + + tmp = self.get_interface('vrf') + if tmp == vrf: + return None + self.set_interface('vrf', vrf) self._set_vrf_ct_zone(vrf) @@ -464,6 +519,10 @@ class Interface(Control): >>> from vyos.ifconfig import Interface >>> Interface('eth0').set_arp_cache_tmo(40) """ + tmo = str(int(tmo) * 1000) + tmp = self.get_interface('arp_cache_tmo') + if tmp == tmo: + return None return self.set_interface('arp_cache_tmo', tmo) def set_tcp_ipv4_mss(self, mss): @@ -540,6 +599,9 @@ class Interface(Control): particular interfaces. Only for more complex setups like load- balancing, does this behaviour cause problems. """ + tmp = self.get_interface('arp_filter') + if tmp == arp_filter: + return None return self.set_interface('arp_filter', arp_filter) def set_arp_accept(self, arp_accept): @@ -556,6 +618,9 @@ class Interface(Control): gratuitous arp frame, the arp table will be updated regardless if this setting is on or off. """ + tmp = self.get_interface('arp_accept') + if tmp == arp_accept: + return None return self.set_interface('arp_accept', arp_accept) def set_arp_announce(self, arp_announce): @@ -577,6 +642,9 @@ class Interface(Control): receiving answer from the resolved target while decreasing the level announces more valid sender's information. """ + tmp = self.get_interface('arp_announce') + if tmp == arp_announce: + return None return self.set_interface('arp_announce', arp_announce) def set_arp_ignore(self, arp_ignore): @@ -589,12 +657,16 @@ class Interface(Control): 1 - reply only if the target IP address is local address configured on the incoming interface """ + tmp = self.get_interface('arp_ignore') + if tmp == arp_ignore: + return None return self.set_interface('arp_ignore', arp_ignore) def set_ipv4_forwarding(self, forwarding): - """ - Configure IPv4 forwarding. - """ + """ Configure IPv4 forwarding. """ + tmp = self.get_interface('ipv4_forwarding') + if tmp == forwarding: + return None return self.set_interface('ipv4_forwarding', forwarding) def set_ipv4_source_validation(self, value): @@ -623,6 +695,9 @@ class Interface(Control): print(f'WARNING: Global source-validation is set to "{global_setting}\n"' \ 'this overrides per interface setting!') + tmp = self.get_interface('rp_filter') + if int(tmp) == value: + return None return self.set_interface('rp_filter', value) def set_ipv6_accept_ra(self, accept_ra): @@ -638,6 +713,9 @@ class Interface(Control): 2 - Overrule forwarding behaviour. Accept Router Advertisements even if forwarding is enabled. """ + tmp = self.get_interface('ipv6_accept_ra') + if tmp == accept_ra: + return None return self.set_interface('ipv6_accept_ra', accept_ra) def set_ipv6_autoconf(self, autoconf): @@ -645,6 +723,9 @@ class Interface(Control): Autoconfigure addresses using Prefix Information in Router Advertisements. """ + tmp = self.get_interface('ipv6_autoconf') + if tmp == autoconf: + return None return self.set_interface('ipv6_autoconf', autoconf) def add_ipv6_eui64_address(self, prefix): @@ -698,6 +779,9 @@ class Interface(Control): 3. Router Advertisements are ignored unless accept_ra is 2. 4. Redirects are ignored. """ + tmp = self.get_interface('ipv6_forwarding') + if tmp == forwarding: + return None return self.set_interface('ipv6_forwarding', forwarding) def set_ipv6_dad_messages(self, dad): @@ -705,6 +789,9 @@ class Interface(Control): The amount of Duplicate Address Detection probes to send. Default: 1 """ + tmp = self.get_interface('ipv6_dad_transmits') + if tmp == dad: + return None return self.set_interface('ipv6_dad_transmits', dad) def set_link_detect(self, link_filter): @@ -727,6 +814,9 @@ class Interface(Control): >>> from vyos.ifconfig import Interface >>> Interface('eth0').set_link_detect(1) """ + tmp = self.get_interface('link_detect') + if tmp == link_filter: + return None return self.set_interface('link_detect', link_filter) def get_alias(self): @@ -751,6 +841,9 @@ class Interface(Control): >>> Interface('eth0').set_ifalias('') """ + tmp = self.get_interface('alias') + if tmp == ifalias: + return None self.set_interface('alias', ifalias) def get_admin_state(self): @@ -826,6 +919,9 @@ class Interface(Control): >>> from vyos.ifconfig import Interface >>> Interface('eth0').set_proxy_arp(1) """ + tmp = self.get_interface('proxy_arp') + if tmp == enable: + return None self.set_interface('proxy_arp', enable) def set_proxy_arp_pvlan(self, enable): @@ -852,6 +948,9 @@ class Interface(Control): >>> from vyos.ifconfig import Interface >>> Interface('eth0').set_proxy_arp_pvlan(1) """ + tmp = self.get_interface('proxy_arp_pvlan') + if tmp == enable: + return None self.set_interface('proxy_arp_pvlan', enable) def get_addr_v4(self): @@ -1092,7 +1191,9 @@ class Interface(Control): lease_file = f'{config_base}_{ifname}.leases' # Stop client with old config files to get the right IF_METRIC. - self._cmd(f'systemctl stop dhclient@{ifname}.service') + systemd_service = f'dhclient@{ifname}.service' + if is_systemd_service_active(systemd_service): + self._cmd(f'systemctl stop {systemd_service}') if enable and 'disable' not in self._config: if dict_search('dhcp_options.host_name', self._config) == None: @@ -1138,8 +1239,9 @@ class Interface(Control): # for interfaces which are yet not up and running. return self._popen(f'systemctl restart dhcp6c@{ifname}.service') else: - self._popen(f'systemctl stop dhcp6c@{ifname}.service') - + systemd_service = f'dhcp6c@{ifname}.service' + if is_systemd_service_active(systemd_service): + self._cmd(f'systemctl stop {systemd_service}') if os.path.isfile(config_file): os.remove(config_file) @@ -1186,7 +1288,6 @@ class Interface(Control): mirror_cmd += f'tc filter add dev {source_if} parent {parent} protocol all prio 10 u32 match u32 0 0 flowid 1:1 action mirred egress mirror dev {mirror_if}' self._popen(mirror_cmd) - def set_xdp(self, state): """ Enable Kernel XDP support. State can be either True or False. @@ -1289,7 +1390,7 @@ class Interface(Control): # unbinding will call 'ip link set dev eth0 nomaster' which will # also drop the interface out of a bridge or bond - thus this is # checked before - self.set_vrf(config.get('vrf', '')) + self.set_vrf(config.get('vrf', None)) # Configure MSS value for IPv4 TCP connections tmp = dict_search('ip.adjust_mss', config) -- cgit v1.2.3 From e28a80a2b742ea3d9d4bcb8ae66c7a0d51aaaff6 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sun, 19 Sep 2021 21:59:43 +0200 Subject: vyos.configdict: bugfix: leaf_node_changed() must return empty dict when node is added Commit f476e456 ("vyos.configdict: leaf_node_changed() must return empty dict when node is added") returned [''] as "empty" dict - but this is not empty. >>> if ['']: ... print('foo') ... foo It should rather be: [] --- python/vyos/configdict.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/vyos/configdict.py b/python/vyos/configdict.py index 24b76fb0b..8d7142049 100644 --- a/python/vyos/configdict.py +++ b/python/vyos/configdict.py @@ -118,7 +118,7 @@ def leaf_node_changed(conf, path): (new, old) = D.get_value_diff(path) if new != old: if old is None: - return [''] + return [] if isinstance(old, str): return [old] if isinstance(old, list): -- cgit v1.2.3 From e80d0aebd691f1a707ab534b4d1340fa0b793e01 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sun, 19 Sep 2021 22:06:02 +0200 Subject: vyos.ifconfig: T2738: do not remove OS assigned IP addresses from interface When using VRRP on any given interface and performing an action against that interface - be it even only changing the alias - will trigger a removal of the VRRP IP address. The issue is caused by: # determine IP addresses which are assigned to the interface and build a # list of addresses which are no longer in the dict so they can be removed cur_addr = self.get_addr() for addr in list_diff(cur_addr, new_addr): When the script calls into the library - we will drop all IP addresses set on the adapter but not available in the config dict. We should only remove the IP addresses marked by the CLI to be deleted! --- python/vyos/configdict.py | 3 +++ python/vyos/ifconfig/interface.py | 20 +++++++++++--------- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/python/vyos/configdict.py b/python/vyos/configdict.py index 8d7142049..5c6836e97 100644 --- a/python/vyos/configdict.py +++ b/python/vyos/configdict.py @@ -375,6 +375,9 @@ def get_interface_dict(config, base, ifname=''): # XXX: T2665: blend in proper DHCPv6-PD default values dict = T2665_set_dhcpv6pd_defaults(dict) + address = leaf_node_changed(config, ['address']) + if address: dict.update({'address_old' : address}) + # Check if we are a member of a bridge device bridge = is_member(config, ifname, 'bridge') if bridge: dict.update({'is_bridge_member' : bridge}) diff --git a/python/vyos/ifconfig/interface.py b/python/vyos/ifconfig/interface.py index 14b782db4..963f47c89 100755 --- a/python/vyos/ifconfig/interface.py +++ b/python/vyos/ifconfig/interface.py @@ -1085,6 +1085,8 @@ class Interface(Control): >>> j.get_addr() ['2001:db8::ffff/64'] """ + if not addr: + raise ValueError() # remove from interface if addr == 'dhcp': @@ -1364,16 +1366,16 @@ class Interface(Control): # determine IP addresses which are assigned to the interface and build a # list of addresses which are no longer in the dict so they can be removed - cur_addr = self.get_addr() - for addr in list_diff(cur_addr, new_addr): - # we will delete all interface specific IP addresses if they are not - # explicitly configured on the CLI - if is_ipv6_link_local(addr): - eui64 = mac2eui64(self.get_mac(), 'fe80::/64') - if addr != f'{eui64}/64': + if 'address_old' in config: + for addr in list_diff(config['address_old'], new_addr): + # we will delete all interface specific IP addresses if they are not + # explicitly configured on the CLI + if is_ipv6_link_local(addr): + eui64 = mac2eui64(self.get_mac(), 'fe80::/64') + if addr != f'{eui64}/64': + self.del_addr(addr) + else: self.del_addr(addr) - else: - self.del_addr(addr) for addr in new_addr: self.add_addr(addr) -- cgit v1.2.3 From d768aee9bd93280210980d621ecc7b2da1a8d4af Mon Sep 17 00:00:00 2001 From: Lucas Christian Date: Sun, 19 Sep 2021 17:02:42 -0700 Subject: ipsec: T1441: Clean up vti-up-down script for XFRM interfaces --- data/templates/ipsec/swanctl/peer.tmpl | 4 ++-- src/etc/ipsec.d/vti-up-down | 12 ------------ 2 files changed, 2 insertions(+), 14 deletions(-) diff --git a/data/templates/ipsec/swanctl/peer.tmpl b/data/templates/ipsec/swanctl/peer.tmpl index e039e98aa..8c3776bf1 100644 --- a/data/templates/ipsec/swanctl/peer.tmpl +++ b/data/templates/ipsec/swanctl/peer.tmpl @@ -60,7 +60,7 @@ life_time = {{ vti_esp.lifetime }}s local_ts = 0.0.0.0/0,::/0 remote_ts = 0.0.0.0/0,::/0 - updown = "/etc/ipsec.d/vti-up-down {{ peer_conf.vti.bind }} {{ peer_conf.dhcp_interface if peer_conf.dhcp_interface is defined else 'no' }}" + updown = "/etc/ipsec.d/vti-up-down {{ peer_conf.vti.bind }}" {# The key defaults to 0 and will match any policies which similarly do not have a lookup key configuration. #} {# Thus we simply shift the key by one to also support a vti0 interface #} {% set if_id = peer_conf.vti.bind | replace('vti', '') | int +1 %} @@ -119,7 +119,7 @@ dpd_action = {{ dpd_translate[ike.dead_peer_detection.action] }} {% endif %} {% if peer_conf.vti is defined and peer_conf.vti.bind is defined %} - updown = "/etc/ipsec.d/vti-up-down {{ peer_conf.vti.bind }} {{ peer_conf.dhcp_interface if peer_conf.dhcp_interface is defined else 'no' }}" + updown = "/etc/ipsec.d/vti-up-down {{ peer_conf.vti.bind }}" {# The key defaults to 0 and will match any policies which similarly do not have a lookup key configuration. #} {# Thus we simply shift the key by one to also support a vti0 interface #} {% set if_id = peer_conf.vti.bind | replace('vti', '') | int +1 %} diff --git a/src/etc/ipsec.d/vti-up-down b/src/etc/ipsec.d/vti-up-down index 011013a2e..1ffb32955 100755 --- a/src/etc/ipsec.d/vti-up-down +++ b/src/etc/ipsec.d/vti-up-down @@ -29,19 +29,10 @@ from vyos.util import call from vyos.util import get_interface_config from vyos.util import get_interface_address -def get_dhcp_address(interface): - addr = get_interface_address(interface) - if not addr: - return None - if len(addr['addr_info']) == 0: - return None - return addr['addr_info'][0]['local'] - if __name__ == '__main__': verb = os.getenv('PLUTO_VERB') connection = os.getenv('PLUTO_CONNECTION') interface = sys.argv[1] - dhcp_interface = sys.argv[2] openlog(ident=f'vti-up-down', logoption=LOG_PID, facility=LOG_INFO) syslog(f'Interface {interface} {verb} {connection}') @@ -63,9 +54,6 @@ if __name__ == '__main__': if verb in ['up-client', 'up-host']: if not vti_link_up: - if dhcp_interface != 'no': - local_ip = get_dhcp_address(dhcp_interface) - call(f'sudo ip tunnel change {interface} local {local_ip}') if 'disable' not in vti_dict: call(f'sudo ip link set {interface} up') else: -- cgit v1.2.3 From 672a70613aa6c987bca417f93b587eddccbfd53a Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Mon, 20 Sep 2021 07:38:59 +0200 Subject: vyos.ifconfig: T2738: can only read from a file when it exists When IPv6 is disbaled on an interface also the sysfs files related to IPv6 for this interface vanish. We need to check if the file exists before we read it. --- python/vyos/ifconfig/control.py | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/python/vyos/ifconfig/control.py b/python/vyos/ifconfig/control.py index d41dfef47..7a6b36e7c 100644 --- a/python/vyos/ifconfig/control.py +++ b/python/vyos/ifconfig/control.py @@ -18,11 +18,12 @@ import os from inspect import signature from inspect import _empty -from vyos import debug +from vyos.ifconfig.section import Section from vyos.util import popen from vyos.util import cmd -from vyos.ifconfig.section import Section - +from vyos.util import read_file +from vyos.util import write_file +from vyos import debug class Control(Section): _command_get = {} @@ -116,20 +117,18 @@ class Control(Section): Provide a single primitive w/ error checking for reading from sysfs. """ value = None - with open(filename, 'r') as f: - value = f.read().rstrip('\n') - - self._debug_msg("read '{}' < '{}'".format(value, filename)) + if os.path.exists(filename): + value = read_file(filename) + self._debug_msg("read '{}' < '{}'".format(value, filename)) return value def _write_sysfs(self, filename, value): """ Provide a single primitive w/ error checking for writing to sysfs. """ - self._debug_msg("write '{}' > '{}'".format(value, filename)) if os.path.isfile(filename): - with open(filename, 'w') as f: - f.write(str(value)) + write_file(filename, str(value)) + self._debug_msg("write '{}' > '{}'".format(value, filename)) return True return False -- cgit v1.2.3 From 5bf9ac9318a2f56ae6b45098c66781393c133883 Mon Sep 17 00:00:00 2001 From: sarthurdev <965089+sarthurdev@users.noreply.github.com> Date: Mon, 20 Sep 2021 14:48:34 +0200 Subject: github: Add .gitattributes to override language detection --- .gitattributes | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 .gitattributes diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 000000000..ea2cc59e4 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,4 @@ +data/templates/**/*.conf linguist-language=Jinja +*.tmpl linguist-language=Jinja +*.xml.i linguist-language=XML +*.xml.in linguist-language=XML -- cgit v1.2.3 From a8b2e52148da69615285d03e8f89c1ac03990861 Mon Sep 17 00:00:00 2001 From: sarthurdev <965089+sarthurdev@users.noreply.github.com> Date: Mon, 20 Sep 2021 15:00:52 +0200 Subject: xml: Update routing-passive-interface-xml.i file extension to standard .xml.i --- .../include/routing-passive-interface-xml.i | 24 ---------------------- .../include/routing-passive-interface.xml.i | 24 ++++++++++++++++++++++ interface-definitions/protocols-rip.xml.in | 2 +- 3 files changed, 25 insertions(+), 25 deletions(-) delete mode 100644 interface-definitions/include/routing-passive-interface-xml.i create mode 100644 interface-definitions/include/routing-passive-interface.xml.i diff --git a/interface-definitions/include/routing-passive-interface-xml.i b/interface-definitions/include/routing-passive-interface-xml.i deleted file mode 100644 index 9bd4dac2a..000000000 --- a/interface-definitions/include/routing-passive-interface-xml.i +++ /dev/null @@ -1,24 +0,0 @@ - - - - Suppress routing updates on an interface - - default - - - - txt - Interface to be passive (i.e. suppress routing updates) - - - default - Default to suppress routing updates on all interfaces - - - ^(default)$ - - - - - - diff --git a/interface-definitions/include/routing-passive-interface.xml.i b/interface-definitions/include/routing-passive-interface.xml.i new file mode 100644 index 000000000..43dfb5e44 --- /dev/null +++ b/interface-definitions/include/routing-passive-interface.xml.i @@ -0,0 +1,24 @@ + + + + Suppress routing updates on an interface + + default + + + + txt + Interface to be passive (i.e. suppress routing updates) + + + default + Default to suppress routing updates on all interfaces + + + ^(default)$ + + + + + + diff --git a/interface-definitions/protocols-rip.xml.in b/interface-definitions/protocols-rip.xml.in index 4fcfcfc27..d3be4e1af 100644 --- a/interface-definitions/protocols-rip.xml.in +++ b/interface-definitions/protocols-rip.xml.in @@ -155,7 +155,7 @@ #include - #include + #include Redistribute information from another routing protocol -- cgit v1.2.3 From 081e23996feb60ad903caf8b0a4587f5dacc69bf Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Mon, 20 Sep 2021 21:50:56 +0200 Subject: vyos.ifconfig: get_mac_synthetic() must generate a stable "MAC" Commit b7d30137b1 ("vyos.ifconfig: provide generic get_mac_synthetic() method") provided a common helper to generate MAC addresses used by EUI64 addresses for interfaces not having a layer2 interface (WireGuard or ip tunnel). The problem is that every call to the helper always yielded a new MAC address. This becomes problematic when IPv6 link-local addresses are generated and modified on the interface as multiple link-local (fe80::/64) addresses can easily be added to the interface leaving ... a mess. This commit changes the way how the "synthetic" MAC is generated, we generate a UUID which is stable as it is based on the interface name. We take out the last 48 bits of the UUID and form the "MAC" address. --- python/vyos/ifconfig/interface.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/python/vyos/ifconfig/interface.py b/python/vyos/ifconfig/interface.py index 963f47c89..0256ca5df 100755 --- a/python/vyos/ifconfig/interface.py +++ b/python/vyos/ifconfig/interface.py @@ -27,6 +27,8 @@ from netifaces import ifaddresses # this is not the same as socket.AF_INET/INET6 from netifaces import AF_INET from netifaces import AF_INET6 +from uuid import uuid3 +from uuid import NAMESPACE_DNS from vyos import ConfigError from vyos.configdict import list_diff @@ -56,7 +58,6 @@ from vyos.ifconfig import Section from netaddr import EUI from netaddr import mac_unix_expanded -from random import getrandbits class Interface(Control): # This is the class which will be used to create @@ -458,9 +459,14 @@ class Interface(Control): >>> Interface('eth0').get_mac() '00:50:ab:cd:ef:00' """ - # we choose 40 random bytes for the MAC address, this gives - # us e.g. EUI('00-EA-EE-D6-A3-C8') or EUI('00-41-B9-0D-F2-2A') - tmp = EUI(getrandbits(48)).value + # calculate a UUID based on the interface name - this is as predictable + # as an interface MAC address and thus can be used in the same way + tmp = uuid3(NAMESPACE_DNS, self.ifname) + # take the last 48 bits from the UUID string + tmp = str(tmp).split('-')[-1] + # Convert pseudo random string into EUI format which now represents a + # MAC address + tmp = EUI(tmp).value # set locally administered bit in MAC address tmp |= 0xf20000000000 # convert integer to "real" MAC address representation -- cgit v1.2.3 From 3f6ae12908f54222f2f79a87bed51f71e2fbac87 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Mon, 20 Sep 2021 21:55:43 +0200 Subject: ifconfig: T2104: cleanup IPv6 EUI-64 handling in update() --- python/vyos/ifconfig/interface.py | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/python/vyos/ifconfig/interface.py b/python/vyos/ifconfig/interface.py index 0256ca5df..8857f30e9 100755 --- a/python/vyos/ifconfig/interface.py +++ b/python/vyos/ifconfig/interface.py @@ -1482,16 +1482,11 @@ class Interface(Control): self.set_mtu(config.get('mtu')) # Delete old IPv6 EUI64 addresses before changing MAC - tmp = dict_search('ipv6.address.eui64_old', config) - if tmp: - for addr in tmp: - self.del_ipv6_eui64_address(addr) + for addr in (dict_search('ipv6.address.eui64_old', config) or []): + self.del_ipv6_eui64_address(addr) # Manage IPv6 link-local addresses - tmp = dict_search('ipv6.address.no_default_link_local', config) - # we must check explicitly for None type as if the key is set we will - # get an empty dict () - if isinstance(tmp, dict): + if dict_search('ipv6.address.no_default_link_local', config) != None: self.del_ipv6_eui64_address('fe80::/64') else: self.add_ipv6_eui64_address('fe80::/64') -- cgit v1.2.3 From 61aed576582cedc38cafa758651f1446d4ba4d34 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Mon, 20 Sep 2021 21:56:20 +0200 Subject: smoketest: use assertNotIn() in base interface test --- smoketest/scripts/cli/base_interfaces_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/smoketest/scripts/cli/base_interfaces_test.py b/smoketest/scripts/cli/base_interfaces_test.py index 6a5b9c4ee..8a84199d9 100644 --- a/smoketest/scripts/cli/base_interfaces_test.py +++ b/smoketest/scripts/cli/base_interfaces_test.py @@ -214,7 +214,7 @@ class BasicInterfaceTest: self.cli_commit() for interface in self._interfaces: - self.assertTrue(AF_INET6 not in ifaddresses(interface)) + self.assertNotIn(AF_INET6, ifaddresses(interface)) def test_interface_mtu(self): if not self._test_mtu: -- cgit v1.2.3 From 1b269e0798f294b9b893190a14f5dc48e65207c5 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Tue, 21 Sep 2021 08:15:35 +0200 Subject: smoketest: ipsec: T1441: adjust to latest VTI/XFRM interface changes Commit d768aee9 ("ipsec: T1441: Clean up vti-up-down script for XFRM interfaces") removed a parameter from the updown scripts which is no longer necessary as XFRM interfaces are superior to VTI interfaces b/c they use dynamic endpoints by default. --- smoketest/scripts/cli/test_vpn_ipsec.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/smoketest/scripts/cli/test_vpn_ipsec.py b/smoketest/scripts/cli/test_vpn_ipsec.py index 1747c1cfc..93569c4ec 100755 --- a/smoketest/scripts/cli/test_vpn_ipsec.py +++ b/smoketest/scripts/cli/test_vpn_ipsec.py @@ -243,7 +243,7 @@ class TestVPNIPsec(VyOSUnitTestSHIM.TestCase): f'remote_ts = 172.17.10.0/24,172.17.11.0/24', f'if_id_in = {if_id}', # will be 11 for vti10 - shifted by one f'if_id_out = {if_id}', - f'updown = "/etc/ipsec.d/vti-up-down {vti} no"' + f'updown = "/etc/ipsec.d/vti-up-down {vti}"' ] for line in swanctl_conf_lines: self.assertIn(line, swanctl_conf) @@ -372,7 +372,7 @@ class TestVPNIPsec(VyOSUnitTestSHIM.TestCase): f'remote_addrs = {peer_ip}', f'local_ts = 0.0.0.0/0,::/0', f'remote_ts = 0.0.0.0/0,::/0', - f'updown = "/etc/ipsec.d/vti-up-down {vti} no"', + f'updown = "/etc/ipsec.d/vti-up-down {vti}"', f'if_id_in = {if_id}', # will be 11 for vti10 f'if_id_out = {if_id}', f'ipcomp = no', -- cgit v1.2.3 From 7a5edd23864f5eb4d8c40e4e35212d88796656e1 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Tue, 21 Sep 2021 08:17:25 +0200 Subject: xml: ospf: fix routing-passive-interface-xml.i include Commit a8b2e52148d ("xml: Update routing-passive-interface-xml.i file extension to standard .xml.i") only altered the RIP include statement but did not alter the OSPF include. --- interface-definitions/include/ospf/protocol-common-config.xml.i | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface-definitions/include/ospf/protocol-common-config.xml.i b/interface-definitions/include/ospf/protocol-common-config.xml.i index d8556ebf5..0139296ec 100644 --- a/interface-definitions/include/ospf/protocol-common-config.xml.i +++ b/interface-definitions/include/ospf/protocol-common-config.xml.i @@ -606,7 +606,7 @@ #include -#include +#include Interface to exclude when using 'passive-interface default' -- cgit v1.2.3 From 689d1824d251ea9fbd81bf0c941dbd36e33ef420 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Tue, 21 Sep 2021 20:01:21 +0200 Subject: dhcp-server: T3839: support domain-search and ntp-server config per shared-network --- data/templates/dhcp-server/dhcpd.conf.tmpl | 12 +++++++++--- interface-definitions/dhcp-server.xml.in | 21 ++++++--------------- interface-definitions/include/dhcp/ntp-server.xml.i | 15 +++++++++++++++ 3 files changed, 30 insertions(+), 18 deletions(-) create mode 100644 interface-definitions/include/dhcp/ntp-server.xml.i diff --git a/data/templates/dhcp-server/dhcpd.conf.tmpl b/data/templates/dhcp-server/dhcpd.conf.tmpl index d774b4827..003c585dd 100644 --- a/data/templates/dhcp-server/dhcpd.conf.tmpl +++ b/data/templates/dhcp-server/dhcpd.conf.tmpl @@ -72,6 +72,12 @@ shared-network {{ network | replace('_','-') }} { {% if network_config.domain_name is defined and network_config.domain_name is not none %} option domain-name "{{ network_config.domain_name }}"; {% endif %} +{% if network_config.domain_search is defined and network_config.domain_search is not none %} + option domain-search "{{ network_config.domain_search | join('", "') }}"; +{% endif %} +{% if network_config.ntp_server is defined and network_config.ntp_server is not none %} + option ntp-servers {{ network_config.ntp_server | join(', ') }}; +{% endif %} {% if network_config.ping_check is defined %} ping-check true; {% endif %} @@ -88,6 +94,9 @@ shared-network {{ network | replace('_','-') }} { {% if subnet_config.name_server is defined and subnet_config.name_server is not none %} option domain-name-servers {{ subnet_config.name_server | join(', ') }}; {% endif %} +{% if subnet_config.domain_name is defined and subnet_config.domain_name is not none %} + option domain-name "{{ subnet_config.domain_name }}"; +{% endif %} {% if subnet_config.domain_search is defined and subnet_config.domain_search is not none %} option domain-search "{{ subnet_config.domain_search | join('", "') }}"; {% endif %} @@ -129,9 +138,6 @@ shared-network {{ network | replace('_','-') }} { {% if subnet_config.server_identifier is defined and subnet_config.server_identifier is not none %} option dhcp-server-identifier {{ subnet_config.server_identifier }}; {% endif %} -{% if subnet_config.domain_name is defined and subnet_config.domain_name is not none %} - option domain-name "{{ subnet_config.domain_name }}"; -{% endif %} {% if subnet_config.subnet_parameters is defined and subnet_config.subnet_parameters is not none %} # The following {{ subnet_config.subnet_parameters | length }} line(s) were added as # subnet-parameters in the CLI and have not been validated!!! diff --git a/interface-definitions/dhcp-server.xml.in b/interface-definitions/dhcp-server.xml.in index 2707ce96d..47bdc4db1 100644 --- a/interface-definitions/dhcp-server.xml.in +++ b/interface-definitions/dhcp-server.xml.in @@ -103,10 +103,12 @@ #include + #include + #include #include - #include #include #include + #include Additional shared-network parameters for DHCP server. @@ -165,9 +167,10 @@ - #include #include #include + #include + #include Enable DHCP failover support for this subnet @@ -207,19 +210,7 @@ 86400 - - - IP address of NTP server - - ipv4 - NTP server IPv4 address - - - - - - - + #include #include diff --git a/interface-definitions/include/dhcp/ntp-server.xml.i b/interface-definitions/include/dhcp/ntp-server.xml.i new file mode 100644 index 000000000..32d8207e5 --- /dev/null +++ b/interface-definitions/include/dhcp/ntp-server.xml.i @@ -0,0 +1,15 @@ + + + + IP address of NTP server + + ipv4 + NTP server IPv4 address + + + + + + + + -- cgit v1.2.3 From c1ac0630cfe0ee65569fbe435cc006ade20fed22 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Tue, 21 Sep 2021 20:05:52 +0200 Subject: vrrp: keepalived: T2720: adjust to Jinja2 trim_blocks feature This is a successor to commit a2ac9fac16e ("vyos.template: T2720: always enable Jinja2 trim_blocks feature"). It only shifts the whitespaces / indents inside the keepalived configuration file. --- data/templates/vrrp/keepalived.conf.tmpl | 111 ++++++++++++++----------------- src/conf_mode/vrrp.py | 7 +- 2 files changed, 56 insertions(+), 62 deletions(-) diff --git a/data/templates/vrrp/keepalived.conf.tmpl b/data/templates/vrrp/keepalived.conf.tmpl index c01101d85..2e2f62ae7 100644 --- a/data/templates/vrrp/keepalived.conf.tmpl +++ b/data/templates/vrrp/keepalived.conf.tmpl @@ -10,8 +10,7 @@ global_defs { } {% for group in groups %} - -{% if group.health_check_script %} +{% if group.health_check_script is defined and group.health_check_script is not none %} vrrp_script healthcheck_{{ group.name }} { script "{{ group.health_check_script }}" interval {{ group.health_check_interval }} @@ -19,88 +18,78 @@ vrrp_script healthcheck_{{ group.name }} { rise 1 } -{% endif %} +{% endif %} vrrp_instance {{ group.name }} { - {% if group.description %} - # {{ group.description }} - {% endif %} - + {{ '# ' ~ group.description if group.description is defined }} state BACKUP interface {{ group.interface }} virtual_router_id {{ group.vrid }} priority {{ group.priority }} advert_int {{ group.advertise_interval }} - - {% if group.preempt %} +{% if group.preempt is defined and group.preempt is not none %} preempt_delay {{ group.preempt_delay }} - {% else %} +{% else %} nopreempt - {% endif %} - - {% if group.peer_address %} +{% endif %} +{% if group.peer_address is defined and group.peer_address is not none %} unicast_peer { {{ group.peer_address }} } - {% endif %} - - {% if group.hello_source %} - {% if group.peer_address %} - unicast_src_ip {{ group.hello_source }} - {% else %} - mcast_src_ip {{ group.hello_source }} - {% endif %} - {% endif %} - - {% if group.use_vmac and group.peer_address %} - use_vmac {{group.interface}}v{{group.vrid}} - vmac_xmit_base - {% elif group.use_vmac %} - use_vmac {{group.interface}}v{{group.vrid}} - {% endif %} - - {% if group.auth_password %} - authentication { +{% endif %} +{% if group.hello_source is defined and group.hello_source is not none %} +{% if group.peer_address is defined and group.peer_address is not none %} + unicast_src_ip {{ group.hello_source }} +{% else %} + mcast_src_ip {{ group.hello_source }} +{% endif %} +{% endif %} +{% if group.use_vmac is defined and group.peer_address is defined %} + use_vmac {{ group.interface }}v{{ group.vrid }} + vmac_xmit_base +{% elif group.use_vmac is defined %} + use_vmac {{ group.interface }}v{{ group.vrid }} +{% endif %} +{% if group.auth_password is defined and group.auth_password is not none %} + authentication { auth_pass "{{ group.auth_password }}" auth_type {{ group.auth_type }} - } - {% endif %} - + } +{% endif %} +{% if group.virtual_addresses is defined and group.virtual_addresses is not none %} virtual_ipaddress { - {% for addr in group.virtual_addresses %} +{% for addr in group.virtual_addresses %} {{ addr }} - {% endfor %} +{% endfor %} } - - {% if group.virtual_addresses_excluded %} +{% endif %} +{% if group.virtual_addresses_excluded is defined and group.virtual_addresses_excluded is not none %} virtual_ipaddress_excluded { - {% for addr in group.virtual_addresses_excluded %} +{% for addr in group.virtual_addresses_excluded %} {{ addr }} - {% endfor %} +{% endfor %} } - {% endif %} - - {% if group.health_check_script %} +{% endif %} +{% if group.health_check_script is defined and group.health_check_script is not none %} track_script { healthcheck_{{ group.name }} } - {% endif %} +{% endif %} } - {% endfor %} -{% for sync_group in sync_groups %} +{% if sync_groups is defined and sync_groups is not none %} +{% for sync_group in sync_groups %} vrrp_sync_group {{ sync_group.name }} { - group { - {% for member in sync_group.members %} - {{ member }} - {% endfor %} - } - - {% if sync_group.conntrack_sync %} - {% set vyos_helper = "/usr/libexec/vyos/vyos-vrrp-conntracksync.sh" %} - notify_master "{{ vyos_helper }} master {{ sync_group.name }}" - notify_backup "{{ vyos_helper }} backup {{ sync_group.name }}" - notify_fault "{{ vyos_helper }} fault {{ sync_group.name }}" - {% endif %} + group { +{% for member in sync_group.members %} + {{ member }} +{% endfor %} + } +{% if sync_group.conntrack_sync %} +{% set vyos_helper = "/usr/libexec/vyos/vyos-vrrp-conntracksync.sh" %} + notify_master "{{ vyos_helper }} master {{ sync_group.name }}" + notify_backup "{{ vyos_helper }} backup {{ sync_group.name }}" + notify_fault "{{ vyos_helper }} fault {{ sync_group.name }}" +{% endif %} } - -{% endfor %} +{% endfor %} +{% endif %} diff --git a/src/conf_mode/vrrp.py b/src/conf_mode/vrrp.py index 680a80859..2ece792dc 100755 --- a/src/conf_mode/vrrp.py +++ b/src/conf_mode/vrrp.py @@ -17,7 +17,12 @@ import os from sys import exit -from ipaddress import ip_address, ip_interface, IPv4Interface, IPv6Interface, IPv4Address, IPv6Address +from ipaddress import ip_address +from ipaddress import ip_interface +from ipaddress import IPv4Interface +from ipaddress import IPv6Interface +from ipaddress import IPv4Address +from ipaddress import IPv6Address from json import dumps from pathlib import Path -- cgit v1.2.3 From 6541bdbe792a3cc420f0367e673f27763528376c Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Tue, 21 Sep 2021 20:05:59 +0200 Subject: xml: vrrp: T616: add missing valueHelp for "authentication type" --- interface-definitions/vrrp.xml.in | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/interface-definitions/vrrp.xml.in b/interface-definitions/vrrp.xml.in index 7bbe25347..69a37239c 100644 --- a/interface-definitions/vrrp.xml.in +++ b/interface-definitions/vrrp.xml.in @@ -60,6 +60,14 @@ plaintext-password ah + + plaintext-password + Simple password string + + + ah + AH - IPSEC (not recommended) + ^(plaintext-password|ah)$ -- cgit v1.2.3 From ddda0d66faa73900ed2b8fec1dde38ffc4a49fcd Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Tue, 21 Sep 2021 20:06:23 +0200 Subject: smoketest: vrrp: T616: add basic smoketest to verify keepalived configuration --- smoketest/scripts/cli/test_ha_vrrp.py | 167 ++++++++++++++++++++++++++++++++++ 1 file changed, 167 insertions(+) create mode 100755 smoketest/scripts/cli/test_ha_vrrp.py diff --git a/smoketest/scripts/cli/test_ha_vrrp.py b/smoketest/scripts/cli/test_ha_vrrp.py new file mode 100755 index 000000000..03618c7d8 --- /dev/null +++ b/smoketest/scripts/cli/test_ha_vrrp.py @@ -0,0 +1,167 @@ +#!/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 +import re +import unittest + +from base_vyostest_shim import VyOSUnitTestSHIM + +from vyos.configsession import ConfigSession +from vyos.configsession import ConfigSessionError +from vyos.util import cmd +from vyos.util import process_named_running +from vyos.util import read_file + +from vyos.template import inc_ip + +PROCESS_NAME = 'keepalived' +KEEPALIVED_CONF = '/etc/keepalived/keepalived.conf' +base_path = ['high-availability', 'vrrp'] + +vrrp_interface = 'eth1' +groups = ['VLAN77', 'VLAN78', 'VLAN201'] + +def getConfig(string, end='}'): + command = f'cat {KEEPALIVED_CONF} | sed -n "/^{string}/,/^{end}/p"' + out = cmd(command) + return out + +class TestVRRP(VyOSUnitTestSHIM.TestCase): + def tearDown(self): + # Check for running process + self.assertTrue(process_named_running(PROCESS_NAME)) + + self.cli_delete(base_path) + self.cli_commit() + + # Process must be terminated after deleting the config + self.assertFalse(process_named_running(PROCESS_NAME)) + + def test_01_default_values(self): + for group in groups: + vlan_id = group.lstrip('VLAN') + vip = f'100.64.{vlan_id}.1/24' + group_base = base_path + ['group', group] + + self.cli_set(['interfaces', 'ethernet', vrrp_interface, 'vif', vlan_id, 'address', inc_ip(vip, 1) + '/' + vip.split('/')[-1]]) + + self.cli_set(group_base + ['description', group]) + self.cli_set(group_base + ['interface', f'{vrrp_interface}.{vlan_id}']) + self.cli_set(group_base + ['virtual-address', vip]) + self.cli_set(group_base + ['vrid', vlan_id]) + + # commit changes + self.cli_commit() + + for group in groups: + vlan_id = group.lstrip('VLAN') + vip = f'100.64.{vlan_id}.1/24' + + config = getConfig(f'vrrp_instance {group}') + + self.assertIn(f'# {group}', config) + self.assertIn(f'interface {vrrp_interface}.{vlan_id}', config) + self.assertIn(f'virtual_router_id {vlan_id}', config) + self.assertIn(f'priority 100', config) # default value + self.assertIn(f'advert_int 1', config) # default value + self.assertIn(f'preempt_delay 0', config) # default value + self.assertIn(f' {vip}', config) + + def test_02_simple_options(self): + advertise_interval = '77' + priority = '123' + preempt_delay = '400' + + for group in groups: + vlan_id = group.lstrip('VLAN') + vip = f'100.64.{vlan_id}.1/24' + group_base = base_path + ['group', group] + + self.cli_set(['interfaces', 'ethernet', vrrp_interface, 'vif', vlan_id, 'address', inc_ip(vip, 1) + '/' + vip.split('/')[-1]]) + + self.cli_set(group_base + ['description', group]) + self.cli_set(group_base + ['interface', f'{vrrp_interface}.{vlan_id}']) + self.cli_set(group_base + ['virtual-address', vip]) + self.cli_set(group_base + ['vrid', vlan_id]) + + self.cli_set(group_base + ['advertise-interval', advertise_interval]) + self.cli_set(group_base + ['priority', priority]) + self.cli_set(group_base + ['preempt-delay', preempt_delay]) + + self.cli_set(group_base + ['rfc3768-compatibility']) + + # Authentication + self.cli_set(group_base + ['authentication', 'type', 'plaintext-password']) + self.cli_set(group_base + ['authentication', 'password', f'vyos-{group}']) + + # commit changes + self.cli_commit() + + for group in groups: + vlan_id = group.lstrip('VLAN') + vip = f'100.64.{vlan_id}.1/24' + + config = getConfig(f'vrrp_instance {group}') + self.assertIn(f'# {group}', config) + self.assertIn(f'state BACKUP', config) + self.assertIn(f'interface {vrrp_interface}.{vlan_id}', config) + self.assertIn(f'virtual_router_id {vlan_id}', config) + self.assertIn(f'priority {priority}', config) + self.assertIn(f'advert_int {advertise_interval}', config) + self.assertIn(f'preempt_delay {preempt_delay}', config) + self.assertIn(f'use_vmac {vrrp_interface}.{vlan_id}v{vlan_id}', config) + self.assertIn(f' {vip}', config) + + # Authentication + self.assertIn(f'auth_pass "vyos-{group}"', config) + self.assertIn(f'auth_type PASS', config) + + def test_03_sync_group(self): + sync_group = 'VyOS' + + for group in groups: + vlan_id = group.lstrip('VLAN') + vip = f'100.64.{vlan_id}.1/24' + group_base = base_path + ['group', group] + + self.cli_set(['interfaces', 'ethernet', vrrp_interface, 'vif', vlan_id, 'address', inc_ip(vip, 1) + '/' + vip.split('/')[-1]]) + + self.cli_set(group_base + ['interface', f'{vrrp_interface}.{vlan_id}']) + self.cli_set(group_base + ['virtual-address', vip]) + self.cli_set(group_base + ['vrid', vlan_id]) + + self.cli_set(base_path + ['sync-group', sync_group, 'member', group]) + + # commit changes + self.cli_commit() + + for group in groups: + vlan_id = group.lstrip('VLAN') + vip = f'100.64.{vlan_id}.1/24' + config = getConfig(f'vrrp_instance {group}') + + self.assertIn(f'interface {vrrp_interface}.{vlan_id}', config) + self.assertIn(f'virtual_router_id {vlan_id}', config) + self.assertIn(f' {vip}', config) + + config = getConfig(f'vrrp_sync_group {sync_group}') + self.assertIn(r'group {', config) + for group in groups: + self.assertIn(f'{group}', config) + +if __name__ == '__main__': + unittest.main(verbosity=2, failfast=True) -- cgit v1.2.3 From b243795eba1b36cadd81c3149e833bdf5c5bea70 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Tue, 21 Sep 2021 20:29:36 +0200 Subject: vrrp: keepalived: T616: move configuration to volatile /run directory Move keepalived configuration from /etc/keepalived to /run/keepalived. --- data/templates/vrrp/keepalived.conf.tmpl | 3 +-- python/vyos/ifconfig/vrrp.py | 8 ++++---- smoketest/scripts/cli/test_ha_vrrp.py | 6 ++---- src/conf_mode/vrrp.py | 5 ++++- .../systemd/system/keepalived.service.d/override.conf | 10 ++++++++++ src/system/keepalived-fifo.py | 16 ++++++++-------- 6 files changed, 29 insertions(+), 19 deletions(-) diff --git a/data/templates/vrrp/keepalived.conf.tmpl b/data/templates/vrrp/keepalived.conf.tmpl index 2e2f62ae7..2b53b04af 100644 --- a/data/templates/vrrp/keepalived.conf.tmpl +++ b/data/templates/vrrp/keepalived.conf.tmpl @@ -5,7 +5,7 @@ global_defs { dynamic_interfaces script_user root - notify_fifo /run/keepalived_notify_fifo + notify_fifo /run/keepalived/keepalived_notify_fifo notify_fifo_script /usr/libexec/vyos/system/keepalived-fifo.py } @@ -16,7 +16,6 @@ vrrp_script healthcheck_{{ group.name }} { interval {{ group.health_check_interval }} fall {{ group.health_check_count }} rise 1 - } {% endif %} diff --git a/python/vyos/ifconfig/vrrp.py b/python/vyos/ifconfig/vrrp.py index b522cc1ab..481b0284a 100644 --- a/python/vyos/ifconfig/vrrp.py +++ b/python/vyos/ifconfig/vrrp.py @@ -32,14 +32,14 @@ class VRRPNoData(VRRPError): class VRRP(object): _vrrp_prefix = '00:00:5E:00:01:' location = { - 'pid': '/run/keepalived.pid', - 'fifo': '/run/keepalived_notify_fifo', + 'pid': '/run/keepalived/keepalived.pid', + 'fifo': '/run/keepalived/keepalived_notify_fifo', 'state': '/tmp/keepalived.data', 'stats': '/tmp/keepalived.stats', 'json': '/tmp/keepalived.json', 'daemon': '/etc/default/keepalived', - 'config': '/etc/keepalived/keepalived.conf', - 'vyos': '/run/keepalived_config.dict', + 'config': '/run/keepalived/keepalived.conf', + 'vyos': '/run/keepalived/keepalived_config.dict', } _signal = { diff --git a/smoketest/scripts/cli/test_ha_vrrp.py b/smoketest/scripts/cli/test_ha_vrrp.py index 03618c7d8..9c8d26699 100755 --- a/smoketest/scripts/cli/test_ha_vrrp.py +++ b/smoketest/scripts/cli/test_ha_vrrp.py @@ -14,22 +14,20 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -import os -import re import unittest from base_vyostest_shim import VyOSUnitTestSHIM from vyos.configsession import ConfigSession from vyos.configsession import ConfigSessionError +from vyos.ifconfig.vrrp import VRRP from vyos.util import cmd from vyos.util import process_named_running from vyos.util import read_file - from vyos.template import inc_ip PROCESS_NAME = 'keepalived' -KEEPALIVED_CONF = '/etc/keepalived/keepalived.conf' +KEEPALIVED_CONF = VRRP.location['config'] base_path = ['high-availability', 'vrrp'] vrrp_interface = 'eth1' diff --git a/src/conf_mode/vrrp.py b/src/conf_mode/vrrp.py index 2ece792dc..f11dce879 100755 --- a/src/conf_mode/vrrp.py +++ b/src/conf_mode/vrrp.py @@ -30,6 +30,7 @@ import vyos.config from vyos import ConfigError from vyos.util import call +from vyos.util import makedir from vyos.template import render from vyos.ifconfig.vrrp import VRRP @@ -136,7 +137,9 @@ def get_config(config=None): sync_groups.append(sync_group) # create a file with dict with proposed configuration - with open("{}.temp".format(VRRP.location['vyos']), 'w') as dict_file: + dirname = os.path.dirname(VRRP.location['vyos']) + makedir(dirname) + with open(VRRP.location['vyos'] + ".temp", 'w') as dict_file: dict_file.write(dumps({'vrrp_groups': vrrp_groups, 'sync_groups': sync_groups})) return (vrrp_groups, sync_groups) diff --git a/src/etc/systemd/system/keepalived.service.d/override.conf b/src/etc/systemd/system/keepalived.service.d/override.conf index 9fcabf652..e338b90a2 100644 --- a/src/etc/systemd/system/keepalived.service.d/override.conf +++ b/src/etc/systemd/system/keepalived.service.d/override.conf @@ -1,2 +1,12 @@ +[Unit] +ConditionPathExists= +ConditionPathExists=/run/keepalived/keepalived.conf +After= +After=vyos-router.service + [Service] KillMode=process +ExecStart= +ExecStart=/usr/sbin/keepalived --use-file /run/keepalived/keepalived.conf --pid /run/keepalived/keepalived.pid --dont-fork $DAEMON_ARGS +PIDFile= +PIDFile=/run/keepalived/keepalived.pid diff --git a/src/system/keepalived-fifo.py b/src/system/keepalived-fifo.py index 3b4330e9b..159fd0f54 100755 --- a/src/system/keepalived-fifo.py +++ b/src/system/keepalived-fifo.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2020 VyOS maintainers and contributors +# Copyright (C) 2020-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 @@ -13,7 +13,6 @@ # # You should have received a copy of the GNU General Public License # along with this program. If not, see . -# import os import time @@ -22,11 +21,12 @@ import argparse import threading import re import json -from pathlib import Path -from queue import Queue import logging + +from queue import Queue from logging.handlers import SysLogHandler +from vyos.ifconfig.vrrp import VRRP from vyos.util import cmd # configure logging @@ -62,7 +62,7 @@ class KeepalivedFifo: def _config_load(self): try: # read the dictionary file with configuration - with open('/run/keepalived_config.dict', 'r') as dict_file: + with open(VRRP.location['vyos'], 'r') as dict_file: vrrp_config_dict = json.load(dict_file) self.vrrp_config = {'vrrp_groups': {}, 'sync_groups': {}} # save VRRP instances to the new dictionary @@ -95,8 +95,8 @@ class KeepalivedFifo: # create FIFO pipe def pipe_create(self): - if Path(self.pipe_path).exists(): - logger.info("PIPE already exist: {}".format(self.pipe_path)) + if os.path.exists(self.pipe_path): + logger.info(f"PIPE already exist: {self.pipe_path}") else: os.mkfifo(self.pipe_path) @@ -135,7 +135,7 @@ class KeepalivedFifo: if n_type == 'GROUP': if os.path.exists(mdns_running_file): cmd(mdns_update_command) - + if n_name in self.vrrp_config['sync_groups'] and n_state in self.vrrp_config['sync_groups'][n_name]: n_script = self.vrrp_config['sync_groups'][n_name].get(n_state) if n_script: -- cgit v1.2.3 From 590cf0e626f6a5e813ec4f3021c028a5e098e27d Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Tue, 21 Sep 2021 20:29:49 +0200 Subject: vrrp: keepalived: T616: enable script security --- data/templates/vrrp/keepalived.conf.tmpl | 3 +++ 1 file changed, 3 insertions(+) diff --git a/data/templates/vrrp/keepalived.conf.tmpl b/data/templates/vrrp/keepalived.conf.tmpl index 2b53b04af..3696c8395 100644 --- a/data/templates/vrrp/keepalived.conf.tmpl +++ b/data/templates/vrrp/keepalived.conf.tmpl @@ -5,6 +5,9 @@ global_defs { dynamic_interfaces script_user root + # Don't run scripts configured to be run as root if any part of the path + # is writable by a non-root user. + enable_script_security notify_fifo /run/keepalived/keepalived_notify_fifo notify_fifo_script /usr/libexec/vyos/system/keepalived-fifo.py } -- cgit v1.2.3 From 761631d662ccc18827d017d197c0e0100ac36219 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Tue, 21 Sep 2021 20:31:06 +0200 Subject: vrrp: keepalived: T3847: migrate to get_config_dict() --- data/templates/vrrp/daemon.tmpl | 5 - data/templates/vrrp/keepalived.conf.tmpl | 121 ++++---- interface-definitions/vrrp.xml.in | 7 +- python/vyos/ifconfig/vrrp.py | 25 +- src/conf_mode/vrrp.py | 344 +++++++-------------- .../system/keepalived.service.d/override.conf | 3 +- src/system/keepalived-fifo.py | 73 ++--- 7 files changed, 237 insertions(+), 341 deletions(-) delete mode 100644 data/templates/vrrp/daemon.tmpl diff --git a/data/templates/vrrp/daemon.tmpl b/data/templates/vrrp/daemon.tmpl deleted file mode 100644 index c9dbea72d..000000000 --- a/data/templates/vrrp/daemon.tmpl +++ /dev/null @@ -1,5 +0,0 @@ -# Autogenerated by VyOS -# Options to pass to keepalived - -# DAEMON_ARGS are appended to the keepalived command-line -DAEMON_ARGS="--snmp" diff --git a/data/templates/vrrp/keepalived.conf.tmpl b/data/templates/vrrp/keepalived.conf.tmpl index 3696c8395..a1e47abc3 100644 --- a/data/templates/vrrp/keepalived.conf.tmpl +++ b/data/templates/vrrp/keepalived.conf.tmpl @@ -12,85 +12,92 @@ global_defs { notify_fifo_script /usr/libexec/vyos/system/keepalived-fifo.py } -{% for group in groups %} -{% if group.health_check_script is defined and group.health_check_script is not none %} -vrrp_script healthcheck_{{ group.name }} { - script "{{ group.health_check_script }}" - interval {{ group.health_check_interval }} - fall {{ group.health_check_count }} +{% if group is defined and group is not none %} +{% for name, group_config in group.items() if group_config.disable is not defined %} +{% if group_config.health_check is defined and group_config.health_check.script is defined and group_config.health_check.script is not none %} +vrrp_script healthcheck_{{ name }} { + script "{{ group_config.health_check.script }}" + interval {{ group_config.health_check.interval }} + fall {{ group_config.health_check.failure_count }} rise 1 } -{% endif %} - -vrrp_instance {{ group.name }} { - {{ '# ' ~ group.description if group.description is defined }} +{% endif %} +vrrp_instance {{ name }} { + {{ '# ' ~ group_config.description if group_config.description is defined }} state BACKUP - interface {{ group.interface }} - virtual_router_id {{ group.vrid }} - priority {{ group.priority }} - advert_int {{ group.advertise_interval }} -{% if group.preempt is defined and group.preempt is not none %} - preempt_delay {{ group.preempt_delay }} -{% else %} + interface {{ group_config.interface }} + virtual_router_id {{ group_config.vrid }} + priority {{ group_config.priority }} + advert_int {{ group_config.advertise_interval }} +{% if group_config.no_preempt is not defined and group_config.preempt_delay is defined and group_config.preempt_delay is not none %} + preempt_delay {{ group_config.preempt_delay }} +{% elif group_config.no_preempt is defined %} nopreempt -{% endif %} -{% if group.peer_address is defined and group.peer_address is not none %} - unicast_peer { {{ group.peer_address }} } -{% endif %} -{% if group.hello_source is defined and group.hello_source is not none %} -{% if group.peer_address is defined and group.peer_address is not none %} - unicast_src_ip {{ group.hello_source }} -{% else %} - mcast_src_ip {{ group.hello_source }} {% endif %} -{% endif %} -{% if group.use_vmac is defined and group.peer_address is defined %} - use_vmac {{ group.interface }}v{{ group.vrid }} +{% if group_config.peer_address is defined and group_config.peer_address is not none %} + unicast_peer { {{ group_config.peer_address }} } +{% endif %} +{% if group_config.hello_source_address is defined and group_config.hello_source_address is not none %} +{% if group_config.peer_address is defined and group_config.peer_address is not none %} + unicast_src_ip {{ group_config.hello_source_address }} +{% else %} + mcast_src_ip {{ group_config.hello_source_address }} +{% endif %} +{% endif %} +{% if group_config.rfc3768_compatibility is defined and group_config.peer_address is defined %} + use_vmac {{ group_config.interface }}v{{ group_config.vrid }} vmac_xmit_base -{% elif group.use_vmac is defined %} - use_vmac {{ group.interface }}v{{ group.vrid }} -{% endif %} -{% if group.auth_password is defined and group.auth_password is not none %} +{% elif group_config.rfc3768_compatibility is defined %} + use_vmac {{ group_config.interface }}v{{ group_config.vrid }} +{% endif %} +{% if group_config.authentication is defined and group_config.authentication is not none %} authentication { - auth_pass "{{ group.auth_password }}" - auth_type {{ group.auth_type }} + auth_pass "{{ group_config.authentication.password }}" +{% if group_config.authentication.type == 'plaintext-password' %} + auth_type PASS +{% else %} + auth_type {{ group_config.authentication.type | upper }} +{% endif %} } -{% endif %} -{% if group.virtual_addresses is defined and group.virtual_addresses is not none %} +{% endif %} +{% if group_config.virtual_address is defined and group_config.virtual_address is not none %} virtual_ipaddress { -{% for addr in group.virtual_addresses %} +{% for addr in group_config.virtual_address %} {{ addr }} -{% endfor %} +{% endfor %} } -{% endif %} -{% if group.virtual_addresses_excluded is defined and group.virtual_addresses_excluded is not none %} +{% endif %} +{% if group_config.virtual_address_excluded is defined and group_config.virtual_address_excluded is not none %} virtual_ipaddress_excluded { -{% for addr in group.virtual_addresses_excluded %} +{% for addr in group_config.virtual_address_excluded %} {{ addr }} -{% endfor %} +{% endfor %} } -{% endif %} -{% if group.health_check_script is defined and group.health_check_script is not none %} +{% endif %} +{% if group_config.health_check is defined and group_config.health_check.script is defined and group_config.health_check.script is not none %} track_script { - healthcheck_{{ group.name }} + healthcheck_{{ name }} } -{% endif %} +{% endif %} } -{% endfor %} +{% endfor %} +{% endif %} -{% if sync_groups is defined and sync_groups is not none %} -{% for sync_group in sync_groups %} -vrrp_sync_group {{ sync_group.name }} { +{% if sync_group is defined and sync_group is not none %} +{% for name, group_config in sync_group.items() if group_config.disable is not defined %} +vrrp_sync_group {{ name }} { group { -{% for member in sync_group.members %} +{% if group_config.member is defined and group_config.member is not none %} +{% for member in group_config.member %} {{ member }} -{% endfor %} +{% endfor %} +{% endif %} } -{% if sync_group.conntrack_sync %} +{% if conntrack_sync_group is defined and conntrack_sync_group == name %} {% set vyos_helper = "/usr/libexec/vyos/vyos-vrrp-conntracksync.sh" %} - notify_master "{{ vyos_helper }} master {{ sync_group.name }}" - notify_backup "{{ vyos_helper }} backup {{ sync_group.name }}" - notify_fault "{{ vyos_helper }} fault {{ sync_group.name }}" + notify_master "{{ vyos_helper }} master {{ name }}" + notify_backup "{{ vyos_helper }} backup {{ name }}" + notify_fault "{{ vyos_helper }} fault {{ name }}" {% endif %} } {% endfor %} diff --git a/interface-definitions/vrrp.xml.in b/interface-definitions/vrrp.xml.in index 69a37239c..d57d213d9 100644 --- a/interface-definitions/vrrp.xml.in +++ b/interface-definitions/vrrp.xml.in @@ -35,6 +35,7 @@ + 1 @@ -94,6 +95,7 @@ + 3 @@ -102,6 +104,7 @@ + 60 @@ -164,6 +167,7 @@ + 0 @@ -176,11 +180,12 @@ + 100 - Use VRRP virtual MAC address as per RFC3768 + diff --git a/python/vyos/ifconfig/vrrp.py b/python/vyos/ifconfig/vrrp.py index 481b0284a..47aaadecd 100644 --- a/python/vyos/ifconfig/vrrp.py +++ b/python/vyos/ifconfig/vrrp.py @@ -22,6 +22,7 @@ from time import sleep from tabulate import tabulate from vyos import util +from vyos.configquery import ConfigTreeQuery class VRRPError(Exception): pass @@ -39,7 +40,6 @@ class VRRP(object): 'json': '/tmp/keepalived.json', 'daemon': '/etc/default/keepalived', 'config': '/run/keepalived/keepalived.conf', - 'vyos': '/run/keepalived/keepalived_config.dict', } _signal = { @@ -111,17 +111,20 @@ class VRRP(object): @classmethod def disabled(cls): - if not os.path.exists(cls.location['vyos']): - return [] - disabled = [] - config = json.loads(util.read_file(cls.location['vyos'])) - - # add disabled groups to the list - for group in config['vrrp_groups']: - if group['disable']: - disabled.append( - [group['name'], group['interface'], group['vrid'], 'DISABLED', '']) + base = ['high-availability', 'vrrp'] + conf = ConfigTreeQuery() + if conf.exists(base): + # Read VRRP configuration directly from CLI + vrrp_config_dict = conf.get_config_dict(base, key_mangling=('-', '_'), + get_first_key=True) + + # add disabled groups to the list + if 'group' in vrrp_config_dict: + for group, group_config in vrrp_config_dict['group'].items(): + if 'disable' not in group_config: + continue + disabled.append([group, group_config['interface'], group_config['vrid'], 'DISABLED', '']) # return list with disabled instances return disabled diff --git a/src/conf_mode/vrrp.py b/src/conf_mode/vrrp.py index f11dce879..8bb237102 100755 --- a/src/conf_mode/vrrp.py +++ b/src/conf_mode/vrrp.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2018-2020 VyOS maintainers and contributors +# Copyright (C) 2018-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 @@ -17,252 +17,136 @@ import os from sys import exit -from ipaddress import ip_address from ipaddress import ip_interface from ipaddress import IPv4Interface from ipaddress import IPv6Interface -from ipaddress import IPv4Address -from ipaddress import IPv6Address -from json import dumps -from pathlib import Path - -import vyos.config - -from vyos import ConfigError -from vyos.util import call -from vyos.util import makedir -from vyos.template import render +from vyos.config import Config +from vyos.configdict import dict_merge from vyos.ifconfig.vrrp import VRRP - +from vyos.template import render +from vyos.template import is_ipv4 +from vyos.template import is_ipv6 +from vyos.util import call +from vyos.xml import defaults +from vyos import ConfigError from vyos import airbag airbag.enable() def get_config(config=None): - vrrp_groups = [] - sync_groups = [] - if config: - config = config + conf = config else: - config = vyos.config.Config() - - # Get the VRRP groups - for group_name in config.list_nodes("high-availability vrrp group"): - config.set_level("high-availability vrrp group {0}".format(group_name)) - - # Retrieve the values - group = {"preempt": True, "use_vmac": False, "disable": False} - - if config.exists("disable"): - group["disable"] = True - - group["name"] = group_name - group["vrid"] = config.return_value("vrid") - group["interface"] = config.return_value("interface") - group["description"] = config.return_value("description") - group["advertise_interval"] = config.return_value("advertise-interval") - group["priority"] = config.return_value("priority") - group["hello_source"] = config.return_value("hello-source-address") - group["peer_address"] = config.return_value("peer-address") - group["sync_group"] = config.return_value("sync-group") - group["preempt_delay"] = config.return_value("preempt-delay") - group["virtual_addresses"] = config.return_values("virtual-address") - group["virtual_addresses_excluded"] = config.return_values("virtual-address-excluded") - - group["auth_password"] = config.return_value("authentication password") - group["auth_type"] = config.return_value("authentication type") - - group["health_check_script"] = config.return_value("health-check script") - group["health_check_interval"] = config.return_value("health-check interval") - group["health_check_count"] = config.return_value("health-check failure-count") - - group["master_script"] = config.return_value("transition-script master") - group["backup_script"] = config.return_value("transition-script backup") - group["fault_script"] = config.return_value("transition-script fault") - group["stop_script"] = config.return_value("transition-script stop") - group["script_mode_force"] = config.exists("transition-script mode-force") - - if config.exists("no-preempt"): - group["preempt"] = False - if config.exists("rfc3768-compatibility"): - group["use_vmac"] = True - - # Substitute defaults where applicable - if not group["advertise_interval"]: - group["advertise_interval"] = 1 - if not group["priority"]: - group["priority"] = 100 - if not group["preempt_delay"]: - group["preempt_delay"] = 0 - if not group["health_check_interval"]: - group["health_check_interval"] = 60 - if not group["health_check_count"]: - group["health_check_count"] = 3 - - # FIXUP: translate our option for auth type to keepalived's syntax - # for simplicity - if group["auth_type"]: - if group["auth_type"] == "plaintext-password": - group["auth_type"] = "PASS" - else: - group["auth_type"] = "AH" - - vrrp_groups.append(group) - - config.set_level("") - - # Get the sync group used for conntrack-sync - conntrack_sync_group = None - if config.exists("service conntrack-sync failover-mechanism vrrp"): - conntrack_sync_group = config.return_value("service conntrack-sync failover-mechanism vrrp sync-group") - - # Get the sync groups - for sync_group_name in config.list_nodes("high-availability vrrp sync-group"): - config.set_level("high-availability vrrp sync-group {0}".format(sync_group_name)) - - sync_group = {"conntrack_sync": False} - sync_group["name"] = sync_group_name - sync_group["members"] = config.return_values("member") - if conntrack_sync_group: - if conntrack_sync_group == sync_group_name: - sync_group["conntrack_sync"] = True - - # add transition script configuration - sync_group["master_script"] = config.return_value("transition-script master") - sync_group["backup_script"] = config.return_value("transition-script backup") - sync_group["fault_script"] = config.return_value("transition-script fault") - sync_group["stop_script"] = config.return_value("transition-script stop") - - sync_groups.append(sync_group) - - # create a file with dict with proposed configuration - dirname = os.path.dirname(VRRP.location['vyos']) - makedir(dirname) - with open(VRRP.location['vyos'] + ".temp", 'w') as dict_file: - dict_file.write(dumps({'vrrp_groups': vrrp_groups, 'sync_groups': sync_groups})) - - return (vrrp_groups, sync_groups) - - -def verify(data): - vrrp_groups, sync_groups = data - - for group in vrrp_groups: - # Check required fields - if not group["vrid"]: - raise ConfigError("vrid is required but not set in VRRP group {0}".format(group["name"])) - if not group["interface"]: - raise ConfigError("interface is required but not set in VRRP group {0}".format(group["name"])) - if not group["virtual_addresses"]: - raise ConfigError("virtual-address is required but not set in VRRP group {0}".format(group["name"])) - - if group["auth_password"] and (not group["auth_type"]): - raise ConfigError("authentication type is required but not set in VRRP group {0}".format(group["name"])) - - # Keepalived doesn't allow mixing IPv4 and IPv6 in one group, so we mirror that restriction - - # XXX: filter on map object is destructive, so we force it to list. - # Additionally, filter objects always evaluate to True, empty or not, - # so we force them to lists as well. - vaddrs = list(map(lambda i: ip_interface(i), group["virtual_addresses"])) - vaddrs4 = list(filter(lambda x: isinstance(x, IPv4Interface), vaddrs)) - vaddrs6 = list(filter(lambda x: isinstance(x, IPv6Interface), vaddrs)) - - if vaddrs4 and vaddrs6: - raise ConfigError("VRRP group {0} mixes IPv4 and IPv6 virtual addresses, this is not allowed. Create separate groups for IPv4 and IPv6".format(group["name"])) - - if vaddrs4: - if group["hello_source"]: - hsa = ip_address(group["hello_source"]) - if isinstance(hsa, IPv6Address): - raise ConfigError("VRRP group {0} uses IPv4 but its hello-source-address is IPv6".format(group["name"])) - if group["peer_address"]: - pa = ip_address(group["peer_address"]) - if isinstance(pa, IPv6Address): - raise ConfigError("VRRP group {0} uses IPv4 but its peer-address is IPv6".format(group["name"])) - - if vaddrs6: - if group["hello_source"]: - hsa = ip_address(group["hello_source"]) - if isinstance(hsa, IPv4Address): - raise ConfigError("VRRP group {0} uses IPv6 but its hello-source-address is IPv4".format(group["name"])) - if group["peer_address"]: - pa = ip_address(group["peer_address"]) - if isinstance(pa, IPv4Address): - raise ConfigError("VRRP group {0} uses IPv6 but its peer-address is IPv4".format(group["name"])) - - # Warn the user about the deprecated mode-force option - if group['script_mode_force']: - print("""Warning: "transition-script mode-force" VRRP option is deprecated and will be removed in VyOS 1.4.""") - print("""It's no longer necessary, so you can safely remove it from your config now.""") - - # Disallow same VRID on multiple interfaces - _groups = sorted(vrrp_groups, key=(lambda x: x["interface"])) - count = len(_groups) - 1 - index = 0 - while (index < count): - if (_groups[index]["vrid"] == _groups[index + 1]["vrid"]) and (_groups[index]["interface"] == _groups[index + 1]["interface"]): - raise ConfigError("VRID {0} is used in groups {1} and {2} that both use interface {3}. Groups on the same interface must use different VRIDs".format( - _groups[index]["vrid"], _groups[index]["name"], _groups[index + 1]["name"], _groups[index]["interface"])) - else: - index += 1 + conf = Config() + + base = ['high-availability', 'vrrp'] + if not conf.exists(base): + return None + + vrrp = conf.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True) + # We have gathered the dict representation of the CLI, but there are default + # options which we need to update into the dictionary retrived. + if 'group' in vrrp: + default_values = defaults(base + ['group']) + for group in vrrp['group']: + vrrp['group'][group] = dict_merge(default_values, vrrp['group'][group]) + + ## Get the sync group used for conntrack-sync + conntrack_path = ['service', 'conntrack-sync', 'failover-mechanism', 'vrrp', 'sync-group'] + if conf.exists(conntrack_path): + vrrp['conntrack_sync_group'] = conf.return_value(conntrack_path) + + return vrrp + +def verify(vrrp): + if not vrrp: + return None + + used_vrid_if = [] + if 'group' in vrrp: + for group, group_config in vrrp['group'].items(): + # Check required fields + if 'vrid' not in group_config: + raise ConfigError(f'VRID is required but not set in VRRP group "{group}"') + + if 'interface' not in group_config: + raise ConfigError(f'Interface is required but not set in VRRP group "{group}"') + + if 'virtual_address' not in group_config: + raise ConfigError(f'virtual-address is required but not set in VRRP group "{group}"') + + if 'authentication' in group_config: + if not {'password', 'type'} <= set(group_config['authentication']): + raise ConfigError(f'Authentication requires both type and passwortd to be set in VRRP group "{group}"') + + # We can not use a VRID once per interface + interface = group_config['interface'] + vrid = group_config['vrid'] + tmp = {'interface': interface, 'vrid': vrid} + if tmp in used_vrid_if: + raise ConfigError(f'VRID "{vrid}" can only be used once on interface "{interface}"!') + used_vrid_if.append(tmp) + + # Keepalived doesn't allow mixing IPv4 and IPv6 in one group, so we mirror that restriction + + # XXX: filter on map object is destructive, so we force it to list. + # Additionally, filter objects always evaluate to True, empty or not, + # so we force them to lists as well. + vaddrs = list(map(lambda i: ip_interface(i), group_config['virtual_address'])) + vaddrs4 = list(filter(lambda x: isinstance(x, IPv4Interface), vaddrs)) + vaddrs6 = list(filter(lambda x: isinstance(x, IPv6Interface), vaddrs)) + + if vaddrs4 and vaddrs6: + raise ConfigError(f'VRRP group "{group}" mixes IPv4 and IPv6 virtual addresses, this is not allowed.\n' \ + 'Create individual groups for IPv4 and IPv6!') + if vaddrs4: + if 'hello_source_address' in group_config: + if is_ipv6(group_config['hello_source_address']): + raise ConfigError(f'VRRP group "{group}" uses IPv4 but hello-source-address is IPv6!') + + if 'peer_address' in group_config: + if is_ipv6(group_config['peer_address']): + raise ConfigError(f'VRRP group "{group}" uses IPv4 but peer-address is IPv6!') + + if vaddrs6: + if 'hello_source_address' in group_config: + if is_ipv4(group_config['hello_source_address']): + raise ConfigError(f'VRRP group "{group}" uses IPv6 but hello-source-address is IPv4!') + + if 'peer_address' in group_config: + if is_ipv4(group_config['peer_address']): + raise ConfigError(f'VRRP group "{group}" uses IPv6 but peer-address is IPv4!') + + # Warn the user about the deprecated mode-force option + if 'transition_script' in group_config and 'mode_force' in group_config['transition_script']: + print("""Warning: "transition-script mode-force" VRRP option is deprecated and will be removed in VyOS 1.4.""") + print("""It's no longer necessary, so you can safely remove it from your config now.""") # Check sync groups - vrrp_group_names = list(map(lambda x: x["name"], vrrp_groups)) - - for sync_group in sync_groups: - for m in sync_group["members"]: - if not (m in vrrp_group_names): - raise ConfigError("VRRP sync-group {0} refers to VRRP group {1}, but group {1} does not exist".format(sync_group["name"], m)) - - -def generate(data): - vrrp_groups, sync_groups = data - - # Remove disabled groups from the sync group member lists - for sync_group in sync_groups: - for member in sync_group["members"]: - g = list(filter(lambda x: x["name"] == member, vrrp_groups))[0] - if g["disable"]: - print("Warning: ignoring disabled VRRP group {0} in sync-group {1}".format(g["name"], sync_group["name"])) - # Filter out disabled groups - vrrp_groups = list(filter(lambda x: x["disable"] is not True, vrrp_groups)) - - render(VRRP.location['config'], 'vrrp/keepalived.conf.tmpl', - {"groups": vrrp_groups, "sync_groups": sync_groups}) - render(VRRP.location['daemon'], 'vrrp/daemon.tmpl', {}) + if 'sync_group' in vrrp: + for sync_group, sync_config in vrrp['sync_group'].items(): + if 'member' in sync_config: + for member in sync_config['member']: + if member not in vrrp['group']: + raise ConfigError(f'VRRP sync-group "{sync_group}" refers to VRRP group "{member}", '\ + 'but it does not exist!') + +def generate(vrrp): + if not vrrp: + return None + + render(VRRP.location['config'], 'vrrp/keepalived.conf.tmpl', vrrp) return None +def apply(vrrp): + service_name = 'keepalived.service' + if not vrrp: + call(f'systemctl stop {service_name}') + return None -def apply(data): - vrrp_groups, sync_groups = data - if vrrp_groups: - # safely rename a temporary file with configuration dict - try: - dict_file = Path("{}.temp".format(VRRP.location['vyos'])) - dict_file.rename(Path(VRRP.location['vyos'])) - except Exception as err: - print("Unable to rename the file with keepalived config for FIFO pipe: {}".format(err)) - - if not VRRP.is_running(): - print("Starting the VRRP process") - ret = call("systemctl restart keepalived.service") - else: - print("Reloading the VRRP process") - ret = call("systemctl reload keepalived.service") - - if ret != 0: - raise ConfigError("keepalived failed to start") - else: - # VRRP is removed in the commit - print("Stopping the VRRP process") - call("systemctl stop keepalived.service") - os.unlink(VRRP.location['daemon']) - + call(f'systemctl restart {service_name}') return None - if __name__ == '__main__': try: c = get_config() @@ -270,5 +154,5 @@ if __name__ == '__main__': generate(c) apply(c) except ConfigError as e: - print("VRRP error: {0}".format(str(e))) + print(e) exit(1) diff --git a/src/etc/systemd/system/keepalived.service.d/override.conf b/src/etc/systemd/system/keepalived.service.d/override.conf index e338b90a2..1c68913f2 100644 --- a/src/etc/systemd/system/keepalived.service.d/override.conf +++ b/src/etc/systemd/system/keepalived.service.d/override.conf @@ -6,7 +6,8 @@ After=vyos-router.service [Service] KillMode=process +EnvironmentFile= ExecStart= -ExecStart=/usr/sbin/keepalived --use-file /run/keepalived/keepalived.conf --pid /run/keepalived/keepalived.pid --dont-fork $DAEMON_ARGS +ExecStart=/usr/sbin/keepalived --use-file /run/keepalived/keepalived.conf --pid /run/keepalived/keepalived.pid --dont-fork --snmp PIDFile= PIDFile=/run/keepalived/keepalived.pid diff --git a/src/system/keepalived-fifo.py b/src/system/keepalived-fifo.py index 159fd0f54..1fba0d75b 100755 --- a/src/system/keepalived-fifo.py +++ b/src/system/keepalived-fifo.py @@ -27,6 +27,7 @@ from queue import Queue from logging.handlers import SysLogHandler from vyos.ifconfig.vrrp import VRRP +from vyos.configquery import ConfigTreeQuery from vyos.util import cmd # configure logging @@ -44,12 +45,13 @@ mdns_update_command = 'sudo /usr/libexec/vyos/conf_mode/service_mdns-repeater.py class KeepalivedFifo: # init - read command arguments def __init__(self): - logger.info("Starting FIFO pipe for Keepalived") + logger.info('Starting FIFO pipe for Keepalived') # define program arguments cmd_args_parser = argparse.ArgumentParser(description='Create FIFO pipe for keepalived and process notify events', add_help=False) cmd_args_parser.add_argument('PIPE', help='path to the FIFO pipe') # parse arguments cmd_args = cmd_args_parser.parse_args() + self._config_load() self.pipe_path = cmd_args.PIPE @@ -61,33 +63,34 @@ class KeepalivedFifo: # load configuration def _config_load(self): try: - # read the dictionary file with configuration - with open(VRRP.location['vyos'], 'r') as dict_file: - vrrp_config_dict = json.load(dict_file) + base = ['high-availability', 'vrrp'] + conf = ConfigTreeQuery() + if not conf.exists(base): + raise ValueError() + + # Read VRRP configuration directly from CLI + vrrp_config_dict = conf.get_config_dict(base, key_mangling=('-', '_'), + get_first_key=True) self.vrrp_config = {'vrrp_groups': {}, 'sync_groups': {}} - # save VRRP instances to the new dictionary - for vrrp_group in vrrp_config_dict['vrrp_groups']: - self.vrrp_config['vrrp_groups'][vrrp_group['name']] = { - 'STOP': vrrp_group.get('stop_script'), - 'FAULT': vrrp_group.get('fault_script'), - 'BACKUP': vrrp_group.get('backup_script'), - 'MASTER': vrrp_group.get('master_script') - } - # save VRRP sync groups to the new dictionary - for sync_group in vrrp_config_dict['sync_groups']: - self.vrrp_config['sync_groups'][sync_group['name']] = { - 'STOP': sync_group.get('stop_script'), - 'FAULT': sync_group.get('fault_script'), - 'BACKUP': sync_group.get('backup_script'), - 'MASTER': sync_group.get('master_script') - } - logger.debug("Loaded configuration: {}".format(self.vrrp_config)) + for key in ['group', 'sync_group']: + if key not in vrrp_config_dict: + continue + for group, group_config in vrrp_config_dict[key].items(): + if 'transition_script' not in group_config: + continue + self.vrrp_config['vrrp_groups'][group] = { + 'STOP': group_config['transition_script'].get('stop'), + 'FAULT': group_config['transition_script'].get('fault'), + 'BACKUP': group_config['transition_script'].get('backup'), + 'MASTER': group_config['transition_script'].get('master'), + } + logger.info(f'Loaded configuration: {self.vrrp_config}') except Exception as err: - logger.error("Unable to load configuration: {}".format(err)) + logger.error(f'Unable to load configuration: {err}') # run command def _run_command(self, command): - logger.debug("Running the command: {}".format(command)) + logger.debug(f'Running the command: {command}') try: cmd(command) except OSError as err: @@ -96,13 +99,13 @@ class KeepalivedFifo: # create FIFO pipe def pipe_create(self): if os.path.exists(self.pipe_path): - logger.info(f"PIPE already exist: {self.pipe_path}") + logger.info(f'PIPE already exist: {self.pipe_path}') else: os.mkfifo(self.pipe_path) # process message from pipe def pipe_process(self): - logger.debug("Message processing start") + logger.debug('Message processing start') regex_notify = re.compile(r'^(?P\w+) "(?P[\w-]+)" (?P\w+) (?P\d+)$', re.MULTILINE) while self.stopme.is_set() is False: # wait for a new message event from pipe_wait @@ -113,14 +116,14 @@ class KeepalivedFifo: # get all messages from queue and try to process them while self.message_queue.empty() is not True: message = self.message_queue.get() - logger.debug("Received message: {}".format(message)) + logger.debug(f'Received message: {message}') notify_message = regex_notify.search(message) # try to process a message if it looks valid if notify_message: n_type = notify_message.group('type') n_name = notify_message.group('name') n_state = notify_message.group('state') - logger.info("{} {} changed state to {}".format(n_type, n_name, n_state)) + logger.info(f'{n_type} {n_name} changed state to {n_state}') # check and run commands for VRRP instances if n_type == 'INSTANCE': if os.path.exists(mdns_running_file): @@ -143,16 +146,16 @@ class KeepalivedFifo: # mark task in queue as done self.message_queue.task_done() except Exception as err: - logger.error("Error processing message: {}".format(err)) - logger.debug("Terminating messages processing thread") + logger.error(f'Error processing message: {err}') + logger.debug('Terminating messages processing thread') # wait for messages def pipe_wait(self): - logger.debug("Message reading start") + logger.debug('Message reading start') self.pipe_read = os.open(self.pipe_path, os.O_RDONLY | os.O_NONBLOCK) while self.stopme.is_set() is False: # sleep a bit to not produce 100% CPU load - time.sleep(0.1) + time.sleep(0.250) try: # try to read a message from PIPE message = os.read(self.pipe_read, 500) @@ -165,21 +168,19 @@ class KeepalivedFifo: except Exception as err: # ignore the "Resource temporarily unavailable" error if err.errno != 11: - logger.error("Error receiving message: {}".format(err)) + logger.error(f'Error receiving message: {err}') - logger.debug("Closing FIFO pipe") + logger.debug('Closing FIFO pipe') os.close(self.pipe_read) - # handle SIGTERM signal to allow finish all messages processing def sigterm_handle(signum, frame): - logger.info("Ending processing: Received SIGTERM signal") + logger.info('Ending processing: Received SIGTERM signal') fifo.stopme.set() thread_wait_message.join() fifo.message_event.set() thread_process_message.join() - signal.signal(signal.SIGTERM, sigterm_handle) # init our class -- cgit v1.2.3 From 301f96c10779400ffb1d5a0e824202dda17c3971 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Tue, 21 Sep 2021 21:55:39 +0200 Subject: vrrp: keepalived: T616: use common description building block --- interface-definitions/vrrp.xml.in | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/interface-definitions/vrrp.xml.in b/interface-definitions/vrrp.xml.in index d57d213d9..cea60d9cb 100644 --- a/interface-definitions/vrrp.xml.in +++ b/interface-definitions/vrrp.xml.in @@ -77,11 +77,7 @@ - - - Group description - - + #include #include -- cgit v1.2.3 From 407db335b8eda0c9b8a090cde806a9107d3dbd96 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Tue, 21 Sep 2021 22:00:09 +0200 Subject: vrrp: keepalived: T3847: remove "transition-script mode-force" option --- interface-definitions/vrrp.xml.in | 9 ------- src/conf_mode/vrrp.py | 6 ----- src/migration-scripts/vrrp/2-to-3 | 52 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 52 insertions(+), 15 deletions(-) create mode 100755 src/migration-scripts/vrrp/2-to-3 diff --git a/interface-definitions/vrrp.xml.in b/interface-definitions/vrrp.xml.in index cea60d9cb..d43ff83de 100644 --- a/interface-definitions/vrrp.xml.in +++ b/interface-definitions/vrrp.xml.in @@ -221,15 +221,6 @@ - - - - Disable VRRP state checking (deprecated, will be removed in VyOS 1.4) - - - - - diff --git a/src/conf_mode/vrrp.py b/src/conf_mode/vrrp.py index 8bb237102..ba156e915 100755 --- a/src/conf_mode/vrrp.py +++ b/src/conf_mode/vrrp.py @@ -116,12 +116,6 @@ def verify(vrrp): if 'peer_address' in group_config: if is_ipv4(group_config['peer_address']): raise ConfigError(f'VRRP group "{group}" uses IPv6 but peer-address is IPv4!') - - # Warn the user about the deprecated mode-force option - if 'transition_script' in group_config and 'mode_force' in group_config['transition_script']: - print("""Warning: "transition-script mode-force" VRRP option is deprecated and will be removed in VyOS 1.4.""") - print("""It's no longer necessary, so you can safely remove it from your config now.""") - # Check sync groups if 'sync_group' in vrrp: for sync_group, sync_config in vrrp['sync_group'].items(): diff --git a/src/migration-scripts/vrrp/2-to-3 b/src/migration-scripts/vrrp/2-to-3 new file mode 100755 index 000000000..b96ca139d --- /dev/null +++ b/src/migration-scripts/vrrp/2-to-3 @@ -0,0 +1,52 @@ +#!/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 . + +# T3847: vrrp config cleanup + +from sys import argv +from vyos.configtree import ConfigTree + +if (len(argv) < 1): + print('Must specify file name!') + exit(1) + +file_name = argv[1] + +with open(file_name, 'r') as f: + config_file = f.read() + +base = ['high-availability', 'vrrp'] +config = ConfigTree(config_file) + +if not config.exists(base): + # Nothing to do + exit(0) + +if config.exists(base + ['group']): + for group in config.list_nodes(base + ['group']): + group_base = base + ['group', group] + + # Deprecated option + tmp = group_base + ['transition-script', 'mode-force'] + if config.exists(tmp): + config.delete(tmp) + +try: + with open(file_name, 'w') as f: + f.write(config.to_string()) +except OSError as e: + print(f'Failed to save the modified config: {e}') + exit(1) -- cgit v1.2.3 From 2a57f786931793d59cc5ec2c3a925807b6ee3938 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Tue, 21 Sep 2021 22:04:13 +0200 Subject: vrrp: keepalived: T3847: add common transition-script building block This is used for both VRRP groups and sync-groups. --- .../include/vrrp-transition-script.xml.i | 41 +++++++++++ interface-definitions/vrrp.xml.in | 80 +--------------------- 2 files changed, 43 insertions(+), 78 deletions(-) create mode 100644 interface-definitions/include/vrrp-transition-script.xml.i diff --git a/interface-definitions/include/vrrp-transition-script.xml.i b/interface-definitions/include/vrrp-transition-script.xml.i new file mode 100644 index 000000000..cf57c3c74 --- /dev/null +++ b/interface-definitions/include/vrrp-transition-script.xml.i @@ -0,0 +1,41 @@ + + + + VRRP transition scripts + + + + + Script to run on VRRP state transition to master + + + + + + + + Script to run on VRRP state transition to backup + + + + + + + + Script to run on VRRP state transition to fault + + + + + + + + Script to run on VRRP state transition to stop + + + + + + + + diff --git a/interface-definitions/vrrp.xml.in b/interface-definitions/vrrp.xml.in index d43ff83de..44bd7e454 100644 --- a/interface-definitions/vrrp.xml.in +++ b/interface-definitions/vrrp.xml.in @@ -184,45 +184,7 @@ - - - VRRP transition scripts - - - - - Script to run on VRRP state transition to master - - - - - - - - Script to run on VRRP state transition to backup - - - - - - - - Script to run on VRRP state transition to fault - - - - - - - - Script to run on VRRP state transition to stop - - - - - - - + #include Virtual address (IPv4 or IPv6, but they must not be mixed in one group) @@ -293,45 +255,7 @@ - - - VRRP transition scripts - - - - - Script to run on VRRP state transition to master - - - - - - - - Script to run on VRRP state transition to backup - - - - - - - - Script to run on VRRP state transition to fault - - - - - - - - Script to run on VRRP state transition to stop - - - - - - - + #include -- cgit v1.2.3 From b9d4d8c67d6b41ebbf5b570c7c6b09e05520e737 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Tue, 21 Sep 2021 22:07:33 +0200 Subject: vrrp: keepalived: T3847: migrate/streamline CLI options Rename virtual-address -> address as we always talk about an IP address. --- data/templates/vrrp/keepalived.conf.tmpl | 8 ++++---- interface-definitions/vrrp.xml.in | 7 +++---- smoketest/scripts/cli/test_ha_vrrp.py | 6 +++--- src/conf_mode/vrrp.py | 6 +++--- src/migration-scripts/vrrp/2-to-3 | 10 ++++++++++ 5 files changed, 23 insertions(+), 14 deletions(-) diff --git a/data/templates/vrrp/keepalived.conf.tmpl b/data/templates/vrrp/keepalived.conf.tmpl index a1e47abc3..e31bfaac0 100644 --- a/data/templates/vrrp/keepalived.conf.tmpl +++ b/data/templates/vrrp/keepalived.conf.tmpl @@ -60,16 +60,16 @@ vrrp_instance {{ name }} { {% endif %} } {% endif %} -{% if group_config.virtual_address is defined and group_config.virtual_address is not none %} +{% if group_config.address is defined and group_config.address is not none %} virtual_ipaddress { -{% for addr in group_config.virtual_address %} +{% for addr in group_config.address %} {{ addr }} {% endfor %} } {% endif %} -{% if group_config.virtual_address_excluded is defined and group_config.virtual_address_excluded is not none %} +{% if group_config.excluded_address is defined and group_config.excluded_address is not none %} virtual_ipaddress_excluded { -{% for addr in group_config.virtual_address_excluded %} +{% for addr in group_config.excluded_address %} {{ addr }} {% endfor %} } diff --git a/interface-definitions/vrrp.xml.in b/interface-definitions/vrrp.xml.in index 44bd7e454..44a9a1f54 100644 --- a/interface-definitions/vrrp.xml.in +++ b/interface-definitions/vrrp.xml.in @@ -185,9 +185,9 @@ #include - + - Virtual address (IPv4 or IPv6, but they must not be mixed in one group) + Virtual IP address ipv4 IPv4 virtual address @@ -200,11 +200,10 @@ - Virtual address must be a valid IPv4 or IPv6 address with prefix length (e.g. 192.0.2.3/24 or 2001:db8:ff::10/64) - + Virtual address (If you need additional IPv4 and IPv6 in same group) diff --git a/smoketest/scripts/cli/test_ha_vrrp.py b/smoketest/scripts/cli/test_ha_vrrp.py index 9c8d26699..4a743fe5e 100755 --- a/smoketest/scripts/cli/test_ha_vrrp.py +++ b/smoketest/scripts/cli/test_ha_vrrp.py @@ -59,7 +59,7 @@ class TestVRRP(VyOSUnitTestSHIM.TestCase): self.cli_set(group_base + ['description', group]) self.cli_set(group_base + ['interface', f'{vrrp_interface}.{vlan_id}']) - self.cli_set(group_base + ['virtual-address', vip]) + self.cli_set(group_base + ['address', vip]) self.cli_set(group_base + ['vrid', vlan_id]) # commit changes @@ -93,7 +93,7 @@ class TestVRRP(VyOSUnitTestSHIM.TestCase): self.cli_set(group_base + ['description', group]) self.cli_set(group_base + ['interface', f'{vrrp_interface}.{vlan_id}']) - self.cli_set(group_base + ['virtual-address', vip]) + self.cli_set(group_base + ['address', vip]) self.cli_set(group_base + ['vrid', vlan_id]) self.cli_set(group_base + ['advertise-interval', advertise_interval]) @@ -139,7 +139,7 @@ class TestVRRP(VyOSUnitTestSHIM.TestCase): self.cli_set(['interfaces', 'ethernet', vrrp_interface, 'vif', vlan_id, 'address', inc_ip(vip, 1) + '/' + vip.split('/')[-1]]) self.cli_set(group_base + ['interface', f'{vrrp_interface}.{vlan_id}']) - self.cli_set(group_base + ['virtual-address', vip]) + self.cli_set(group_base + ['address', vip]) self.cli_set(group_base + ['vrid', vlan_id]) self.cli_set(base_path + ['sync-group', sync_group, 'member', group]) diff --git a/src/conf_mode/vrrp.py b/src/conf_mode/vrrp.py index ba156e915..ffe9dd276 100755 --- a/src/conf_mode/vrrp.py +++ b/src/conf_mode/vrrp.py @@ -72,8 +72,8 @@ def verify(vrrp): if 'interface' not in group_config: raise ConfigError(f'Interface is required but not set in VRRP group "{group}"') - if 'virtual_address' not in group_config: - raise ConfigError(f'virtual-address is required but not set in VRRP group "{group}"') + if 'address' not in group_config: + raise ConfigError(f'Virtual IP address is required but not set in VRRP group "{group}"') if 'authentication' in group_config: if not {'password', 'type'} <= set(group_config['authentication']): @@ -92,7 +92,7 @@ def verify(vrrp): # XXX: filter on map object is destructive, so we force it to list. # Additionally, filter objects always evaluate to True, empty or not, # so we force them to lists as well. - vaddrs = list(map(lambda i: ip_interface(i), group_config['virtual_address'])) + vaddrs = list(map(lambda i: ip_interface(i), group_config['address'])) vaddrs4 = list(filter(lambda x: isinstance(x, IPv4Interface), vaddrs)) vaddrs6 = list(filter(lambda x: isinstance(x, IPv6Interface), vaddrs)) diff --git a/src/migration-scripts/vrrp/2-to-3 b/src/migration-scripts/vrrp/2-to-3 index b96ca139d..1151ae18c 100755 --- a/src/migration-scripts/vrrp/2-to-3 +++ b/src/migration-scripts/vrrp/2-to-3 @@ -44,6 +44,16 @@ if config.exists(base + ['group']): if config.exists(tmp): config.delete(tmp) + # Rename virtual-address -> address + tmp = group_base + ['virtual-address'] + if config.exists(tmp): + config.rename(tmp, 'address') + + # Rename virtual-address-excluded -> excluded-address + tmp = group_base + ['virtual-address-excluded'] + if config.exists(tmp): + config.rename(tmp, 'excluded-address') + try: with open(file_name, 'w') as f: f.write(config.to_string()) -- cgit v1.2.3 From 3114c7ccbdf867f9cdaa436792937b16ea4d85bf Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Wed, 22 Sep 2021 08:18:42 +0200 Subject: vrrp: keepalived: T3847: enable no_tag_node_value_mangle for get_config_dict() Commit 761631d6 ("vrrp: keepalived: T3847: migrate to get_config_dict()") switched to the new python function get_config_dict(), when we deal with tag nodes that can contain a hyphen, we should also set no_tag_node_value_mangle in order to preserve it. This caused a dict lookup error as the hyphens in the test scripts got replaced by an _. --- data/templates/vrrp/keepalived.conf.tmpl | 4 +++- src/conf_mode/vrrp.py | 3 ++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/data/templates/vrrp/keepalived.conf.tmpl b/data/templates/vrrp/keepalived.conf.tmpl index e31bfaac0..b4824a994 100644 --- a/data/templates/vrrp/keepalived.conf.tmpl +++ b/data/templates/vrrp/keepalived.conf.tmpl @@ -23,7 +23,9 @@ vrrp_script healthcheck_{{ name }} { } {% endif %} vrrp_instance {{ name }} { - {{ '# ' ~ group_config.description if group_config.description is defined }} +{% if group_config.description is defined and group_config.description is not none %} + # {{ group_config.description }} +{% endif %} state BACKUP interface {{ group_config.interface }} virtual_router_id {{ group_config.vrid }} diff --git a/src/conf_mode/vrrp.py b/src/conf_mode/vrrp.py index ffe9dd276..e8f1c1f99 100755 --- a/src/conf_mode/vrrp.py +++ b/src/conf_mode/vrrp.py @@ -43,7 +43,8 @@ def get_config(config=None): if not conf.exists(base): return None - vrrp = conf.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True) + vrrp = conf.get_config_dict(base, key_mangling=('-', '_'), + get_first_key=True, no_tag_node_value_mangle=True) # We have gathered the dict representation of the CLI, but there are default # options which we need to update into the dictionary retrived. if 'group' in vrrp: -- cgit v1.2.3 From 6935c263f5022896e96bec9daa8a5ba29b04b51b Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Wed, 22 Sep 2021 08:33:45 +0200 Subject: smoketest: vrrp: delete interface vifs after test --- smoketest/scripts/cli/test_ha_vrrp.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/smoketest/scripts/cli/test_ha_vrrp.py b/smoketest/scripts/cli/test_ha_vrrp.py index 4a743fe5e..bbf24095b 100755 --- a/smoketest/scripts/cli/test_ha_vrrp.py +++ b/smoketest/scripts/cli/test_ha_vrrp.py @@ -43,6 +43,10 @@ class TestVRRP(VyOSUnitTestSHIM.TestCase): # Check for running process self.assertTrue(process_named_running(PROCESS_NAME)) + for group in groups: + vlan_id = group.lstrip('VLAN') + self.cli_set(['interfaces', 'ethernet', vrrp_interface, 'vif', vlan_id]) + self.cli_delete(base_path) self.cli_commit() -- cgit v1.2.3 From d1a2124559eb53f832bcaa467b8adc321ffbc5f9 Mon Sep 17 00:00:00 2001 From: Nicolas Riebesel Date: Thu, 23 Sep 2021 01:27:03 +0200 Subject: openvpn: T3642: Add option for TLS 1.3 --- interface-definitions/interfaces-openvpn.xml.in | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/interface-definitions/interfaces-openvpn.xml.in b/interface-definitions/interfaces-openvpn.xml.in index 2ecac78e2..d67549d87 100644 --- a/interface-definitions/interfaces-openvpn.xml.in +++ b/interface-definitions/interfaces-openvpn.xml.in @@ -678,7 +678,7 @@ Specify the minimum required TLS version - 1.0 1.1 1.2 + 1.0 1.1 1.2 1.3 1.0 @@ -692,8 +692,12 @@ 1.2 TLS v1.2 + + 1.3 + TLS v1.3 + - ^(1.0|1.1|1.2)$ + ^(1.0|1.1|1.2|1.3)$ -- cgit v1.2.3 From 4084046987ab52f8c77b0393c1820d37a2124bbd Mon Sep 17 00:00:00 2001 From: Nicolas Riebesel Date: Thu, 23 Sep 2021 01:28:22 +0200 Subject: openvpn: T3642: Fix password_protected check --- src/conf_mode/interfaces-openvpn.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/conf_mode/interfaces-openvpn.py b/src/conf_mode/interfaces-openvpn.py index 02b7f83bf..ce62a8b82 100755 --- a/src/conf_mode/interfaces-openvpn.py +++ b/src/conf_mode/interfaces-openvpn.py @@ -134,7 +134,7 @@ def verify_pki(openvpn): if tls['certificate'] not in pki['certificate']: raise ConfigError(f'Invalid certificate on openvpn interface {interface}') - if dict_search_args(pki, 'certificate', tls['certificate'], 'private', 'password_protected'): + if dict_search_args(pki, 'certificate', tls['certificate'], 'private', 'password_protected') is not None: raise ConfigError(f'Cannot use encrypted private key on openvpn interface {interface}') if mode == 'server' and 'dh_params' not in tls and not is_ec_private_key(pki, tls['certificate']): -- cgit v1.2.3 From b4926009b15da34a0e76029447732c17e8a1431b Mon Sep 17 00:00:00 2001 From: Nicolas Riebesel Date: Thu, 23 Sep 2021 01:29:12 +0200 Subject: openvpn: T3642: Openvpn does not work without dh parameter in EC mode --- data/templates/openvpn/server.conf.tmpl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/data/templates/openvpn/server.conf.tmpl b/data/templates/openvpn/server.conf.tmpl index 9e4cc6813..6aedc3786 100644 --- a/data/templates/openvpn/server.conf.tmpl +++ b/data/templates/openvpn/server.conf.tmpl @@ -176,6 +176,8 @@ tls-version-min {{ tls.tls_version_min }} {% endif %} {% if tls.dh_params is defined and tls.dh_params is not none %} dh /run/openvpn/{{ ifname }}_dh.pem +{% elif mode == 'server' and tls.private_key is defined %} +dh none {% endif %} {% if tls.auth_key is defined and tls.auth_key is not none %} {% if mode == 'client' %} -- cgit v1.2.3 From 579a08f88958a0428caef2c7c5a5107c33c80698 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Thu, 23 Sep 2021 09:42:12 +0200 Subject: smoketest: ospf: debug output only syslog and FRR The Kernel output seemed to be not that helpful and only polluted the log. Now we only gather the syslog and FRRs configuration --- smoketest/scripts/cli/test_protocols_ospf.py | 1 - 1 file changed, 1 deletion(-) diff --git a/smoketest/scripts/cli/test_protocols_ospf.py b/smoketest/scripts/cli/test_protocols_ospf.py index 3f13eec80..0529eefbd 100755 --- a/smoketest/scripts/cli/test_protocols_ospf.py +++ b/smoketest/scripts/cli/test_protocols_ospf.py @@ -234,7 +234,6 @@ class TestProtocolsOSPF(VyOSUnitTestSHIM.TestCase): self.assertIn(f' redistribute {protocol} metric {metric} metric-type {metric_type} route-map {route_map}', frrconfig) except: log.debug(frrconfig) - log.debug(cmd('sudo dmesg')) log.debug(cmd('sudo cat /var/log/messages')) log.debug(cmd('vtysh -c "show run"')) self.fail('Now we can hopefully see why OSPF fails!') -- cgit v1.2.3 From 46ecdd015185263c42180a29555101b758399148 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Thu, 23 Sep 2021 16:13:21 +0200 Subject: T3850: Revert "login: T1948: add missing ssh-public key name regex" This reverts commit 514da738173696c70440c959b9d7ec9afd77fbae. --- interface-definitions/system-login.xml.in | 3 --- 1 file changed, 3 deletions(-) diff --git a/interface-definitions/system-login.xml.in b/interface-definitions/system-login.xml.in index f4613b8a2..4bfe82268 100644 --- a/interface-definitions/system-login.xml.in +++ b/interface-definitions/system-login.xml.in @@ -44,9 +44,6 @@ Remote access public keys - - ^[-_a-zA-Z0-9@]+$ - txt Key identifier used by ssh-keygen (usually of form user@host) -- cgit v1.2.3 From 6187ce264a39cd72285f6cb73cc746a04268c253 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Thu, 23 Sep 2021 18:28:49 +0200 Subject: smoketest: T3850: use as complicated as possible public-key name --- smoketest/scripts/cli/test_system_login.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/smoketest/scripts/cli/test_system_login.py b/smoketest/scripts/cli/test_system_login.py index af3a5851c..0addd630e 100755 --- a/smoketest/scripts/cli/test_system_login.py +++ b/smoketest/scripts/cli/test_system_login.py @@ -94,7 +94,7 @@ class TestSystemLogin(VyOSUnitTestSHIM.TestCase): def test_system_user_ssh_key(self): ssh_user = 'ssh-test_user' - public_keys = 'vyos' + public_keys = 'vyos_test@domain-foo.com' type = 'ssh-rsa' self.cli_set(base_path + ['user', ssh_user, 'authentication', 'public-keys', public_keys, 'key', ssh_pubkey.replace('\n','')]) -- cgit v1.2.3 From 8ba8f0e097527e3aaaf8b395bfc07cce47e2c788 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sat, 25 Sep 2021 08:55:36 +0200 Subject: vyos.ifconfig: dhcp: T3300: always re-start dhcp client instead of start Commit dd2eb5e5686655 ("dhcp: T3300: add DHCP default route distance") changed the logic on how the DHCP process is going to be started. The systemd unit was always "started" even if it was already running. It should rather be re-started to track changes in e.g. the DHCP hostname setting. --- python/vyos/ifconfig/interface.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/vyos/ifconfig/interface.py b/python/vyos/ifconfig/interface.py index 8857f30e9..b9ba90b61 100755 --- a/python/vyos/ifconfig/interface.py +++ b/python/vyos/ifconfig/interface.py @@ -1221,7 +1221,7 @@ class Interface(Control): # 'up' check is mandatory b/c even if the interface is A/D, as soon as # the DHCP client is started the interface will be placed in u/u state. # This is not what we intended to do when disabling an interface. - return self._cmd(f'systemctl start dhclient@{ifname}.service') + return self._cmd(f'systemctl restart {systemd_service}') else: # cleanup old config files for file in [config_file, options_file, pid_file, lease_file]: -- cgit v1.2.3 From d1c58addd881e06b389799a9c14d8ebf5d03c567 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sat, 25 Sep 2021 08:55:50 +0200 Subject: vyos.ifconfig: dhcpv6: re-use systemd_service definition variable --- python/vyos/ifconfig/interface.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/python/vyos/ifconfig/interface.py b/python/vyos/ifconfig/interface.py index b9ba90b61..4e881ad66 100755 --- a/python/vyos/ifconfig/interface.py +++ b/python/vyos/ifconfig/interface.py @@ -1238,16 +1238,16 @@ class Interface(Control): ifname = self.ifname config_file = f'/run/dhcp6c/dhcp6c.{ifname}.conf' + systemd_service = f'dhcp6c@{ifname}.service' if enable and 'disable' not in self._config: render(config_file, 'dhcp-client/ipv6.tmpl', self._config) - # We must ignore any return codes. This is required to enable DHCPv6-PD - # for interfaces which are yet not up and running. - return self._popen(f'systemctl restart dhcp6c@{ifname}.service') + # We must ignore any return codes. This is required to enable + # DHCPv6-PD for interfaces which are yet not up and running. + return self._popen(f'systemctl restart {systemd_service}') else: - systemd_service = f'dhcp6c@{ifname}.service' if is_systemd_service_active(systemd_service): self._cmd(f'systemctl stop {systemd_service}') if os.path.isfile(config_file): -- cgit v1.2.3 From da45720bf5d5f6165334c1aa3a57d2d31c77b8c7 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sat, 25 Sep 2021 10:07:29 +0200 Subject: ipsec: T2816: ipsec-dhclient-hook should use vyos.util.read_file() / write_file() --- src/etc/dhcp/dhclient-exit-hooks.d/ipsec-dhclient-hook | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/src/etc/dhcp/dhclient-exit-hooks.d/ipsec-dhclient-hook b/src/etc/dhcp/dhclient-exit-hooks.d/ipsec-dhclient-hook index a7a9a2ce6..5cd9321ef 100755 --- a/src/etc/dhcp/dhclient-exit-hooks.d/ipsec-dhclient-hook +++ b/src/etc/dhcp/dhclient-exit-hooks.d/ipsec-dhclient-hook @@ -35,19 +35,14 @@ fi python3 - < Date: Sat, 25 Sep 2021 10:08:20 +0200 Subject: ipsec: T2816: ipsec-dhclient-hook should use exit(0) --- src/etc/dhcp/dhclient-exit-hooks.d/ipsec-dhclient-hook | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/etc/dhcp/dhclient-exit-hooks.d/ipsec-dhclient-hook b/src/etc/dhcp/dhclient-exit-hooks.d/ipsec-dhclient-hook index 5cd9321ef..4da12ee02 100755 --- a/src/etc/dhcp/dhclient-exit-hooks.d/ipsec-dhclient-hook +++ b/src/etc/dhcp/dhclient-exit-hooks.d/ipsec-dhclient-hook @@ -80,4 +80,6 @@ if __name__ == '__main__': call('sudo ipsec rereadall') call('sudo ipsec reload') call('sudo swanctl -q') + + exit(0) PYEND \ No newline at end of file -- cgit v1.2.3 From bcf7a9bb38c537bb1bdc5b37c680e3c65a785278 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sat, 25 Sep 2021 10:09:30 +0200 Subject: ipsec: T2816: ipsec-dhclient-hook should only run if swanctl.conf exists --- .../dhcp/dhclient-exit-hooks.d/ipsec-dhclient-hook | 33 +++++++++++----------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/src/etc/dhcp/dhclient-exit-hooks.d/ipsec-dhclient-hook b/src/etc/dhcp/dhclient-exit-hooks.d/ipsec-dhclient-hook index 4da12ee02..61a89e62a 100755 --- a/src/etc/dhcp/dhclient-exit-hooks.d/ipsec-dhclient-hook +++ b/src/etc/dhcp/dhclient-exit-hooks.d/ipsec-dhclient-hook @@ -61,25 +61,26 @@ if __name__ == '__main__': new_ip = os.getenv('new_ip_address') old_ip = os.getenv('old_ip_address') - conf_lines = read_file(SWANCTL_CONF) - found = False - to_match = f'# dhcp:{interface}' + if os.path.exists(SWANCTL_CONF): + conf_lines = read_file(SWANCTL_CONF) + found = False + to_match = f'# dhcp:{interface}' - for i, line in enumerate(conf_lines): - if line.find(to_match) > 0: - conf_lines[i] = line.replace(old_ip, new_ip) - found = True + for i, line in enumerate(conf_lines): + if line.find(to_match) > 0: + conf_lines[i] = line.replace(old_ip, new_ip) + found = True - for i, line in enumerate(secrets_lines): - if line.find(to_match) > 0: - secrets_lines[i] = line.replace(old_ip, new_ip) + for i, line in enumerate(secrets_lines): + if line.find(to_match) > 0: + secrets_lines[i] = line.replace(old_ip, new_ip) - if found: - write_file(SWANCTL_CONF, conf_lines) - ipsec_down(old_ip) - call('sudo ipsec rereadall') - call('sudo ipsec reload') - call('sudo swanctl -q') + if found: + write_file(SWANCTL_CONF, conf_lines) + ipsec_down(old_ip) + call('sudo ipsec rereadall') + call('sudo ipsec reload') + call('sudo swanctl -q') exit(0) PYEND \ No newline at end of file -- cgit v1.2.3 From 801123eff1bf232ca1e5202ceb0989c2fba34c86 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sat, 25 Sep 2021 16:34:52 +0200 Subject: op-mode: bgp: "show bgp ipv4|ipv6" should display routing table The node was missed out when adding the XML definitions. --- op-mode-definitions/include/bgp/show-bgp-common.xml.i | 2 ++ 1 file changed, 2 insertions(+) diff --git a/op-mode-definitions/include/bgp/show-bgp-common.xml.i b/op-mode-definitions/include/bgp/show-bgp-common.xml.i index 0664b11fc..e81b26b3e 100644 --- a/op-mode-definitions/include/bgp/show-bgp-common.xml.i +++ b/op-mode-definitions/include/bgp/show-bgp-common.xml.i @@ -22,6 +22,7 @@ #include #include + ${vyos_op_scripts_dir}/vtysh_wrapper.sh $@ @@ -44,6 +45,7 @@ #include #include + ${vyos_op_scripts_dir}/vtysh_wrapper.sh $@ -- cgit v1.2.3 From 4b287511af74448ebf1cdc44c7880989a6a02da1 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sat, 25 Sep 2021 16:47:28 +0200 Subject: bgp: xml: T2387: use "generic-description" building block over BGP specific one There is no benefit in the BGP specific definition of a "description" node. --- interface-definitions/include/bgp/neighbor-description.xml.i | 7 ------- interface-definitions/include/bgp/protocol-common-config.xml.i | 4 ++-- 2 files changed, 2 insertions(+), 9 deletions(-) delete mode 100644 interface-definitions/include/bgp/neighbor-description.xml.i diff --git a/interface-definitions/include/bgp/neighbor-description.xml.i b/interface-definitions/include/bgp/neighbor-description.xml.i deleted file mode 100644 index 3095d2560..000000000 --- a/interface-definitions/include/bgp/neighbor-description.xml.i +++ /dev/null @@ -1,7 +0,0 @@ - - - - Neighbor specific description - - - diff --git a/interface-definitions/include/bgp/protocol-common-config.xml.i b/interface-definitions/include/bgp/protocol-common-config.xml.i index 2b22bac7d..40b9a56de 100644 --- a/interface-definitions/include/bgp/protocol-common-config.xml.i +++ b/interface-definitions/include/bgp/protocol-common-config.xml.i @@ -960,9 +960,9 @@ + #include #include #include - #include #include #include #include @@ -1418,9 +1418,9 @@ #include + #include #include #include - #include #include #include #include -- cgit v1.2.3 From 993daec9296589f122737e0c210f31879fcb1dab Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sat, 25 Sep 2021 16:56:39 +0200 Subject: bgp: T3657: add "neighbor fe80::202 interface source-interface 'eth1'" command --- data/templates/frr/bgpd.frr.tmpl | 3 +++ interface-definitions/include/bgp/protocol-common-config.xml.i | 1 + 2 files changed, 4 insertions(+) diff --git a/data/templates/frr/bgpd.frr.tmpl b/data/templates/frr/bgpd.frr.tmpl index 987b922da..27a2b98a5 100644 --- a/data/templates/frr/bgpd.frr.tmpl +++ b/data/templates/frr/bgpd.frr.tmpl @@ -90,6 +90,9 @@ {% if config.interface.peer_group is defined and config.interface.peer_group is not none %} neighbor {{ neighbor }} interface peer-group {{ config.interface.peer_group }} {% endif %} +{% if config.interface.source_interface is defined and config.interface.source_interface is not none %} + neighbor {{ neighbor }} interface {{ config.interface.source_interface }} +{% endif %} {% if config.interface.v6only is defined and config.interface.v6only is not none %} {% if config.interface.v6only.peer_group is defined and config.interface.v6only.peer_group is not none %} neighbor {{ neighbor }} interface v6only peer-group {{ config.interface.v6only.peer_group }} diff --git a/interface-definitions/include/bgp/protocol-common-config.xml.i b/interface-definitions/include/bgp/protocol-common-config.xml.i index 40b9a56de..30033bc50 100644 --- a/interface-definitions/include/bgp/protocol-common-config.xml.i +++ b/interface-definitions/include/bgp/protocol-common-config.xml.i @@ -974,6 +974,7 @@ #include #include + #include Enable BGP with v6 link-local only -- cgit v1.2.3 From 0ee26592772a14e829d9d1f8e64f9db875f31a63 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sun, 26 Sep 2021 10:57:56 +0200 Subject: op-mode: reboot/poweroff: T3857: send wall message to all users --- src/op_mode/powerctrl.py | 33 ++++++++++++++++++++------------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/src/op_mode/powerctrl.py b/src/op_mode/powerctrl.py index f8b5a3dda..679b03c0b 100755 --- a/src/op_mode/powerctrl.py +++ b/src/op_mode/powerctrl.py @@ -92,37 +92,40 @@ def cancel_shutdown(): try: run('/sbin/shutdown -c --no-wall') except OSError as e: - exit("Could not cancel a reboot or poweroff: %s" % e) + exit(f'Could not cancel a reboot or poweroff: {e}') - message = 'Scheduled {} has been cancelled {}'.format(output['MODE'], timenow) + mode = output['MODE'] + message = f'Scheduled {mode} has been cancelled {timenow}' run(f'wall {message} > /dev/null 2>&1') else: print("Reboot or poweroff is not scheduled") def execute_shutdown(time, reboot=True, ask=True): + action = "reboot" if reboot else "poweroff" if not ask: - action = "reboot" if reboot else "poweroff" - if not ask_yes_no("Are you sure you want to %s this system?" % action): + if not ask_yes_no(f"Are you sure you want to {action} this system?"): exit(0) - - action = "-r" if reboot else "-P" + action_cmd = "-r" if reboot else "-P" if len(time) == 0: # T870 legacy reboot job support chk_vyatta_based_reboots() ### - out = cmd(f'/sbin/shutdown {action} now', stderr=STDOUT) + out = cmd(f'/sbin/shutdown {action_cmd} now', stderr=STDOUT) print(out.split(",", 1)[0]) return elif len(time) == 1: # Assume the argument is just time ts = parse_time(time[0]) if ts: - cmd(f'/sbin/shutdown {action} {time[0]}', stderr=STDOUT) + cmd(f'/sbin/shutdown {action_cmd} {time[0]}', stderr=STDOUT) + # Inform all other logged in users about the reboot/shutdown + wall_msg = f'System {action} is scheduled {time[0]}' + cmd(f'/usr/bin/wall "{wall_msg}"') else: - exit("Invalid time \"{0}\". The valid format is HH:MM".format(time[0])) + exit(f'Invalid time "{time[0]}". The valid format is HH:MM') elif len(time) == 2: # Assume it's date and time ts = parse_time(time[0]) @@ -131,14 +134,18 @@ def execute_shutdown(time, reboot=True, ask=True): t = datetime.combine(ds, ts) td = t - datetime.now() t2 = 1 + int(td.total_seconds())//60 # Get total minutes - cmd('/sbin/shutdown {action} {t2}', stderr=STDOUT) + + cmd(f'/sbin/shutdown {action_cmd} {t2}', stderr=STDOUT) + # Inform all other logged in users about the reboot/shutdown + wall_msg = f'System {action} is scheduled {time[1]} {time[0]}' + cmd(f'/usr/bin/wall "{wall_msg}"') else: if not ts: - exit("Invalid time \"{0}\". The valid format is HH:MM".format(time[0])) + exit(f'Invalid time "{time[0]}". The valid format is HH:MM') else: - exit("Invalid time \"{0}\". A valid format is YYYY-MM-DD [HH:MM]".format(time[1])) + exit(f'Invalid date "{time[1]}". A valid format is YYYY-MM-DD [HH:MM]') else: - exit("Could not decode date and time. Valids formats are HH:MM or YYYY-MM-DD HH:MM") + exit('Could not decode date and time. Valids formats are HH:MM or YYYY-MM-DD HH:MM') check_shutdown() -- cgit v1.2.3 From 289a495c8f45322d58bd849aa8aca48cf0210b03 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sun, 26 Sep 2021 11:08:57 +0200 Subject: ospfv3: T3859: add "log-adjacency-changes" CLI command --- data/templates/frr/ospf6d.frr.tmpl | 3 +++ .../include/ospf/log-adjacency-changes.xml.i | 15 +++++++++++++++ .../include/ospf/protocol-common-config.xml.i | 14 +------------- interface-definitions/protocols-ospfv3.xml.in | 1 + 4 files changed, 20 insertions(+), 13 deletions(-) create mode 100644 interface-definitions/include/ospf/log-adjacency-changes.xml.i diff --git a/data/templates/frr/ospf6d.frr.tmpl b/data/templates/frr/ospf6d.frr.tmpl index 0026c0d2c..a8c53738f 100644 --- a/data/templates/frr/ospf6d.frr.tmpl +++ b/data/templates/frr/ospf6d.frr.tmpl @@ -76,6 +76,9 @@ router ospf6 distance ospf6 {{ 'intra-area ' + distance.ospfv3.intra_area if distance.ospfv3.intra_area is defined }} {{ 'inter-area ' + distance.ospfv3.inter_area if distance.ospfv3.inter_area is defined }} {{ 'external ' + distance.ospfv3.external if distance.ospfv3.external is defined }} {% endif %} {% endif %} +{% if log_adjacency_changes is defined %} + log-adjacency-changes {{ "detail" if log_adjacency_changes.detail is defined }} +{% endif %} {% if parameters is defined and parameters is not none %} {% if parameters.router_id is defined and parameters.router_id is not none %} ospf6 router-id {{ parameters.router_id }} diff --git a/interface-definitions/include/ospf/log-adjacency-changes.xml.i b/interface-definitions/include/ospf/log-adjacency-changes.xml.i new file mode 100644 index 000000000..24c6cbe7a --- /dev/null +++ b/interface-definitions/include/ospf/log-adjacency-changes.xml.i @@ -0,0 +1,15 @@ + + + + Log adjacency state changes + + + + + Log all state changes + + + + + + diff --git a/interface-definitions/include/ospf/protocol-common-config.xml.i b/interface-definitions/include/ospf/protocol-common-config.xml.i index 0139296ec..80931ac15 100644 --- a/interface-definitions/include/ospf/protocol-common-config.xml.i +++ b/interface-definitions/include/ospf/protocol-common-config.xml.i @@ -435,19 +435,7 @@ - - - Log adjacency state changes - - - - - Log all state changes - - - - - +#include OSPF maximum and infinite-distance metric diff --git a/interface-definitions/protocols-ospfv3.xml.in b/interface-definitions/protocols-ospfv3.xml.in index 7b42c448d..99cfec661 100644 --- a/interface-definitions/protocols-ospfv3.xml.in +++ b/interface-definitions/protocols-ospfv3.xml.in @@ -186,6 +186,7 @@ #include + #include OSPFv3 specific parameters -- cgit v1.2.3 From 8d6861290f39298701b0a89bd358545763cee14b Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sun, 26 Sep 2021 12:28:37 +0200 Subject: vyos.ifconfig: T3860: bugfix in get_mac_synthetic() Commit 081e23996f (vyos.ifconfig: get_mac_synthetic() must generate a stable "MAC") calculated a "stable" synthetic MAC address per the interface based on UUID and the interface name. The problem is that this calculation is too stable when run on multiple instances of VyOS on different hosts/hypervisors. Having R1 and R2 setup a connection both via "tun10" interface will become the same "synthetic" MAC address manifesting in the same link-local IPv6 address. This e.g. breaks OSPFv3 badly as both neighbors communicate using the same link-local address. As workaround one can: set interfaces tunnel tun1337 address 'fe80::1:1337/64' set interfaces tunnel tun1337 ipv6 address no-default-link-local This commit changes the way in how the synthetic MAC address is generated. It's based on the first 48 bits of a sha256 sum build from a CPU ID retrieved via DMI, the MAC address of eth0 and the interface name as used before. This should add enough entropy to get a stable pseudo MAC address. --- python/vyos/ifconfig/interface.py | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/python/vyos/ifconfig/interface.py b/python/vyos/ifconfig/interface.py index 4e881ad66..9b9d982d1 100755 --- a/python/vyos/ifconfig/interface.py +++ b/python/vyos/ifconfig/interface.py @@ -27,8 +27,6 @@ from netifaces import ifaddresses # this is not the same as socket.AF_INET/INET6 from netifaces import AF_INET from netifaces import AF_INET6 -from uuid import uuid3 -from uuid import NAMESPACE_DNS from vyos import ConfigError from vyos.configdict import list_diff @@ -459,11 +457,22 @@ class Interface(Control): >>> Interface('eth0').get_mac() '00:50:ab:cd:ef:00' """ - # calculate a UUID based on the interface name - this is as predictable - # as an interface MAC address and thus can be used in the same way - tmp = uuid3(NAMESPACE_DNS, self.ifname) - # take the last 48 bits from the UUID string - tmp = str(tmp).split('-')[-1] + from hashlib import sha256 + + # Get processor ID number + cpu_id = self._cmd('sudo dmidecode -t 4 | grep ID | head -n1 | sed "s/.*ID://;s/ //g"') + # Get system eth0 base MAC address - every system has eth0 + eth0_mac = Interface('eth0').get_mac() + + sha = sha256() + # Calculate SHA256 sum based on the CPU ID number, eth0 mac address and + # this interface identifier - this is as predictable as an interface + # MAC address and thus can be used in the same way + sha.update(cpu_id.encode()) + sha.update(eth0_mac.encode()) + sha.update(self.ifname.encode()) + # take the most significant 48 bits from the SHA256 string + tmp = sha.hexdigest()[:12] # Convert pseudo random string into EUI format which now represents a # MAC address tmp = EUI(tmp).value -- cgit v1.2.3 From 3bc79ff3cb40eeb36a33de7112d558abb96cb22f Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sun, 26 Sep 2021 13:08:37 +0200 Subject: ospf: T3757: add completion help when refering to area ID This extends commit 6f87d8c910 ("ospf: T3757: support to configure area at an interface level") with a completion helper to show which Area ID is already in use when configuring the area for an interface. --- interface-definitions/include/ospf/protocol-common-config.xml.i | 3 +++ 1 file changed, 3 insertions(+) diff --git a/interface-definitions/include/ospf/protocol-common-config.xml.i b/interface-definitions/include/ospf/protocol-common-config.xml.i index 80931ac15..982e519a9 100644 --- a/interface-definitions/include/ospf/protocol-common-config.xml.i +++ b/interface-definitions/include/ospf/protocol-common-config.xml.i @@ -364,6 +364,9 @@ Enable OSPF on this interface + + protocols ospf area + u32 OSPF area ID as decimal notation -- cgit v1.2.3 From 579c64f5ab5c6bc140f72045ca243fa3f2134ba3 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sun, 26 Sep 2021 13:17:08 +0200 Subject: op-mode: pki: T3826: perform input validation when listing certificates --- src/op_mode/pki.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/op_mode/pki.py b/src/op_mode/pki.py index e1428c581..2283cd820 100755 --- a/src/op_mode/pki.py +++ b/src/op_mode/pki.py @@ -858,8 +858,18 @@ if __name__ == '__main__': elif args.action == 'show': if args.ca: - show_certificate_authority(None if args.ca == 'all' else args.ca) + ca_name = None if args.ca == 'all' else args.ca + if ca_name: + if not conf.exists(['pki', 'ca', ca_name]): + print(f'CA "{ca_name}" does not exist!') + exit(1) + show_certificate_authority(ca_name) elif args.certificate: + cert_name = None if args.certificate == 'all' else args.certificate + if cert_name: + if not conf.exists(['pki', 'certificate', cert_name]): + print(f'Certificate "{cert_name}" does not exist!') + exit(1) show_certificate(None if args.certificate == 'all' else args.certificate) elif args.crl: show_crl(None if args.crl == 'all' else args.crl) -- cgit v1.2.3 From cf05add82e5469c9befefd6b52936c6b2fedda88 Mon Sep 17 00:00:00 2001 From: Daniil Baturin Date: Sun, 26 Sep 2021 10:11:23 -0500 Subject: T3866: ignore interfaces without "address" in DNS forwarding migration --- src/migration-scripts/dns-forwarding/1-to-2 | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/migration-scripts/dns-forwarding/1-to-2 b/src/migration-scripts/dns-forwarding/1-to-2 index 8c4f4b5c7..ba10c26f2 100755 --- a/src/migration-scripts/dns-forwarding/1-to-2 +++ b/src/migration-scripts/dns-forwarding/1-to-2 @@ -67,8 +67,14 @@ if config.exists(base + ['listen-on']): # retrieve corresponding interface addresses in CIDR format # those need to be converted in pure IP addresses without network information path = ['interfaces', section, intf, 'address'] - for addr in config.return_values(path): - listen_addr.append( ip_interface(addr).ip ) + try: + for addr in config.return_values(path): + listen_addr.append( ip_interface(addr).ip ) + except: + # Some interface types do not use "address" option (e.g. OpenVPN) + # and may not even have a fixed address + print("Could not retrieve the address of the interface {} from the config".format(intf)) + print("You will need to update your DNS forwarding configuration manually") for addr in listen_addr: config.set(base + ['listen-address'], value=addr, replace=False) -- cgit v1.2.3 From 0d7cd4ed5725d3e79faad5abc0801631c2ffc813 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sun, 26 Sep 2021 19:24:47 +0200 Subject: vxlan: T3867: add multicast validator for group address The group CLI node takes a multicast IPv4 or IPv6 address - this must be input validated to not case any OS exception cpo@LR1.wue3# show interfaces vxlan vxlan vxlan0 { + group 254.0.0.1 source-address 172.18.254.201 + source-interface dum0 vni 10 } Results in OSError beeing rasied with the following context: Error: argument "254.0.0.1" is wrong: invalid group address --- interface-definitions/interfaces-vxlan.xml.in | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/interface-definitions/interfaces-vxlan.xml.in b/interface-definitions/interfaces-vxlan.xml.in index 43b73a2e9..0a8a88596 100644 --- a/interface-definitions/interfaces-vxlan.xml.in +++ b/interface-definitions/interfaces-vxlan.xml.in @@ -31,8 +31,10 @@ Multicast IPv6 group address - + + + Multicast IPv4/IPv6 address required #include -- cgit v1.2.3 From b8a8b7f7919fa3973b3d27b9d92c60b0ae710de7 Mon Sep 17 00:00:00 2001 From: Viacheslav Date: Mon, 27 Sep 2021 17:42:56 +0000 Subject: nat66: T3853: Change priority to 500 Service ndppd should start after tunnels. --- interface-definitions/nat66.xml.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface-definitions/nat66.xml.in b/interface-definitions/nat66.xml.in index 077f0d5cf..11d986c96 100644 --- a/interface-definitions/nat66.xml.in +++ b/interface-definitions/nat66.xml.in @@ -3,7 +3,7 @@ IPv6-to-IPv6 Network Prefix Translation (NAT66/NPT) Settings - 220 + 500 -- cgit v1.2.3 From c6d0227d96e4191488464fc48f5a2b163af7b29e Mon Sep 17 00:00:00 2001 From: Viacheslav Date: Mon, 27 Sep 2021 12:04:31 +0000 Subject: openvpn: T690: Fix template for gateway and metric Some OpenVPN clients doesnt support option gateway and metric. Set metric option only when 'metric' was added in config explicity. (cherry picked from commit 96681d8bf1ede069b573a4cbe3a2493c374d048e) --- data/templates/openvpn/server.conf.tmpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/templates/openvpn/server.conf.tmpl b/data/templates/openvpn/server.conf.tmpl index 6aedc3786..bdf88b85f 100644 --- a/data/templates/openvpn/server.conf.tmpl +++ b/data/templates/openvpn/server.conf.tmpl @@ -76,7 +76,7 @@ server {{ subnet | address_from_cidr }} {{ subnet | netmask_from_cidr }} nopool {% if server.push_route is defined and server.push_route is not none %} {% for route, route_config in server.push_route.items() %} {% if route | is_ipv4 %} -push "route {{ route | address_from_cidr }} {{ route | netmask_from_cidr }} {{ subnet | first_host_address }} {{ route_config.metric if route_config.metric is defined else "0" }}" +push "route {{ route | address_from_cidr }} {{ route | netmask_from_cidr }}{% if route_config.metric is defined %} {{ subnet | first_host_address }} {{ route_config.metric }}{% endif %}" {% elif route | is_ipv6 %} push "route-ipv6 {{ route }}" {% endif %} -- cgit v1.2.3 From c488f55bd1cf14238ab5255a0354ce20b6c0e188 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Mon, 27 Sep 2021 20:28:22 +0200 Subject: smoketest: interface test base class for 802.1q should not extend testing to QoS --- smoketest/scripts/cli/base_interfaces_test.py | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/smoketest/scripts/cli/base_interfaces_test.py b/smoketest/scripts/cli/base_interfaces_test.py index 8a84199d9..9e1145c7c 100644 --- a/smoketest/scripts/cli/base_interfaces_test.py +++ b/smoketest/scripts/cli/base_interfaces_test.py @@ -286,30 +286,12 @@ class BasicInterfaceTest: base = self._base_path + [interface, 'vif', vlan] for address in self._test_addr: self.cli_set(base + ['address', address]) - self.cli_set(base + ['ingress-qos', '0:1']) - self.cli_set(base + ['egress-qos', '1:6']) self.cli_commit() for intf in self._interfaces: for vlan in self._vlan_range: vif = f'{intf}.{vlan}' - tmp = get_interface_config(f'{vif}') - - tmp2 = dict_search('linkinfo.info_data.ingress_qos', tmp) - for item in tmp2 if tmp2 else []: - from_key = item['from'] - to_key = item['to'] - self.assertEqual(from_key, 0) - self.assertEqual(to_key, 1) - - tmp2 = dict_search('linkinfo.info_data.egress_qos', tmp) - for item in tmp2 if tmp2 else []: - from_key = item['from'] - to_key = item['to'] - self.assertEqual(from_key, 1) - self.assertEqual(to_key, 6) - for address in self._test_addr: self.assertTrue(is_intf_addr_assigned(vif, address)) -- cgit v1.2.3 From 0f25b50ea8c0bf4b92a93db9392066d99d354449 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Mon, 27 Sep 2021 20:29:21 +0200 Subject: smoketest: interface test base class QoS cleanup There is no need to delete the old ingres-qos and egres-qos values as it's not a multi node and thus the values are simply overwritten. Also address validation is not required as it's done in a dedicates test. --- smoketest/scripts/cli/base_interfaces_test.py | 7 ------- 1 file changed, 7 deletions(-) diff --git a/smoketest/scripts/cli/base_interfaces_test.py b/smoketest/scripts/cli/base_interfaces_test.py index 9e1145c7c..90c534796 100644 --- a/smoketest/scripts/cli/base_interfaces_test.py +++ b/smoketest/scripts/cli/base_interfaces_test.py @@ -359,8 +359,6 @@ class BasicInterfaceTest: for vlan in self._vlan_range: base = self._base_path + [interface, 'vif', vlan] - for address in self._test_addr: - self.cli_set(base + ['address', address]) self.cli_set(base + ['ingress-qos', '0:1']) self.cli_set(base + ['egress-qos', '1:6']) @@ -385,9 +383,6 @@ class BasicInterfaceTest: self.assertEqual(from_key, 1) self.assertEqual(to_key, 6) - for address in self._test_addr: - self.assertTrue(is_intf_addr_assigned(vif, address)) - self.assertEqual(Interface(vif).get_admin_state(), 'up') new_ingress_qos_from = 1 @@ -398,8 +393,6 @@ class BasicInterfaceTest: base = self._base_path + [interface] for vlan in self._vlan_range: base = self._base_path + [interface, 'vif', vlan] - self.cli_delete(base + ['ingress-qos', '0:1']) - self.cli_delete(base + ['egress-qos', '1:6']) self.cli_set(base + ['ingress-qos', f'{new_ingress_qos_from}:{new_ingress_qos_to}']) self.cli_set(base + ['egress-qos', f'{new_egress_qos_from}:{new_egress_qos_to}']) -- cgit v1.2.3 From f4732f348ff4b0a9d82ec698f4a8b3c6107f9101 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Mon, 27 Sep 2021 20:43:02 +0200 Subject: frr: T2175: rename daemon Jinja2 templates to match (d)aemon suffix --- data/templates/frr/bfd.frr.tmpl | 44 ----------------- data/templates/frr/bfdd.frr.tmpl | 44 +++++++++++++++++ data/templates/frr/rip.frr.tmpl | 96 ------------------------------------- data/templates/frr/ripd.frr.tmpl | 96 +++++++++++++++++++++++++++++++++++++ data/templates/frr/ripng.frr.tmpl | 60 ----------------------- data/templates/frr/ripngd.frr.tmpl | 60 +++++++++++++++++++++++ data/templates/frr/static.frr.tmpl | 49 ------------------- data/templates/frr/staticd.frr.tmpl | 49 +++++++++++++++++++ src/conf_mode/protocols_bfd.py | 2 +- src/conf_mode/protocols_rip.py | 2 +- src/conf_mode/protocols_ripng.py | 2 +- src/conf_mode/protocols_static.py | 2 +- 12 files changed, 253 insertions(+), 253 deletions(-) delete mode 100644 data/templates/frr/bfd.frr.tmpl create mode 100644 data/templates/frr/bfdd.frr.tmpl delete mode 100644 data/templates/frr/rip.frr.tmpl create mode 100644 data/templates/frr/ripd.frr.tmpl delete mode 100644 data/templates/frr/ripng.frr.tmpl create mode 100644 data/templates/frr/ripngd.frr.tmpl delete mode 100644 data/templates/frr/static.frr.tmpl create mode 100644 data/templates/frr/staticd.frr.tmpl diff --git a/data/templates/frr/bfd.frr.tmpl b/data/templates/frr/bfd.frr.tmpl deleted file mode 100644 index 16f8be92c..000000000 --- a/data/templates/frr/bfd.frr.tmpl +++ /dev/null @@ -1,44 +0,0 @@ -! -bfd -{% if profile is defined and profile is not none %} -{% for profile_name, profile_config in profile.items() %} - profile {{ profile_name }} - detect-multiplier {{ profile_config.interval.multiplier }} - receive-interval {{ profile_config.interval.receive }} - transmit-interval {{ profile_config.interval.transmit }} -{% if profile_config.interval['echo-interval'] is defined and profile_config.interval['echo-interval'] is not none %} - echo-interval {{ profile_config.interval['echo-interval'] }} -{% endif %} -{% if profile_config['echo-mode'] is defined %} - echo-mode -{% endif %} -{% if profile_config.shutdown is defined %} - shutdown -{% else %} - no shutdown -{% endif %} - exit -{% endfor %} -{% endif %} -{% if peer is defined and peer is not none %} -{% for peer_name, peer_config in peer.items() %} - peer {{ peer_name }}{{ ' multihop' if peer_config.multihop is defined }}{{ ' local-address ' + peer_config.source.address if peer_config.source is defined and peer_config.source.address is defined }}{{ ' interface ' + peer_config.source.interface if peer_config.source is defined and peer_config.source.interface is defined }} - detect-multiplier {{ peer_config.interval.multiplier }} - receive-interval {{ peer_config.interval.receive }} - transmit-interval {{ peer_config.interval.transmit }} -{% if peer_config.interval['echo-interval'] is defined and peer_config.interval['echo-interval'] is not none %} - echo-interval {{ peer_config.interval['echo-interval'] }} -{% endif %} -{% if peer_config['echo-mode'] is defined %} - echo-mode -{% endif %} -{% if peer_config.shutdown is defined %} - shutdown -{% else %} - no shutdown -{% endif %} - exit -{% endfor %} -{% endif %} - end -! diff --git a/data/templates/frr/bfdd.frr.tmpl b/data/templates/frr/bfdd.frr.tmpl new file mode 100644 index 000000000..16f8be92c --- /dev/null +++ b/data/templates/frr/bfdd.frr.tmpl @@ -0,0 +1,44 @@ +! +bfd +{% if profile is defined and profile is not none %} +{% for profile_name, profile_config in profile.items() %} + profile {{ profile_name }} + detect-multiplier {{ profile_config.interval.multiplier }} + receive-interval {{ profile_config.interval.receive }} + transmit-interval {{ profile_config.interval.transmit }} +{% if profile_config.interval['echo-interval'] is defined and profile_config.interval['echo-interval'] is not none %} + echo-interval {{ profile_config.interval['echo-interval'] }} +{% endif %} +{% if profile_config['echo-mode'] is defined %} + echo-mode +{% endif %} +{% if profile_config.shutdown is defined %} + shutdown +{% else %} + no shutdown +{% endif %} + exit +{% endfor %} +{% endif %} +{% if peer is defined and peer is not none %} +{% for peer_name, peer_config in peer.items() %} + peer {{ peer_name }}{{ ' multihop' if peer_config.multihop is defined }}{{ ' local-address ' + peer_config.source.address if peer_config.source is defined and peer_config.source.address is defined }}{{ ' interface ' + peer_config.source.interface if peer_config.source is defined and peer_config.source.interface is defined }} + detect-multiplier {{ peer_config.interval.multiplier }} + receive-interval {{ peer_config.interval.receive }} + transmit-interval {{ peer_config.interval.transmit }} +{% if peer_config.interval['echo-interval'] is defined and peer_config.interval['echo-interval'] is not none %} + echo-interval {{ peer_config.interval['echo-interval'] }} +{% endif %} +{% if peer_config['echo-mode'] is defined %} + echo-mode +{% endif %} +{% if peer_config.shutdown is defined %} + shutdown +{% else %} + no shutdown +{% endif %} + exit +{% endfor %} +{% endif %} + end +! diff --git a/data/templates/frr/rip.frr.tmpl b/data/templates/frr/rip.frr.tmpl deleted file mode 100644 index cabc236f0..000000000 --- a/data/templates/frr/rip.frr.tmpl +++ /dev/null @@ -1,96 +0,0 @@ -! -{# RIP key-chain definition #} -{% if interface is defined and interface is not none %} -{% for iface, iface_config in interface.items() %} -{% if iface_config.authentication is defined and iface_config.authentication.md5 is defined and iface_config.authentication.md5 is not none %} -key chain {{ iface }}-rip -{% for key_id, key_options in iface_config.authentication.md5.items() %} - key {{ key_id }} -{% if key_options.password is defined and key_options.password is not none %} - key-string {{ key_options.password }} -{% endif %} -{% endfor %} -{% endif %} -{% endfor %} -{% endif %} -! -{# Interface specific configuration #} -{% if interface is defined and interface is not none %} -{% for iface, iface_config in interface.items() %} -interface {{ iface }} -{% if iface_config.authentication is defined and iface_config.authentication.plaintext_password is defined and iface_config.authentication.plaintext_password is not none %} - ip rip authentication mode text - ip rip authentication string {{ iface_config.authentication.plaintext_password }} -{% elif iface_config.authentication is defined and iface_config.authentication.md5 is defined and iface_config.authentication.md5 is not none %} - ip rip authentication key-chain {{ iface }}-rip - ip rip authentication mode md5 -{% endif %} -{% if iface_config.split_horizon is defined and iface_config.split_horizon.disable is defined %} - no ip rip split-horizon -{% endif %} -{% if iface_config.split_horizon is defined and iface_config.split_horizon.poison_reverse is defined %} - ip rip split-horizon poisoned-reverse -{% endif %} -{% endfor %} -{% endif %} -! -router rip -{% if default_distance is defined and default_distance is not none %} - distance {{ default_distance }} -{% endif %} -{% if network_distance is defined and network_distance is not none %} -{% for network, network_config in network_distance.items() %} -{% if network_config.distance is defined and network_config.distance is not none %} - distance {{ network_config.distance }} {{ network }} -{% endif %} -{% endfor %} -{% endif %} -{% if neighbor is defined and neighbor is not none %} -{% for address in neighbor %} - neighbor {{ address }} -{% endfor %} -{% endif %} -{% if distribute_list is defined and distribute_list is not none %} -{% if distribute_list.access_list is defined and distribute_list.access_list is not none %} -{% if distribute_list.access_list.in is defined and distribute_list.access_list.in is not none %} - distribute-list {{ distribute_list.access_list.in }} in -{% endif %} -{% if distribute_list.access_list.out is defined and distribute_list.access_list.out is not none %} - distribute-list {{ distribute_list.access_list.out }} out -{% endif %} -{% endif %} -{% if distribute_list.interface is defined and distribute_list.interface is not none %} -{% for interface, interface_config in distribute_list.interface.items() %} -{% if interface_config.access_list is defined and interface_config.access_list is not none %} -{% if interface_config.access_list.in is defined and interface_config.access_list.in is not none %} - distribute-list {{ interface_config.access_list.in }} in {{ interface }} -{% endif %} -{% if interface_config.access_list.out is defined and interface_config.access_list.out is not none %} - distribute-list {{ interface_config.access_list.out }} out {{ interface }} -{% endif %} -{% endif %} -{% if interface_config.prefix_list is defined and interface_config.prefix_list is not none %} -{% if interface_config.prefix_list.in is defined and interface_config.prefix_list.in is not none %} - distribute-list prefix {{ interface_config.prefix_list.in }} in {{ interface }} -{% endif %} -{% if interface_config.prefix_list.out is defined and interface_config.prefix_list.out is not none %} - distribute-list prefix {{ interface_config.prefix_list.out }} out {{ interface }} -{% endif %} -{% endif %} -{% endfor %} -{% endif %} -{% if distribute_list.prefix_list is defined and distribute_list.prefix_list is not none %} -{% if distribute_list.prefix_list.in is defined and distribute_list.prefix_list.in is not none %} - distribute-list prefix {{ distribute_list.prefix_list.in }} in -{% endif %} -{% if distribute_list.prefix_list.out is defined and distribute_list.prefix_list.out is not none %} - distribute-list prefix {{ distribute_list.prefix_list.out }} out -{% endif %} -{% endif %} -{% endif %} -{% include 'frr/rip_ripng.frr.j2' %} -! -{% if route_map is defined and route_map is not none %} -ip protocol rip route-map {{ route_map }} -{% endif %} -! diff --git a/data/templates/frr/ripd.frr.tmpl b/data/templates/frr/ripd.frr.tmpl new file mode 100644 index 000000000..cabc236f0 --- /dev/null +++ b/data/templates/frr/ripd.frr.tmpl @@ -0,0 +1,96 @@ +! +{# RIP key-chain definition #} +{% if interface is defined and interface is not none %} +{% for iface, iface_config in interface.items() %} +{% if iface_config.authentication is defined and iface_config.authentication.md5 is defined and iface_config.authentication.md5 is not none %} +key chain {{ iface }}-rip +{% for key_id, key_options in iface_config.authentication.md5.items() %} + key {{ key_id }} +{% if key_options.password is defined and key_options.password is not none %} + key-string {{ key_options.password }} +{% endif %} +{% endfor %} +{% endif %} +{% endfor %} +{% endif %} +! +{# Interface specific configuration #} +{% if interface is defined and interface is not none %} +{% for iface, iface_config in interface.items() %} +interface {{ iface }} +{% if iface_config.authentication is defined and iface_config.authentication.plaintext_password is defined and iface_config.authentication.plaintext_password is not none %} + ip rip authentication mode text + ip rip authentication string {{ iface_config.authentication.plaintext_password }} +{% elif iface_config.authentication is defined and iface_config.authentication.md5 is defined and iface_config.authentication.md5 is not none %} + ip rip authentication key-chain {{ iface }}-rip + ip rip authentication mode md5 +{% endif %} +{% if iface_config.split_horizon is defined and iface_config.split_horizon.disable is defined %} + no ip rip split-horizon +{% endif %} +{% if iface_config.split_horizon is defined and iface_config.split_horizon.poison_reverse is defined %} + ip rip split-horizon poisoned-reverse +{% endif %} +{% endfor %} +{% endif %} +! +router rip +{% if default_distance is defined and default_distance is not none %} + distance {{ default_distance }} +{% endif %} +{% if network_distance is defined and network_distance is not none %} +{% for network, network_config in network_distance.items() %} +{% if network_config.distance is defined and network_config.distance is not none %} + distance {{ network_config.distance }} {{ network }} +{% endif %} +{% endfor %} +{% endif %} +{% if neighbor is defined and neighbor is not none %} +{% for address in neighbor %} + neighbor {{ address }} +{% endfor %} +{% endif %} +{% if distribute_list is defined and distribute_list is not none %} +{% if distribute_list.access_list is defined and distribute_list.access_list is not none %} +{% if distribute_list.access_list.in is defined and distribute_list.access_list.in is not none %} + distribute-list {{ distribute_list.access_list.in }} in +{% endif %} +{% if distribute_list.access_list.out is defined and distribute_list.access_list.out is not none %} + distribute-list {{ distribute_list.access_list.out }} out +{% endif %} +{% endif %} +{% if distribute_list.interface is defined and distribute_list.interface is not none %} +{% for interface, interface_config in distribute_list.interface.items() %} +{% if interface_config.access_list is defined and interface_config.access_list is not none %} +{% if interface_config.access_list.in is defined and interface_config.access_list.in is not none %} + distribute-list {{ interface_config.access_list.in }} in {{ interface }} +{% endif %} +{% if interface_config.access_list.out is defined and interface_config.access_list.out is not none %} + distribute-list {{ interface_config.access_list.out }} out {{ interface }} +{% endif %} +{% endif %} +{% if interface_config.prefix_list is defined and interface_config.prefix_list is not none %} +{% if interface_config.prefix_list.in is defined and interface_config.prefix_list.in is not none %} + distribute-list prefix {{ interface_config.prefix_list.in }} in {{ interface }} +{% endif %} +{% if interface_config.prefix_list.out is defined and interface_config.prefix_list.out is not none %} + distribute-list prefix {{ interface_config.prefix_list.out }} out {{ interface }} +{% endif %} +{% endif %} +{% endfor %} +{% endif %} +{% if distribute_list.prefix_list is defined and distribute_list.prefix_list is not none %} +{% if distribute_list.prefix_list.in is defined and distribute_list.prefix_list.in is not none %} + distribute-list prefix {{ distribute_list.prefix_list.in }} in +{% endif %} +{% if distribute_list.prefix_list.out is defined and distribute_list.prefix_list.out is not none %} + distribute-list prefix {{ distribute_list.prefix_list.out }} out +{% endif %} +{% endif %} +{% endif %} +{% include 'frr/rip_ripng.frr.j2' %} +! +{% if route_map is defined and route_map is not none %} +ip protocol rip route-map {{ route_map }} +{% endif %} +! diff --git a/data/templates/frr/ripng.frr.tmpl b/data/templates/frr/ripng.frr.tmpl deleted file mode 100644 index 25df15121..000000000 --- a/data/templates/frr/ripng.frr.tmpl +++ /dev/null @@ -1,60 +0,0 @@ -! -{# Interface specific configuration #} -{% if interface is defined and interface is not none %} -{% for iface, iface_config in interface.items() %} -interface {{ iface }} -{% if iface_config.split_horizon is defined and iface_config.split_horizon.disable is defined %} - no ipv6 rip split-horizon -{% endif %} -{% if iface_config.split_horizon is defined and iface_config.split_horizon.poison_reverse is defined %} - ipv6 rip split-horizon poisoned-reverse -{% endif %} -{% endfor %} -{% endif %} -! -router ripng -{% if aggregate_address is defined and aggregate_address is not none %} -{% for prefix in aggregate_address %} - aggregate-address {{ prefix }} -{% endfor %} -{% endif %} -{% if distribute_list is defined and distribute_list is not none %} -{% if distribute_list.access_list is defined and distribute_list.access_list is not none %} -{% if distribute_list.access_list.in is defined and distribute_list.access_list.in is not none %} - ipv6 distribute-list {{ distribute_list.access_list.in }} in -{% endif %} -{% if distribute_list.access_list.out is defined and distribute_list.access_list.out is not none %} - ipv6 distribute-list {{ distribute_list.access_list.out }} out -{% endif %} -{% endif %} -{% if distribute_list.interface is defined and distribute_list.interface is not none %} -{% for interface, interface_config in distribute_list.interface.items() %} -{% if interface_config.access_list is defined and interface_config.access_list is not none %} -{% if interface_config.access_list.in is defined and interface_config.access_list.in is not none %} - ipv6 distribute-list {{ interface_config.access_list.in }} in {{ interface }} -{% endif %} -{% if interface_config.access_list.out is defined and interface_config.access_list.out is not none %} - ipv6 distribute-list {{ interface_config.access_list.out }} out {{ interface }} -{% endif %} -{% endif %} -{% if interface_config.prefix_list is defined and interface_config.prefix_list is not none %} -{% if interface_config.prefix_list.in is defined and interface_config.prefix_list.in is not none %} - ipv6 distribute-list prefix {{ interface_config.prefix_list.in }} in {{ interface }} -{% endif %} -{% if interface_config.prefix_list.out is defined and interface_config.prefix_list.out is not none %} - ipv6 distribute-list prefix {{ interface_config.prefix_list.out }} out {{ interface }} -{% endif %} -{% endif %} -{% endfor %} -{% endif %} -{% if distribute_list.prefix_list is defined and distribute_list.prefix_list is not none %} -{% if distribute_list.prefix_list.in is defined and distribute_list.prefix_list.in is not none %} - ipv6 distribute-list prefix {{ distribute_list.prefix_list.in }} in -{% endif %} -{% if distribute_list.prefix_list.out is defined and distribute_list.prefix_list.out is not none %} - ipv6 distribute-list prefix {{ distribute_list.prefix_list.out }} out -{% endif %} -{% endif %} -{% endif %} -{% include 'frr/rip_ripng.frr.j2' %} -! diff --git a/data/templates/frr/ripngd.frr.tmpl b/data/templates/frr/ripngd.frr.tmpl new file mode 100644 index 000000000..25df15121 --- /dev/null +++ b/data/templates/frr/ripngd.frr.tmpl @@ -0,0 +1,60 @@ +! +{# Interface specific configuration #} +{% if interface is defined and interface is not none %} +{% for iface, iface_config in interface.items() %} +interface {{ iface }} +{% if iface_config.split_horizon is defined and iface_config.split_horizon.disable is defined %} + no ipv6 rip split-horizon +{% endif %} +{% if iface_config.split_horizon is defined and iface_config.split_horizon.poison_reverse is defined %} + ipv6 rip split-horizon poisoned-reverse +{% endif %} +{% endfor %} +{% endif %} +! +router ripng +{% if aggregate_address is defined and aggregate_address is not none %} +{% for prefix in aggregate_address %} + aggregate-address {{ prefix }} +{% endfor %} +{% endif %} +{% if distribute_list is defined and distribute_list is not none %} +{% if distribute_list.access_list is defined and distribute_list.access_list is not none %} +{% if distribute_list.access_list.in is defined and distribute_list.access_list.in is not none %} + ipv6 distribute-list {{ distribute_list.access_list.in }} in +{% endif %} +{% if distribute_list.access_list.out is defined and distribute_list.access_list.out is not none %} + ipv6 distribute-list {{ distribute_list.access_list.out }} out +{% endif %} +{% endif %} +{% if distribute_list.interface is defined and distribute_list.interface is not none %} +{% for interface, interface_config in distribute_list.interface.items() %} +{% if interface_config.access_list is defined and interface_config.access_list is not none %} +{% if interface_config.access_list.in is defined and interface_config.access_list.in is not none %} + ipv6 distribute-list {{ interface_config.access_list.in }} in {{ interface }} +{% endif %} +{% if interface_config.access_list.out is defined and interface_config.access_list.out is not none %} + ipv6 distribute-list {{ interface_config.access_list.out }} out {{ interface }} +{% endif %} +{% endif %} +{% if interface_config.prefix_list is defined and interface_config.prefix_list is not none %} +{% if interface_config.prefix_list.in is defined and interface_config.prefix_list.in is not none %} + ipv6 distribute-list prefix {{ interface_config.prefix_list.in }} in {{ interface }} +{% endif %} +{% if interface_config.prefix_list.out is defined and interface_config.prefix_list.out is not none %} + ipv6 distribute-list prefix {{ interface_config.prefix_list.out }} out {{ interface }} +{% endif %} +{% endif %} +{% endfor %} +{% endif %} +{% if distribute_list.prefix_list is defined and distribute_list.prefix_list is not none %} +{% if distribute_list.prefix_list.in is defined and distribute_list.prefix_list.in is not none %} + ipv6 distribute-list prefix {{ distribute_list.prefix_list.in }} in +{% endif %} +{% if distribute_list.prefix_list.out is defined and distribute_list.prefix_list.out is not none %} + ipv6 distribute-list prefix {{ distribute_list.prefix_list.out }} out +{% endif %} +{% endif %} +{% endif %} +{% include 'frr/rip_ripng.frr.j2' %} +! diff --git a/data/templates/frr/static.frr.tmpl b/data/templates/frr/static.frr.tmpl deleted file mode 100644 index db59a44c2..000000000 --- a/data/templates/frr/static.frr.tmpl +++ /dev/null @@ -1,49 +0,0 @@ -{% from 'frr/static_routes_macro.j2' import static_routes %} -! -{% set ip_prefix = 'ip' %} -{% set ipv6_prefix = 'ipv6' %} -{% if vrf is defined and vrf is not none %} -{# We need to add an additional whitespace in front of the prefix #} -{# when VRFs are in use, thus we use a variable for prefix handling #} -{% set ip_prefix = ' ip' %} -{% set ipv6_prefix = ' ipv6' %} -vrf {{ vrf }} -{% endif %} -{# IPv4 routing #} -{% if route is defined and route is not none %} -{% for prefix, prefix_config in route.items() %} -{{ static_routes(ip_prefix, prefix, prefix_config) }} -{%- endfor -%} -{% endif %} -{# IPv6 routing #} -{% if route6 is defined and route6 is not none %} -{% for prefix, prefix_config in route6.items() %} -{{ static_routes(ipv6_prefix, prefix, prefix_config) }} -{%- endfor -%} -{% endif %} -{% if vrf is defined and vrf is not none %} - exit-vrf -{% endif %} -! -{# Policy route tables #} -{% if table is defined and table is not none %} -{% for table_id, table_config in table.items() %} -{% if table_config.route is defined and table_config.route is not none %} -{% for prefix, prefix_config in table_config.route.items() %} -{{ static_routes('ip', prefix, prefix_config, table_id) }} -{%- endfor -%} -{% endif %} -! -{% if table_config.route6 is defined and table_config.route6 is not none %} -{% for prefix, prefix_config in table_config.route6.items() %} -{{ static_routes('ipv6', prefix, prefix_config, table_id) }} -{%- endfor -%} -{% endif %} -! -{% endfor %} -{% endif %} -! -{% if route_map is defined and route_map is not none %} -ip protocol static route-map {{ route_map }} -! -{% endif %} diff --git a/data/templates/frr/staticd.frr.tmpl b/data/templates/frr/staticd.frr.tmpl new file mode 100644 index 000000000..db59a44c2 --- /dev/null +++ b/data/templates/frr/staticd.frr.tmpl @@ -0,0 +1,49 @@ +{% from 'frr/static_routes_macro.j2' import static_routes %} +! +{% set ip_prefix = 'ip' %} +{% set ipv6_prefix = 'ipv6' %} +{% if vrf is defined and vrf is not none %} +{# We need to add an additional whitespace in front of the prefix #} +{# when VRFs are in use, thus we use a variable for prefix handling #} +{% set ip_prefix = ' ip' %} +{% set ipv6_prefix = ' ipv6' %} +vrf {{ vrf }} +{% endif %} +{# IPv4 routing #} +{% if route is defined and route is not none %} +{% for prefix, prefix_config in route.items() %} +{{ static_routes(ip_prefix, prefix, prefix_config) }} +{%- endfor -%} +{% endif %} +{# IPv6 routing #} +{% if route6 is defined and route6 is not none %} +{% for prefix, prefix_config in route6.items() %} +{{ static_routes(ipv6_prefix, prefix, prefix_config) }} +{%- endfor -%} +{% endif %} +{% if vrf is defined and vrf is not none %} + exit-vrf +{% endif %} +! +{# Policy route tables #} +{% if table is defined and table is not none %} +{% for table_id, table_config in table.items() %} +{% if table_config.route is defined and table_config.route is not none %} +{% for prefix, prefix_config in table_config.route.items() %} +{{ static_routes('ip', prefix, prefix_config, table_id) }} +{%- endfor -%} +{% endif %} +! +{% if table_config.route6 is defined and table_config.route6 is not none %} +{% for prefix, prefix_config in table_config.route6.items() %} +{{ static_routes('ipv6', prefix, prefix_config, table_id) }} +{%- endfor -%} +{% endif %} +! +{% endfor %} +{% endif %} +! +{% if route_map is defined and route_map is not none %} +ip protocol static route-map {{ route_map }} +! +{% endif %} diff --git a/src/conf_mode/protocols_bfd.py b/src/conf_mode/protocols_bfd.py index 348bae59f..539fd7b8e 100755 --- a/src/conf_mode/protocols_bfd.py +++ b/src/conf_mode/protocols_bfd.py @@ -92,7 +92,7 @@ def generate(bfd): bfd['new_frr_config'] = '' return None - bfd['new_frr_config'] = render_to_string('frr/bfd.frr.tmpl', bfd) + bfd['new_frr_config'] = render_to_string('frr/bfdd.frr.tmpl', bfd) def apply(bfd): # Save original configuration prior to starting any commit actions diff --git a/src/conf_mode/protocols_rip.py b/src/conf_mode/protocols_rip.py index e56eb1f56..6b78f6f2d 100755 --- a/src/conf_mode/protocols_rip.py +++ b/src/conf_mode/protocols_rip.py @@ -93,7 +93,7 @@ def generate(rip): rip['new_frr_config'] = '' return None - rip['new_frr_config'] = render_to_string('frr/rip.frr.tmpl', rip) + rip['new_frr_config'] = render_to_string('frr/ripd.frr.tmpl', rip) return None diff --git a/src/conf_mode/protocols_ripng.py b/src/conf_mode/protocols_ripng.py index aaec5dacb..bc4954f63 100755 --- a/src/conf_mode/protocols_ripng.py +++ b/src/conf_mode/protocols_ripng.py @@ -95,7 +95,7 @@ def generate(ripng): ripng['new_frr_config'] = '' return None - ripng['new_frr_config'] = render_to_string('frr/ripng.frr.tmpl', ripng) + ripng['new_frr_config'] = render_to_string('frr/ripngd.frr.tmpl', ripng) return None def apply(ripng): diff --git a/src/conf_mode/protocols_static.py b/src/conf_mode/protocols_static.py index 338247e30..597fcc443 100755 --- a/src/conf_mode/protocols_static.py +++ b/src/conf_mode/protocols_static.py @@ -80,7 +80,7 @@ def verify(static): return None def generate(static): - static['new_frr_config'] = render_to_string('frr/static.frr.tmpl', static) + static['new_frr_config'] = render_to_string('frr/staticd.frr.tmpl', static) return None def apply(static): -- cgit v1.2.3 From a0202eaf489cfc59ee5358621ab674a5a515c978 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Mon, 27 Sep 2021 20:52:09 +0200 Subject: igmp: T2230: fix Jinja2 and FRR indention --- data/templates/frr/igmp.frr.tmpl | 60 ++++++++++++++++++++-------------------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/data/templates/frr/igmp.frr.tmpl b/data/templates/frr/igmp.frr.tmpl index cdb7ee6cc..49b5aeaa5 100644 --- a/data/templates/frr/igmp.frr.tmpl +++ b/data/templates/frr/igmp.frr.tmpl @@ -1,41 +1,41 @@ ! {% for iface in old_ifaces %} interface {{ iface }} -{% for group in old_ifaces[iface].gr_join %} -{% if old_ifaces[iface].gr_join[group] %} -{% for source in old_ifaces[iface].gr_join[group] %} -no ip igmp join {{ group }} {{ source }} -{% endfor %} -{% else %} -no ip igmp join {{ group }} -{% endif %} -{% endfor %} -no ip igmp +{% for group in old_ifaces[iface].gr_join %} +{% if old_ifaces[iface].gr_join[group] %} +{% for source in old_ifaces[iface].gr_join[group] %} + no ip igmp join {{ group }} {{ source }} +{% endfor %} +{% else %} + no ip igmp join {{ group }} +{% endif %} +{% endfor %} + no ip igmp ! {% endfor %} {% for iface in ifaces %} interface {{ iface }} -{% if ifaces[iface].version %} -ip igmp version {{ ifaces[iface].version }} -{% else %} +{% if ifaces[iface].version %} + ip igmp version {{ ifaces[iface].version }} +{% else %} {# IGMP default version 3 #} -ip igmp -{% endif %} -{% if ifaces[iface].query_interval %} -ip igmp query-interval {{ ifaces[iface].query_interval }} -{% endif %} -{% if ifaces[iface].query_max_resp_time %} -ip igmp query-max-response-time {{ ifaces[iface].query_max_resp_time }} -{% endif %} -{% for group in ifaces[iface].gr_join %} -{% if ifaces[iface].gr_join[group] %} -{% for source in ifaces[iface].gr_join[group] %} -ip igmp join {{ group }} {{ source }} -{% endfor %} -{% else %} -ip igmp join {{ group }} -{% endif %} -{% endfor %} + ip igmp +{% endif %} +{% if ifaces[iface].query_interval %} + ip igmp query-interval {{ ifaces[iface].query_interval }} +{% endif %} +{% if ifaces[iface].query_max_resp_time %} + ip igmp query-max-response-time {{ ifaces[iface].query_max_resp_time }} +{% endif %} +{% for group in ifaces[iface].gr_join %} +{% if ifaces[iface].gr_join[group] %} +{% for source in ifaces[iface].gr_join[group] %} + ip igmp join {{ group }} {{ source }} +{% endfor %} +{% else %} + ip igmp join {{ group }} +{% endif %} +{% endfor %} ! {% endfor %} ! -- cgit v1.2.3 From 17dc7cd0aaca5c4ae14d3dc843de7a5b612ab5ed Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Mon, 27 Sep 2021 20:53:57 +0200 Subject: nat66: T3863: ndppd requires interfaces to be present --- src/conf_mode/nat66.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/conf_mode/nat66.py b/src/conf_mode/nat66.py index f8bc073bb..fb376a434 100755 --- a/src/conf_mode/nat66.py +++ b/src/conf_mode/nat66.py @@ -117,7 +117,7 @@ def verify(nat): raise ConfigError(f'{err_msg} outbound-interface not specified') if config['outbound_interface'] not in interfaces(): - print(f'WARNING: rule "{rule}" interface "{config["outbound_interface"]}" does not exist on this system') + raise ConfigError(f'WARNING: rule "{rule}" interface "{config["outbound_interface"]}" does not exist on this system') addr = dict_search('translation.address', config) if addr != None: -- cgit v1.2.3 From bf196240de573c4aaf09f07dd6d196b3aadbcf52 Mon Sep 17 00:00:00 2001 From: John Estabrook Date: Tue, 21 Sep 2021 11:01:32 -0500 Subject: interface-names: T3869: add /run/udev/vyos to defaults --- python/vyos/defaults.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/python/vyos/defaults.py b/python/vyos/defaults.py index dacdbdef2..00b14a985 100644 --- a/python/vyos/defaults.py +++ b/python/vyos/defaults.py @@ -25,8 +25,8 @@ directories = { "templates": "/usr/share/vyos/templates/", "certbot": "/config/auth/letsencrypt", "api_schema": "/usr/libexec/vyos/services/api/graphql/graphql/schema/", - "api_templates": "/usr/libexec/vyos/services/api/graphql/recipes/templates/" - + "api_templates": "/usr/libexec/vyos/services/api/graphql/recipes/templates/", + "vyos_udev_dir": "/run/udev/vyos" } cfg_group = 'vyattacfg' -- cgit v1.2.3 From b55aa569b87a34a1902a9674437b645407eda519 Mon Sep 17 00:00:00 2001 From: John Estabrook Date: Tue, 21 Sep 2021 11:00:51 -0500 Subject: interface-names: T3869: add vyos_interface_rescan --- src/helpers/vyos-interface-rescan.py | 206 +++++++++++++++++++++++++++++++++++ 1 file changed, 206 insertions(+) create mode 100755 src/helpers/vyos-interface-rescan.py diff --git a/src/helpers/vyos-interface-rescan.py b/src/helpers/vyos-interface-rescan.py new file mode 100755 index 000000000..1ac1810e0 --- /dev/null +++ b/src/helpers/vyos-interface-rescan.py @@ -0,0 +1,206 @@ +#!/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 +import stat +import argparse +import logging +import netaddr + +from vyos.configtree import ConfigTree +from vyos.defaults import directories +from vyos.util import get_cfg_group_id + +debug = False + +vyos_udev_dir = directories['vyos_udev_dir'] +vyos_log_dir = directories['log'] +log_file = os.path.splitext(os.path.basename(__file__))[0] +vyos_log_file = os.path.join(vyos_log_dir, log_file) + +logger = logging.getLogger(__name__) +handler = logging.FileHandler(vyos_log_file, mode='a') +formatter = logging.Formatter('%(levelname)s: %(message)s') +handler.setFormatter(formatter) +logger.addHandler(handler) + +passlist = { + '02:07:01' : 'Interlan', + '02:60:60' : '3Com', + '02:60:8c' : '3Com', + '02:a0:c9' : 'Intel', + '02:aa:3c' : 'Olivetti', + '02:cf:1f' : 'CMC', + '02:e0:3b' : 'Prominet', + '02:e6:d3' : 'BTI', + '52:54:00' : 'Realtek', + '52:54:4c' : 'Novell 2000', + '52:54:ab' : 'Realtec', + 'e2:0c:0f' : 'Kingston Technologies' +} + +def is_multicast(addr: netaddr.eui.EUI) -> bool: + return bool(addr.words[0] & 0b1) + +def is_locally_administered(addr: netaddr.eui.EUI) -> bool: + return bool(addr.words[0] & 0b10) + +def is_on_passlist(hwid: str) -> bool: + top = hwid.rsplit(':', 3)[0] + if top in list(passlist): + return True + return False + +def is_persistent(hwid: str) -> bool: + addr = netaddr.EUI(hwid) + if is_multicast(addr): + return False + if is_locally_administered(addr) and not is_on_passlist(hwid): + return False + return True + +def get_wireless_physical_device(intf: str) -> str: + if 'wlan' not in intf: + return '' + try: + tmp = os.readlink(f'/sys/class/net/{intf}/phy80211') + except OSError: + logger.critical(f"Failed to read '/sys/class/net/{intf}/phy80211'") + return '' + phy = os.path.basename(tmp) + logger.info(f"wireless phy is {phy}") + return phy + +def get_interface_type(intf: str) -> str: + if 'eth' in intf: + intf_type = 'ethernet' + elif 'wlan' in intf: + intf_type = 'wireless' + else: + logger.critical('Unrecognized interface type!') + intf_type = '' + return intf_type + +def get_new_interfaces() -> dict: + """ Read any new interface data left in /run/udev/vyos by vyos_net_name + """ + interfaces = {} + + for intf in os.listdir(vyos_udev_dir): + path = os.path.join(vyos_udev_dir, intf) + try: + with open(path) as f: + hwid = f.read().rstrip() + except OSError as e: + logger.error(f"OSError {e}") + continue + interfaces[intf] = hwid + + # reverse sort to simplify insertion in config + interfaces = {key: value for key, value in sorted(interfaces.items(), + reverse=True)} + return interfaces + +def filter_interfaces(intfs: dict) -> dict: + """ Ignore no longer existing interfaces or non-persistent mac addresses + """ + filtered = {} + + for intf, hwid in intfs.items(): + if not os.path.isdir(os.path.join('/sys/class/net', intf)): + continue + if not is_persistent(hwid): + continue + filtered[intf] = hwid + + return filtered + +def interface_rescan(config_path: str): + """ Read new data and update config file + """ + interfaces = get_new_interfaces() + + logger.debug(f"interfaces from udev: {interfaces}") + + interfaces = filter_interfaces(interfaces) + + logger.debug(f"filtered interfaces: {interfaces}") + + try: + with open(config_path) as f: + config_file = f.read() + except OSError as e: + logger.critical(f"OSError {e}") + exit(1) + + config = ConfigTree(config_file) + + for intf, hwid in interfaces.items(): + logger.info(f"Writing '{intf}' '{hwid}' to config file") + intf_type = get_interface_type(intf) + if not intf_type: + continue + if not config.exists(['interfaces', intf_type]): + config.set(['interfaces', intf_type]) + config.set_tag(['interfaces', intf_type]) + config.set(['interfaces', intf_type, intf, 'hw-id'], value=hwid) + + if intf_type == 'wireless': + phy = get_wireless_physical_device(intf) + if not phy: + continue + config.set(['interfaces', intf_type, intf, 'physical-device'], + value=phy) + + try: + with open(config_path, 'w') as f: + f.write(config.to_string()) + except OSError as e: + logger.critical(f"OSError {e}") + +def main(): + global debug + + argparser = argparse.ArgumentParser( + formatter_class=argparse.RawTextHelpFormatter) + argparser.add_argument('configfile', type=str) + argparser.add_argument('--debug', action='store_true') + args = argparser.parse_args() + + if args.debug: + debug = True + logger.setLevel(logging.DEBUG) + else: + logger.setLevel(logging.INFO) + + configfile = args.configfile + + # preserve vyattacfg group write access to running config + os.setgid(get_cfg_group_id()) + os.umask(0o002) + + # log file perms are not automatic; this could be cleaner by moving to a + # logging config file + os.chown(vyos_log_file, 0, get_cfg_group_id()) + os.chmod(vyos_log_file, + stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP | stat.S_IWGRP | stat.S_IROTH) + + interface_rescan(configfile) + +if __name__ == '__main__': + main() -- cgit v1.2.3 From e90ee6852e25a3a87b3f8333ed588197d3782408 Mon Sep 17 00:00:00 2001 From: John Estabrook Date: Sun, 26 Sep 2021 19:37:39 -0500 Subject: interface-names: T3869: add vyos_net_name --- src/helpers/vyos_net_name | 229 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 229 insertions(+) create mode 100755 src/helpers/vyos_net_name diff --git a/src/helpers/vyos_net_name b/src/helpers/vyos_net_name new file mode 100755 index 000000000..0652e98b1 --- /dev/null +++ b/src/helpers/vyos_net_name @@ -0,0 +1,229 @@ +#!/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 +import re +import time +import logging +import threading +from sys import argv + +from vyos.configtree import ConfigTree +from vyos.defaults import directories +from vyos.util import cmd + +vyos_udev_dir = directories['vyos_udev_dir'] +vyos_log_dir = '/run/udev/log' +vyos_log_file = os.path.join(vyos_log_dir, 'vyos-net-name') + +config_path = '/opt/vyatta/etc/config/config.boot' +config_status = '/tmp/vyos-config-status' + +lock = threading.Lock() + +try: + os.mkdir(vyos_log_dir) +except FileExistsError: + pass + +logging.basicConfig(filename=vyos_log_file, level=logging.DEBUG) + +def boot_configuration_complete() -> bool: + """ Check if vyos-router has completed, hence hotplug event + """ + if os.path.isfile(config_status): + return True + return False + +def is_available(intfs: dict, intf_name: str) -> bool: + """ Check if interface name is already assigned + """ + if intf_name in list(intfs.values()): + return False + return True + +def find_available(intfs: dict, prefix: str) -> str: + """ Find lowest indexed iterface name that is not assigned + """ + index_list = [int(x.replace(prefix, '')) for x in list(intfs.values()) if prefix in x] + index_list.sort() + # find 'holes' in list, if any + missing = sorted(set(range(index_list[0], index_list[-1])) - set(index_list)) + if missing: + return f'{prefix}{missing[0]}' + + return f'{prefix}{len(index_list)}' + +def get_biosdevname(ifname: str) -> str: + """ Use legacy vyatta-biosdevname to query for name + + This is carried over for compatability only, and will likely be dropped + going forward. + XXX: This throws an error, and likely has for a long time, unnoticed + since vyatta_net_name redirected stderr to /dev/null. + """ + if 'eth' not in ifname: + return ifname + if os.path.isdir('/proc/xen'): + return ifname + + time.sleep(1) + + try: + biosname = cmd(f'/sbin/biosdevname --policy all_ethN -i {ifname}') + except Exception as e: + logging.error(f'biosdevname error: {e}') + biosname = '' + + return ifname if biosname == '' else biosname + +def leave_rescan_hint(intf_name: str, hwid: str): + """Write interface information reported by udev + + This script is called while the root mount is still read-only. Leave + information in /run/udev: file name, the interface; contents, the + hardware id. + """ + try: + os.mkdir(vyos_udev_dir) + except FileExistsError: + pass + except Exception as e: + logging.critical(f"Error creating rescan hint directory: {e}") + exit(1) + + try: + with open(os.path.join(vyos_udev_dir, intf_name), 'w') as f: + f.write(hwid) + except OSError as e: + logging.critical(f"OSError {e}") + +def get_configfile_interfaces() -> dict: + """Read existing interfaces from config file + """ + interfaces: dict = {} + + if not os.path.isfile(config_path): + # If the case, then we are running off of livecd; return empty + return interfaces + + try: + with open(config_path) as f: + config_file = f.read() + except OSError as e: + logging.critical(f"OSError {e}") + exit(1) + + config = ConfigTree(config_file) + + base = ['interfaces', 'ethernet'] + if config.exists(base): + eth_intfs = config.list_nodes(base) + for intf in eth_intfs: + path = base + [intf, 'hw-id'] + if not config.exists(path): + logging.warning(f"no 'hw-id' entry for {intf}") + continue + hwid = config.return_value(path) + if hwid in list(interfaces): + logging.warning(f"multiple entries for {hwid}: {interfaces[hwid]}, {intf}") + continue + interfaces[hwid] = intf + + base = ['interfaces', 'wireless'] + if config.exists(base): + wlan_intfs = config.list_nodes(base) + for intf in wlan_intfs: + path = base + [intf, 'hw-id'] + if not config.exists(path): + logging.warning(f"no 'hw-id' entry for {intf}") + continue + hwid = config.return_value(path) + if hwid in list(interfaces): + logging.warning(f"multiple entries for {hwid}: {interfaces[hwid]}, {intf}") + continue + interfaces[hwid] = intf + + logging.debug(f"config file entries: {interfaces}") + + return interfaces + +def add_assigned_interfaces(intfs: dict): + """Add interfaces found by previous invocation of udev rule + """ + if not os.path.isdir(vyos_udev_dir): + return + + for intf in os.listdir(vyos_udev_dir): + path = os.path.join(vyos_udev_dir, intf) + try: + with open(path) as f: + hwid = f.read().rstrip() + except OSError as e: + logging.error(f"OSError {e}") + continue + intfs[hwid] = intf + +def on_boot_event(intf_name: str, hwid: str, predefined: str = '') -> str: + """Called on boot by vyos-router: 'coldplug' in vyatta_net_name + """ + logging.info(f"lookup {intf_name}, {hwid}") + interfaces = get_configfile_interfaces() + logging.debug(f"config file interfaces are {interfaces}") + + if hwid in list(interfaces) and intf_name == interfaces[hwid]: + logging.info(f"use mapping from config file: '{hwid}' -> '{intf_name}'") + return intf_name + + add_assigned_interfaces(interfaces) + logging.debug(f"adding assigned interfaces: {interfaces}") + + if predefined: + newname = predefined + logging.info(f"predefined interface name for '{intf_name}' is '{newname}'") + else: + newname = get_biosdevname(intf_name) + logging.info(f"biosdevname returned '{newname}' for '{intf_name}'") + + if not is_available(interfaces, newname): + prefix = re.sub(r'\d+$', '', newname) + newname = find_available(interfaces, prefix) + + logging.info(f"new name for '{intf_name}' is '{newname}'") + + leave_rescan_hint(newname, hwid) + + return newname + +def hotplug_event(): + # Not yet implemented, since interface-rescan will only be run on boot. + pass + +if len(argv) > 3: + predef_name = argv[3] +else: + predef_name = '' + +lock.acquire() +if not boot_configuration_complete(): + res = on_boot_event(argv[1], argv[2], predefined=predef_name) + logging.debug(f"on boot, returned name is {res}") +else: + logging.debug("boot configuration complete") +lock.release() + -- cgit v1.2.3 From 437a2d847e473bd4a6dc28e09880e7e34d79ec6a Mon Sep 17 00:00:00 2001 From: John Estabrook Date: Sun, 26 Sep 2021 19:39:19 -0500 Subject: interface-names: T3869: install vyos_net_name to udev directory --- debian/rules | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/debian/rules b/debian/rules index c7a7138e1..5a58aeeb6 100755 --- a/debian/rules +++ b/debian/rules @@ -120,6 +120,10 @@ override_dh_auto_install: mkdir -p $(DIR)/$(VYOS_BIN_DIR) cp -r smoketest/bin/* $(DIR)/$(VYOS_BIN_DIR) + # Install udev script + mkdir -p $(DIR)/usr/lib/udev + cp src/helpers/vyos_net_name $(DIR)/usr/lib/udev + ifeq ($(DEB_TARGET_ARCH),amd64) # We only install XDP on amd64 systems mkdir -p $(DIR)/$(VYOS_DATA_DIR)/xdp -- cgit v1.2.3 From bfc5d6a1a48578a47369bde40388ec02bcedfa33 Mon Sep 17 00:00:00 2001 From: John Estabrook Date: Sun, 26 Sep 2021 19:41:42 -0500 Subject: interface-names: T3869: update udev rules --- src/etc/udev/rules.d/65-vyatta-net.rules | 26 -------------------------- src/etc/udev/rules.d/65-vyos-net.rules | 26 ++++++++++++++++++++++++++ 2 files changed, 26 insertions(+), 26 deletions(-) delete mode 100644 src/etc/udev/rules.d/65-vyatta-net.rules create mode 100644 src/etc/udev/rules.d/65-vyos-net.rules diff --git a/src/etc/udev/rules.d/65-vyatta-net.rules b/src/etc/udev/rules.d/65-vyatta-net.rules deleted file mode 100644 index 2b48c1213..000000000 --- a/src/etc/udev/rules.d/65-vyatta-net.rules +++ /dev/null @@ -1,26 +0,0 @@ -# These rules use vyatta_net_name to persistently name network interfaces -# per "hwid" association in the Vyatta configuration file. - -ACTION!="add", GOTO="vyatta_net_end" -SUBSYSTEM!="net", GOTO="vyatta_net_end" - -# ignore the interface if a name has already been set -NAME=="?*", GOTO="vyatta_net_end" - -# Do name change for ethernet and wireless devices only -KERNEL!="eth*|wlan*", GOTO="vyatta_net_end" - -# ignore "secondary" monitor interfaces of mac80211 drivers -KERNEL=="wlan*", ATTRS{type}=="803", GOTO="vyatta_net_end" - -# If using VyOS predefined names -ENV{VYOS_IFNAME}!="eth*", GOTO="end_vyos_predef_names" - -DRIVERS=="?*", PROGRAM="vyatta_net_name %k $attr{address} $env{VYOS_IFNAME}", NAME="%c", GOTO="vyatta_net_end" - -LABEL="end_vyos_predef_names" - -# ignore interfaces without a driver link like bridges and VLANs -DRIVERS=="?*", PROGRAM="vyatta_net_name %k $attr{address}", NAME="%c" - -LABEL="vyatta_net_end" diff --git a/src/etc/udev/rules.d/65-vyos-net.rules b/src/etc/udev/rules.d/65-vyos-net.rules new file mode 100644 index 000000000..c8d5750dd --- /dev/null +++ b/src/etc/udev/rules.d/65-vyos-net.rules @@ -0,0 +1,26 @@ +# These rules use vyos_net_name to persistently name network interfaces +# per "hwid" association in the VyOS configuration file. + +ACTION!="add", GOTO="vyos_net_end" +SUBSYSTEM!="net", GOTO="vyos_net_end" + +# ignore the interface if a name has already been set +NAME=="?*", GOTO="vyos_net_end" + +# Do name change for ethernet and wireless devices only +KERNEL!="eth*|wlan*", GOTO="vyos_net_end" + +# ignore "secondary" monitor interfaces of mac80211 drivers +KERNEL=="wlan*", ATTRS{type}=="803", GOTO="vyos_net_end" + +# If using VyOS predefined names +ENV{VYOS_IFNAME}!="eth*", GOTO="end_vyos_predef_names" + +DRIVERS=="?*", PROGRAM="vyos_net_name %k $attr{address} $env{VYOS_IFNAME}", NAME="%c", GOTO="vyos_net_end" + +LABEL="end_vyos_predef_names" + +# ignore interfaces without a driver link like bridges and VLANs +DRIVERS=="?*", PROGRAM="vyos_net_name %k $attr{address}", NAME="%c" + +LABEL="vyos_net_end" -- cgit v1.2.3 From 0b414bcd2930a1469df0a747962f4650d0fb964b Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Thu, 30 Sep 2021 20:24:17 +0200 Subject: vyos.ethtool: T3874: do not throw exception if adapter has issues with autoneg Instead of throwing an exception when an adapters autoneg capabilities can not be detected, just pretend it does not support autoneg. --- python/vyos/ethtool.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/python/vyos/ethtool.py b/python/vyos/ethtool.py index bc95767b1..eb5b0a456 100644 --- a/python/vyos/ethtool.py +++ b/python/vyos/ethtool.py @@ -45,7 +45,7 @@ class Ethtool: _ring_buffers = { } _ring_buffers_max = { } _driver_name = None - _auto_negotiation = None + _auto_negotiation = False _flow_control = False _flow_control_enabled = None @@ -84,10 +84,6 @@ class Ethtool: tmp = line.split()[-1] self._auto_negotiation = bool(tmp == 'on') - if self._auto_negotiation == None: - raise ValueError(f'Could not determine auto-negotiation settings '\ - f'for interface {ifname}!') - # Now populate features dictionaty out, err = popen(f'ethtool --show-features {ifname}') # skip the first line, it only says: "Features for eth0": -- cgit v1.2.3 From 2974628487abb9127922bf695331fd706a1d0e51 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Thu, 30 Sep 2021 20:28:31 +0200 Subject: dhcp-server: T2230: add subnet description into rendered config --- data/templates/dhcp-server/dhcpd.conf.tmpl | 3 +++ 1 file changed, 3 insertions(+) diff --git a/data/templates/dhcp-server/dhcpd.conf.tmpl b/data/templates/dhcp-server/dhcpd.conf.tmpl index 003c585dd..233e2cc53 100644 --- a/data/templates/dhcp-server/dhcpd.conf.tmpl +++ b/data/templates/dhcp-server/dhcpd.conf.tmpl @@ -90,6 +90,9 @@ shared-network {{ network | replace('_','-') }} { {% endif %} {% if network_config.subnet is defined and network_config.subnet is not none %} {% for subnet, subnet_config in network_config.subnet.items() %} +{% if subnet_config.description is defined and subnet_config.description is not none %} + # {{ subnet_config.description }} +{% endif %} subnet {{ subnet | address_from_cidr }} netmask {{ subnet | netmask_from_cidr }} { {% if subnet_config.name_server is defined and subnet_config.name_server is not none %} option domain-name-servers {{ subnet_config.name_server | join(', ') }}; -- cgit v1.2.3 From 75559f559cb13a83089d68d7fbd63fd0e8a02a46 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Fri, 1 Oct 2021 15:01:15 +0200 Subject: smoketest: vrrp: validate rfc3768-compatibility is not set (cherry picked from commit eab6e6830a566af647d7e1b24197bf945788c3d0) --- smoketest/scripts/cli/test_ha_vrrp.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/smoketest/scripts/cli/test_ha_vrrp.py b/smoketest/scripts/cli/test_ha_vrrp.py index bbf24095b..8c5bb86d8 100755 --- a/smoketest/scripts/cli/test_ha_vrrp.py +++ b/smoketest/scripts/cli/test_ha_vrrp.py @@ -81,6 +81,7 @@ class TestVRRP(VyOSUnitTestSHIM.TestCase): self.assertIn(f'priority 100', config) # default value self.assertIn(f'advert_int 1', config) # default value self.assertIn(f'preempt_delay 0', config) # default value + self.assertNotIn(f'use_vmac', config) self.assertIn(f' {vip}', config) def test_02_simple_options(self): @@ -158,6 +159,7 @@ class TestVRRP(VyOSUnitTestSHIM.TestCase): self.assertIn(f'interface {vrrp_interface}.{vlan_id}', config) self.assertIn(f'virtual_router_id {vlan_id}', config) + self.assertNotIn(f'use_vmac', config) self.assertIn(f' {vip}', config) config = getConfig(f'vrrp_sync_group {sync_group}') @@ -166,4 +168,4 @@ class TestVRRP(VyOSUnitTestSHIM.TestCase): self.assertIn(f'{group}', config) if __name__ == '__main__': - unittest.main(verbosity=2, failfast=True) + unittest.main(verbosity=2) -- cgit v1.2.3 From 3600f42ba23fc42de5f13b9d79c24f6fa0043187 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sat, 2 Oct 2021 12:02:57 +0200 Subject: dns: forwarding: T3882: remove deprecated code to work with PowerDNS 4.5 (cherry picked from commit 8e6c48563d1612916bd7fcc665d70bfa77ec5667) --- data/templates/dns-forwarding/recursor.conf.tmpl | 3 +-- src/conf_mode/dns_forwarding.py | 15 --------------- 2 files changed, 1 insertion(+), 17 deletions(-) diff --git a/data/templates/dns-forwarding/recursor.conf.tmpl b/data/templates/dns-forwarding/recursor.conf.tmpl index 9e0ad5d17..d44f756e8 100644 --- a/data/templates/dns-forwarding/recursor.conf.tmpl +++ b/data/templates/dns-forwarding/recursor.conf.tmpl @@ -10,8 +10,7 @@ threads=1 allow-from={{ allow_from | join(',') }} log-common-errors=yes non-local-bind=yes -query-local-address={{ source_address_v4 | join(',') }} -query-local-address6={{ source_address_v6 | join(',') }} +query-local-address={{ source_address | join(',') }} lua-config-file=recursor.conf.lua # cache-size diff --git a/src/conf_mode/dns_forwarding.py b/src/conf_mode/dns_forwarding.py index c44e6c974..06366362a 100755 --- a/src/conf_mode/dns_forwarding.py +++ b/src/conf_mode/dns_forwarding.py @@ -66,21 +66,6 @@ def get_config(config=None): if conf.exists(base_nameservers_dhcp): dns.update({'system_name_server_dhcp': conf.return_values(base_nameservers_dhcp)}) - # Split the source_address property into separate IPv4 and IPv6 lists - # NOTE: In future versions of pdns-recursor (> 4.4.0), this logic can be removed - # as both IPv4 and IPv6 addresses can be specified in a single setting. - source_address_v4 = [] - source_address_v6 = [] - - for source_address in dns['source_address']: - if is_ipv6(source_address): - source_address_v6.append(source_address) - else: - source_address_v4.append(source_address) - - dns.update({'source_address_v4': source_address_v4}) - dns.update({'source_address_v6': source_address_v6}) - return dns def verify(dns): -- cgit v1.2.3 From 044e9dc8bc7e3d946b0ba1f1edfe06b5323aeadd Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sat, 2 Oct 2021 18:50:38 +0200 Subject: vyos.ifconfig: T3883: bugfix VRF deletion We can not pass None as VRF name, this raises an exception. OSError: [Errno 255] failed to run command: ip link set dev eth2 master None (cherry picked from commit e687502b1cf4a3e15c562a3662afcbe0776b1fe7) --- python/vyos/ifconfig/interface.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/vyos/ifconfig/interface.py b/python/vyos/ifconfig/interface.py index 9b9d982d1..e6dbd861b 100755 --- a/python/vyos/ifconfig/interface.py +++ b/python/vyos/ifconfig/interface.py @@ -1407,7 +1407,7 @@ class Interface(Control): # unbinding will call 'ip link set dev eth0 nomaster' which will # also drop the interface out of a bridge or bond - thus this is # checked before - self.set_vrf(config.get('vrf', None)) + self.set_vrf(config.get('vrf', '')) # Configure MSS value for IPv4 TCP connections tmp = dict_search('ip.adjust_mss', config) -- cgit v1.2.3 From 15413605b40b40c9157ef2ea1213dac3ccf3cc21 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Mon, 4 Oct 2021 19:51:31 +0200 Subject: T3889: Revert "dhcpv6-pd: T421: disable wide dhcpv6 client debug messages" This reverts commit 6b48900358ce9b01eaa78e3a086e95a26064f0df. --- src/systemd/dhcp6c@.service | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/systemd/dhcp6c@.service b/src/systemd/dhcp6c@.service index fdd6d7d88..9a97ee261 100644 --- a/src/systemd/dhcp6c@.service +++ b/src/systemd/dhcp6c@.service @@ -9,7 +9,7 @@ StartLimitIntervalSec=0 WorkingDirectory=/run/dhcp6c Type=forking PIDFile=/run/dhcp6c/dhcp6c.%i.pid -ExecStart=/usr/sbin/dhcp6c -k /run/dhcp6c/dhcp6c.%i.sock -c /run/dhcp6c/dhcp6c.%i.conf -p /run/dhcp6c/dhcp6c.%i.pid %i +ExecStart=/usr/sbin/dhcp6c -D -k /run/dhcp6c/dhcp6c.%i.sock -c /run/dhcp6c/dhcp6c.%i.conf -p /run/dhcp6c/dhcp6c.%i.pid %i Restart=on-failure RestartSec=20 -- cgit v1.2.3 From f43e02715d92d59da937454d6b9dfeb0e725bed4 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Mon, 4 Oct 2021 19:45:22 +0200 Subject: op-mode: T3889: migrate to journalctl when reading daemon logs (cherry picked from commit 3b2523b816556aa911459097c2476a2da4542151) --- op-mode-definitions/show-log.xml.in | 37 ++++++++++++++++++++++++------------- 1 file changed, 24 insertions(+), 13 deletions(-) diff --git a/op-mode-definitions/show-log.xml.in b/op-mode-definitions/show-log.xml.in index 92c1cf016..f31c85245 100644 --- a/op-mode-definitions/show-log.xml.in +++ b/op-mode-definitions/show-log.xml.in @@ -6,7 +6,7 @@ Show contents of current master log file - /bin/journalctl + journalctl --boot @@ -18,7 +18,7 @@ Show listing of authorization attempts - /bin/journalctl --quiet SYSLOG_FACILITY=10 SYSLOG_FACILITY=4 + journalctl --boot --quiet SYSLOG_FACILITY=10 SYSLOG_FACILITY=4 @@ -30,7 +30,7 @@ Show log for Conntrack-sync - cat $(printf "%s\n" /var/log/messages* | sort -nr ) | grep -e conntrackd + journalctl --boot --unit conntrackd.service @@ -89,7 +89,7 @@ Show log for HTTPs - cat $(printf "%s\n" /var/log/messages* | sort -nr) | grep -e nginx + journalctl --boot --unit nginx.service @@ -133,7 +133,7 @@ Show log for LLDP - cat $(printf "%s\n" /var/log/messages* | sort -nr) | grep -e lldpd + journalctl --boot --unit lldpd.service @@ -141,17 +141,28 @@ egrep -i "kernel:.*\[NAT-[A-Z]{3,}-[0-9]+(-MASQ)?\]" $(find /var/log -maxdepth 1 -type f -name messages\* | sort -t. -k2nr) - + Show log for OpenVPN - cat $(printf "%s\n" /var/log/messages* | sort -nr) | grep -e openvpn - + journalctl --boot --unit openvpn@*.service + + + + Show OpenVPN log on specific interface + + interfaces openvpn + + + journalctl --boot --unit openvpn@$5.service + + + Show log for Simple Network Monitoring Protocol (SNMP) - cat $(printf "%s\n" /var/log/messages* | sort -nr) | grep -e snmpd + journalctl --boot --unit snmpd.service @@ -195,13 +206,13 @@ Show log for PPTP - cat $(printf "%s\n" /var/log/messages* | sort -nr) | grep -e accel-pptp -e ppp + journalctl --boot --unit accel-ppp@pptp.service Show log for SSTP - cat $(printf "%s\n" /var/log/messages* | sort -nr) | grep -e accel-sstp -e ppp + journalctl --boot --unit accel-ppp@sstp.service @@ -209,13 +220,13 @@ Show log for Virtual Router Redundancy Protocol (VRRP) - cat $(printf "%s\n" /var/log/messages* | sort -nr) | grep -e Keepalived_vrrp + journalctl --boot --unit keepalived.service Show log for Webproxy - cat $(printf "%s\n" /var/log/messages* | sort -nr) | grep -e "squid" + journalctl --boot --unit squid.service -- cgit v1.2.3 From b7189cd1df327621a304ca65626a517223db6432 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Mon, 4 Oct 2021 19:49:07 +0200 Subject: op-mode: dhcpv(v6): T3890: retrieve both server and client logfiles * rename: "show log dhcp" will become "show log dhcp server" * add: "show log dhcp client" to display logs from ALL DHCP client processes * add: "show log dhcp client interface " to display logs from individual DHCP client processes * add: "show log dhcpv6 server" to display infos about running DHCPv6 server * add: "show log dhcpv6 client" to display logs from ALL DHCPv6 client processes * add: "show log dhcpv6 client interface " to display logs from individual DHCPv6 client processes (cherry picked from commit ffd73958e42c20f69ded64393491966e0c9230c6) --- op-mode-definitions/show-log.xml.in | 60 +++++++++++++++++++++++++++++++++++-- 1 file changed, 57 insertions(+), 3 deletions(-) diff --git a/op-mode-definitions/show-log.xml.in b/op-mode-definitions/show-log.xml.in index f31c85245..3156d822a 100644 --- a/op-mode-definitions/show-log.xml.in +++ b/op-mode-definitions/show-log.xml.in @@ -32,12 +32,66 @@ journalctl --boot --unit conntrackd.service - + Show log for Dynamic Host Control Protocol (DHCP) - cat $(printf "%s\n" /var/log/messages* | sort -nr) | grep dhcpd - + + + + Show log for DHCP server + + journalctl --boot --unit isc-dhcp-server.service + + + + Show DHCP client logs + + journalctl --boot --unit "dhclient@*.service" + + + + Show DHCP client log on specific interface + + + + + journalctl --boot --unit "dhclient@$6.service" + + + + + + + + Show log for Dynamic Host Control Protocol IPv6 (DHCPv6) + + + + + Show log for DHCPv6 server + + journalctl --boot --unit isc-dhcp-server6.service + + + + Show DHCPv6 client logs + + journalctl --boot --unit "dhcp6c@*.service" + + + + Show DHCPv6 client log on specific interface + + + + + journalctl --boot --unit "dhcp6c@$6.service" + + + + + Show log for Firewall -- cgit v1.2.3 From 74a8c4b42b5ad31cdf34ddea07f83f7bff86be87 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Mon, 4 Oct 2021 22:25:19 +0200 Subject: bgp: T3741: "parameter default no-ipv4-unicast" is now a default option --- data/templates/frr/bgpd.frr.tmpl | 2 - .../include/bgp/protocol-common-config.xml.i | 6 -- smoketest/configs/bgp-small-ipv4-unicast | 77 ++++++++++++++++++++++ smoketest/scripts/cli/test_protocols_bgp.py | 3 - src/migration-scripts/bgp/1-to-2 | 77 ++++++++++++++++++++++ 5 files changed, 154 insertions(+), 11 deletions(-) create mode 100644 smoketest/configs/bgp-small-ipv4-unicast create mode 100755 src/migration-scripts/bgp/1-to-2 diff --git a/data/templates/frr/bgpd.frr.tmpl b/data/templates/frr/bgpd.frr.tmpl index 27a2b98a5..a35930c93 100644 --- a/data/templates/frr/bgpd.frr.tmpl +++ b/data/templates/frr/bgpd.frr.tmpl @@ -230,10 +230,8 @@ router bgp {{ local_as }} {{ 'vrf ' ~ vrf if vrf is defined and vrf is not none {% else %} no bgp ebgp-requires-policy {% endif %} -{% if parameters is defined and parameters.default is defined and parameters.default.no_ipv4_unicast is defined %} {# Option must be set before any neighbor - see https://phabricator.vyos.net/T3463 #} no bgp default ipv4-unicast -{% endif %} {# Workaround for T2100 until we have decided about a migration script #} no bgp network import-check {% if address_family is defined and address_family is not none %} diff --git a/interface-definitions/include/bgp/protocol-common-config.xml.i b/interface-definitions/include/bgp/protocol-common-config.xml.i index 30033bc50..2dfae517e 100644 --- a/interface-definitions/include/bgp/protocol-common-config.xml.i +++ b/interface-definitions/include/bgp/protocol-common-config.xml.i @@ -1253,12 +1253,6 @@ - - - Deactivate IPv4 unicast for a peer by default - - - diff --git a/smoketest/configs/bgp-small-ipv4-unicast b/smoketest/configs/bgp-small-ipv4-unicast new file mode 100644 index 000000000..a4dcd6218 --- /dev/null +++ b/smoketest/configs/bgp-small-ipv4-unicast @@ -0,0 +1,77 @@ +interfaces { + ethernet eth0 { + address 192.0.2.1 + address 2001:db8::1/64 + } + loopback lo { + } +} +protocols { + bgp 65001 { + address-family { + ipv4-unicast { + network 10.0.150.0/23 { + } + } + ipv6-unicast { + network 2001:db8:200::/40 { + } + } + } + neighbor 192.0.2.10 { + remote-as 65010 + } + neighbor 192.0.2.11 { + remote-as 65011 + } + neighbor 2001:db8::10 { + remote-as 65010 + } + neighbor 2001:db8::11 { + remote-as 65011 + } + parameters { + log-neighbor-changes + } + } +} +service { + ssh { + disable-host-validation + port 22 + } +} +system { + config-management { + commit-revisions 200 + } + console { + device ttyS0 { + speed 115200 + } + } + domain-name vyos.net + host-name vyos + login { + user vyos { + authentication { + encrypted-password $6$O5gJRlDYQpj$MtrCV9lxMnZPMbcxlU7.FI793MImNHznxGoMFgm3Q6QP3vfKJyOSRCt3Ka/GzFQyW1yZS4NS616NLHaIPPFHc0 + plaintext-password "" + } + } + } + syslog { + global { + facility all { + level notice + } + facility protocols { + level debug + } + } + } +} + +/* Warning: Do not remove the following line. */ +/* === vyatta-config-version: "broadcast-relay@1:cluster@1:config-management@1:conntrack-sync@1:conntrack@1:dhcp-relay@2:dhcp-server@5:dns-forwarding@1:firewall@5:ipsec@5:l2tp@1:mdns@1:nat@4:ntp@1:pptp@1:qos@1:quagga@6:snmp@1:ssh@1:system@9:vrrp@2:wanloadbalance@3:webgui@1:webproxy@1:webproxy@2:zone-policy@1" === */ +/* Release version: 1.2.5 */ diff --git a/smoketest/scripts/cli/test_protocols_bgp.py b/smoketest/scripts/cli/test_protocols_bgp.py index 29b5aa9d1..16284ed01 100755 --- a/smoketest/scripts/cli/test_protocols_bgp.py +++ b/smoketest/scripts/cli/test_protocols_bgp.py @@ -221,8 +221,6 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase): # Default local preference (higher = more preferred, default value is 100) self.cli_set(base_path + ['parameters', 'default', 'local-pref', local_pref]) - # Deactivate IPv4 unicast for a peer by default - self.cli_set(base_path + ['parameters', 'default', 'no-ipv4-unicast']) self.cli_set(base_path + ['parameters', 'graceful-restart', 'stalepath-time', stalepath_time]) self.cli_set(base_path + ['parameters', 'graceful-shutdown']) self.cli_set(base_path + ['parameters', 'ebgp-requires-policy']) @@ -246,7 +244,6 @@ 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' no bgp default ipv4-unicast', 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) diff --git a/src/migration-scripts/bgp/1-to-2 b/src/migration-scripts/bgp/1-to-2 new file mode 100755 index 000000000..4c6d5ceb8 --- /dev/null +++ b/src/migration-scripts/bgp/1-to-2 @@ -0,0 +1,77 @@ +#!/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 . + +# T3741: no-ipv4-unicast is now enabled by default + +from sys import argv +from sys import exit + +from vyos.configtree import ConfigTree +from vyos.template import is_ipv4 + +if (len(argv) < 1): + print("Must specify file name!") + exit(1) + +file_name = argv[1] + +with open(file_name, 'r') as f: + config_file = f.read() + +base = ['protocols', 'bgp'] +config = ConfigTree(config_file) + +if not config.exists(base): + # Nothing to do + exit(0) + +# This is now a default option - simply delete it. +# As it was configured explicitly - we can also bail out early as we need to +# do nothing! +if config.exists(base + ['parameters', 'default', 'no-ipv4-unicast']): + config.delete(base + ['parameters', 'default', 'no-ipv4-unicast']) + + # Check if the "default" node is now empty, if so - remove it + if len(config.list_nodes(base + ['parameters', 'default'])) == 0: + config.delete(base + ['parameters', 'default']) + + # Check if the "default" node is now empty, if so - remove it + if len(config.list_nodes(base + ['parameters'])) == 0: + config.delete(base + ['parameters']) + + exit(0) + +# As we now install a new default option into BGP we need to migrate all +# existing BGP neighbors and restore the old behavior +if config.exists(base + ['neighbor']): + for neighbor in config.list_nodes(base + ['neighbor']): + peer_group = base + ['neighbor', neighbor, 'peer-group'] + if config.exists(peer_group): + peer_group_name = config.return_value(peer_group) + # peer group enables old behavior for neighbor - bail out + if config.exists(base + ['peer-group', peer_group_name, 'address-family', 'ipv4-unicast']): + continue + + afi_ipv4 = base + ['neighbor', neighbor, 'address-family', 'ipv4-unicast'] + if not config.exists(afi_ipv4): + config.set(afi_ipv4) + +try: + with open(file_name, 'w') as f: + f.write(config.to_string()) +except OSError as e: + print(f'Failed to save the modified config: {e}') + exit(1) -- cgit v1.2.3 From 15d4977f5d52d3fc5f9c2fa501964739f2335741 Mon Sep 17 00:00:00 2001 From: Volodymyr Date: Tue, 5 Oct 2021 12:08:24 +0000 Subject: container: T3881: Fix description for container --- interface-definitions/containers.xml.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface-definitions/containers.xml.in b/interface-definitions/containers.xml.in index bf672307c..fb8241d71 100644 --- a/interface-definitions/containers.xml.in +++ b/interface-definitions/containers.xml.in @@ -141,7 +141,7 @@ - Mount a volume into the container + Restart options for container no on-failure always -- cgit v1.2.3 From a2920ce9e2289c3e4035b69bd2ac8574cd85b076 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Tue, 5 Oct 2021 19:43:31 +0200 Subject: smoketest: bgp: T3741: bugfix invalid IP address (missing prefix size) --- smoketest/configs/bgp-small-ipv4-unicast | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/smoketest/configs/bgp-small-ipv4-unicast b/smoketest/configs/bgp-small-ipv4-unicast index a4dcd6218..83f1effd2 100644 --- a/smoketest/configs/bgp-small-ipv4-unicast +++ b/smoketest/configs/bgp-small-ipv4-unicast @@ -1,6 +1,6 @@ interfaces { ethernet eth0 { - address 192.0.2.1 + address 192.0.2.1/24 address 2001:db8::1/64 } loopback lo { -- cgit v1.2.3 From adc7ef387d40e92bd7163ee6b401e99e554394a3 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Tue, 5 Oct 2021 19:43:08 +0200 Subject: op-mode: T3889: do not display redundant hostname when reading logs (cherry picked from commit 30cf3bc79e2253a004fcbbf76c9f99c52e7bc216) --- op-mode-definitions/show-log.xml.in | 38 ++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/op-mode-definitions/show-log.xml.in b/op-mode-definitions/show-log.xml.in index 3156d822a..4c0a7913b 100644 --- a/op-mode-definitions/show-log.xml.in +++ b/op-mode-definitions/show-log.xml.in @@ -6,7 +6,7 @@ Show contents of current master log file - journalctl --boot + journalctl --no-hostname --boot @@ -18,7 +18,7 @@ Show listing of authorization attempts - journalctl --boot --quiet SYSLOG_FACILITY=10 SYSLOG_FACILITY=4 + journalctl --no-hostname --boot --quiet SYSLOG_FACILITY=10 SYSLOG_FACILITY=4 @@ -30,7 +30,7 @@ Show log for Conntrack-sync - journalctl --boot --unit conntrackd.service + journalctl --no-hostname --boot --unit conntrackd.service @@ -41,13 +41,13 @@ Show log for DHCP server - journalctl --boot --unit isc-dhcp-server.service + journalctl --no-hostname --boot --unit isc-dhcp-server.service Show DHCP client logs - journalctl --boot --unit "dhclient@*.service" + journalctl --no-hostname --boot --unit "dhclient@*.service" @@ -56,7 +56,7 @@ - journalctl --boot --unit "dhclient@$6.service" + journalctl --no-hostname --boot --unit "dhclient@$6.service" @@ -71,13 +71,13 @@ Show log for DHCPv6 server - journalctl --boot --unit isc-dhcp-server6.service + journalctl --no-hostname --boot --unit isc-dhcp-server6.service Show DHCPv6 client logs - journalctl --boot --unit "dhcp6c@*.service" + journalctl --no-hostname --boot --unit "dhcp6c@*.service" @@ -86,7 +86,7 @@ - journalctl --boot --unit "dhcp6c@$6.service" + journalctl --no-hostname --boot --unit "dhcp6c@$6.service" @@ -143,7 +143,7 @@ Show log for HTTPs - journalctl --boot --unit nginx.service + journalctl --no-hostname --boot --unit nginx.service @@ -173,7 +173,7 @@ <NUMBER> - tail -n "$6" /lib/live/mount/persistence/boot/$4/rw/var/log/messages | ${VYATTA_PAGER:-cat} + tail -n "$6" /lib/live/mount/persistence/boot/$4/rw/var/log/messages | ${VYATTA_PAGER:-cat} @@ -187,7 +187,7 @@ Show log for LLDP - journalctl --boot --unit lldpd.service + journalctl --no-hostname --boot --unit lldpd.service @@ -199,7 +199,7 @@ Show log for OpenVPN - journalctl --boot --unit openvpn@*.service + journalctl --no-hostname --boot --unit openvpn@*.service @@ -208,7 +208,7 @@ interfaces openvpn - journalctl --boot --unit openvpn@$5.service + journalctl --no-hostname --boot --unit openvpn@$5.service @@ -216,7 +216,7 @@ Show log for Simple Network Monitoring Protocol (SNMP) - journalctl --boot --unit snmpd.service + journalctl --no-hostname --boot --unit snmpd.service @@ -260,13 +260,13 @@ Show log for PPTP - journalctl --boot --unit accel-ppp@pptp.service + journalctl --no-hostname --boot --unit accel-ppp@pptp.service Show log for SSTP - journalctl --boot --unit accel-ppp@sstp.service + journalctl --no-hostname --boot --unit accel-ppp@sstp.service @@ -274,13 +274,13 @@ Show log for Virtual Router Redundancy Protocol (VRRP) - journalctl --boot --unit keepalived.service + journalctl --no-hostname --boot --unit keepalived.service Show log for Webproxy - journalctl --boot --unit squid.service + journalctl --no-hostname --boot --unit squid.service -- cgit v1.2.3