From deb3fba81b6219a18d72eea48c644ec3c2cb724a Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sun, 20 Sep 2020 13:10:21 +0200 Subject: vyos.configdict: T2665: cleanup get_interface_dict() default dict handling --- python/vyos/configdict.py | 139 ++++++++++++++++++++++------------------------ 1 file changed, 66 insertions(+), 73 deletions(-) diff --git a/python/vyos/configdict.py b/python/vyos/configdict.py index bfc70b772..ef1b452a8 100644 --- a/python/vyos/configdict.py +++ b/python/vyos/configdict.py @@ -18,9 +18,11 @@ A library for retrieving value dicts from VyOS configs in a declarative fashion. """ import os -from enum import Enum from copy import deepcopy +from vyos.util import vyos_dict_search +from vyos.validate import is_member +from vyos.xml import defaults from vyos import ConfigError def retrieve_config(path_hash, base_path, config): @@ -104,47 +106,6 @@ def list_diff(first, second): second = set(second) return [item for item in first if item not in second] -def T2665_default_dict_cleanup(dict): - """ Cleanup default keys for tag nodes https://phabricator.vyos.net/T2665. """ - # Cleanup - for vif in ['vif', 'vif_s']: - if vif in dict: - for key in ['ip', 'mtu', 'dhcpv6_options']: - if key in dict[vif]: - del dict[vif][key] - - # cleanup VIF-S defaults - if 'vif_c' in dict[vif]: - for key in ['ip', 'mtu', 'dhcpv6_options']: - if key in dict[vif]['vif_c']: - del dict[vif]['vif_c'][key] - # If there is no vif-c defined and we just cleaned the default - # keys - we can clean the entire vif-c dict as it's useless - if not dict[vif]['vif_c']: - del dict[vif]['vif_c'] - - # If there is no real vif/vif-s defined and we just cleaned the default - # keys - we can clean the entire vif dict as it's useless - if not dict[vif]: - del dict[vif] - - if 'dhcpv6_options' in dict and 'pd' in dict['dhcpv6_options']: - if 'length' in dict['dhcpv6_options']['pd']: - del dict['dhcpv6_options']['pd']['length'] - - # delete empty dicts - if 'dhcpv6_options' in dict: - if 'pd' in dict['dhcpv6_options']: - # test if 'pd' is an empty node so we can remove it - if not dict['dhcpv6_options']['pd']: - del dict['dhcpv6_options']['pd'] - - # test if 'dhcpv6_options' is an empty node so we can remove it - if not dict['dhcpv6_options']: - del dict['dhcpv6_options'] - - return dict - def leaf_node_changed(conf, path): """ Check if a leaf node was altered. If it has been altered - values has been @@ -207,13 +168,21 @@ def get_removed_vlans(conf, dict): return dict +def T2665_set_dhcpv6pd_defaults(config_dict): + """ Properly configure DHCPv6 default options in the dictionary. If there is + no DHCPv6 configured at all, it is safe to remove the entire configuration. + """ + # As this is the same for every interface type it is safe to assume this + # for ethernet + pd_defaults = defaults(['interfaces', 'ethernet', 'dhcpv6-options', 'pd']) -def dict_add_dhcpv6pd_defaults(defaults, config_dict): # Implant default dictionary for DHCPv6-PD instances - if 'dhcpv6_options' in config_dict and 'pd' in config_dict['dhcpv6_options']: - for pd, pd_config in config_dict['dhcpv6_options']['pd'].items(): - config_dict['dhcpv6_options']['pd'][pd] = dict_merge( - defaults, pd_config) + if vyos_dict_search('dhcpv6_options.pd.length', config_dict): + del config_dict['dhcpv6_options']['pd']['length'] + + for pd in (vyos_dict_search('dhcpv6_options.pd', config_dict) or []): + config_dict['dhcpv6_options']['pd'][pd] = dict_merge(pd_defaults, + config_dict['dhcpv6_options']['pd'][pd]) return config_dict @@ -225,10 +194,6 @@ def get_interface_dict(config, base, ifname=''): Will return a dictionary with the necessary interface configuration """ - from vyos.util import vyos_dict_search - from vyos.validate import is_member - from vyos.xml import defaults - if not ifname: # determine tagNode instance if 'VYOS_TAGNODE_VALUE' not in os.environ: @@ -238,6 +203,12 @@ def get_interface_dict(config, base, ifname=''): # retrieve interface default values default_values = defaults(base) + # We take care about VLAN (vif, vif-s, vif-c) default values later on when + # parsing vlans in default dict and merge the "proper" values in correctly, + # see T2665. + for vif in ['vif', 'vif_s']: + if vif in default_values: del default_values[vif] + # 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) @@ -249,11 +220,20 @@ def get_interface_dict(config, base, ifname=''): # Add interface instance name into dictionary dict.update({'ifname': ifname}) + # XXX: T2665: When there is no DHCPv6-PD configuration given, we can safely + # remove the default values from the dict. + if 'dhcpv6_options' not in dict: + if 'dhcpv6_options' in default_values: + del default_values['dhcpv6_options'] + # We have gathered the dict representation of the CLI, but there are # default options which we need to update into the dictionary # retrived. dict = dict_merge(default_values, dict) + # XXX: T2665: blend in proper DHCPv6-PD default values + dict = T2665_set_dhcpv6pd_defaults(dict) + # Check if we are a member of a bridge device bridge = is_member(config, ifname, 'bridge') if bridge: @@ -276,36 +256,49 @@ def get_interface_dict(config, base, ifname=''): else: dict['ipv6']['address'].update({'eui64_old': eui64}) - # remove wrongly inserted values - dict = T2665_default_dict_cleanup(dict) - - # Implant default dictionary for DHCPv6-PD instances - default_pd_values = defaults(base + ['dhcpv6-options', 'pd']) - dict = dict_add_dhcpv6pd_defaults(default_pd_values, dict) - # Implant default dictionary in vif/vif-s VLAN interfaces. Values are # identical for all types of VLAN interfaces as they all include the same # XML definitions which hold the defaults. - default_vif_values = defaults(base + ['vif']) for vif, vif_config in dict.get('vif', {}).items(): - dict['vif'][vif] = dict_add_dhcpv6pd_defaults( - default_pd_values, vif_config) - dict['vif'][vif] = T2665_default_dict_cleanup( - dict_merge(default_vif_values, vif_config)) + default_vif_values = defaults(base + ['vif']) + # XXX: T2665: When there is no DHCPv6-PD configuration given, we can safely + # remove the default values from the dict. + if not 'dhcpv6_options' in vif_config: + del default_vif_values['dhcpv6_options'] + + dict['vif'][vif] = dict_merge(default_vif_values, vif_config) + # XXX: T2665: blend in proper DHCPv6-PD default values + dict['vif'][vif] = T2665_set_dhcpv6pd_defaults(dict['vif'][vif]) for vif_s, vif_s_config in dict.get('vif_s', {}).items(): - dict['vif_s'][vif_s] = dict_add_dhcpv6pd_defaults( - default_pd_values, vif_s_config) - dict['vif_s'][vif_s] = T2665_default_dict_cleanup( - dict_merge(default_vif_values, vif_s_config)) + default_vif_s_values = defaults(base + ['vif-s']) + # XXX: T2665: we only wan't the vif-s defaults - do not care about vif-c + if 'vif_c' in default_vif_s_values: del default_vif_s_values['vif_c'] + + # XXX: T2665: When there is no DHCPv6-PD configuration given, we can safely + # remove the default values from the dict. + if not 'dhcpv6_options' in vif_s_config: + del default_vif_s_values['dhcpv6_options'] + + dict['vif_s'][vif_s] = dict_merge(default_vif_s_values, vif_s_config) + # XXX: T2665: blend in proper DHCPv6-PD default values + dict['vif_s'][vif_s] = T2665_set_dhcpv6pd_defaults( + dict['vif_s'][vif_s]) + for vif_c, vif_c_config in vif_s_config.get('vif_c', {}).items(): - dict['vif_s'][vif_s]['vif_c'][vif_c] = dict_add_dhcpv6pd_defaults( - default_pd_values, vif_c_config) - dict['vif_s'][vif_s]['vif_c'][vif_c] = T2665_default_dict_cleanup( - dict_merge(default_vif_values, vif_c_config)) + default_vif_c_values = defaults(base + ['vif-s', 'vif-c']) + + # XXX: T2665: When there is no DHCPv6-PD configuration given, we can safely + # remove the default values from the dict. + if not 'dhcpv6_options' in vif_c_config: + del default_vif_c_values['dhcpv6_options'] + + dict['vif_s'][vif_s]['vif_c'][vif_c] = dict_merge( + default_vif_c_values, vif_c_config) + # XXX: T2665: blend in proper DHCPv6-PD default values + dict['vif_s'][vif_s]['vif_c'][vif_c] = T2665_set_dhcpv6pd_defaults( + dict['vif_s'][vif_s]['vif_c'][vif_c]) # Check vif, vif-s/vif-c VLAN interfaces for removal dict = get_removed_vlans(config, dict) - return dict - -- cgit v1.2.3 From e64ab9ec34ce8cb221f3c82787d3641efa30aac0 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sun, 20 Sep 2020 13:12:16 +0200 Subject: smoketest: T2903: test 802.1ad (Q-in-Q) ethertype --- smoketest/scripts/cli/base_interfaces_test.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/smoketest/scripts/cli/base_interfaces_test.py b/smoketest/scripts/cli/base_interfaces_test.py index 14ec7e137..047c19dd0 100644 --- a/smoketest/scripts/cli/base_interfaces_test.py +++ b/smoketest/scripts/cli/base_interfaces_test.py @@ -14,12 +14,15 @@ import os import unittest +import json from netifaces import ifaddresses, AF_INET, AF_INET6 from vyos.configsession import ConfigSession from vyos.ifconfig import Interface from vyos.util import read_file +from vyos.util import cmd +from vyos.util import vyos_dict_search from vyos.validate import is_intf_addr_assigned, is_ipv6_link_local class BasicInterfaceTest: @@ -212,8 +215,12 @@ class BasicInterfaceTest: self.session.set(base + ['address', address]) self.session.commit() + for interface in self._interfaces: for vif_s in self._qinq_range: + tmp = json.loads(cmd(f'ip -d -j link show dev {interface}.{vif_s}'))[0] + self.assertEqual(vyos_dict_search('linkinfo.info_data.protocol', tmp), '802.1ad') + for vif_c in self._vlan_range: vif = f'{interface}.{vif_s}.{vif_c}' for address in self._test_addr: -- cgit v1.2.3 From 0a21905786d31e759416dd335d87726f9ed46ffa Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sun, 20 Sep 2020 13:40:51 +0200 Subject: vif-s: ifconfig: T2903: use explicit VLAN protocol over raw numbers In the past we had to provide the ethertype value used for the VLAN protocol (0x88A8 -> 802.1ad or 0x8100 -> 802.1q). This should be changed to a more user friendly CLI node (protocol over ethertype) and 802.1ad over it's raw value 0x88A8. There is no need in presenting RAW information from the ethernet header to the user. Also iproute2 calls it protocol which makes way more sense over the "raw" value. --- interface-definitions/include/vif-s.xml.i | 19 ++++++----- python/vyos/ifconfig/interface.py | 18 +++------- src/migration-scripts/interfaces/12-to-13 | 57 +++++++++++++++++++++++++++++++ 3 files changed, 72 insertions(+), 22 deletions(-) create mode 100755 src/migration-scripts/interfaces/12-to-13 diff --git a/interface-definitions/include/vif-s.xml.i b/interface-definitions/include/vif-s.xml.i index a6d7c81ce..cd0afe742 100644 --- a/interface-definitions/include/vif-s.xml.i +++ b/interface-definitions/include/vif-s.xml.i @@ -13,25 +13,26 @@ #include #include #include - + - Set Ethertype + Protocol used for service VLAN (default: 802.1ad) - 0x88A8 0x8100 + 802.1ad 802.1q - 0x88A8 - 802.1ad + 802.1ad + Provider Bridging (IEEE 802.1ad, Q-inQ), ethertype 0x88a8 - 0x8100 - 802.1q + 802.1q + VLAN-tagged frame (IEEE 802.1q), ethertype 0x8100 - (0x88A8|0x8100) + (802.1q|802.1ad) - Ethertype must be 0x88A8 or 0x8100 + Ethertype must be 802.1ad or 802.1q + 802.1ad diff --git a/python/vyos/ifconfig/interface.py b/python/vyos/ifconfig/interface.py index be97b411b..4e420dc1d 100644 --- a/python/vyos/ifconfig/interface.py +++ b/python/vyos/ifconfig/interface.py @@ -50,14 +50,6 @@ from vyos.ifconfig.vrrp import VRRP from vyos.ifconfig.operational import Operational from vyos.ifconfig import Section -def get_ethertype(ethertype_val): - if ethertype_val == '0x88A8': - return '802.1ad' - elif ethertype_val == '0x8100': - return '802.1q' - else: - raise ConfigError('invalid ethertype "{}"'.format(ethertype_val)) - class Interface(Control): # This is the class which will be used to create # self.operational, it allows subclasses, such as @@ -1013,7 +1005,7 @@ class Interface(Control): # create/update 802.1ad (Q-in-Q VLANs) for vif_s_id, vif_s_config in config.get('vif_s', {}).items(): tmp = deepcopy(VLANIf.get_config()) - tmp['ethertype'] = get_ethertype(vif_s_config.get('ethertype', '0x88A8')) + tmp['protocol'] = vif_s_config['protocol'] tmp['source_interface'] = ifname tmp['vlan_id'] = vif_s_id @@ -1061,13 +1053,13 @@ class VLANIf(Interface): 'type': 'vlan', 'source_interface': '', 'vlan_id': '', - 'ethertype': '', + 'protocol': '', 'ingress_qos': '', 'egress_qos': '', } options = Interface.options + \ - ['source_interface', 'vlan_id', 'ethertype', 'ingress_qos', 'egress_qos'] + ['source_interface', 'vlan_id', 'protocol', 'ingress_qos', 'egress_qos'] def remove(self): """ @@ -1096,8 +1088,8 @@ class VLANIf(Interface): return cmd = 'ip link add link {source_interface} name {ifname} type vlan id {vlan_id}' - if self.config['ethertype']: - cmd += ' proto {ethertype}' + if self.config['protocol']: + cmd += ' protocol {protocol}' if self.config['ingress_qos']: cmd += ' ingress-qos-map {ingress_qos}' if self.config['egress_qos']: diff --git a/src/migration-scripts/interfaces/12-to-13 b/src/migration-scripts/interfaces/12-to-13 new file mode 100755 index 000000000..17d1d0b0a --- /dev/null +++ b/src/migration-scripts/interfaces/12-to-13 @@ -0,0 +1,57 @@ +#!/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 . + +# - T2903: Change vif-s ethertype from numeric number to literal +# - 0x88a8 -> 802.1ad +# - 0x8100 -> 802.1q + +from sys import exit, argv +from vyos.configtree import ConfigTree + +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) + + for type in config.list_nodes(['interfaces']): + for interface in config.list_nodes(['interfaces', type]): + if not config.exists(['interfaces', type, interface, 'vif-s']): + continue + + for vif_s in config.list_nodes(['interfaces', type, interface, 'vif-s']): + base_path = ['interfaces', type, interface, 'vif-s', vif_s] + if config.exists(base_path + ['ethertype']): + protocol = '802.1ad' + tmp = config.return_value(base_path + ['ethertype']) + if tmp == '0x8100': + protocol = '802.1q' + + config.set(base_path + ['protocol'], value=protocol) + config.delete(base_path + ['ethertype']) + + 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 993f6873c02f3f79013acedfe61ce705bdb3a4d0 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sun, 20 Sep 2020 13:53:55 +0200 Subject: wwan: ifconfig: T2905: sync CLI nodes in dialup interfaces Both PPPoE and WWAN interfaces are dialer interfaces handled by ppp, but use different CLI nodes for the same functionality. PPPoE has "connect-on-demand" to initiate an "on-demand" dialing and WWAN uses "ondemand" for this purpose. Rename WWAN "ondemand" node to "connect-on-demand". --- data/templates/wwan/peer.tmpl | 2 +- .../include/interface-dial-on-demand.xml.i | 6 ++++++ interface-definitions/interfaces-pppoe.xml.in | 7 +------ interface-definitions/interfaces-wirelessmodem.xml.in | 7 +------ smoketest/scripts/cli/test_interfaces_wirelessmodem.py | 2 +- src/migration-scripts/interfaces/12-to-13 | 14 ++++++++++++++ 6 files changed, 24 insertions(+), 14 deletions(-) create mode 100644 interface-definitions/include/interface-dial-on-demand.xml.i diff --git a/data/templates/wwan/peer.tmpl b/data/templates/wwan/peer.tmpl index aa759f741..e23881bf8 100644 --- a/data/templates/wwan/peer.tmpl +++ b/data/templates/wwan/peer.tmpl @@ -21,7 +21,7 @@ noauth crtscts lock persist -{{ "demand" if ondemand is defined }} +{{ "demand" if connect_on_demand is defined }} connect '/usr/sbin/chat -v -t6 -f /etc/ppp/peers/chat.{{ ifname }}' 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..c14ddf6f5 --- /dev/null +++ b/interface-definitions/include/interface-dial-on-demand.xml.i @@ -0,0 +1,6 @@ + + + Establishment connection automatically when traffic is sent + + + diff --git a/interface-definitions/interfaces-pppoe.xml.in b/interface-definitions/interfaces-pppoe.xml.in index 8a6c61312..b6208e0b9 100644 --- a/interface-definitions/interfaces-pppoe.xml.in +++ b/interface-definitions/interfaces-pppoe.xml.in @@ -42,12 +42,7 @@ - - - Automatic establishment of PPPOE connection when traffic is sent - - - + #include Default route insertion behaviour (default: auto) diff --git a/interface-definitions/interfaces-wirelessmodem.xml.in b/interface-definitions/interfaces-wirelessmodem.xml.in index d375b808d..96604ff00 100644 --- a/interface-definitions/interfaces-wirelessmodem.xml.in +++ b/interface-definitions/interfaces-wirelessmodem.xml.in @@ -80,12 +80,7 @@ - - - Only dial when traffic is available - - - + #include diff --git a/smoketest/scripts/cli/test_interfaces_wirelessmodem.py b/smoketest/scripts/cli/test_interfaces_wirelessmodem.py index 40cd03b93..efc9c0e98 100755 --- a/smoketest/scripts/cli/test_interfaces_wirelessmodem.py +++ b/smoketest/scripts/cli/test_interfaces_wirelessmodem.py @@ -43,7 +43,7 @@ class WWANInterfaceTest(unittest.TestCase): def test_wlm_1(self): for interface in self._interfaces: self.session.set(base_path + [interface, 'no-peer-dns']) - self.session.set(base_path + [interface, 'ondemand']) + self.session.set(base_path + [interface, 'connect-on-demand']) # check validate() - APN must be configure with self.assertRaises(ConfigSessionError): diff --git a/src/migration-scripts/interfaces/12-to-13 b/src/migration-scripts/interfaces/12-to-13 index 17d1d0b0a..f866ca9a6 100755 --- a/src/migration-scripts/interfaces/12-to-13 +++ b/src/migration-scripts/interfaces/12-to-13 @@ -17,6 +17,8 @@ # - T2903: Change vif-s ethertype from numeric number to literal # - 0x88a8 -> 802.1ad # - 0x8100 -> 802.1q +# - T2905: Change WWAN "ondemand" node to "connect-on-demand" to have identical +# CLI nodes for both types of dialer interfaces from sys import exit, argv from vyos.configtree import ConfigTree @@ -32,6 +34,9 @@ if __name__ == '__main__': config = ConfigTree(config_file) + # + # T2903 + # for type in config.list_nodes(['interfaces']): for interface in config.list_nodes(['interfaces', type]): if not config.exists(['interfaces', type, interface, 'vif-s']): @@ -48,6 +53,15 @@ if __name__ == '__main__': config.set(base_path + ['protocol'], value=protocol) config.delete(base_path + ['ethertype']) + # + # T2905 + # + wwan_base = ['interfaces', 'wirelessmodem'] + if config.exists(wwan_base): + for interface in config.list_nodes(wwan_base): + if config.exists(wwan_base + [interface, 'ondemand']): + config.rename(wwan_base + [interface, 'ondemand'], 'connect-on-demand') + try: with open(file_name, 'w') as f: f.write(config.to_string()) -- cgit v1.2.3 From 103e8404cdea70dad486940f209b9683f1c7b936 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sun, 20 Sep 2020 15:18:50 +0200 Subject: ifconfig: T2653: remove duplicates of get_config() A lot of derived classes from Interface implemented their own get_config() method which more or less was the same everywhere. We also hat different qualifiers like @staticmethod or @classmethod. This is now changed to only have the @classmethod in Interface base class which will return the necessary dictionary keys for the required interfaces. This change is a mid reduction in lines of code which is always a very nice thing! --- python/vyos/ifconfig/geneve.py | 13 ----------- python/vyos/ifconfig/interface.py | 25 +++++++++------------ python/vyos/ifconfig/l2tpv3.py | 34 +++++++++-------------------- python/vyos/ifconfig/macsec.py | 16 -------------- python/vyos/ifconfig/macvlan.py | 15 +------------ python/vyos/ifconfig/vxlan.py | 14 ------------ python/vyos/ifconfig/wireless.py | 16 -------------- src/conf_mode/interfaces-geneve.py | 11 +++++----- src/conf_mode/interfaces-l2tpv3.py | 10 ++++----- src/conf_mode/interfaces-macsec.py | 12 +++++----- src/conf_mode/interfaces-pseudo-ethernet.py | 11 +++++----- src/conf_mode/interfaces-vxlan.py | 10 ++++----- src/conf_mode/interfaces-wireless.py | 10 ++++----- 13 files changed, 51 insertions(+), 146 deletions(-) diff --git a/python/vyos/ifconfig/geneve.py b/python/vyos/ifconfig/geneve.py index 0a13043cc..5c4597be8 100644 --- a/python/vyos/ifconfig/geneve.py +++ b/python/vyos/ifconfig/geneve.py @@ -13,7 +13,6 @@ # You should have received a copy of the GNU Lesser General Public # License along with this library. If not, see . -from copy import deepcopy from vyos.ifconfig.interface import Interface @Interface.register @@ -51,18 +50,6 @@ class GeneveIf(Interface): # interface is always A/D down. It needs to be enabled explicitly self.set_admin_state('down') - @classmethod - def get_config(cls): - """ - GENEVE interfaces require a configuration when they are added using - iproute2. This static method will provide the configuration dictionary - used by this class. - - Example: - >> dict = GeneveIf().get_config() - """ - return deepcopy(cls.default) - 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 diff --git a/python/vyos/ifconfig/interface.py b/python/vyos/ifconfig/interface.py index 4e420dc1d..807191b3d 100644 --- a/python/vyos/ifconfig/interface.py +++ b/python/vyos/ifconfig/interface.py @@ -174,6 +174,15 @@ class Interface(Control): def exists(cls, ifname): return os.path.exists(f'/sys/class/net/{ifname}') + @classmethod + def get_config(cls): + """ + Some but not all interfaces require a configuration when they are added + using iproute2. This method will provide the configuration dictionary + used by this class. + """ + return deepcopy(cls.default) + def __init__(self, ifname, **kargs): """ This is the base interface class which supports basic IP/MAC address @@ -1084,7 +1093,7 @@ class VLANIf(Interface): def _create(self): # bail out early if interface already exists - if os.path.exists(f'/sys/class/net/{self.ifname}'): + if self.exists(f'{self.ifname}'): return cmd = 'ip link add link {source_interface} name {ifname} type vlan id {vlan_id}' @@ -1100,20 +1109,6 @@ class VLANIf(Interface): # interface is always A/D down. It needs to be enabled explicitly self.set_admin_state('down') - @staticmethod - def get_config(): - """ - MACsec interfaces require a configuration when they are added using - iproute2. This static method will provide the configuration dictionary - used by this class. - - Example: - >> dict = VLANIf().get_config() - """ - config = deepcopy(__class__.default) - del config['type'] - return config - def set_admin_state(self, state): """ Set interface administrative state to be 'up' or 'down' diff --git a/python/vyos/ifconfig/l2tpv3.py b/python/vyos/ifconfig/l2tpv3.py index 33740921e..5fd90f9cf 100644 --- a/python/vyos/ifconfig/l2tpv3.py +++ b/python/vyos/ifconfig/l2tpv3.py @@ -13,7 +13,6 @@ # You should have received a copy of the GNU Lesser General Public # License along with this library. If not, see . -import os from vyos.ifconfig.interface import Interface @Interface.register @@ -28,6 +27,15 @@ class L2TPv3If(Interface): default = { 'type': 'l2tp', + 'peer_tunnel_id': '', + 'local_port': 0, + 'remote_port': 0, + 'encapsulation': 'udp', + 'local_address': '', + 'remote_address': '', + 'session_id': '', + 'tunnel_id': '', + 'peer_session_id': '' } definition = { **Interface.definition, @@ -73,7 +81,7 @@ class L2TPv3If(Interface): >>> i.remove() """ - if os.path.exists('/sys/class/net/{}'.format(self.config['ifname'])): + if self.exists(self.config['ifname']): # interface is always A/D down. It needs to be enabled explicitly self.set_admin_state('down') @@ -86,25 +94,3 @@ class L2TPv3If(Interface): cmd = 'ip l2tp del tunnel tunnel_id {tunnel_id}' self._cmd(cmd.format(**self.config)) - @staticmethod - def get_config(): - """ - L2TPv3 interfaces require a configuration when they are added using - iproute2. This static method will provide the configuration dictionary - used by this class. - - Example: - >> dict = L2TPv3If().get_config() - """ - config = { - 'peer_tunnel_id': '', - 'local_port': 0, - 'remote_port': 0, - 'encapsulation': 'udp', - 'local_address': '', - 'remote_address': '', - 'session_id': '', - 'tunnel_id': '', - 'peer_session_id': '' - } - return config diff --git a/python/vyos/ifconfig/macsec.py b/python/vyos/ifconfig/macsec.py index 6f570d162..456686ea6 100644 --- a/python/vyos/ifconfig/macsec.py +++ b/python/vyos/ifconfig/macsec.py @@ -56,22 +56,6 @@ class MACsecIf(Interface): # interface is always A/D down. It needs to be enabled explicitly self.set_admin_state('down') - @staticmethod - def get_config(): - """ - MACsec interfaces require a configuration when they are added using - iproute2. This static method will provide the configuration dictionary - used by this class. - - Example: - >> dict = MACsecIf().get_config() - """ - config = { - 'security_cipher': '', - 'source_interface': '', - } - return config - 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 diff --git a/python/vyos/ifconfig/macvlan.py b/python/vyos/ifconfig/macvlan.py index 9c1d09c1c..2447fec77 100644 --- a/python/vyos/ifconfig/macvlan.py +++ b/python/vyos/ifconfig/macvlan.py @@ -1,4 +1,4 @@ -# Copyright 2019 VyOS maintainers and contributors +# Copyright 2019-2020 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 @@ -13,7 +13,6 @@ # You should have received a copy of the GNU Lesser General Public # License along with this library. If not, see . -from copy import deepcopy from vyos.ifconfig.interface import Interface @Interface.register @@ -53,18 +52,6 @@ class MACVLANIf(Interface): cmd = f'ip link set dev {ifname} type macvlan mode {mode}' return self._cmd(cmd) - @classmethod - def get_config(cls): - """ - MACVLAN interfaces require a configuration when they are added using - iproute2. This method will provide the configuration dictionary used - by this class. - - Example: - >> dict = MACVLANIf().get_config() - """ - return deepcopy(cls.default) - 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 diff --git a/python/vyos/ifconfig/vxlan.py b/python/vyos/ifconfig/vxlan.py index dba62b61a..ad1f605ed 100644 --- a/python/vyos/ifconfig/vxlan.py +++ b/python/vyos/ifconfig/vxlan.py @@ -13,8 +13,6 @@ # You should have received a copy of the GNU Lesser General Public # License along with this library. If not, see . -from copy import deepcopy - from vyos import ConfigError from vyos.ifconfig.interface import Interface @@ -97,18 +95,6 @@ class VXLANIf(Interface): self._cmd(cmd) - @classmethod - def get_config(cls): - """ - VXLAN interfaces require a configuration when they are added using - iproute2. This static method will provide the configuration dictionary - used by this class. - - Example: - >> dict = VXLANIf().get_config() - """ - return deepcopy(cls.default) - 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 diff --git a/python/vyos/ifconfig/wireless.py b/python/vyos/ifconfig/wireless.py index 346577119..37703d242 100644 --- a/python/vyos/ifconfig/wireless.py +++ b/python/vyos/ifconfig/wireless.py @@ -50,22 +50,6 @@ class WiFiIf(Interface): .format(**self.config) self._cmd(cmd) - @staticmethod - def get_config(): - """ - WiFi interfaces require a configuration when they are added using - iw (type/phy). This static method will provide the configuration - ictionary used by this class. - - Example: - >> conf = WiFiIf().get_config() - """ - config = { - 'phy': 'phy0' - } - return config - - 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 diff --git a/src/conf_mode/interfaces-geneve.py b/src/conf_mode/interfaces-geneve.py index cc2cf025a..af7c121f4 100755 --- a/src/conf_mode/interfaces-geneve.py +++ b/src/conf_mode/interfaces-geneve.py @@ -17,7 +17,6 @@ import os from sys import exit -from copy import deepcopy from netifaces import interfaces from vyos.config import Config @@ -62,7 +61,6 @@ def verify(geneve): def generate(geneve): return None - def apply(geneve): # Check if GENEVE interface already exists if geneve['ifname'] in interfaces(): @@ -72,10 +70,11 @@ def apply(geneve): g.remove() if 'deleted' not in geneve: - # GENEVE interface needs to be created on-block - # instead of passing a ton of arguments, I just use a dict - # that is managed by vyos.ifconfig - conf = deepcopy(GeneveIf.get_config()) + # This is a special type of interface which needs additional parameters + # when created using iproute2. Instead of passing a ton of arguments, + # use a dictionary provided by the interface class which holds all the + # options necessary. + conf = GeneveIf.get_config() # Assign GENEVE instance configuration parameters to config dict conf['vni'] = geneve['vni'] diff --git a/src/conf_mode/interfaces-l2tpv3.py b/src/conf_mode/interfaces-l2tpv3.py index 144cee5fe..2653ff19c 100755 --- a/src/conf_mode/interfaces-l2tpv3.py +++ b/src/conf_mode/interfaces-l2tpv3.py @@ -17,7 +17,6 @@ import os from sys import exit -from copy import deepcopy from netifaces import interfaces from vyos.config import Config @@ -88,10 +87,11 @@ def generate(l2tpv3): return None def apply(l2tpv3): - # L2TPv3 interface needs to be created/deleted on-block, instead of - # passing a ton of arguments, I just use a dict that is managed by - # vyos.ifconfig - conf = deepcopy(L2TPv3If.get_config()) + # This is a special type of interface which needs additional parameters + # when created using iproute2. Instead of passing a ton of arguments, + # use a dictionary provided by the interface class which holds all the + # options necessary. + conf = L2TPv3If.get_config() # Check if L2TPv3 interface already exists if l2tpv3['ifname'] in interfaces(): diff --git a/src/conf_mode/interfaces-macsec.py b/src/conf_mode/interfaces-macsec.py index 2866ccc0a..73c80866a 100755 --- a/src/conf_mode/interfaces-macsec.py +++ b/src/conf_mode/interfaces-macsec.py @@ -16,7 +16,6 @@ import os -from copy import deepcopy from sys import exit from vyos.config import Config @@ -102,12 +101,11 @@ def apply(macsec): os.unlink(wpa_suppl_conf.format(**macsec)) else: - # MACsec interfaces require a configuration when they are added using - # iproute2. This static method will provide the configuration - # dictionary used by this class. - - # XXX: subject of removal after completing T2653 - conf = deepcopy(MACsecIf.get_config()) + # This is a special type of interface which needs additional parameters + # when created using iproute2. Instead of passing a ton of arguments, + # use a dictionary provided by the interface class which holds all the + # options necessary. + conf = MACsecIf.get_config() conf['source_interface'] = macsec['source_interface'] conf['security_cipher'] = macsec['security']['cipher'] diff --git a/src/conf_mode/interfaces-pseudo-ethernet.py b/src/conf_mode/interfaces-pseudo-ethernet.py index 59edca1cc..98397b28f 100755 --- a/src/conf_mode/interfaces-pseudo-ethernet.py +++ b/src/conf_mode/interfaces-pseudo-ethernet.py @@ -14,9 +14,6 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -import os - -from copy import deepcopy from sys import exit from vyos.config import Config @@ -101,9 +98,11 @@ def apply(peth): if 'mode_old' in peth: MACVLANIf(peth['ifname']).remove() - # MACVLAN interface needs to be created on-block instead of passing a ton - # of arguments, I just use a dict that is managed by vyos.ifconfig - conf = deepcopy(MACVLANIf.get_config()) + # This is a special type of interface which needs additional parameters + # when created using iproute2. Instead of passing a ton of arguments, + # use a dictionary provided by the interface class which holds all the + # options necessary. + conf = MACVLANIf.get_config() # Assign MACVLAN instance configuration parameters to config dict conf['source_interface'] = peth['source_interface'] diff --git a/src/conf_mode/interfaces-vxlan.py b/src/conf_mode/interfaces-vxlan.py index bea3aa25b..a00c58608 100755 --- a/src/conf_mode/interfaces-vxlan.py +++ b/src/conf_mode/interfaces-vxlan.py @@ -17,7 +17,6 @@ import os from sys import exit -from copy import deepcopy from netifaces import interfaces from vyos.config import Config @@ -95,10 +94,11 @@ def apply(vxlan): v.remove() if 'deleted' not in vxlan: - # VXLAN interface needs to be created on-block - # instead of passing a ton of arguments, I just use a dict - # that is managed by vyos.ifconfig - conf = deepcopy(VXLANIf.get_config()) + # This is a special type of interface which needs additional parameters + # when created using iproute2. Instead of passing a ton of arguments, + # use a dictionary provided by the interface class which holds all the + # options necessary. + conf = VXLANIf.get_config() # Assign VXLAN instance configuration parameters to config dict for tmp in ['vni', 'group', 'source_address', 'source_interface', 'remote', 'port']: diff --git a/src/conf_mode/interfaces-wireless.py b/src/conf_mode/interfaces-wireless.py index c6c843e7b..be59b72b5 100755 --- a/src/conf_mode/interfaces-wireless.py +++ b/src/conf_mode/interfaces-wireless.py @@ -18,7 +18,6 @@ import os from sys import exit from re import findall -from copy import deepcopy from netaddr import EUI, mac_unix_expanded from vyos.config import Config @@ -233,10 +232,11 @@ def apply(wifi): if 'deleted' in wifi: WiFiIf(interface).remove() else: - # WiFi interface needs to be created on-block (e.g. mode or physical - # interface) instead of passing a ton of arguments, I just use a dict - # that is managed by vyos.ifconfig - conf = deepcopy(WiFiIf.get_config()) + # This is a special type of interface which needs additional parameters + # when created using iproute2. Instead of passing a ton of arguments, + # use a dictionary provided by the interface class which holds all the + # options necessary. + conf = WiFiIf.get_config() # Assign WiFi instance configuration parameters to config dict conf['phy'] = wifi['physical_device'] -- cgit v1.2.3 From a420f7637f80a9ad16160efcac8c2095fa52382b Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sun, 20 Sep 2020 21:05:03 +0200 Subject: macsec: T2023: add missing mtu CLI option Base MTU for MACsec is 1468 bytes (encryption headers), but we leave room for 802.1ad and 802.1q VLAN tags, thus the limit is lowered to 1460 bytes to not make the user juggle with the MTU bytes if he enables VLAN support later on, which is yet to come. --- interface-definitions/interfaces-macsec.xml.in | 3 ++- src/conf_mode/interfaces-macsec.py | 18 ++++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/interface-definitions/interfaces-macsec.xml.in b/interface-definitions/interfaces-macsec.xml.in index dfef387d2..9384726cc 100644 --- a/interface-definitions/interfaces-macsec.xml.in +++ b/interface-definitions/interfaces-macsec.xml.in @@ -107,8 +107,9 @@ #include #include - #include + #include #include + #include diff --git a/src/conf_mode/interfaces-macsec.py b/src/conf_mode/interfaces-macsec.py index 73c80866a..abf8b05c3 100755 --- a/src/conf_mode/interfaces-macsec.py +++ b/src/conf_mode/interfaces-macsec.py @@ -21,6 +21,7 @@ from sys import exit from vyos.config import Config from vyos.configdict import get_interface_dict from vyos.ifconfig import MACsecIf +from vyos.ifconfig import Interface from vyos.template import render from vyos.util import call from vyos.configverify import verify_vrf @@ -46,6 +47,14 @@ def get_config(config=None): base = ['interfaces', 'macsec'] macsec = get_interface_dict(conf, base) + # MACsec is "special" the default MTU is 1460 - update accordingly + # as the config_level is already st in get_interface_dict() - we can use [] + tmp = conf.get_config_dict([], key_mangling=('-', '_'), get_first_key=True) + if 'mtu' not in tmp: + # base MTU for MACsec is 1468 bytes, but we leave room for 802.1ad and + # 802.1q VLAN tags, thus the limit is 1460 bytes. + macsec['mtu'] = '1460' + # Check if interface has been removed if 'deleted' in macsec: source_interface = conf.return_effective_value( @@ -79,6 +88,15 @@ def verify(macsec): raise ConfigError('Missing mandatory MACsec security ' 'keys as encryption is enabled!') + if 'source_interface' in macsec: + # MACsec adds a 40 byte overhead (32 byte MACsec + 8 bytes VLAN 802.1ad + # and 802.1q) - we need to check the underlaying MTU if our configured + # MTU is at least 40 bytes less then the MTU of our physical interface. + underlay_mtu = int(Interface(macsec['source_interface']).get_mtu()) + if underlay_mtu < (int(macsec['mtu']) + 40): + raise ConfigError('MACsec overhead does not fit into underlaying device MTU,\n' \ + f'{underlay_mtu} bytes is too small!') + return None -- cgit v1.2.3 From 7ea3802aa3de99ec78fbe0cf24a8527b80c927db Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Mon, 21 Sep 2020 17:30:27 +0200 Subject: smoketest: macsec: T2023: test MTU setting --- data/templates/macsec/wpa_supplicant.conf.tmpl | 2 +- smoketest/scripts/cli/test_interfaces_macsec.py | 39 +++++++++++++++---------- 2 files changed, 25 insertions(+), 16 deletions(-) diff --git a/data/templates/macsec/wpa_supplicant.conf.tmpl b/data/templates/macsec/wpa_supplicant.conf.tmpl index 1731bf160..5b353def8 100644 --- a/data/templates/macsec/wpa_supplicant.conf.tmpl +++ b/data/templates/macsec/wpa_supplicant.conf.tmpl @@ -1,4 +1,4 @@ -# autogenerated by interfaces-macsec.py +### Autogenerated by interfaces-macsec.py ### # see full documentation: # https://w1.fi/cgit/hostap/plain/wpa_supplicant/wpa_supplicant.conf diff --git a/smoketest/scripts/cli/test_interfaces_macsec.py b/smoketest/scripts/cli/test_interfaces_macsec.py index 0f1b6486d..30b040b97 100755 --- a/smoketest/scripts/cli/test_interfaces_macsec.py +++ b/smoketest/scripts/cli/test_interfaces_macsec.py @@ -23,8 +23,8 @@ from base_interfaces_test import BasicInterfaceTest from vyos.configsession import ConfigSessionError from vyos.util import read_file -def get_config_value(intf, key): - tmp = read_file(f'/run/wpa_supplicant/{intf}.conf') +def get_config_value(interface, key): + tmp = read_file(f'/run/wpa_supplicant/{interface}.conf') tmp = re.findall(r'\n?{}=(.*)'.format(key), tmp) return tmp[0] @@ -49,52 +49,61 @@ class MACsecInterfaceTest(BasicInterfaceTest.BaseTest): mode - both using different mandatory settings, lets test encryption as the basic authentication test has been performed using the base class tests """ - intf = 'macsec0' - src_intf = 'eth0' + interface = 'macsec0' + src_interface = 'eth0' mak_cak = '232e44b7fda6f8e2d88a07bf78a7aff4' mak_ckn = '40916f4b23e3d548ad27eedd2d10c6f98c2d21684699647d63d41b500dfe8836' replay_window = '64' - self.session.set(self._base_path + [intf, 'security', 'encrypt']) + self.session.set(self._base_path + [interface, 'security', 'encrypt']) # check validate() - Cipher suite must be set for MACsec with self.assertRaises(ConfigSessionError): self.session.commit() - self.session.set(self._base_path + [intf, 'security', 'cipher', 'gcm-aes-128']) + self.session.set(self._base_path + [interface, 'security', 'cipher', 'gcm-aes-128']) # check validate() - Physical source interface must be set for MACsec with self.assertRaises(ConfigSessionError): self.session.commit() - self.session.set(self._base_path + [intf, 'source-interface', src_intf]) + self.session.set(self._base_path + [interface, 'source-interface', src_interface]) + + # check validate() - Physical source interface MTU must be higher then our MTU + self.session.set(self._base_path + [interface, 'mtu', '1500']) + with self.assertRaises(ConfigSessionError): + self.session.commit() + self.session.delete(self._base_path + [interface, 'mtu']) # check validate() - MACsec security keys mandartory when encryption is enabled with self.assertRaises(ConfigSessionError): self.session.commit() - self.session.set(self._base_path + [intf, 'security', 'mka', 'cak', mak_cak]) + self.session.set(self._base_path + [interface, 'security', 'mka', 'cak', mak_cak]) # check validate() - MACsec security keys mandartory when encryption is enabled with self.assertRaises(ConfigSessionError): self.session.commit() - self.session.set(self._base_path + [intf, 'security', 'mka', 'ckn', mak_ckn]) + self.session.set(self._base_path + [interface, 'security', 'mka', 'ckn', mak_ckn]) - self.session.set(self._base_path + [intf, 'security', 'replay-window', replay_window]) + self.session.set(self._base_path + [interface, 'security', 'replay-window', replay_window]) self.session.commit() - tmp = get_config_value(src_intf, 'macsec_integ_only') + tmp = get_config_value(src_interface, 'macsec_integ_only') self.assertTrue("0" in tmp) - tmp = get_config_value(src_intf, 'mka_cak') + tmp = get_config_value(src_interface, 'mka_cak') self.assertTrue(mak_cak in tmp) - tmp = get_config_value(src_intf, 'mka_ckn') + tmp = get_config_value(src_interface, 'mka_ckn') self.assertTrue(mak_ckn in tmp) # check that the default priority of 255 is programmed - tmp = get_config_value(src_intf, 'mka_priority') + tmp = get_config_value(src_interface, 'mka_priority') self.assertTrue("255" in tmp) - tmp = get_config_value(src_intf, 'macsec_replay_window') + tmp = get_config_value(src_interface, 'macsec_replay_window') self.assertTrue(replay_window in tmp) + tmp = read_file(f'/sys/class/net/{interface}/mtu') + self.assertEqual(tmp, '1460') + # Check for running process self.assertTrue("wpa_supplicant" in (p.name() for p in process_iter())) -- cgit v1.2.3 From 4ce3720109cdd6648e5251da98bf11902c443e2e Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Mon, 21 Sep 2020 17:30:53 +0200 Subject: smoketest: kernel: check for mandatory bond/lacp and bridge options --- smoketest/scripts/system/test_kernel_options.py | 47 +++++++++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100755 smoketest/scripts/system/test_kernel_options.py diff --git a/smoketest/scripts/system/test_kernel_options.py b/smoketest/scripts/system/test_kernel_options.py new file mode 100755 index 000000000..6316521f6 --- /dev/null +++ b/smoketest/scripts/system/test_kernel_options.py @@ -0,0 +1,47 @@ +#!/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 re +import platform +import unittest + +from vyos.util import read_file + +kernel = platform.release() +config = read_file(f'/boot/config-{kernel}') + +class TestKernelModules(unittest.TestCase): + """ VyOS makes use of a lot of Kernel drivers, modules and features. The + required modules which are essential for VyOS should be tested that they are + available in the Kernel that is run. """ + + def test_bond_interface(self): + """ The bond/lacp interface must be enabled in the OS Kernel """ + for option in ['CONFIG_BONDING']: + tmp = re.findall(f'{option}=(y|m)', config) + self.assertTrue(tmp) + + def test_bridge_interface(self): + """ The bridge interface must be enabled in the OS Kernel """ + for option in ['CONFIG_BRIDGE', + 'CONFIG_BRIDGE_IGMP_SNOOPING', + 'CONFIG_BRIDGE_VLAN_FILTERING']: + tmp = re.findall(f'{option}=(y|m)', config) + self.assertTrue(tmp) + +if __name__ == '__main__': + unittest.main() + -- cgit v1.2.3 From 6635d8822a42cffcb4df9a44d87a0c4fb79ef698 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Mon, 21 Sep 2020 17:31:26 +0200 Subject: wireless: T2887: hostapd: add bridge option --- data/templates/wifi/hostapd.conf.tmpl | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/data/templates/wifi/hostapd.conf.tmpl b/data/templates/wifi/hostapd.conf.tmpl index 132c4ce40..3980fb896 100644 --- a/data/templates/wifi/hostapd.conf.tmpl +++ b/data/templates/wifi/hostapd.conf.tmpl @@ -11,6 +11,21 @@ device_name={{ description | truncate(32, True) }} # command line parameter. interface={{ ifname }} +{% if is_bridge_member is defined %} +# In case of atheros and nl80211 driver interfaces, an additional +# configuration parameter, bridge, may be used to notify hostapd if the +# interface is included in a bridge. This parameter is not used with Host AP +# driver. If the bridge parameter is not set, the drivers will automatically +# figure out the bridge interface (assuming sysfs is enabled and mounted to +# /sys) and this parameter may not be needed. +# +# For nl80211, this parameter can be used to request the AP interface to be +# added to the bridge automatically (brctl may refuse to do this before hostapd +# has been started to change the interface mode). If needed, the bridge +# interface is also created. +bridge={{ is_bridge_member }} +{% endif %} + # Driver interface type (hostap/wired/none/nl80211/bsd); # default: hostap). nl80211 is used with all Linux mac80211 drivers. # Use driver=none if building hostapd as a standalone RADIUS server that does -- cgit v1.2.3 From d22b476e0e1ca2a173ecf9c85596b4f02646e772 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Mon, 21 Sep 2020 17:32:55 +0200 Subject: wireless: T2887: help when searching hidden SSIDs in station mode --- data/templates/wifi/wpa_supplicant.conf.tmpl | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/data/templates/wifi/wpa_supplicant.conf.tmpl b/data/templates/wifi/wpa_supplicant.conf.tmpl index 9ddad35fd..f84892dc0 100644 --- a/data/templates/wifi/wpa_supplicant.conf.tmpl +++ b/data/templates/wifi/wpa_supplicant.conf.tmpl @@ -1,7 +1,13 @@ -# WPA supplicant config +### Autogenerated by interfaces-macsec.py ### + +# see full documentation: +# https://w1.fi/cgit/hostap/plain/wpa_supplicant/wpa_supplicant.conf + network={ ssid="{{ ssid }}" + scan_ssid=1 {% if security is defined and security.wpa is defined and security.wpa.passphrase is defined %} + key_mgmt=WPA-PSK psk="{{ security.wpa.passphrase }}" {% else %} key_mgmt=NONE -- cgit v1.2.3 From 79b1ab8dc67c9011a3d5e5397ad4d73a6c537d80 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Mon, 21 Sep 2020 22:21:12 +0200 Subject: bridge: ifconfig: T2653: only delete member interfaces which still exist When removing e.g. a macsec interface and also its associated member interface from the bridge, it will happen that the macsec interface instance is long gone before we reach the code in the bridge interface which will remove it from the bridge itself. When this is the case, we can not call BridgeIf.del_port() as it will throw an exception that the interface does not exist. We now only remove a bridge member if the interface in question is still available in the kernel. --- python/vyos/ifconfig/bridge.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/python/vyos/ifconfig/bridge.py b/python/vyos/ifconfig/bridge.py index 4c76fe996..c133a56fc 100644 --- a/python/vyos/ifconfig/bridge.py +++ b/python/vyos/ifconfig/bridge.py @@ -13,6 +13,8 @@ # You should have received a copy of the GNU Lesser General Public # License along with this library. If not, see . +from netifaces import interfaces + from vyos.ifconfig.interface import Interface from vyos.ifconfig.stp import STP from vyos.validate import assert_boolean @@ -228,8 +230,8 @@ class BridgeIf(Interface): # remove interface from bridge tmp = vyos_dict_search('member.interface_remove', config) - if tmp: - for member in tmp: + for member in (tmp or []): + if member in interfaces(): self.del_port(member) STPBridgeIf = STP.enable(BridgeIf) -- cgit v1.2.3 From e31dfd9f5542b0572e3ece89bdc347679b08aa72 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Mon, 21 Sep 2020 22:24:25 +0200 Subject: macsec: T2788: source-interface must not be member of a bridge Add verify() step to ensure the macsec source-interface is not already part of a bridge interface. This should probably also be checked for bond interfaces. --- src/conf_mode/interfaces-macsec.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/conf_mode/interfaces-macsec.py b/src/conf_mode/interfaces-macsec.py index abf8b05c3..73b62dcf1 100755 --- a/src/conf_mode/interfaces-macsec.py +++ b/src/conf_mode/interfaces-macsec.py @@ -28,6 +28,7 @@ from vyos.configverify import verify_vrf from vyos.configverify import verify_address from vyos.configverify import verify_bridge_delete from vyos.configverify import verify_source_interface +from vyos.validate import is_member from vyos import ConfigError from vyos import airbag airbag.enable() @@ -61,6 +62,11 @@ def get_config(config=None): base + ['source-interface']) macsec.update({'source_interface': source_interface}) + if 'source_interface' in macsec: + # Check if source interface is used by another bridge + tmp = is_member(conf, macsec['source_interface'], 'bridge') + if tmp: macsec.update({'is_bridge_member_source_interface' : tmp}) + return macsec @@ -88,6 +94,10 @@ def verify(macsec): raise ConfigError('Missing mandatory MACsec security ' 'keys as encryption is enabled!') + if 'is_bridge_member_source_interface' in macsec: + raise ConfigError('source-interface is already member of bridge ' \ + '{is_bridge_member_source_interface}!'.format(**macsec)) + if 'source_interface' in macsec: # MACsec adds a 40 byte overhead (32 byte MACsec + 8 bytes VLAN 802.1ad # and 802.1q) - we need to check the underlaying MTU if our configured -- cgit v1.2.3 From d28a6a516d449ede788816574c35061fbf7d6485 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Tue, 22 Sep 2020 18:35:44 +0200 Subject: ifconfig: T2653: move is_member() from vyos.vylidate to vyos.configdict --- python/vyos/configdict.py | 54 +++++++++++++++++++++++++---- python/vyos/validate.py | 41 ---------------------- src/conf_mode/interfaces-bonding.py | 19 +++++----- src/conf_mode/interfaces-bridge.py | 27 +++++++-------- src/conf_mode/interfaces-macsec.py | 10 ------ src/conf_mode/interfaces-openvpn.py | 3 +- src/conf_mode/interfaces-pseudo-ethernet.py | 25 +------------ src/conf_mode/interfaces-tunnel.py | 9 ++--- 8 files changed, 77 insertions(+), 111 deletions(-) diff --git a/python/vyos/configdict.py b/python/vyos/configdict.py index ef1b452a8..4a4a767f3 100644 --- a/python/vyos/configdict.py +++ b/python/vyos/configdict.py @@ -21,8 +21,9 @@ import os from copy import deepcopy from vyos.util import vyos_dict_search -from vyos.validate import is_member from vyos.xml import defaults +from vyos.xml import is_tag +from vyos.xml import is_leaf from vyos import ConfigError def retrieve_config(path_hash, base_path, config): @@ -186,6 +187,47 @@ def T2665_set_dhcpv6pd_defaults(config_dict): return config_dict +def is_member(conf, interface, intftype=None): + """ + Checks if passed interface is member of other interface of specified type. + intftype is optional, if not passed it will search all known types + (currently bridge and bonding) + + Returns: + None -> Interface is not a member + interface name -> Interface is a member of this interface + False -> interface type cannot have members + """ + ret_val = None + intftypes = ['bonding', 'bridge'] + if intftype not in intftypes + [None]: + raise ValueError(( + f'unknown interface type "{intftype}" or it cannot ' + f'have member interfaces')) + + intftype = intftypes if intftype == None else [intftype] + + # set config level to root + old_level = conf.get_level() + conf.set_level([]) + + for it in intftype: + base = ['interfaces', it] + for intf in conf.list_nodes(base): + memberintf = base + [intf, 'member', 'interface'] + if is_tag(memberintf): + if interface in conf.list_nodes(memberintf): + ret_val = intf + break + elif is_leaf(memberintf): + if ( conf.exists(memberintf) and + interface in conf.return_values(memberintf) ): + ret_val = intf + break + + old_level = conf.set_level(old_level) + return ret_val + def get_interface_dict(config, base, ifname=''): """ Common utility function to retrieve and mandgle the interfaces available @@ -236,17 +278,15 @@ def get_interface_dict(config, base, ifname=''): # Check if we are a member of a bridge device bridge = is_member(config, ifname, 'bridge') - if bridge: - dict.update({'is_bridge_member' : bridge}) + if bridge: dict.update({'is_bridge_member' : bridge}) # Check if we are a member of a bond device bond = is_member(config, ifname, 'bonding') - if bond: - dict.update({'is_bond_member' : bond}) + if bond: dict.update({'is_bond_member' : bond}) + mac = leaf_node_changed(config, ['mac']) - if mac: - dict.update({'mac_old' : mac}) + if mac: dict.update({'mac_old' : mac}) eui64 = leaf_node_changed(config, ['ipv6', 'address', 'eui64']) if eui64: diff --git a/python/vyos/validate.py b/python/vyos/validate.py index ceeb6888a..691cf3c8e 100644 --- a/python/vyos/validate.py +++ b/python/vyos/validate.py @@ -19,7 +19,6 @@ import netifaces import ipaddress from vyos.util import cmd -from vyos import xml # Important note when you are adding new validation functions: # @@ -267,46 +266,6 @@ def assert_mac(m): raise ValueError(f'{m} is a VRRP MAC address') -def is_member(conf, interface, intftype=None): - """ - Checks if passed interface is member of other interface of specified type. - intftype is optional, if not passed it will search all known types - (currently bridge and bonding) - - Returns: - None -> Interface is not a member - interface name -> Interface is a member of this interface - False -> interface type cannot have members - """ - ret_val = None - if intftype not in ['bonding', 'bridge', None]: - raise ValueError(( - f'unknown interface type "{intftype}" or it cannot ' - f'have member interfaces')) - - intftype = ['bonding', 'bridge'] if intftype == None else [intftype] - - # set config level to root - old_level = conf.get_level() - conf.set_level([]) - - for it in intftype: - base = ['interfaces', it] - for intf in conf.list_nodes(base): - memberintf = base + [intf, 'member', 'interface'] - if xml.is_tag(memberintf): - if interface in conf.list_nodes(memberintf): - ret_val = intf - break - elif xml.is_leaf(memberintf): - if ( conf.exists(memberintf) and - interface in conf.return_values(memberintf) ): - ret_val = intf - break - - old_level = conf.set_level(old_level) - return ret_val - def has_address_configured(conf, intf): """ Checks if interface has an address configured. diff --git a/src/conf_mode/interfaces-bonding.py b/src/conf_mode/interfaces-bonding.py index a9679b47c..5ac4feb77 100755 --- a/src/conf_mode/interfaces-bonding.py +++ b/src/conf_mode/interfaces-bonding.py @@ -22,6 +22,7 @@ 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.configdict import is_member from vyos.configverify import verify_address from vyos.configverify import verify_bridge_delete from vyos.configverify import verify_dhcpv6 @@ -30,7 +31,7 @@ from vyos.configverify import verify_vlan_config from vyos.configverify import verify_vrf from vyos.ifconfig import BondIf from vyos.ifconfig import Section -from vyos.validate import is_member +from vyos.util import vyos_dict_search from vyos.validate import has_address_configured from vyos import ConfigError from vyos import airbag @@ -98,14 +99,13 @@ def get_config(config=None): # also present the interfaces to be removed from the bond as dictionary bond['member'].update({'interface_remove': tmp}) - if 'member' in bond and 'interface' in bond['member']: + if vyos_dict_search('member.interface', bond): for interface, interface_config in bond['member']['interface'].items(): - # Check if we are a member of another bond device + # Check if member interface is already member of another bridge tmp = is_member(conf, interface, 'bridge') - if tmp: - interface_config.update({'is_bridge_member' : tmp}) + if tmp: interface_config.update({'is_bridge_member' : tmp}) - # Check if we are a member of a bond device + # Check if member interface is already member of a bond tmp = is_member(conf, interface, 'bonding') if tmp and tmp != bond['ifname']: interface_config.update({'is_bond_member' : tmp}) @@ -144,10 +144,9 @@ def verify(bond): verify_vlan_config(bond) bond_name = bond['ifname'] - if 'member' in bond: - member = bond.get('member') - for interface, interface_config in member.get('interface', {}).items(): - error_msg = f'Can not add interface "{interface}" to bond "{bond_name}", ' + if vyos_dict_search('member.interface', bond): + for interface, interface_config in bond['member']['interface'].items(): + error_msg = f'Can not add interface "{interface}" to bond, ' if interface == 'lo': raise ConfigError('Loopback interface "lo" can not be added to a bond') diff --git a/src/conf_mode/interfaces-bridge.py b/src/conf_mode/interfaces-bridge.py index 47c8c05f9..3bddac023 100755 --- a/src/conf_mode/interfaces-bridge.py +++ b/src/conf_mode/interfaces-bridge.py @@ -22,13 +22,15 @@ from netifaces import interfaces from vyos.config import Config from vyos.configdict import get_interface_dict from vyos.configdict import node_changed +from vyos.configdict import is_member from vyos.configverify import verify_dhcpv6 from vyos.configverify import verify_vrf from vyos.ifconfig import BridgeIf -from vyos.validate import is_member, has_address_configured +from vyos.validate import has_address_configured from vyos.xml import defaults from vyos.util import cmd +from vyos.util import vyos_dict_search from vyos import ConfigError from vyos import airbag @@ -54,8 +56,8 @@ def get_config(config=None): else: bridge.update({'member': {'interface_remove': tmp }}) - if 'member' in bridge and 'interface' in bridge['member']: - # XXX TT2665 we need a copy of the dict keys for iteration, else we will get: + if vyos_dict_search('member.interface', bridge): + # XXX: T2665: we need a copy of the dict keys for iteration, else we will get: # RuntimeError: dictionary changed size during iteration for interface in list(bridge['member']['interface']): for key in ['cost', 'priority']: @@ -69,20 +71,19 @@ def get_config(config=None): for interface, interface_config in bridge['member']['interface'].items(): interface_config.update(default_member_values) - # Check if we are a member of another bridge device + # Check if member interface is already member of another bridge tmp = is_member(conf, interface, 'bridge') if tmp and tmp != bridge['ifname']: interface_config.update({'is_bridge_member' : tmp}) - # Check if we are a member of a bond device + # Check if member interface is already member of a bond tmp = is_member(conf, interface, 'bonding') - if tmp: - interface_config.update({'is_bond_member' : tmp}) + if tmp: interface_config.update({'is_bond_member' : tmp}) + # Bridge members must not have an assigned address tmp = has_address_configured(conf, interface) - if tmp: - interface_config.update({'has_address' : ''}) + if tmp: interface_config.update({'has_address' : ''}) return bridge @@ -93,11 +94,9 @@ def verify(bridge): verify_dhcpv6(bridge) verify_vrf(bridge) - if 'member' in bridge: - member = bridge.get('member') - bridge_name = bridge['ifname'] - for interface, interface_config in member.get('interface', {}).items(): - error_msg = f'Can not add interface "{interface}" to bridge "{bridge_name}", ' + if vyos_dict_search('member.interface', bridge): + for interface, interface_config in bridge['member']['interface'].items(): + error_msg = f'Can not add interface "{interface}" to bridge, ' if interface == 'lo': raise ConfigError('Loopback interface "lo" can not be added to a bridge') diff --git a/src/conf_mode/interfaces-macsec.py b/src/conf_mode/interfaces-macsec.py index 73b62dcf1..abf8b05c3 100755 --- a/src/conf_mode/interfaces-macsec.py +++ b/src/conf_mode/interfaces-macsec.py @@ -28,7 +28,6 @@ from vyos.configverify import verify_vrf from vyos.configverify import verify_address from vyos.configverify import verify_bridge_delete from vyos.configverify import verify_source_interface -from vyos.validate import is_member from vyos import ConfigError from vyos import airbag airbag.enable() @@ -62,11 +61,6 @@ def get_config(config=None): base + ['source-interface']) macsec.update({'source_interface': source_interface}) - if 'source_interface' in macsec: - # Check if source interface is used by another bridge - tmp = is_member(conf, macsec['source_interface'], 'bridge') - if tmp: macsec.update({'is_bridge_member_source_interface' : tmp}) - return macsec @@ -94,10 +88,6 @@ def verify(macsec): raise ConfigError('Missing mandatory MACsec security ' 'keys as encryption is enabled!') - if 'is_bridge_member_source_interface' in macsec: - raise ConfigError('source-interface is already member of bridge ' \ - '{is_bridge_member_source_interface}!'.format(**macsec)) - if 'source_interface' in macsec: # MACsec adds a 40 byte overhead (32 byte MACsec + 8 bytes VLAN 802.1ad # and 802.1q) - we need to check the underlaying MTU if our configured diff --git a/src/conf_mode/interfaces-openvpn.py b/src/conf_mode/interfaces-openvpn.py index 958b305dd..f83590209 100755 --- a/src/conf_mode/interfaces-openvpn.py +++ b/src/conf_mode/interfaces-openvpn.py @@ -26,10 +26,11 @@ from shutil import rmtree from vyos.config import Config from vyos.configdict import list_diff +from vyos.configdict import is_member from vyos.ifconfig import VTunIf from vyos.template import render from vyos.util import call, chown, chmod_600, chmod_755 -from vyos.validate import is_addr_assigned, is_member, is_ipv4 +from vyos.validate import is_addr_assigned, is_ipv4 from vyos import ConfigError from vyos import airbag diff --git a/src/conf_mode/interfaces-pseudo-ethernet.py b/src/conf_mode/interfaces-pseudo-ethernet.py index 98397b28f..ddbef56ac 100755 --- a/src/conf_mode/interfaces-pseudo-ethernet.py +++ b/src/conf_mode/interfaces-pseudo-ethernet.py @@ -25,7 +25,6 @@ from vyos.configverify import verify_bridge_delete from vyos.configverify import verify_source_interface from vyos.configverify import verify_vlan_config from vyos.ifconfig import MACVLANIf -from vyos.validate import is_member from vyos import ConfigError from vyos import airbag @@ -44,19 +43,7 @@ def get_config(config=None): peth = get_interface_dict(conf, base) mode = leaf_node_changed(conf, ['mode']) - if mode: - peth.update({'mode_old' : mode}) - - # Check if source-interface is member of a bridge device - if 'source_interface' in peth: - bridge = is_member(conf, peth['source_interface'], 'bridge') - if bridge: - peth.update({'source_interface_is_bridge_member' : bridge}) - - # Check if we are a member of a bond device - bond = is_member(conf, peth['source_interface'], 'bonding') - if bond: - peth.update({'source_interface_is_bond_member' : bond}) + if mode: peth.update({'mode_old' : mode}) return peth @@ -69,16 +56,6 @@ def verify(peth): verify_vrf(peth) verify_address(peth) - if 'source_interface_is_bridge_member' in peth: - raise ConfigError( - 'Source interface "{source_interface}" can not be used as it is already a ' - 'member of bridge "{source_interface_is_bridge_member}"!'.format(**peth)) - - if 'source_interface_is_bond_member' in peth: - raise ConfigError( - 'Source interface "{source_interface}" can not be used as it is already a ' - 'member of bond "{source_interface_is_bond_member}"!'.format(**peth)) - # use common function to verify VLAN configuration verify_vlan_config(peth) return None diff --git a/src/conf_mode/interfaces-tunnel.py b/src/conf_mode/interfaces-tunnel.py index 11d8d6edc..f1d885b15 100755 --- a/src/conf_mode/interfaces-tunnel.py +++ b/src/conf_mode/interfaces-tunnel.py @@ -22,10 +22,11 @@ from copy import deepcopy from netifaces import interfaces from vyos.config import Config +from vyos.configdict import is_member from vyos.ifconfig import Interface, GREIf, GRETapIf, IPIPIf, IP6GREIf, IPIP6If, IP6IP6If, SitIf, Sit6RDIf from vyos.ifconfig.afi import IP4, IP6 from vyos.configdict import list_diff -from vyos.validate import is_ipv4, is_ipv6, is_member +from vyos.validate import is_ipv4, is_ipv6 from vyos import ConfigError from vyos.dicts import FixedDict @@ -170,8 +171,8 @@ class ConfigurationState(object): """ >>> conf.get_values('addresses', 'address') will place a list of the new IP present in 'interface dummy dum1 address' - into the dictionnary entry "-add" (here 'addresses-add') using - Config.return_values and will add the the one which were removed in into + into the dictionnary entry "-add" (here 'addresses-add') using + Config.return_values and will add the the one which were removed in into the entry "-del" (here addresses-del') """ add_name = f'{name}-add' @@ -263,7 +264,7 @@ class ConfigurationState(object): d = d[lpath[-1]] # XXX: it should have provided me the content and not the key self._conf.set_level(l) - return d + return d def to_api(self): """ -- cgit v1.2.3 From 83a9ce7991195c709736eec234fea3d60cde7582 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Tue, 22 Sep 2020 18:37:00 +0200 Subject: ifconfig: T2653: bond: bridge: ensure member interface is not a source-interface As we already check that a bond/bridge member interface is not a member of any other bridge or bond, the check must be extended. We also need to ensure that the bond member interface is not used as a source-interface to pppoe, macsec, tunnel, pseudo-ethernet, vxlan interfaces. --- python/vyos/configdict.py | 46 +++++++++++++++++++++++++++++++++++++ python/vyos/configverify.py | 15 ++++++++++-- src/conf_mode/interfaces-bonding.py | 9 ++++++++ src/conf_mode/interfaces-bridge.py | 8 +++++++ 4 files changed, 76 insertions(+), 2 deletions(-) diff --git a/python/vyos/configdict.py b/python/vyos/configdict.py index 4a4a767f3..58ecd3f17 100644 --- a/python/vyos/configdict.py +++ b/python/vyos/configdict.py @@ -228,6 +228,41 @@ def is_member(conf, interface, intftype=None): old_level = conf.set_level(old_level) return ret_val +def is_source_interface(conf, interface, intftype=None): + """ + Checks if passed interface is configured as source-interface of other + interfaces of specified type. intftype is optional, if not passed it will + search all known types (currently pppoe, macsec, pseudo-ethernet, tunnel + and vxlan) + + Returns: + None -> Interface is not a member + interface name -> Interface is a member of this interface + False -> interface type cannot have members + """ + ret_val = None + intftypes = ['macsec', 'pppoe', 'pseudo-ethernet', 'tunnel', 'vxlan'] + if intftype not in intftypes + [None]: + raise ValueError(f'unknown interface type "{intftype}" or it can not ' + 'have a source-interface') + + intftype = intftypes if intftype == None else [intftype] + + # set config level to root + old_level = conf.get_level() + conf.set_level([]) + + for it in intftype: + base = ['interfaces', it] + for intf in conf.list_nodes(base): + lower_intf = base + [intf, 'source-interface'] + if conf.exists(lower_intf) and interface in conf.return_values(lower_intf): + ret_val = intf + break + + old_level = conf.set_level(old_level) + return ret_val + def get_interface_dict(config, base, ifname=''): """ Common utility function to retrieve and mandgle the interfaces available @@ -284,6 +319,17 @@ def get_interface_dict(config, base, ifname=''): bond = is_member(config, ifname, 'bonding') if bond: dict.update({'is_bond_member' : bond}) + # Some interfaces come with a source_interface which must also not be part + # of any other bond or bridge interface as it is exclusivly assigned as the + # Kernels "lower" interface to this new "virtual/upper" interface. + if 'source_interface' in dict: + # Check if source interface is member of another bridge + tmp = is_member(config, dict['source_interface'], 'bridge') + if tmp: dict.update({'source_interface_is_bridge_member' : tmp}) + + # Check if source interface is member of another bridge + tmp = is_member(config, dict['source_interface'], 'bonding') + if tmp: dict.update({'source_interface_is_bond_member' : tmp}) mac = leaf_node_changed(config, ['mac']) if mac: dict.update({'mac_old' : mac}) diff --git a/python/vyos/configverify.py b/python/vyos/configverify.py index 7e1930878..bf4e26fa7 100644 --- a/python/vyos/configverify.py +++ b/python/vyos/configverify.py @@ -82,9 +82,20 @@ def verify_source_interface(config): if 'source_interface' not in config: raise ConfigError('Physical source-interface required for ' 'interface "{ifname}"'.format(**config)) + if config['source_interface'] not in interfaces(): - raise ConfigError('Source interface {source_interface} does not ' - 'exist'.format(**config)) + raise ConfigError('Specified source-interface {source_interface} does ' + 'not exist'.format(**config)) + + if 'source_interface_is_bridge_member' in config: + raise ConfigError('Invalid source-interface {source_interface}. Interface ' + 'is already a member of bridge ' + '{source_interface_is_bridge_member}'.format(**config)) + + if 'source_interface_is_bond_member' in config: + raise ConfigError('Invalid source-interface {source_interface}. Interface ' + 'is already a member of bond ' + '{source_interface_is_bond_member}'.format(**config)) def verify_dhcpv6(config): """ diff --git a/src/conf_mode/interfaces-bonding.py b/src/conf_mode/interfaces-bonding.py index 5ac4feb77..aece2a04b 100755 --- a/src/conf_mode/interfaces-bonding.py +++ b/src/conf_mode/interfaces-bonding.py @@ -23,6 +23,7 @@ from vyos.config import Config from vyos.configdict import get_interface_dict from vyos.configdict import leaf_node_changed from vyos.configdict import is_member +from vyos.configdict import is_source_interface from vyos.configverify import verify_address from vyos.configverify import verify_bridge_delete from vyos.configverify import verify_dhcpv6 @@ -110,6 +111,10 @@ def get_config(config=None): if tmp and tmp != bond['ifname']: interface_config.update({'is_bond_member' : tmp}) + # Check if member interface is used as source-interface on another interface + tmp = is_source_interface(conf, interface) + if tmp: interface_config.update({'is_source_interface' : tmp}) + # bond members must not have an assigned address tmp = has_address_configured(conf, interface) if tmp: interface_config.update({'has_address' : ''}) @@ -162,6 +167,10 @@ def verify(bond): tmp = interface_config['is_bond_member'] raise ConfigError(error_msg + f'it is already a member of bond "{tmp}"!') + if 'is_source_interface' in interface_config: + tmp = interface_config['is_source_interface'] + raise ConfigError(error_msg + f'it is the source-interface of "{tmp}"!') + if 'has_address' in interface_config: raise ConfigError(error_msg + 'it has an address assigned!') diff --git a/src/conf_mode/interfaces-bridge.py b/src/conf_mode/interfaces-bridge.py index 3bddac023..485decb17 100755 --- a/src/conf_mode/interfaces-bridge.py +++ b/src/conf_mode/interfaces-bridge.py @@ -23,6 +23,7 @@ from vyos.config import Config from vyos.configdict import get_interface_dict from vyos.configdict import node_changed from vyos.configdict import is_member +from vyos.configdict import is_source_interface from vyos.configverify import verify_dhcpv6 from vyos.configverify import verify_vrf from vyos.ifconfig import BridgeIf @@ -80,6 +81,9 @@ def get_config(config=None): tmp = is_member(conf, interface, 'bonding') if tmp: interface_config.update({'is_bond_member' : tmp}) + # Check if member interface is used as source-interface on another interface + tmp = is_source_interface(conf, interface) + if tmp: interface_config.update({'is_source_interface' : tmp}) # Bridge members must not have an assigned address tmp = has_address_configured(conf, interface) @@ -112,6 +116,10 @@ def verify(bridge): tmp = interface_config['is_bond_member'] raise ConfigError(error_msg + f'it is already a member of bond "{tmp}"!') + if 'is_source_interface' in interface_config: + tmp = interface_config['is_source_interface'] + raise ConfigError(error_msg + f'it is the source-interface of "{tmp}"!') + if 'has_address' in interface_config: raise ConfigError(error_msg + 'it has an address assigned!') -- cgit v1.2.3 From feb491584d84636883000634bd4bba9b014b2c83 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Tue, 22 Sep 2020 18:43:34 +0200 Subject: smoketest: macsec: T2023: check that source-interface is not used by any other interface --- smoketest/scripts/cli/test_interfaces_macsec.py | 137 ++++++++++++++++-------- 1 file changed, 91 insertions(+), 46 deletions(-) diff --git a/smoketest/scripts/cli/test_interfaces_macsec.py b/smoketest/scripts/cli/test_interfaces_macsec.py index 30b040b97..fab5433de 100755 --- a/smoketest/scripts/cli/test_interfaces_macsec.py +++ b/smoketest/scripts/cli/test_interfaces_macsec.py @@ -16,10 +16,12 @@ import re import unittest + from psutil import process_iter +from base_interfaces_test import BasicInterfaceTest +from netifaces import interfaces from vyos.ifconfig import Section -from base_interfaces_test import BasicInterfaceTest from vyos.configsession import ConfigSessionError from vyos.util import read_file @@ -32,80 +34,123 @@ class MACsecInterfaceTest(BasicInterfaceTest.BaseTest): def setUp(self): super().setUp() self._base_path = ['interfaces', 'macsec'] - self._options = { - 'macsec0': ['source-interface eth0', - 'security cipher gcm-aes-128'] - } + self._options = { 'macsec0': ['source-interface eth0', 'security cipher gcm-aes-128'] } # if we have a physical eth1 interface, add a second macsec instance if 'eth1' in Section.interfaces("ethernet"): - macsec = { 'macsec1': ['source-interface eth1', 'security cipher gcm-aes-128'] } + macsec = { 'macsec1': [f'source-interface eth1', 'security cipher gcm-aes-128'] } self._options.update(macsec) self._interfaces = list(self._options) def test_encryption(self): - """ MACsec can be operating in authentication and encryption - mode - both using different mandatory settings, lets test - encryption as the basic authentication test has been performed - using the base class tests """ - interface = 'macsec0' - src_interface = 'eth0' + """ MACsec can be operating in authentication and encryption mode - both + using different mandatory settings, lets test encryption as the basic + authentication test has been performed using the base class tests """ + mak_cak = '232e44b7fda6f8e2d88a07bf78a7aff4' mak_ckn = '40916f4b23e3d548ad27eedd2d10c6f98c2d21684699647d63d41b500dfe8836' replay_window = '64' - self.session.set(self._base_path + [interface, 'security', 'encrypt']) - # check validate() - Cipher suite must be set for MACsec - with self.assertRaises(ConfigSessionError): - self.session.commit() - self.session.set(self._base_path + [interface, 'security', 'cipher', 'gcm-aes-128']) + for interface, option_value in self._options.items(): + for option in option_value: + if option.split()[0] == 'source-interface': + src_interface = option.split()[1] - # check validate() - Physical source interface must be set for MACsec - with self.assertRaises(ConfigSessionError): - self.session.commit() - self.session.set(self._base_path + [interface, 'source-interface', src_interface]) + self.session.set(self._base_path + [interface] + option.split()) - # check validate() - Physical source interface MTU must be higher then our MTU - self.session.set(self._base_path + [interface, 'mtu', '1500']) - with self.assertRaises(ConfigSessionError): + # Encrypt link + self.session.set(self._base_path + [interface, 'security', 'encrypt']) + + # check validate() - Physical source interface MTU must be higher then our MTU + self.session.set(self._base_path + [interface, 'mtu', '1500']) + with self.assertRaises(ConfigSessionError): + self.session.commit() + self.session.delete(self._base_path + [interface, 'mtu']) + + # check validate() - MACsec security keys mandartory when encryption is enabled + with self.assertRaises(ConfigSessionError): + self.session.commit() + self.session.set(self._base_path + [interface, 'security', 'mka', 'cak', mak_cak]) + + # check validate() - MACsec security keys mandartory when encryption is enabled + with self.assertRaises(ConfigSessionError): + self.session.commit() + self.session.set(self._base_path + [interface, 'security', 'mka', 'ckn', mak_ckn]) + + self.session.set(self._base_path + [interface, 'security', 'replay-window', replay_window]) + + # final commit of settings self.session.commit() - self.session.delete(self._base_path + [interface, 'mtu']) - # check validate() - MACsec security keys mandartory when encryption is enabled + tmp = get_config_value(src_interface, 'macsec_integ_only') + self.assertTrue("0" in tmp) + + tmp = get_config_value(src_interface, 'mka_cak') + self.assertTrue(mak_cak in tmp) + + tmp = get_config_value(src_interface, 'mka_ckn') + self.assertTrue(mak_ckn in tmp) + + # check that the default priority of 255 is programmed + tmp = get_config_value(src_interface, 'mka_priority') + self.assertTrue("255" in tmp) + + tmp = get_config_value(src_interface, 'macsec_replay_window') + self.assertTrue(replay_window in tmp) + + tmp = read_file(f'/sys/class/net/{interface}/mtu') + self.assertEqual(tmp, '1460') + + # Check for running process + self.assertTrue("wpa_supplicant" in (p.name() for p in process_iter())) + + def test_mandatory_toptions(self): + interface = 'macsec1' + self.session.set(self._base_path + [interface]) + + # check validate() - source interface is mandatory with self.assertRaises(ConfigSessionError): self.session.commit() - self.session.set(self._base_path + [interface, 'security', 'mka', 'cak', mak_cak]) + self.session.set(self._base_path + [interface, 'source-interface', 'eth0']) - # check validate() - MACsec security keys mandartory when encryption is enabled + # check validate() - cipher is mandatory with self.assertRaises(ConfigSessionError): self.session.commit() - self.session.set(self._base_path + [interface, 'security', 'mka', 'ckn', mak_ckn]) + self.session.set(self._base_path + [interface, 'security', 'cipher', 'gcm-aes-128']) - self.session.set(self._base_path + [interface, 'security', 'replay-window', replay_window]) + # final commit and verify self.session.commit() + self.assertIn(interface, interfaces()) - tmp = get_config_value(src_interface, 'macsec_integ_only') - self.assertTrue("0" in tmp) + def test_source_interface(self): + """ Ensure source-interface can bot be part of any other bond or bridge """ - tmp = get_config_value(src_interface, 'mka_cak') - self.assertTrue(mak_cak in tmp) + base_bridge = ['interfaces', 'bridge', 'br200'] + base_bond = ['interfaces', 'bonding', 'bond200'] - tmp = get_config_value(src_interface, 'mka_ckn') - self.assertTrue(mak_ckn in tmp) + for interface, option_value in self._options.items(): + for option in option_value: + self.session.set(self._base_path + [interface] + option.split()) + if option.split()[0] == 'source-interface': + src_interface = option.split()[1] - # check that the default priority of 255 is programmed - tmp = get_config_value(src_interface, 'mka_priority') - self.assertTrue("255" in tmp) + self.session.set(base_bridge + ['member', 'interface', src_interface]) + # check validate() - Source interface must not already be a member of a bridge + with self.assertRaises(ConfigSessionError): + self.session.commit() + self.session.delete(base_bridge) - tmp = get_config_value(src_interface, 'macsec_replay_window') - self.assertTrue(replay_window in tmp) + self.session.set(base_bond + ['member', 'interface', src_interface]) + # check validate() - Source interface must not already be a member of a bridge + with self.assertRaises(ConfigSessionError): + self.session.commit() + self.session.delete(base_bond) - tmp = read_file(f'/sys/class/net/{interface}/mtu') - self.assertEqual(tmp, '1460') - - # Check for running process - self.assertTrue("wpa_supplicant" in (p.name() for p in process_iter())) + # final commit and verify + self.session.commit() + self.assertIn(interface, interfaces()) if __name__ == '__main__': unittest.main() + -- cgit v1.2.3 From bef17a3da6bcd40c0fcee64e68a7fe300a11b2e1 Mon Sep 17 00:00:00 2001 From: sever-sever Date: Tue, 22 Sep 2020 16:55:55 +0000 Subject: conf-mode: T2915: Adding lost option proxy-arp-pvlan for vlan --- interface-definitions/include/vif.xml.i | 1 + 1 file changed, 1 insertion(+) diff --git a/interface-definitions/include/vif.xml.i b/interface-definitions/include/vif.xml.i index 5a4e52122..919e4d493 100644 --- a/interface-definitions/include/vif.xml.i +++ b/interface-definitions/include/vif.xml.i @@ -50,6 +50,7 @@ #include #include #include + #include -- cgit v1.2.3 From b2c61e2127d83cc0a0e27092462b62c2e8e7eaa1 Mon Sep 17 00:00:00 2001 From: Marcus Hoff Date: Tue, 22 Sep 2020 19:44:24 +0200 Subject: openvpn: T2907: add 'none' encryption option to not encrypt any data --- data/templates/openvpn/server.conf.tmpl | 4 +++- interface-definitions/interfaces-openvpn.xml.in | 16 ++++++++++++---- src/conf_mode/interfaces-openvpn.py | 8 +++++++- 3 files changed, 22 insertions(+), 6 deletions(-) diff --git a/data/templates/openvpn/server.conf.tmpl b/data/templates/openvpn/server.conf.tmpl index 401f8e04b..8a1ac6bd8 100644 --- a/data/templates/openvpn/server.conf.tmpl +++ b/data/templates/openvpn/server.conf.tmpl @@ -196,7 +196,9 @@ tls-server # Encryption options {%- if encryption %} -{% if encryption == 'des' -%} +{% if encryption == 'none' -%} +cipher none +{%- elif encryption == 'des' -%} cipher des-cbc {%- elif encryption == '3des' -%} cipher des-ede3-cbc diff --git a/interface-definitions/interfaces-openvpn.xml.in b/interface-definitions/interfaces-openvpn.xml.in index 905c76507..5675379d5 100644 --- a/interface-definitions/interfaces-openvpn.xml.in +++ b/interface-definitions/interfaces-openvpn.xml.in @@ -63,8 +63,12 @@ Standard Data Encryption Algorithm - des 3des bf128 bf256 aes128 aes128gcm aes192 aes192gcm aes256 aes256gcm + none des 3des bf128 bf256 aes128 aes128gcm aes192 aes192gcm aes256 aes256gcm + + none + Disable encryption + des DES algorithm @@ -106,7 +110,7 @@ AES algorithm with 256-bit key GCM - (des|3des|bf128|bf256|aes128|aes128gcm|aes192|aes192gcm|aes256|aes256gcm) + (none|des|3des|bf128|bf256|aes128|aes128gcm|aes192|aes192gcm|aes256|aes256gcm) @@ -114,8 +118,12 @@ Cipher negotiation list for use in server or client mode - des 3des aes128 aes128gcm aes192 aes192gcm aes256 aes256gcm + none des 3des aes128 aes128gcm aes192 aes192gcm aes256 aes256gcm + + none + Disable encryption + des DES algorithm @@ -149,7 +157,7 @@ AES algorithm with 256-bit key GCM - (des|3des|aes128|aes128gcm|aes192|aes192gcm|aes256|aes256gcm) + (none|des|3des|aes128|aes128gcm|aes192|aes192gcm|aes256|aes256gcm) diff --git a/src/conf_mode/interfaces-openvpn.py b/src/conf_mode/interfaces-openvpn.py index f83590209..518dbdc0e 100755 --- a/src/conf_mode/interfaces-openvpn.py +++ b/src/conf_mode/interfaces-openvpn.py @@ -257,7 +257,10 @@ def get_config(config=None): if conf.exists('encryption ncp-ciphers'): _ncp_ciphers = [] for enc in conf.return_values('encryption ncp-ciphers'): - if enc == 'des': + if enc == 'none': + _ncp_ciphers.append('none') + _ncp_ciphers.append('NONE') + elif enc == 'des': _ncp_ciphers.append('des-cbc') _ncp_ciphers.append('DES-CBC') elif enc == '3des': @@ -944,6 +947,9 @@ def verify(openvpn): else: print('Diffie-Hellman prime file is unspecified, assuming ECDH') + if openvpn['encryption'] == 'none': + print('Warning: "encryption none" was specified. NO encryption will be performed and tunnelled data WILL be transmitted in clear text over the network!') + # # Auth user/pass # -- cgit v1.2.3 From 92edd930c49b63247dbbcc370c9f93b3456cb855 Mon Sep 17 00:00:00 2001 From: Marcus Hoff Date: Tue, 22 Sep 2020 19:44:55 +0200 Subject: openvpn: T2906: tls-auth missing key direction --- data/templates/openvpn/server.conf.tmpl | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/data/templates/openvpn/server.conf.tmpl b/data/templates/openvpn/server.conf.tmpl index 8a1ac6bd8..fea310236 100644 --- a/data/templates/openvpn/server.conf.tmpl +++ b/data/templates/openvpn/server.conf.tmpl @@ -181,7 +181,11 @@ dh {{ tls_dh }} {%- endif %} {%- if tls_auth %} -tls-auth {{tls_auth}} +{%- if mode == 'client' %} +tls-auth {{tls_auth}} 1 +{%- elif mode == 'server' %} +tls-auth {{tls_auth}} 0 +{%- endif %} {%- endif %} {%- if tls_role %} -- cgit v1.2.3 From 2b06653a824f21bf5b3a843f109f99096e7500ff Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Thu, 24 Sep 2020 18:22:49 +0200 Subject: dns: forwarding: T2921: template cleanup --- .../recursor.forward-zones.conf.tmpl | 34 +++++++++++----------- .../recursor.vyos-hostsd.conf.lua.tmpl | 28 +++++++++--------- src/conf_mode/dns_forwarding.py | 4 +-- 3 files changed, 33 insertions(+), 33 deletions(-) diff --git a/data/templates/dns-forwarding/recursor.forward-zones.conf.tmpl b/data/templates/dns-forwarding/recursor.forward-zones.conf.tmpl index de5eaee00..e62b9bb81 100644 --- a/data/templates/dns-forwarding/recursor.forward-zones.conf.tmpl +++ b/data/templates/dns-forwarding/recursor.forward-zones.conf.tmpl @@ -3,26 +3,26 @@ # dot zone (catch-all): '+' indicates recursion is desired # (same as forward-zones-recurse) -{#- the code below ensures the order of nameservers is determined first by #} -{#- the order of tags, then by the order of nameservers within that tag #} -{%- set n = namespace(dot_zone_ns='') %} -{%- for tag in name_server_tags_recursor %} -{%- set ns = '' %} -{%- if tag in name_servers %} -{%- set ns = ns + name_servers[tag]|join(', ') %} -{%- set n.dot_zone_ns = (n.dot_zone_ns, ns)|join(', ') if n.dot_zone_ns != '' else ns %} -{%- endif %} +{# the code below ensures the order of nameservers is determined first by #} +{# the order of tags, then by the order of nameservers within that tag #} +{% set n = namespace(dot_zone_ns='') %} +{% for tag in name_server_tags_recursor %} +{% set ns = '' %} +{% if tag in name_servers %} +{% set ns = ns + name_servers[tag]|join(', ') %} +{% set n.dot_zone_ns = (n.dot_zone_ns, ns)|join(', ') if n.dot_zone_ns != '' else ns %} +{% endif %} # {{ tag }}: {{ ns }} -{%- endfor %} +{% endfor %} -{%- if n.dot_zone_ns %} +{% if n.dot_zone_ns %} +.={{ n.dot_zone_ns }} -{%- endif %} +{% endif %} -{% if forward_zones -%} +{% if forward_zones %} # zones added via 'service dns forwarding domain' -{%- for zone, zonedata in forward_zones.items() %} -{% if zonedata['recursion-desired'] %}+{% endif %}{{ zone }}={{ zonedata['nslist']|join(', ') }} -{%- endfor %} -{%- endif %} +{% for zone, zonedata in forward_zones.items() %} +{% if zonedata['recursion-desired'] %}+{% endif %}{{ zone }}={{ zonedata['nslist']|join(', ') }} +{% endfor %} +{% endif %} diff --git a/data/templates/dns-forwarding/recursor.vyos-hostsd.conf.lua.tmpl b/data/templates/dns-forwarding/recursor.vyos-hostsd.conf.lua.tmpl index b0d99d9ae..8fefae0b2 100644 --- a/data/templates/dns-forwarding/recursor.vyos-hostsd.conf.lua.tmpl +++ b/data/templates/dns-forwarding/recursor.vyos-hostsd.conf.lua.tmpl @@ -1,24 +1,24 @@ -- Autogenerated by VyOS (vyos-hostsd) -- -- Do not edit, your changes will get overwritten -- -{% if hosts -%} +{% if hosts %} -- from 'system static-host-mapping' and DHCP server -{%- for tag, taghosts in hosts.items() %} -{%- for host, hostprops in taghosts.items() %} +{% for tag, taghosts in hosts.items() %} +{% for host, hostprops in taghosts.items() %} addNTA("{{ host }}.", "{{ tag }}") -{%- for a in hostprops['aliases'] %} +{% for a in hostprops['aliases'] %} addNTA("{{ a }}.", "{{ tag }} alias") -{%- endfor %} -{%- endfor %} -{%- endfor %} -{%- endif %} +{% endfor %} +{% endfor %} +{% endfor %} +{% endif %} -{% if forward_zones -%} +{% if forward_zones %} -- from 'service dns forwarding domain' -{%- for zone, zonedata in forward_zones.items() %} -{%- if zonedata['addNTA'] %} +{% for zone, zonedata in forward_zones.items() %} +{% if zonedata['addNTA'] %} addNTA("{{ zone }}", "static") -{%- endif %} -{%- endfor %} -{%- endif %} +{% endif %} +{% endfor %} +{% endif %} diff --git a/src/conf_mode/dns_forwarding.py b/src/conf_mode/dns_forwarding.py index 53bc37882..d6eb76d91 100755 --- a/src/conf_mode/dns_forwarding.py +++ b/src/conf_mode/dns_forwarding.py @@ -148,10 +148,10 @@ def generate(dns): return None render(pdns_rec_config_file, 'dns-forwarding/recursor.conf.tmpl', - dns, user=pdns_rec_user, group=pdns_rec_group) + dns, trim_blocks=True, user=pdns_rec_user, group=pdns_rec_group) render(pdns_rec_lua_conf_file, 'dns-forwarding/recursor.conf.lua.tmpl', - dns, user=pdns_rec_user, group=pdns_rec_group) + dns, trim_blocks=True, user=pdns_rec_user, group=pdns_rec_group) # if vyos-hostsd didn't create its files yet, create them (empty) for f in [pdns_rec_hostsd_lua_conf_file, pdns_rec_hostsd_zones_file]: -- cgit v1.2.3 From 4db00f1cd820f4fc462ce3537d692694224e02a4 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Thu, 24 Sep 2020 18:23:08 +0200 Subject: smoketest: dns: forwarding: T2921: add initial testcases --- .../scripts/cli/test_service_dns_forwarding.py | 163 +++++++++++++++++++++ 1 file changed, 163 insertions(+) create mode 100755 smoketest/scripts/cli/test_service_dns_forwarding.py diff --git a/smoketest/scripts/cli/test_service_dns_forwarding.py b/smoketest/scripts/cli/test_service_dns_forwarding.py new file mode 100755 index 000000000..0ae27a4d4 --- /dev/null +++ b/smoketest/scripts/cli/test_service_dns_forwarding.py @@ -0,0 +1,163 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2019-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 re +import os +import unittest + +from psutil import process_iter + +from vyos.configsession import ConfigSession, ConfigSessionError +from vyos.util import read_file +from vyos.util import process_named_running + +CONFIG_FILE = '/run/powerdns/recursor.conf' +FORWARD_FILE = '/run/powerdns/recursor.forward-zones.conf' +PROCESS_NAME= 'pdns-r/worker' + +base_path = ['service', 'dns', 'forwarding'] + +allow_from = ['192.0.2.0/24', '2001:db8::/32'] +listen_adress = ['127.0.0.1', '::1'] + +def get_config_value(key, file=CONFIG_FILE): + tmp = read_file(file) + tmp = re.findall(r'\n{}=+(.*)'.format(key), tmp) + return tmp[0] + +class TestServicePowerDNS(unittest.TestCase): + def setUp(self): + self.session = ConfigSession(os.getpid()) + + def tearDown(self): + # Delete DNS forwarding configuration + self.session.delete(base_path) + self.session.commit() + del self.session + + def test_basic_forwarding(self): + """ Check basic DNS forwarding settings """ + cache_size = '20' + negative_ttl = '120' + + self.session.set(base_path + ['cache-size', cache_size]) + self.session.set(base_path + ['negative-ttl', negative_ttl]) + + # check validate() - allow from must be defined + with self.assertRaises(ConfigSessionError): + self.session.commit() + for network in allow_from: + self.session.set(base_path + ['allow-from', network]) + + # check validate() - listen-address must be defined + with self.assertRaises(ConfigSessionError): + self.session.commit() + for address in listen_adress: + self.session.set(base_path + ['listen-address', address]) + + # configure DNSSEC + self.session.set(base_path + ['dnssec', 'validate']) + + # commit changes + self.session.commit() + + # Check configured cache-size + tmp = get_config_value('max-cache-entries') + self.assertEqual(tmp, cache_size) + + # Networks allowed to query this server + tmp = get_config_value('allow-from') + self.assertEqual(tmp, ','.join(allow_from)) + + # Addresses to listen for DNS queries + tmp = get_config_value('local-address') + self.assertEqual(tmp, ','.join(listen_adress)) + + # Maximum amount of time negative entries are cached + tmp = get_config_value('max-negative-ttl') + self.assertEqual(tmp, negative_ttl) + + # Check for running process + self.assertTrue(process_named_running(PROCESS_NAME)) + + def test_dnssec(self): + """ DNSSEC option testing """ + + for network in allow_from: + self.session.set(base_path + ['allow-from', network]) + for address in listen_adress: + self.session.set(base_path + ['listen-address', address]) + + options = ['off', 'process-no-validate', 'process', 'log-fail', 'validate'] + for option in options: + self.session.set(base_path + ['dnssec', option]) + + # commit changes + self.session.commit() + + tmp = get_config_value('dnssec') + self.assertEqual(tmp, option) + + # Check for running process + self.assertTrue(process_named_running(PROCESS_NAME)) + + def test_external_nameserver(self): + """ Externe Domain Name Servers (DNS) addresses """ + + for network in allow_from: + self.session.set(base_path + ['allow-from', network]) + for address in listen_adress: + self.session.set(base_path + ['listen-address', address]) + + nameservers = ['192.0.2.1', '192.0.2.2'] + for nameserver in nameservers: + self.session.set(base_path + ['name-server', nameserver]) + + # commit changes + self.session.commit() + + tmp = get_config_value(r'\+.', file=FORWARD_FILE) + self.assertEqual(tmp, ', '.join(nameservers)) + + # Check for running process + self.assertTrue(process_named_running(PROCESS_NAME)) + + def test_domain_forwarding(self): + """ Externe Domain Name Servers (DNS) addresses """ + + for network in allow_from: + self.session.set(base_path + ['allow-from', network]) + for address in listen_adress: + self.session.set(base_path + ['listen-address', address]) + + domains = ['vyos.io', 'vyos.net'] + nameservers = ['192.0.2.1', '192.0.2.2'] + for domain in domains: + for nameserver in nameservers: + self.session.set(base_path + ['domain', domain, 'server', nameserver]) + + # commit changes + self.session.commit() + + for domain in domains: + tmp = get_config_value(domain, file=FORWARD_FILE) + self.assertEqual(tmp, ', '.join(nameservers)) + + # Check for running process + self.assertTrue(process_named_running(PROCESS_NAME)) + +if __name__ == '__main__': + unittest.main() -- cgit v1.2.3 From 58ead7415a3fe8d786bdb6fd2a99d0a57770dbd7 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Thu, 24 Sep 2020 19:54:15 +0200 Subject: smoketest: (re-)use process_named_running() from vyos.util --- smoketest/scripts/cli/test_interfaces_macsec.py | 6 +++--- smoketest/scripts/cli/test_interfaces_wireless.py | 10 ++++------ smoketest/scripts/cli/test_service_dns_dynamic.py | 21 +++++---------------- .../scripts/cli/test_service_dns_forwarding.py | 2 -- smoketest/scripts/cli/test_service_mdns-repeater.py | 4 ++-- smoketest/scripts/cli/test_service_pppoe-server.py | 8 ++++---- smoketest/scripts/cli/test_service_router-advert.py | 6 ++---- smoketest/scripts/cli/test_service_snmp.py | 14 ++++++++------ smoketest/scripts/cli/test_service_ssh.py | 15 +++++++-------- smoketest/scripts/cli/test_system_lcd.py | 9 +++++---- smoketest/scripts/cli/test_system_ntp.py | 13 ++++++++----- smoketest/scripts/cli/test_vpn_openconnect.py | 8 +++----- 12 files changed, 51 insertions(+), 65 deletions(-) diff --git a/smoketest/scripts/cli/test_interfaces_macsec.py b/smoketest/scripts/cli/test_interfaces_macsec.py index fab5433de..6d1be86ba 100755 --- a/smoketest/scripts/cli/test_interfaces_macsec.py +++ b/smoketest/scripts/cli/test_interfaces_macsec.py @@ -17,13 +17,13 @@ import re import unittest -from psutil import process_iter from base_interfaces_test import BasicInterfaceTest from netifaces import interfaces -from vyos.ifconfig import Section from vyos.configsession import ConfigSessionError +from vyos.ifconfig import Section from vyos.util import read_file +from vyos.util import process_named_running def get_config_value(interface, key): tmp = read_file(f'/run/wpa_supplicant/{interface}.conf') @@ -103,7 +103,7 @@ class MACsecInterfaceTest(BasicInterfaceTest.BaseTest): self.assertEqual(tmp, '1460') # Check for running process - self.assertTrue("wpa_supplicant" in (p.name() for p in process_iter())) + self.assertTrue(process_named_running('wpa_supplicant')) def test_mandatory_toptions(self): interface = 'macsec1' diff --git a/smoketest/scripts/cli/test_interfaces_wireless.py b/smoketest/scripts/cli/test_interfaces_wireless.py index 691f633b7..0e93b6432 100755 --- a/smoketest/scripts/cli/test_interfaces_wireless.py +++ b/smoketest/scripts/cli/test_interfaces_wireless.py @@ -19,8 +19,7 @@ import re import unittest from base_interfaces_test import BasicInterfaceTest -from psutil import process_iter - +from vyos.util import process_named_running from vyos.util import check_kmod from vyos.util import read_file @@ -54,10 +53,10 @@ class WirelessInterfaceTest(BasicInterfaceTest.BaseTest): for option, option_value in self._options.items(): if 'type access-point' in option_value: # Check for running process - self.assertIn('hostapd', (p.name() for p in process_iter())) + self.assertTrue(process_named_running('hostapd')) elif 'type station' in option_value: # Check for running process - self.assertIn('wpa_supplicant', (p.name() for p in process_iter())) + self.assertTrue(process_named_running('wpa_supplicant')) else: self.assertTrue(False) @@ -137,8 +136,7 @@ class WirelessInterfaceTest(BasicInterfaceTest.BaseTest): self.assertIn(value, tmp) # Check for running process - self.assertIn('hostapd', (p.name() for p in process_iter())) - + self.assertTrue(process_named_running('hostapd')) if __name__ == '__main__': check_kmod('mac80211_hwsim') diff --git a/smoketest/scripts/cli/test_service_dns_dynamic.py b/smoketest/scripts/cli/test_service_dns_dynamic.py index be52360ed..51fa38912 100755 --- a/smoketest/scripts/cli/test_service_dns_dynamic.py +++ b/smoketest/scripts/cli/test_service_dns_dynamic.py @@ -19,10 +19,11 @@ import os import unittest from getpass import getuser -from psutil import process_iter -from vyos.configsession import ConfigSession, ConfigSessionError +from vyos.configsession import ConfigSession +from vyos.configsession import ConfigSessionError from vyos.util import read_file +PROCESS_NAME = 'ddclient' DDCLIENT_CONF = '/run/ddclient/ddclient.conf' base_path = ['service', 'dns', 'dynamic'] @@ -32,17 +33,6 @@ def get_config_value(key): tmp = tmp[0].rstrip(',') return tmp -def check_process(): - """ - Check for running process, process name changes dynamically e.g. - "ddclient - sleeping for 270 seconds", thus we need a different approach - """ - running = False - for p in process_iter(): - if "ddclient" in p.name(): - running = True - return running - class TestServiceDDNS(unittest.TestCase): def setUp(self): self.session = ConfigSession(os.getpid()) @@ -104,8 +94,7 @@ class TestServiceDDNS(unittest.TestCase): self.assertTrue(pwd == "'" + password + "'") # Check for running process - self.assertTrue(check_process()) - + self.assertTrue(process_named_running(PROCESS_NAME)) def test_rfc2136(self): """ Check if DDNS service can be configured and runs """ @@ -135,7 +124,7 @@ class TestServiceDDNS(unittest.TestCase): # TODO: inspect generated configuration file # Check for running process - self.assertTrue(check_process()) + self.assertTrue(process_named_running(PROCESS_NAME)) if __name__ == '__main__': unittest.main() diff --git a/smoketest/scripts/cli/test_service_dns_forwarding.py b/smoketest/scripts/cli/test_service_dns_forwarding.py index 0ae27a4d4..717b5b56d 100755 --- a/smoketest/scripts/cli/test_service_dns_forwarding.py +++ b/smoketest/scripts/cli/test_service_dns_forwarding.py @@ -18,8 +18,6 @@ import re import os import unittest -from psutil import process_iter - from vyos.configsession import ConfigSession, ConfigSessionError from vyos.util import read_file from vyos.util import process_named_running diff --git a/smoketest/scripts/cli/test_service_mdns-repeater.py b/smoketest/scripts/cli/test_service_mdns-repeater.py index 18900b6d2..de73b9914 100755 --- a/smoketest/scripts/cli/test_service_mdns-repeater.py +++ b/smoketest/scripts/cli/test_service_mdns-repeater.py @@ -17,8 +17,8 @@ import os import unittest -from psutil import process_iter from vyos.configsession import ConfigSession +from vyos.util import process_named_running base_path = ['service', 'mdns', 'repeater'] intf_base = ['interfaces', 'dummy'] @@ -45,7 +45,7 @@ class TestServiceMDNSrepeater(unittest.TestCase): self.session.commit() # Check for running process - self.assertTrue("mdns-repeater" in (p.name() for p in process_iter())) + self.assertTrue(process_named_running('mdns-repeater')) if __name__ == '__main__': unittest.main() diff --git a/smoketest/scripts/cli/test_service_pppoe-server.py b/smoketest/scripts/cli/test_service_pppoe-server.py index 901ca792d..3a6b12ef4 100755 --- a/smoketest/scripts/cli/test_service_pppoe-server.py +++ b/smoketest/scripts/cli/test_service_pppoe-server.py @@ -14,15 +14,15 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -import re import os import unittest from configparser import ConfigParser -from psutil import process_iter from vyos.configsession import ConfigSession from vyos.configsession import ConfigSessionError +from vyos.util import process_named_running +process_name = 'accel-pppd' base_path = ['service', 'pppoe-server'] local_if = ['interfaces', 'dummy', 'dum667'] pppoe_conf = '/run/accel-pppd/pppoe.conf' @@ -116,7 +116,7 @@ class TestServicePPPoEServer(unittest.TestCase): self.assertEqual(conf['connlimit']['limit'], '20/min') # Check for running process - self.assertTrue('accel-pppd' in (p.name() for p in process_iter())) + self.assertTrue(process_named_running(process_name)) def test_radius_auth(self): """ Test configuration of RADIUS authentication for PPPoE server """ @@ -161,7 +161,7 @@ class TestServicePPPoEServer(unittest.TestCase): self.assertFalse(conf['ppp'].getboolean('ccp')) # Check for running process - self.assertTrue('accel-pppd' in (p.name() for p in process_iter())) + self.assertTrue(process_named_running(process_name)) if __name__ == '__main__': unittest.main() diff --git a/smoketest/scripts/cli/test_service_router-advert.py b/smoketest/scripts/cli/test_service_router-advert.py index ec2110c8a..238f59e6d 100755 --- a/smoketest/scripts/cli/test_service_router-advert.py +++ b/smoketest/scripts/cli/test_service_router-advert.py @@ -18,9 +18,9 @@ import re import os import unittest -from psutil import process_iter from vyos.configsession import ConfigSession from vyos.util import read_file +from vyos.util import process_named_running RADVD_CONF = '/run/radvd/radvd.conf' @@ -90,10 +90,8 @@ class TestServiceRADVD(unittest.TestCase): tmp = get_config_value('AdvOnLink') self.assertEqual(tmp, 'off') - - # Check for running process - self.assertTrue('radvd' in (p.name() for p in process_iter())) + self.assertTrue(process_named_running('radvd')) if __name__ == '__main__': unittest.main() diff --git a/smoketest/scripts/cli/test_service_snmp.py b/smoketest/scripts/cli/test_service_snmp.py index fb5f5393f..067a3c76b 100755 --- a/smoketest/scripts/cli/test_service_snmp.py +++ b/smoketest/scripts/cli/test_service_snmp.py @@ -19,12 +19,15 @@ import re import unittest from vyos.validate import is_ipv4 -from psutil import process_iter -from vyos.configsession import ConfigSession, ConfigSessionError +from vyos.configsession import ConfigSession +from vyos.configsession import ConfigSessionError from vyos.util import read_file +from vyos.util import process_named_running +PROCESS_NAME = 'snmpd' SNMPD_CONF = '/etc/snmp/snmpd.conf' + base_path = ['service', 'snmp'] def get_config_value(key): @@ -78,7 +81,7 @@ class TestSNMPService(unittest.TestCase): self.assertTrue(expected in config) # Check for running process - self.assertTrue("snmpd" in (p.name() for p in process_iter())) + self.assertTrue(process_named_running(PROCESS_NAME)) def test_snmpv3_sha(self): @@ -113,7 +116,7 @@ class TestSNMPService(unittest.TestCase): # TODO: read in config file and check values # Check for running process - self.assertTrue("snmpd" in (p.name() for p in process_iter())) + self.assertTrue(process_named_running(PROCESS_NAME)) def test_snmpv3_md5(self): """ Check if SNMPv3 can be configured with MD5 authentication and service runs""" @@ -147,8 +150,7 @@ class TestSNMPService(unittest.TestCase): # TODO: read in config file and check values # Check for running process - self.assertTrue("snmpd" in (p.name() for p in process_iter())) - + self.assertTrue(process_named_running(PROCESS_NAME)) if __name__ == '__main__': unittest.main() diff --git a/smoketest/scripts/cli/test_service_ssh.py b/smoketest/scripts/cli/test_service_ssh.py index 79850fe44..0cd00ccce 100755 --- a/smoketest/scripts/cli/test_service_ssh.py +++ b/smoketest/scripts/cli/test_service_ssh.py @@ -18,10 +18,12 @@ import re import os import unittest -from psutil import process_iter -from vyos.configsession import ConfigSession, ConfigSessionError +from vyos.configsession import ConfigSession +from vyos.configsession import ConfigSessionError from vyos.util import read_file +from vyos.util import process_named_running +PROCESS_NAME = 'sshd' SSHD_CONF = '/run/ssh/sshd_config' base_path = ['service', 'ssh'] @@ -30,9 +32,6 @@ def get_config_value(key): tmp = re.findall(f'\n?{key}\s+(.*)', tmp) return tmp -def is_service_running(): - return 'sshd' in (p.name() for p in process_iter()) - class TestServiceSSH(unittest.TestCase): def setUp(self): self.session = ConfigSession(os.getpid()) @@ -62,7 +61,7 @@ class TestServiceSSH(unittest.TestCase): self.assertEqual('22', port) # Check for running process - self.assertTrue(is_service_running()) + self.assertTrue(process_named_running(PROCESS_NAME)) def test_ssh_single(self): """ Check if SSH service can be configured and runs """ @@ -101,7 +100,7 @@ class TestServiceSSH(unittest.TestCase): self.assertTrue("100" in keepalive) # Check for running process - self.assertTrue(is_service_running()) + self.assertTrue(process_named_running(PROCESS_NAME)) def test_ssh_multi(self): """ Check if SSH service can be configured and runs with multiple @@ -128,7 +127,7 @@ class TestServiceSSH(unittest.TestCase): self.assertIn(address, tmp) # Check for running process - self.assertTrue(is_service_running()) + self.assertTrue(process_named_running(PROCESS_NAME)) if __name__ == '__main__': unittest.main() diff --git a/smoketest/scripts/cli/test_system_lcd.py b/smoketest/scripts/cli/test_system_lcd.py index 931a91c53..9385799b0 100755 --- a/smoketest/scripts/cli/test_system_lcd.py +++ b/smoketest/scripts/cli/test_system_lcd.py @@ -18,9 +18,10 @@ import os import unittest from configparser import ConfigParser -from psutil import process_iter from vyos.configsession import ConfigSession +from vyos.util import process_named_running +config_file = '/run/LCDd/LCDd.conf' base_path = ['system', 'lcd'] class TestSystemLCD(unittest.TestCase): @@ -42,13 +43,13 @@ class TestSystemLCD(unittest.TestCase): # load up ini-styled LCDd.conf conf = ConfigParser() - conf.read('/run/LCDd/LCDd.conf') + conf.read(config_file) self.assertEqual(conf['CFontzPacket']['Model'], '533') self.assertEqual(conf['CFontzPacket']['Device'], '/dev/ttyS1') - # both processes running - self.assertTrue('LCDd' in (p.name() for p in process_iter())) + # Check for running process + self.assertTrue(process_named_running('LCDd')) if __name__ == '__main__': unittest.main() diff --git a/smoketest/scripts/cli/test_system_ntp.py b/smoketest/scripts/cli/test_system_ntp.py index 856a28916..2a7c64870 100755 --- a/smoketest/scripts/cli/test_system_ntp.py +++ b/smoketest/scripts/cli/test_system_ntp.py @@ -18,11 +18,14 @@ import re import os import unittest -from psutil import process_iter -from vyos.configsession import ConfigSession, ConfigSessionError -from vyos.template import vyos_address_from_cidr, vyos_netmask_from_cidr +from vyos.configsession import ConfigSession +from vyos.configsession import ConfigSessionError +from vyos.template import vyos_address_from_cidr +from vyos.template import vyos_netmask_from_cidr from vyos.util import read_file +from vyos.util import process_named_running +PROCESS_NAME = 'ntpd' NTP_CONF = '/etc/ntp.conf' base_path = ['system', 'ntp'] @@ -63,7 +66,7 @@ class TestSystemNTP(unittest.TestCase): self.assertTrue(test in tmp) # Check for running process - self.assertTrue("ntpd" in (p.name() for p in process_iter())) + self.assertTrue(process_named_running(PROCESS_NAME)) def test_ntp_clients(self): """ Test the allowed-networks statement """ @@ -102,7 +105,7 @@ class TestSystemNTP(unittest.TestCase): self.assertEqual(tmp, test) # Check for running process - self.assertTrue("ntpd" in (p.name() for p in process_iter())) + self.assertTrue(process_named_running(PROCESS_NAME)) if __name__ == '__main__': unittest.main() diff --git a/smoketest/scripts/cli/test_vpn_openconnect.py b/smoketest/scripts/cli/test_vpn_openconnect.py index d2b82d686..2ba6aabf9 100755 --- a/smoketest/scripts/cli/test_vpn_openconnect.py +++ b/smoketest/scripts/cli/test_vpn_openconnect.py @@ -14,13 +14,11 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -import re import os import unittest -from psutil import process_iter -from vyos.configsession import ConfigSession, ConfigSessionError -from vyos.util import read_file +from vyos.configsession import ConfigSession +from vyos.util import process_named_running OCSERV_CONF = '/run/ocserv/ocserv.conf' base_path = ['vpn', 'openconnect'] @@ -52,7 +50,7 @@ class TestVpnOpenconnect(unittest.TestCase): self.session.commit() # Check for running process - self.assertTrue("ocserv-main" in (p.name() for p in process_iter())) + self.assertTrue(process_named_running('ocserv-main')) if __name__ == '__main__': unittest.main() -- cgit v1.2.3 From 806f35b5856c3f8dae634718a6a9e82cc90bb63a Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Thu, 24 Sep 2020 19:55:54 +0200 Subject: wireless: T2241: add "wds" CLI option --- data/templates/wifi/hostapd.conf.tmpl | 8 +++++++ interface-definitions/interfaces-wireless.xml.in | 6 +++++ python/vyos/ifconfig/wireless.py | 29 ++++++++++++------------ src/conf_mode/interfaces-wireless.py | 1 + 4 files changed, 30 insertions(+), 14 deletions(-) diff --git a/data/templates/wifi/hostapd.conf.tmpl b/data/templates/wifi/hostapd.conf.tmpl index 3980fb896..c5e4240d1 100644 --- a/data/templates/wifi/hostapd.conf.tmpl +++ b/data/templates/wifi/hostapd.conf.tmpl @@ -448,6 +448,14 @@ macaddr_acl=0 max_num_sta={{ max_stations }} {% endif %} +{% if wds is defined %} +# WDS (4-address frame) mode with per-station virtual interfaces +# (only supported with driver=nl80211) +# This mode allows associated stations to use 4-address frames to allow layer 2 +# bridging to be used. +wds_sta=1 +{% endif %} + {% if isolate_stations is defined %} # Client isolation can be used to prevent low-level bridging of frames between # associated stations in the BSS. By default, this bridging is allowed. diff --git a/interface-definitions/interfaces-wireless.xml.in b/interface-definitions/interfaces-wireless.xml.in index a0caf810f..8c594e758 100644 --- a/interface-definitions/interfaces-wireless.xml.in +++ b/interface-definitions/interfaces-wireless.xml.in @@ -770,6 +770,12 @@ #include #include + + + Enable WDS (Wireless Distribution System) + + + diff --git a/python/vyos/ifconfig/wireless.py b/python/vyos/ifconfig/wireless.py index 37703d242..deca68bf0 100644 --- a/python/vyos/ifconfig/wireless.py +++ b/python/vyos/ifconfig/wireless.py @@ -23,8 +23,10 @@ class WiFiIf(Interface): default = { 'type': 'wifi', - 'phy': 'phy0' + 'phy': '', + 'wds': 'off', } + definition = { **Interface.definition, **{ @@ -33,12 +35,19 @@ class WiFiIf(Interface): 'bridgeable': True, } } + options = Interface.options + \ ['phy', 'op_mode'] + _command_set = {**Interface._command_set, **{ + '4addr': { + 'shellcmd': 'iw dev {ifname} set 4addr {value}', + }, + }} + def _create(self): # all interfaces will be added in monitor mode - cmd = 'iw phy {phy} interface add {ifname} type monitor' \ + cmd = 'iw phy {phy} interface add {ifname} type monitor 4addr {wds}' \ .format(**self.config) self._cmd(cmd) @@ -50,28 +59,20 @@ class WiFiIf(Interface): .format(**self.config) self._cmd(cmd) + def set_4aadr_mode(self, state): + return self.set_interface('4addr', state) + 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. """ - # We can not call add_to_bridge() until wpa_supplicant is running, thus - # we will remove the key from the config dict and react to this specal - # case in thie derived class. - # re-add ourselves to any bridge we might have fallen out of - bridge_member = '' - if 'is_bridge_member' in config: - bridge_member = config['is_bridge_member'] - del config['is_bridge_member'] + self.set_4aadr_mode('on' if 'wds' in config else 'off') # call base class first super().update(config) - # re-add ourselves to any bridge we might have fallen out of - if bridge_member: - self.add_to_bridge(bridge_member) - # Enable/Disable of an interface must always be done at the end of the # derived class to make use of the ref-counting set_admin_state() # function. We will only enable the interface if 'up' was called as diff --git a/src/conf_mode/interfaces-wireless.py b/src/conf_mode/interfaces-wireless.py index be59b72b5..f8520aecf 100755 --- a/src/conf_mode/interfaces-wireless.py +++ b/src/conf_mode/interfaces-wireless.py @@ -240,6 +240,7 @@ def apply(wifi): # Assign WiFi instance configuration parameters to config dict conf['phy'] = wifi['physical_device'] + conf['wds'] = 'on' if 'wds' in wifi else 'off' # Finally create the new interface w = WiFiIf(interface, **conf) -- cgit v1.2.3 From 44698fb03cf2017e56adbd161c66be585822b87a Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Thu, 24 Sep 2020 20:35:25 +0200 Subject: smoketest: dns: dynamic: add missing import statement Commit 58ead741 ("smoketest: (re-)use process_named_running() from vyos.util") missed an import statement for process_named_running(). This has been fixed. --- smoketest/scripts/cli/test_service_dns_dynamic.py | 1 + 1 file changed, 1 insertion(+) diff --git a/smoketest/scripts/cli/test_service_dns_dynamic.py b/smoketest/scripts/cli/test_service_dns_dynamic.py index 51fa38912..c7ac87135 100755 --- a/smoketest/scripts/cli/test_service_dns_dynamic.py +++ b/smoketest/scripts/cli/test_service_dns_dynamic.py @@ -22,6 +22,7 @@ from getpass import getuser from vyos.configsession import ConfigSession from vyos.configsession import ConfigSessionError from vyos.util import read_file +from vyos.util import process_named_running PROCESS_NAME = 'ddclient' DDCLIENT_CONF = '/run/ddclient/ddclient.conf' -- cgit v1.2.3 From 7f09beeac924b1bc9bf61f8153870bd4cb939b96 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Thu, 24 Sep 2020 21:31:47 +0200 Subject: dhcpv6-pd: verify: T2923: interface is required where the prefix is assigned When configuring DHCPv6-PD it is mandatory to also specify at least one interface where the newly delegated prefix will be used. Without this setting DHCPv6-PD makes no sense at all. --- python/vyos/configverify.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/python/vyos/configverify.py b/python/vyos/configverify.py index bf4e26fa7..afa6c7f06 100644 --- a/python/vyos/configverify.py +++ b/python/vyos/configverify.py @@ -113,6 +113,11 @@ def verify_dhcpv6(config): # assigned IPv6 subnet from a delegated prefix for pd in vyos_dict_search('dhcpv6_options.pd', config): sla_ids = [] + + if not vyos_dict_search(f'dhcpv6_options.pd.{pd}.interface', config): + raise ConfigError('DHCPv6-PD requires an interface where to assign ' + 'the delegated prefix!') + for interface in vyos_dict_search(f'dhcpv6_options.pd.{pd}.interface', config): sla_id = vyos_dict_search( f'dhcpv6_options.pd.{pd}.interface.{interface}.sla_id', config) -- cgit v1.2.3 From f39f5dde342aa5e14d1fb4155920c61ac5fd11b1 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Fri, 25 Sep 2020 19:21:36 +0200 Subject: dns: forwarding: T2921: migrate to get_config_dict() --- data/configd-include.json | 1 + data/templates/dns-forwarding/recursor.conf.tmpl | 2 +- .../recursor.forward-zones.conf.tmpl | 4 +- .../recursor.vyos-hostsd.conf.lua.tmpl | 4 +- interface-definitions/dns-forwarding.xml.in | 11 +- .../scripts/cli/test_service_dns_forwarding.py | 35 ++++- src/conf_mode/dns_forwarding.py | 172 ++++++++------------- src/services/vyos-hostsd | 21 +-- src/utils/vyos-hostsd-client | 6 +- 9 files changed, 122 insertions(+), 134 deletions(-) diff --git a/data/configd-include.json b/data/configd-include.json index 0c75657e0..2711a29b8 100644 --- a/data/configd-include.json +++ b/data/configd-include.json @@ -2,6 +2,7 @@ "bcast_relay.py", "dhcp_relay.py", "dhcpv6_relay.py", +"dns_forwarding.py", "dynamic_dns.py", "firewall_options.py", "host_name.py", diff --git a/data/templates/dns-forwarding/recursor.conf.tmpl b/data/templates/dns-forwarding/recursor.conf.tmpl index d233b8abc..b0ae3cc61 100644 --- a/data/templates/dns-forwarding/recursor.conf.tmpl +++ b/data/templates/dns-forwarding/recursor.conf.tmpl @@ -21,7 +21,7 @@ max-cache-entries={{ cache_size }} max-negative-ttl={{ negative_ttl }} # ignore-hosts-file -export-etc-hosts={{ export_hosts_file }} +export-etc-hosts={{ 'no' if ignore_hosts_file is defined else 'yes' }} # listen-address local-address={{ listen_address | join(',') }} diff --git a/data/templates/dns-forwarding/recursor.forward-zones.conf.tmpl b/data/templates/dns-forwarding/recursor.forward-zones.conf.tmpl index e62b9bb81..90f35ae1c 100644 --- a/data/templates/dns-forwarding/recursor.forward-zones.conf.tmpl +++ b/data/templates/dns-forwarding/recursor.forward-zones.conf.tmpl @@ -19,10 +19,10 @@ +.={{ n.dot_zone_ns }} {% endif %} -{% if forward_zones %} +{% if forward_zones is defined %} # zones added via 'service dns forwarding domain' {% for zone, zonedata in forward_zones.items() %} -{% if zonedata['recursion-desired'] %}+{% endif %}{{ zone }}={{ zonedata['nslist']|join(', ') }} +{{ "+" if zonedata['recursion_desired'] is defined }}{{ zone }}={{ zonedata['server']|join(', ') }} {% endfor %} {% endif %} diff --git a/data/templates/dns-forwarding/recursor.vyos-hostsd.conf.lua.tmpl b/data/templates/dns-forwarding/recursor.vyos-hostsd.conf.lua.tmpl index 8fefae0b2..784d5c360 100644 --- a/data/templates/dns-forwarding/recursor.vyos-hostsd.conf.lua.tmpl +++ b/data/templates/dns-forwarding/recursor.vyos-hostsd.conf.lua.tmpl @@ -13,10 +13,10 @@ addNTA("{{ a }}.", "{{ tag }} alias") {% endfor %} {% endif %} -{% if forward_zones %} +{% if forward_zones is defined %} -- from 'service dns forwarding domain' {% for zone, zonedata in forward_zones.items() %} -{% if zonedata['addNTA'] %} +{% if zonedata['addnta'] is defined %} addNTA("{{ zone }}", "static") {% endif %} {% endfor %} diff --git a/interface-definitions/dns-forwarding.xml.in b/interface-definitions/dns-forwarding.xml.in index aaf8bb27d..07e63d54a 100644 --- a/interface-definitions/dns-forwarding.xml.in +++ b/interface-definitions/dns-forwarding.xml.in @@ -16,7 +16,7 @@ - DNS forwarding cache size + DNS forwarding cache size (default: 10000) 0-10000 DNS forwarding cache size @@ -25,6 +25,7 @@ + 10000 @@ -37,7 +38,7 @@ - DNSSEC mode + DNSSEC mode (default: process-no-validate) off process-no-validate process log-fail validate @@ -62,9 +63,10 @@ Full blown DNSSEC validation. Send SERVFAIL to clients on bogus responses. - (off|process-no-validate|process|log-fail|validate) + ^(off|process-no-validate|process|log-fail|validate)$ + process-no-validate @@ -146,7 +148,7 @@ - Maximum amount of time negative entries are cached + Maximum amount of time negative entries are cached (default: 3600) 0-7200 Seconds to cache NXDOMAIN entries @@ -155,6 +157,7 @@ + 3600 diff --git a/smoketest/scripts/cli/test_service_dns_forwarding.py b/smoketest/scripts/cli/test_service_dns_forwarding.py index 717b5b56d..5e2f3dfbd 100755 --- a/smoketest/scripts/cli/test_service_dns_forwarding.py +++ b/smoketest/scripts/cli/test_service_dns_forwarding.py @@ -24,6 +24,7 @@ from vyos.util import process_named_running CONFIG_FILE = '/run/powerdns/recursor.conf' FORWARD_FILE = '/run/powerdns/recursor.forward-zones.conf' +HOSTSD_FILE = '/run/powerdns/recursor.vyos-hostsd.conf.lua' PROCESS_NAME= 'pdns-r/worker' base_path = ['service', 'dns', 'forwarding'] @@ -69,6 +70,9 @@ class TestServicePowerDNS(unittest.TestCase): # configure DNSSEC self.session.set(base_path + ['dnssec', 'validate']) + # Do not use local /etc/hosts file in name resolution + self.session.set(base_path + ['ignore-hosts-file']) + # commit changes self.session.commit() @@ -88,6 +92,10 @@ class TestServicePowerDNS(unittest.TestCase): tmp = get_config_value('max-negative-ttl') self.assertEqual(tmp, negative_ttl) + # Do not use local /etc/hosts file in name resolution + tmp = get_config_value('export-etc-hosts') + self.assertEqual(tmp, 'no') + # Check for running process self.assertTrue(process_named_running(PROCESS_NAME)) @@ -130,6 +138,11 @@ class TestServicePowerDNS(unittest.TestCase): tmp = get_config_value(r'\+.', file=FORWARD_FILE) self.assertEqual(tmp, ', '.join(nameservers)) + # Do not use local /etc/hosts file in name resolution + # default: yes + tmp = get_config_value('export-etc-hosts') + self.assertEqual(tmp, 'yes') + # Check for running process self.assertTrue(process_named_running(PROCESS_NAME)) @@ -141,21 +154,39 @@ class TestServicePowerDNS(unittest.TestCase): for address in listen_adress: self.session.set(base_path + ['listen-address', address]) - domains = ['vyos.io', 'vyos.net'] + domains = ['vyos.io', 'vyos.net', 'vyos.com'] nameservers = ['192.0.2.1', '192.0.2.2'] for domain in domains: for nameserver in nameservers: self.session.set(base_path + ['domain', domain, 'server', nameserver]) + # Test 'recursion-desired' flag for only one domain + if domain == domains[0]: + self.session.set(base_path + ['domain', domain, 'recursion-desired']) + + # Test 'negative trust anchor' flag for the second domain only + if domain == domains[1]: + self.session.set(base_path + ['domain', domain, 'addnta']) + # commit changes self.session.commit() + # Test configured name-servers + hosts_conf = read_file(HOSTSD_FILE) for domain in domains: - tmp = get_config_value(domain, file=FORWARD_FILE) + # Test 'recursion-desired' flag for the first domain only + if domain == domains[0]: key =f'\+{domain}' + else: key =f'{domain}' + tmp = get_config_value(key, file=FORWARD_FILE) self.assertEqual(tmp, ', '.join(nameservers)) + # Test 'negative trust anchor' flag for the second domain only + if domain == domains[1]: + self.assertIn(f'addNTA("{domain}", "static")', hosts_conf) + # Check for running process self.assertTrue(process_named_running(PROCESS_NAME)) if __name__ == '__main__': unittest.main() + diff --git a/src/conf_mode/dns_forwarding.py b/src/conf_mode/dns_forwarding.py index d6eb76d91..5101c1e79 100755 --- a/src/conf_mode/dns_forwarding.py +++ b/src/conf_mode/dns_forwarding.py @@ -17,14 +17,17 @@ import os from sys import exit -from copy import deepcopy from vyos.config import Config +from vyos.configdict import dict_merge from vyos.hostsd_client import Client as hostsd_client -from vyos import ConfigError -from vyos.util import call, chown +from vyos.util import call +from vyos.util import chown +from vyos.util import vyos_dict_search from vyos.template import render +from vyos.xml import defaults +from vyos import ConfigError from vyos import airbag airbag.enable() @@ -35,116 +38,63 @@ pdns_rec_hostsd_lua_conf_file = f'{pdns_rec_run_dir}/recursor.vyos-hostsd.conf.l pdns_rec_hostsd_zones_file = f'{pdns_rec_run_dir}/recursor.forward-zones.conf' pdns_rec_config_file = f'{pdns_rec_run_dir}/recursor.conf' -default_config_data = { - 'allow_from': [], - 'cache_size': 10000, - 'export_hosts_file': 'yes', - 'listen_address': [], - 'name_servers': [], - 'negative_ttl': 3600, - 'system': False, - 'domains': {}, - 'dnssec': 'process-no-validate', - 'dhcp_interfaces': [] -} - hostsd_tag = 'static' -def get_config(conf): - dns = deepcopy(default_config_data) +def get_config(config=None): + if config: + conf = config + else: + conf = Config() base = ['service', 'dns', 'forwarding'] - if not conf.exists(base): return None - conf.set_level(base) - - if conf.exists(['allow-from']): - dns['allow_from'] = conf.return_values(['allow-from']) - - if conf.exists(['cache-size']): - cache_size = conf.return_value(['cache-size']) - dns['cache_size'] = cache_size - - if conf.exists('negative-ttl'): - negative_ttl = conf.return_value(['negative-ttl']) - dns['negative_ttl'] = negative_ttl - - if conf.exists(['domain']): - for domain in conf.list_nodes(['domain']): - conf.set_level(base + ['domain', domain]) - entry = { - 'nslist': bracketize_ipv6_addrs(conf.return_values(['server'])), - 'addNTA': conf.exists(['addnta']), - 'recursion-desired': conf.exists(['recursion-desired']) - } - dns['domains'][domain] = entry - - conf.set_level(base) + dns = 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. + default_values = defaults(base) + dns = dict_merge(default_values, dns) - if conf.exists(['ignore-hosts-file']): - dns['export_hosts_file'] = "no" + # some additions to the default dictionary + if 'system' in dns: + base_nameservers = ['system', 'name-server'] + if conf.exists(base_nameservers): + dns.update({'system_name_server': conf.return_values(base_nameservers)}) - if conf.exists(['name-server']): - dns['name_servers'] = bracketize_ipv6_addrs( - conf.return_values(['name-server'])) - - if conf.exists(['system']): - dns['system'] = True - - if conf.exists(['listen-address']): - dns['listen_address'] = conf.return_values(['listen-address']) - - if conf.exists(['dnssec']): - dns['dnssec'] = conf.return_value(['dnssec']) - - if conf.exists(['dhcp']): - dns['dhcp_interfaces'] = conf.return_values(['dhcp']) + base_nameservers_dhcp = ['system', 'name-servers-dhcp'] + if conf.exists(base_nameservers_dhcp): + dns.update({'system_name_server_dhcp': conf.return_values(base_nameservers_dhcp)}) return dns -def bracketize_ipv6_addrs(addrs): - """Wraps each IPv6 addr in addrs in [], leaving IPv4 addrs untouched.""" - return ['[{0}]'.format(a) if a.count(':') > 1 else a for a in addrs] - -def verify(conf, dns): +def verify(dns): # bail out early - looks like removal from running config - if dns is None: + if not dns: return None - if not dns['listen_address']: - raise ConfigError( - "Error: DNS forwarding requires a listen-address") - - if not dns['allow_from']: - raise ConfigError( - "Error: DNS forwarding requires an allow-from network") - - if dns['domains']: - for domain in dns['domains']: - if not dns['domains'][domain]['nslist']: - raise ConfigError(( - f'Error: No server configured for domain {domain}')) - - no_system_nameservers = False - conf.set_level([]) - if dns['system'] and not ( - conf.exists(['system', 'name-server']) or - conf.exists(['system', 'name-servers-dhcp']) ): - no_system_nameservers = True - print(("DNS forwarding warning: No 'system name-server' or " - "'system name-servers-dhcp' set\n")) - - if (no_system_nameservers or not dns['system']) and not ( - dns['name_servers'] or dns['dhcp_interfaces']): - print(("DNS forwarding warning: No 'dhcp', 'name-server' or 'system' " - "nameservers set. Forwarding will operate as a recursor.\n")) + if 'listen_address' not in dns: + raise ConfigError('DNS forwarding requires a listen-address') + + if 'allow_from' not in dns: + raise ConfigError('DNS forwarding requires an allow-from network') + + # we can not use vyos_dict_search() when testing for domain servers + # as a domain will contains dot's which is out dictionary delimiter. + if 'domain' in dns: + for domain in dns['domain']: + if 'server' not in dns['domain'][domain]: + raise ConfigError(f'No server configured for domain {domain}!') + + if 'system' in dns: + if not ('system_name_server' in dns or 'system_name_server_dhcp' in dns): + print("Warning: No 'system name-server' or 'system " \ + "name-servers-dhcp' configured") return None def generate(dns): # bail out early - looks like removal from running config - if dns is None: + if not dns: return None render(pdns_rec_config_file, 'dns-forwarding/recursor.conf.tmpl', @@ -154,17 +104,18 @@ def generate(dns): dns, trim_blocks=True, user=pdns_rec_user, group=pdns_rec_group) # if vyos-hostsd didn't create its files yet, create them (empty) - for f in [pdns_rec_hostsd_lua_conf_file, pdns_rec_hostsd_zones_file]: - with open(f, 'a'): + for file in [pdns_rec_hostsd_lua_conf_file, pdns_rec_hostsd_zones_file]: + with open(file, 'a'): pass - chown(f, user=pdns_rec_user, group=pdns_rec_group) + chown(file, user=pdns_rec_user, group=pdns_rec_group) return None def apply(dns): - if dns is None: + if not dns: # DNS forwarding is removed in the commit - call("systemctl stop pdns-recursor.service") + call('systemctl stop pdns-recursor.service') + if os.path.isfile(pdns_rec_config_file): os.unlink(pdns_rec_config_file) else: @@ -174,8 +125,8 @@ def apply(dns): # add static nameservers to hostsd so they can be joined with other # sources hc.delete_name_servers([hostsd_tag]) - if dns['name_servers']: - hc.add_name_servers({hostsd_tag: dns['name_servers']}) + if 'name_server' in dns: + hc.add_name_servers({hostsd_tag: dns['name_server']}) # delete all nameserver tags hc.delete_name_server_tags_recursor(hc.get_name_server_tags_recursor()) @@ -184,32 +135,33 @@ def apply(dns): # our own tag (static) hc.add_name_server_tags_recursor([hostsd_tag]) - if dns['system']: + if 'system' in dns: hc.add_name_server_tags_recursor(['system']) else: hc.delete_name_server_tags_recursor(['system']) # add dhcp nameserver tags for configured interfaces - for intf in dns['dhcp_interfaces']: - hc.add_name_server_tags_recursor(['dhcp-' + intf, 'dhcpv6-' + intf ]) + if 'system_name_server_dhcp' in dns: + for interface in dns['system_name_server_dhcp']: + hc.add_name_server_tags_recursor(['dhcp-' + interface, + 'dhcpv6-' + interface ]) # hostsd will generate the forward-zones file # the list and keys() are required as get returns a dict, not list hc.delete_forward_zones(list(hc.get_forward_zones().keys())) - if dns['domains']: - hc.add_forward_zones(dns['domains']) + if 'domain' in dns: + hc.add_forward_zones(dns['domain']) # call hostsd to generate forward-zones and its lua-config-file hc.apply() ### finally (re)start pdns-recursor - call("systemctl restart pdns-recursor.service") + call('systemctl restart pdns-recursor.service') if __name__ == '__main__': try: - conf = Config() - c = get_config(conf) - verify(conf, c) + c = get_config() + verify(c) generate(c) apply(c) except ConfigError as e: diff --git a/src/services/vyos-hostsd b/src/services/vyos-hostsd index 0079f7e5c..59dbeda17 100755 --- a/src/services/vyos-hostsd +++ b/src/services/vyos-hostsd @@ -107,16 +107,17 @@ # ### forward_zones ## Additional zones added to pdns-recursor forward-zones-file. -## If recursion-desired is true, '+' will be prepended to the zone line. -## If addNTA is true, a NTA will be added via lua-config-file. +## If recursion_desired is true, '+' will be prepended to the zone line. +## If addnta is true, a NTA (Negative Trust Anchor) will be added via +## lua-config-file. # # { 'type': 'forward_zones', # 'op': 'add', # 'data': { # '': { -# 'nslist': ['', ...], -# 'addNTA': , -# 'recursion-desired': +# 'server': ['', ...], +# 'addnta': , +# 'recursion_desired': # } # ... # } @@ -305,12 +306,12 @@ tag_regex_schema = op_type_schema.extend({ forward_zone_add_schema = op_type_schema.extend({ 'data': { str: { - 'nslist': [str], - 'addNTA': bool, - 'recursion-desired': bool + 'server': [str], + 'addnta': Any({}, None), + 'recursion_desired': Any({}, None), } } - }, required=True) + }, required=False) hosts_add_schema = op_type_schema.extend({ 'data': { @@ -586,7 +587,7 @@ if __name__ == '__main__': context = zmq.Context() socket = context.socket(zmq.REP) - + # Set the right permissions on the socket, then change it back o_mask = os.umask(0o007) socket.bind(SOCKET_PATH) diff --git a/src/utils/vyos-hostsd-client b/src/utils/vyos-hostsd-client index 48ebc83f7..d4d38315a 100755 --- a/src/utils/vyos-hostsd-client +++ b/src/utils/vyos-hostsd-client @@ -99,9 +99,9 @@ try: raise ValueError("--nameservers is required for this operation") client.add_forward_zones( { args.add_forward_zone: { - 'nslist': args.nameservers, - 'addNTA': args.addnta, - 'recursion-desired': args.recursion_desired + 'server': args.nameservers, + 'addnta': args.addnta, + 'recursion_desired': args.recursion_desired } }) elif args.delete_forward_zones: -- cgit v1.2.3 From 4c818baa59046bdc5023abe8b63fa6f62611d115 Mon Sep 17 00:00:00 2001 From: John Estabrook Date: Fri, 25 Sep 2020 10:58:58 -0500 Subject: syslog: T1845: fix indentation level --- src/migration-scripts/system/11-to-12 | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/migration-scripts/system/11-to-12 b/src/migration-scripts/system/11-to-12 index 0c92a0746..36311a19d 100755 --- a/src/migration-scripts/system/11-to-12 +++ b/src/migration-scripts/system/11-to-12 @@ -40,8 +40,8 @@ for host in config.list_nodes(cbase): config.set(cbase + [h, 'facility', fac, 'level'], value=lvl) config.delete(cbase + [host]) - try: - open(file_name,'w').write(config.to_string()) - except OSError as e: - print("Failed to save the modified config: {}".format(e)) - sys.exit(1) +try: + open(file_name,'w').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 e76d9a009632629e6a22b0d77eebc913c9268a6d Mon Sep 17 00:00:00 2001 From: John Estabrook Date: Fri, 25 Sep 2020 11:49:30 -0500 Subject: syslog: T2899: shift system migration files +1 to allow for crux --- src/migration-scripts/system/10-to-11 | 61 ++++------------- src/migration-scripts/system/11-to-12 | 88 +++++++++++++++--------- src/migration-scripts/system/12-to-13 | 85 +++++++++-------------- src/migration-scripts/system/13-to-14 | 56 ++++++++++++---- src/migration-scripts/system/14-to-15 | 21 +++--- src/migration-scripts/system/15-to-16 | 34 +++------- src/migration-scripts/system/16-to-17 | 43 +++--------- src/migration-scripts/system/17-to-18 | 123 +++++++++++++--------------------- src/migration-scripts/system/18-to-19 | 105 +++++++++++++++++++++++++++++ src/migration-scripts/system/9-to-10 | 36 ---------- 10 files changed, 326 insertions(+), 326 deletions(-) create mode 100755 src/migration-scripts/system/18-to-19 delete mode 100755 src/migration-scripts/system/9-to-10 diff --git a/src/migration-scripts/system/10-to-11 b/src/migration-scripts/system/10-to-11 index 1a0233c7d..3c49f0d95 100755 --- a/src/migration-scripts/system/10-to-11 +++ b/src/migration-scripts/system/10-to-11 @@ -1,9 +1,7 @@ #!/usr/bin/env python3 -# Unclutter RADIUS configuration -# -# Move radius-server top level tag nodes to a regular node which allows us -# to specify additional general features for the RADIUS client. +# Operator accounts have been deprecated due to a security issue. Those accounts +# will be converted to regular admin accounts. import sys from vyos.configtree import ConfigTree @@ -18,54 +16,21 @@ with open(file_name, 'r') as f: config_file = f.read() config = ConfigTree(config_file) -cfg_base = ['system', 'login'] -if not (config.exists(cfg_base + ['radius-server']) or config.exists(cfg_base + ['radius-source-address'])): - # Nothing to do +base_level = ['system', 'login', 'user'] + +if not config.exists(base_level): + # Nothing to do, which shouldn't happen anyway + # only if you wipe the config and reboot. sys.exit(0) else: - # - # Migrate "system login radius-source-address" to "system login radius" - # - if config.exists(cfg_base + ['radius-source-address']): - address = config.return_value(cfg_base + ['radius-source-address']) - # delete old configuration node - config.delete(cfg_base + ['radius-source-address']) - # write new configuration node - config.set(cfg_base + ['radius', 'source-address'], value=address) - - # - # Migrate "system login radius-server" tag node to new - # "system login radius server" tag node and also rename the "secret" node to "key" - # - for server in config.list_nodes(cfg_base + ['radius-server']): - base_server = cfg_base + ['radius-server', server] - # "key" node is mandatory - key = config.return_value(base_server + ['secret']) - config.set(cfg_base + ['radius', 'server', server, 'key'], value=key) - - # "port" is optional - if config.exists(base_server + ['port']): - port = config.return_value(base_server + ['port']) - config.set(cfg_base + ['radius', 'server', server, 'port'], value=port) - - # "timeout is optional" - if config.exists(base_server + ['timeout']): - timeout = config.return_value(base_server + ['timeout']) - config.set(cfg_base + ['radius', 'server', server, 'timeout'], value=timeout) - - # format as tag node - config.set_tag(cfg_base + ['radius', 'server']) - - # delete old configuration node - config.delete(base_server) - - # delete top level tag node - if config.exists(cfg_base + ['radius-server']): - config.delete(cfg_base + ['radius-server']) + for user in config.list_nodes(base_level): + if config.exists(base_level + [user, 'level']): + if config.return_value(base_level + [user, 'level']) == 'operator': + config.set(base_level + [user, 'level'], value="admin", replace=True) try: - with open(file_name, 'w') as f: - f.write(config.to_string()) + open(file_name,'w').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/system/11-to-12 b/src/migration-scripts/system/11-to-12 index 36311a19d..1a0233c7d 100755 --- a/src/migration-scripts/system/11-to-12 +++ b/src/migration-scripts/system/11-to-12 @@ -1,47 +1,71 @@ #!/usr/bin/env python3 -# converts 'set system syslog host
:' -# to 'set system syslog host
port ' +# Unclutter RADIUS configuration +# +# Move radius-server top level tag nodes to a regular node which allows us +# to specify additional general features for the RADIUS client. import sys -import re - from vyos.configtree import ConfigTree if (len(sys.argv) < 1): - print("Must specify file name!") - sys.exit(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_file = f.read() config = ConfigTree(config_file) -cbase = ['system', 'syslog', 'host'] - -if not config.exists(cbase): +cfg_base = ['system', 'login'] +if not (config.exists(cfg_base + ['radius-server']) or config.exists(cfg_base + ['radius-source-address'])): + # Nothing to do sys.exit(0) +else: + # + # Migrate "system login radius-source-address" to "system login radius" + # + if config.exists(cfg_base + ['radius-source-address']): + address = config.return_value(cfg_base + ['radius-source-address']) + # delete old configuration node + config.delete(cfg_base + ['radius-source-address']) + # write new configuration node + config.set(cfg_base + ['radius', 'source-address'], value=address) -for host in config.list_nodes(cbase): - if re.search(':[0-9]{1,5}$',host): - h = re.search('^[a-zA-Z\-0-9\.]+', host).group(0) - p = re.sub(':', '', re.search(':[0-9]+$', host).group(0)) - config.set(cbase + [h]) - config.set(cbase + [h, 'port'], value=p) - for fac in config.list_nodes(cbase + [host, 'facility']): - config.set(cbase + [h, 'facility', fac]) - config.set_tag(cbase + [h, 'facility']) - if config.exists(cbase + [host, 'facility', fac, 'protocol']): - proto = config.return_value(cbase + [host, 'facility', fac, 'protocol']) - config.set(cbase + [h, 'facility', fac, 'protocol'], value=proto) - if config.exists(cbase + [host, 'facility', fac, 'level']): - lvl = config.return_value(cbase + [host, 'facility', fac, 'level']) - config.set(cbase + [h, 'facility', fac, 'level'], value=lvl) - config.delete(cbase + [host]) - -try: - open(file_name,'w').write(config.to_string()) -except OSError as e: - print("Failed to save the modified config: {}".format(e)) - sys.exit(1) + # + # Migrate "system login radius-server" tag node to new + # "system login radius server" tag node and also rename the "secret" node to "key" + # + for server in config.list_nodes(cfg_base + ['radius-server']): + base_server = cfg_base + ['radius-server', server] + # "key" node is mandatory + key = config.return_value(base_server + ['secret']) + config.set(cfg_base + ['radius', 'server', server, 'key'], value=key) + + # "port" is optional + if config.exists(base_server + ['port']): + port = config.return_value(base_server + ['port']) + config.set(cfg_base + ['radius', 'server', server, 'port'], value=port) + + # "timeout is optional" + if config.exists(base_server + ['timeout']): + timeout = config.return_value(base_server + ['timeout']) + config.set(cfg_base + ['radius', 'server', server, 'timeout'], value=timeout) + + # format as tag node + config.set_tag(cfg_base + ['radius', 'server']) + + # delete old configuration node + config.delete(base_server) + + # delete top level tag node + if config.exists(cfg_base + ['radius-server']): + config.delete(cfg_base + ['radius-server']) + + 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/system/12-to-13 b/src/migration-scripts/system/12-to-13 index 5b068f4fc..36311a19d 100755 --- a/src/migration-scripts/system/12-to-13 +++ b/src/migration-scripts/system/12-to-13 @@ -1,70 +1,47 @@ #!/usr/bin/env python3 -# Fixup non existent time-zones. Some systems have time-zone set to: Los* -# (Los_Angeles), Den* (Denver), New* (New_York) ... but those are no real IANA -# assigned time zones. In the past they have been silently remapped. -# -# Time to clean it up! -# -# Migrate all configured timezones to real IANA assigned timezones! +# converts 'set system syslog host
:' +# to 'set system syslog host
port ' -import re import sys +import re from vyos.configtree import ConfigTree -from vyos.util import cmd - if (len(sys.argv) < 1): - print("Must specify file name!") - sys.exit(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_file = f.read() config = ConfigTree(config_file) -tz_base = ['system', 'time-zone'] -if not config.exists(tz_base): - # Nothing to do - sys.exit(0) -else: - tz = config.return_value(tz_base) +cbase = ['system', 'syslog', 'host'] - # retrieve all valid timezones - try: - tz_datas = cmd('find /usr/share/zoneinfo/posix -type f -or -type l | sed -e s:/usr/share/zoneinfo/posix/::') - except OSError: - tz_datas = '' - tz_data = tz_datas.split('\n') - - if re.match(r'[Ll][Oo][Ss].+', tz): - tz = 'America/Los_Angeles' - elif re.match(r'[Dd][Ee][Nn].+', tz): - tz = 'America/Denver' - elif re.match(r'[Hh][Oo][Nn][Oo].+', tz): - tz = 'Pacific/Honolulu' - elif re.match(r'[Nn][Ee][Ww].+', tz): - tz = 'America/New_York' - elif re.match(r'[Cc][Hh][Ii][Cc]*.+', tz): - tz = 'America/Chicago' - elif re.match(r'[Aa][Nn][Cc].+', tz): - tz = 'America/Anchorage' - elif re.match(r'[Pp][Hh][Oo].+', tz): - tz = 'America/Phoenix' - elif re.match(r'GMT(.+)?', tz): - tz = 'Etc/' + tz - elif tz not in tz_data: - # assign default UTC timezone - tz = 'UTC' - - # replace timezone data is required - config.set(tz_base, value=tz) +if not config.exists(cbase): + sys.exit(0) - 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) +for host in config.list_nodes(cbase): + if re.search(':[0-9]{1,5}$',host): + h = re.search('^[a-zA-Z\-0-9\.]+', host).group(0) + p = re.sub(':', '', re.search(':[0-9]+$', host).group(0)) + config.set(cbase + [h]) + config.set(cbase + [h, 'port'], value=p) + for fac in config.list_nodes(cbase + [host, 'facility']): + config.set(cbase + [h, 'facility', fac]) + config.set_tag(cbase + [h, 'facility']) + if config.exists(cbase + [host, 'facility', fac, 'protocol']): + proto = config.return_value(cbase + [host, 'facility', fac, 'protocol']) + config.set(cbase + [h, 'facility', fac, 'protocol'], value=proto) + if config.exists(cbase + [host, 'facility', fac, 'level']): + lvl = config.return_value(cbase + [host, 'facility', fac, 'level']) + config.set(cbase + [h, 'facility', fac, 'level'], value=lvl) + config.delete(cbase + [host]) + +try: + open(file_name,'w').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/system/13-to-14 b/src/migration-scripts/system/13-to-14 index c055dad1f..5b068f4fc 100755 --- a/src/migration-scripts/system/13-to-14 +++ b/src/migration-scripts/system/13-to-14 @@ -1,15 +1,19 @@ #!/usr/bin/env python3 + +# Fixup non existent time-zones. Some systems have time-zone set to: Los* +# (Los_Angeles), Den* (Denver), New* (New_York) ... but those are no real IANA +# assigned time zones. In the past they have been silently remapped. +# +# Time to clean it up! # -# Delete 'system ipv6 blacklist' option as the IPv6 module can no longer be -# blacklisted as it is required by e.g. WireGuard and thus will always be -# loaded. +# Migrate all configured timezones to real IANA assigned timezones! -import os +import re import sys -ipv6_blacklist_file = '/etc/modprobe.d/vyatta_blacklist_ipv6.conf' - from vyos.configtree import ConfigTree +from vyos.util import cmd + if (len(sys.argv) < 1): print("Must specify file name!") @@ -21,16 +25,42 @@ with open(file_name, 'r') as f: config_file = f.read() config = ConfigTree(config_file) -ip_base = ['system', 'ipv6'] -if not config.exists(ip_base): +tz_base = ['system', 'time-zone'] +if not config.exists(tz_base): # Nothing to do sys.exit(0) else: - # delete 'system ipv6 blacklist' node - if config.exists(ip_base + ['blacklist']): - config.delete(ip_base + ['blacklist']) - if os.path.isfile(ipv6_blacklist_file): - os.unlink(ipv6_blacklist_file) + tz = config.return_value(tz_base) + + # retrieve all valid timezones + try: + tz_datas = cmd('find /usr/share/zoneinfo/posix -type f -or -type l | sed -e s:/usr/share/zoneinfo/posix/::') + except OSError: + tz_datas = '' + tz_data = tz_datas.split('\n') + + if re.match(r'[Ll][Oo][Ss].+', tz): + tz = 'America/Los_Angeles' + elif re.match(r'[Dd][Ee][Nn].+', tz): + tz = 'America/Denver' + elif re.match(r'[Hh][Oo][Nn][Oo].+', tz): + tz = 'Pacific/Honolulu' + elif re.match(r'[Nn][Ee][Ww].+', tz): + tz = 'America/New_York' + elif re.match(r'[Cc][Hh][Ii][Cc]*.+', tz): + tz = 'America/Chicago' + elif re.match(r'[Aa][Nn][Cc].+', tz): + tz = 'America/Anchorage' + elif re.match(r'[Pp][Hh][Oo].+', tz): + tz = 'America/Phoenix' + elif re.match(r'GMT(.+)?', tz): + tz = 'Etc/' + tz + elif tz not in tz_data: + # assign default UTC timezone + tz = 'UTC' + + # replace timezone data is required + config.set(tz_base, value=tz) try: with open(file_name, 'w') as f: diff --git a/src/migration-scripts/system/14-to-15 b/src/migration-scripts/system/14-to-15 index 2491e3d0d..c055dad1f 100755 --- a/src/migration-scripts/system/14-to-15 +++ b/src/migration-scripts/system/14-to-15 @@ -1,10 +1,14 @@ #!/usr/bin/env python3 # -# Make 'system options reboot-on-panic' valueless +# Delete 'system ipv6 blacklist' option as the IPv6 module can no longer be +# blacklisted as it is required by e.g. WireGuard and thus will always be +# loaded. import os import sys +ipv6_blacklist_file = '/etc/modprobe.d/vyatta_blacklist_ipv6.conf' + from vyos.configtree import ConfigTree if (len(sys.argv) < 1): @@ -17,17 +21,16 @@ with open(file_name, 'r') as f: config_file = f.read() config = ConfigTree(config_file) -base = ['system', 'options'] -if not config.exists(base): +ip_base = ['system', 'ipv6'] +if not config.exists(ip_base): # Nothing to do sys.exit(0) else: - if config.exists(base + ['reboot-on-panic']): - reboot = config.return_value(base + ['reboot-on-panic']) - config.delete(base + ['reboot-on-panic']) - # create new valueless node if action was true - if reboot == "true": - config.set(base + ['reboot-on-panic']) + # delete 'system ipv6 blacklist' node + if config.exists(ip_base + ['blacklist']): + config.delete(ip_base + ['blacklist']) + if os.path.isfile(ipv6_blacklist_file): + os.unlink(ipv6_blacklist_file) try: with open(file_name, 'w') as f: diff --git a/src/migration-scripts/system/15-to-16 b/src/migration-scripts/system/15-to-16 index e70893d55..2491e3d0d 100755 --- a/src/migration-scripts/system/15-to-16 +++ b/src/migration-scripts/system/15-to-16 @@ -1,24 +1,6 @@ #!/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 . - -# * remove "system login user group" node, Why should be add a user to a -# 3rd party group when the system is fully managed by CLI? -# * remove "system login user level" node -# This is the only privilege level left and also the default, what is the -# sense in keeping this orphaned node? +# Make 'system options reboot-on-panic' valueless import os import sys @@ -35,17 +17,17 @@ with open(file_name, 'r') as f: config_file = f.read() config = ConfigTree(config_file) -base = ['system', 'login', 'user'] +base = ['system', 'options'] if not config.exists(base): # Nothing to do sys.exit(0) else: - for user in config.list_nodes(base): - if config.exists(base + [user, 'group']): - config.delete(base + [user, 'group']) - - if config.exists(base + [user, 'level']): - config.delete(base + [user, 'level']) + if config.exists(base + ['reboot-on-panic']): + reboot = config.return_value(base + ['reboot-on-panic']) + config.delete(base + ['reboot-on-panic']) + # create new valueless node if action was true + if reboot == "true": + config.set(base + ['reboot-on-panic']) try: with open(file_name, 'w') as f: diff --git a/src/migration-scripts/system/16-to-17 b/src/migration-scripts/system/16-to-17 index 8f762c0e2..e70893d55 100755 --- a/src/migration-scripts/system/16-to-17 +++ b/src/migration-scripts/system/16-to-17 @@ -14,8 +14,11 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -# remove "system console netconsole" -# remove "system console device modem" +# * remove "system login user group" node, Why should be add a user to a +# 3rd party group when the system is fully managed by CLI? +# * remove "system login user level" node +# This is the only privilege level left and also the default, what is the +# sense in keeping this orphaned node? import os import sys @@ -32,41 +35,17 @@ with open(file_name, 'r') as f: config_file = f.read() config = ConfigTree(config_file) -base = ['system', 'console'] +base = ['system', 'login', 'user'] if not config.exists(base): # Nothing to do sys.exit(0) else: - # remove "system console netconsole" (T2561) - if config.exists(base + ['netconsole']): - config.delete(base + ['netconsole']) + for user in config.list_nodes(base): + if config.exists(base + [user, 'group']): + config.delete(base + [user, 'group']) - if config.exists(base + ['device']): - for device in config.list_nodes(base + ['device']): - dev_path = base + ['device', device] - # remove "system console device modem" (T2570) - if config.exists(dev_path + ['modem']): - config.delete(dev_path + ['modem']) - - # Only continue on USB based serial consoles - if not 'ttyUSB' in device: - continue - - # A serial console has been configured but it does no longer - # exist on the system - cleanup - if not os.path.exists(f'/dev/{device}'): - config.delete(dev_path) - continue - - # migrate from ttyUSB device to new device in /dev/serial/by-bus - for root, dirs, files in os.walk('/dev/serial/by-bus'): - for usb_device in files: - device_file = os.path.realpath(os.path.join(root, usb_device)) - # migrate to new USB device names (T2529) - if os.path.basename(device_file) == device: - config.copy(dev_path, base + ['device', usb_device]) - # Delete old USB node from config - config.delete(dev_path) + if config.exists(base + [user, 'level']): + config.delete(base + [user, 'level']) try: with open(file_name, 'w') as f: diff --git a/src/migration-scripts/system/17-to-18 b/src/migration-scripts/system/17-to-18 index dd2abce00..8f762c0e2 100755 --- a/src/migration-scripts/system/17-to-18 +++ b/src/migration-scripts/system/17-to-18 @@ -13,93 +13,64 @@ # # You should have received a copy of the GNU General Public License # along with this program. If not, see . -# -# migrate disable-dhcp-nameservers (boolean) to name-servers-dhcp -# if disable-dhcp-nameservers is set, just remove it -# else retrieve all interface names that have configured dhcp(v6) address and -# add them to the new name-servers-dhcp node +# remove "system console netconsole" +# remove "system console device modem" + +import os +import sys -from sys import argv, exit -from vyos.ifconfig import Interface from vyos.configtree import ConfigTree -if (len(argv) < 1): +if (len(sys.argv) < 1): print("Must specify file name!") - exit(1) + sys.exit(1) -file_name = argv[1] +file_name = sys.argv[1] with open(file_name, 'r') as f: config_file = f.read() config = ConfigTree(config_file) - -base = ['system'] +base = ['system', 'console'] if not config.exists(base): # Nothing to do - exit(0) - -if config.exists(base + ['disable-dhcp-nameservers']): - config.delete(base + ['disable-dhcp-nameservers']) + sys.exit(0) else: - dhcp_interfaces = [] - - # go through all interfaces searching for 'address dhcp(v6)?' - for sect in Interface.sections(): - sect_base = ['interfaces', sect] - - if not config.exists(sect_base): - continue - - for intf in config.list_nodes(sect_base): - intf_base = sect_base + [intf] - - # try without vlans - if config.exists(intf_base + ['address']): - for addr in config.return_values(intf_base + ['address']): - if addr in ['dhcp', 'dhcpv6']: - dhcp_interfaces.append(intf) - - # try vif - if config.exists(intf_base + ['vif']): - for vif in config.list_nodes(intf_base + ['vif']): - vif_base = intf_base + ['vif', vif] - if config.exists(vif_base + ['address']): - for addr in config.return_values(vif_base + ['address']): - if addr in ['dhcp', 'dhcpv6']: - dhcp_interfaces.append(f'{intf}.{vif}') - - # try vif-s - if config.exists(intf_base + ['vif-s']): - for vif_s in config.list_nodes(intf_base + ['vif-s']): - vif_s_base = intf_base + ['vif-s', vif_s] - if config.exists(vif_s_base + ['address']): - for addr in config.return_values(vif_s_base + ['address']): - if addr in ['dhcp', 'dhcpv6']: - dhcp_interfaces.append(f'{intf}.{vif_s}') - - # try vif-c - if config.exists(intf_base + ['vif-c', vif_c]): - for vif_c in config.list_nodes(vif_s_base + ['vif-c', vif_c]): - vif_c_base = vif_s_base + ['vif-c', vif_c] - if config.exists(vif_c_base + ['address']): - for addr in config.return_values(vif_c_base + ['address']): - if addr in ['dhcp', 'dhcpv6']: - dhcp_interfaces.append(f'{intf}.{vif_s}.{vif_c}') - - # set new config nodes - for intf in dhcp_interfaces: - config.set(base + ['name-servers-dhcp'], value=intf, replace=False) - - # delete old node - config.delete(base + ['disable-dhcp-nameservers']) - -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) - -exit(0) + # remove "system console netconsole" (T2561) + if config.exists(base + ['netconsole']): + config.delete(base + ['netconsole']) + + if config.exists(base + ['device']): + for device in config.list_nodes(base + ['device']): + dev_path = base + ['device', device] + # remove "system console device modem" (T2570) + if config.exists(dev_path + ['modem']): + config.delete(dev_path + ['modem']) + + # Only continue on USB based serial consoles + if not 'ttyUSB' in device: + continue + + # A serial console has been configured but it does no longer + # exist on the system - cleanup + if not os.path.exists(f'/dev/{device}'): + config.delete(dev_path) + continue + + # migrate from ttyUSB device to new device in /dev/serial/by-bus + for root, dirs, files in os.walk('/dev/serial/by-bus'): + for usb_device in files: + device_file = os.path.realpath(os.path.join(root, usb_device)) + # migrate to new USB device names (T2529) + if os.path.basename(device_file) == device: + config.copy(dev_path, base + ['device', usb_device]) + # Delete old USB node from config + config.delete(dev_path) + + 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/system/18-to-19 b/src/migration-scripts/system/18-to-19 new file mode 100755 index 000000000..dd2abce00 --- /dev/null +++ b/src/migration-scripts/system/18-to-19 @@ -0,0 +1,105 @@ +#!/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 . +# + +# migrate disable-dhcp-nameservers (boolean) to name-servers-dhcp +# if disable-dhcp-nameservers is set, just remove it +# else retrieve all interface names that have configured dhcp(v6) address and +# add them to the new name-servers-dhcp node + +from sys import argv, exit +from vyos.ifconfig import Interface +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() + +config = ConfigTree(config_file) + +base = ['system'] +if not config.exists(base): + # Nothing to do + exit(0) + +if config.exists(base + ['disable-dhcp-nameservers']): + config.delete(base + ['disable-dhcp-nameservers']) +else: + dhcp_interfaces = [] + + # go through all interfaces searching for 'address dhcp(v6)?' + for sect in Interface.sections(): + sect_base = ['interfaces', sect] + + if not config.exists(sect_base): + continue + + for intf in config.list_nodes(sect_base): + intf_base = sect_base + [intf] + + # try without vlans + if config.exists(intf_base + ['address']): + for addr in config.return_values(intf_base + ['address']): + if addr in ['dhcp', 'dhcpv6']: + dhcp_interfaces.append(intf) + + # try vif + if config.exists(intf_base + ['vif']): + for vif in config.list_nodes(intf_base + ['vif']): + vif_base = intf_base + ['vif', vif] + if config.exists(vif_base + ['address']): + for addr in config.return_values(vif_base + ['address']): + if addr in ['dhcp', 'dhcpv6']: + dhcp_interfaces.append(f'{intf}.{vif}') + + # try vif-s + if config.exists(intf_base + ['vif-s']): + for vif_s in config.list_nodes(intf_base + ['vif-s']): + vif_s_base = intf_base + ['vif-s', vif_s] + if config.exists(vif_s_base + ['address']): + for addr in config.return_values(vif_s_base + ['address']): + if addr in ['dhcp', 'dhcpv6']: + dhcp_interfaces.append(f'{intf}.{vif_s}') + + # try vif-c + if config.exists(intf_base + ['vif-c', vif_c]): + for vif_c in config.list_nodes(vif_s_base + ['vif-c', vif_c]): + vif_c_base = vif_s_base + ['vif-c', vif_c] + if config.exists(vif_c_base + ['address']): + for addr in config.return_values(vif_c_base + ['address']): + if addr in ['dhcp', 'dhcpv6']: + dhcp_interfaces.append(f'{intf}.{vif_s}.{vif_c}') + + # set new config nodes + for intf in dhcp_interfaces: + config.set(base + ['name-servers-dhcp'], value=intf, replace=False) + + # delete old node + config.delete(base + ['disable-dhcp-nameservers']) + +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) + +exit(0) diff --git a/src/migration-scripts/system/9-to-10 b/src/migration-scripts/system/9-to-10 deleted file mode 100755 index 3c49f0d95..000000000 --- a/src/migration-scripts/system/9-to-10 +++ /dev/null @@ -1,36 +0,0 @@ -#!/usr/bin/env python3 - -# Operator accounts have been deprecated due to a security issue. Those accounts -# will be converted to regular admin accounts. - -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() - -config = ConfigTree(config_file) -base_level = ['system', 'login', 'user'] - -if not config.exists(base_level): - # Nothing to do, which shouldn't happen anyway - # only if you wipe the config and reboot. - sys.exit(0) -else: - for user in config.list_nodes(base_level): - if config.exists(base_level + [user, 'level']): - if config.return_value(base_level + [user, 'level']) == 'operator': - config.set(base_level + [user, 'level'], value="admin", replace=True) - - try: - open(file_name,'w').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 49a79954373eb3f70eddb444e855ed744a322e58 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Fri, 25 Sep 2020 20:32:28 +0200 Subject: ifconfig: T2912: add helper to retrieve interface min/max supported MTU >>> from vyos.ifconfig import Interface >>> tmp=Interface('eth0') >>> tmp.get_min_mtu() 60 >>> tmp.get_max_mtu() 9000 --- python/vyos/ifconfig/interface.py | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/python/vyos/ifconfig/interface.py b/python/vyos/ifconfig/interface.py index 807191b3d..c8ba05edd 100644 --- a/python/vyos/ifconfig/interface.py +++ b/python/vyos/ifconfig/interface.py @@ -78,6 +78,14 @@ class Interface(Control): 'shellcmd': 'ip -json link show dev {ifname}', 'format': lambda j: 'up' if 'UP' in jmespath.search('[*].flags | [0]', json.loads(j)) else 'down', }, + 'min_mtu': { + 'shellcmd': 'ip -json -detail link list dev {ifname}', + 'format': lambda j: jmespath.search('[*].min_mtu | [0]', json.loads(j)), + }, + 'max_mtu': { + 'shellcmd': 'ip -json -detail link list dev {ifname}', + 'format': lambda j: jmespath.search('[*].max_mtu | [0]', json.loads(j)), + }, } _command_set = { @@ -282,6 +290,28 @@ class Interface(Control): cmd = 'ip link del dev {ifname}'.format(**self.config) return self._cmd(cmd) + def get_min_mtu(self): + """ + Get hardware minimum supported MTU + + Example: + >>> from vyos.ifconfig import Interface + >>> Interface('eth0').get_min_mtu() + '60' + """ + return self.get_interface('min_mtu') + + def get_max_mtu(self): + """ + Get hardware maximum supported MTU + + Example: + >>> from vyos.ifconfig import Interface + >>> Interface('eth0').get_max_mtu() + '9000' + """ + return self.get_interface('max_mtu') + def get_mtu(self): """ Get/set interface mtu in bytes. -- cgit v1.2.3 From 818a75c024e4b4c0403ccfe782fb55517f390bef Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Fri, 25 Sep 2020 20:48:15 +0200 Subject: ifconfig: T2653: get_mtu() should return int() for easier comparison --- python/vyos/ifconfig/interface.py | 2 +- src/conf_mode/interfaces-macsec.py | 4 ++-- src/conf_mode/interfaces-vxlan.py | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/python/vyos/ifconfig/interface.py b/python/vyos/ifconfig/interface.py index c8ba05edd..a0f0ffe04 100644 --- a/python/vyos/ifconfig/interface.py +++ b/python/vyos/ifconfig/interface.py @@ -321,7 +321,7 @@ class Interface(Control): >>> Interface('eth0').get_mtu() '1500' """ - return self.get_interface('mtu') + return int(self.get_interface('mtu')) def set_mtu(self, mtu): """ diff --git a/src/conf_mode/interfaces-macsec.py b/src/conf_mode/interfaces-macsec.py index abf8b05c3..a224c540e 100755 --- a/src/conf_mode/interfaces-macsec.py +++ b/src/conf_mode/interfaces-macsec.py @@ -92,8 +92,8 @@ def verify(macsec): # MACsec adds a 40 byte overhead (32 byte MACsec + 8 bytes VLAN 802.1ad # and 802.1q) - we need to check the underlaying MTU if our configured # MTU is at least 40 bytes less then the MTU of our physical interface. - underlay_mtu = int(Interface(macsec['source_interface']).get_mtu()) - if underlay_mtu < (int(macsec['mtu']) + 40): + lower_mtu = Interface(macsec['source_interface']).get_mtu() + if lower_mtu < (int(macsec['mtu']) + 40): raise ConfigError('MACsec overhead does not fit into underlaying device MTU,\n' \ f'{underlay_mtu} bytes is too small!') diff --git a/src/conf_mode/interfaces-vxlan.py b/src/conf_mode/interfaces-vxlan.py index a00c58608..850ea28d7 100755 --- a/src/conf_mode/interfaces-vxlan.py +++ b/src/conf_mode/interfaces-vxlan.py @@ -72,8 +72,8 @@ def verify(vxlan): if 'source_interface' in vxlan: # VXLAN adds a 50 byte overhead - we need to check the underlaying MTU # if our configured MTU is at least 50 bytes less - underlay_mtu = int(Interface(vxlan['source_interface']).get_mtu()) - if underlay_mtu < (int(vxlan['mtu']) + 50): + lower_mtu = Interface(vxlan['source_interface']).get_mtu() + if lower_mtu < (int(vxlan['mtu']) + 50): raise ConfigError('VXLAN has a 50 byte overhead, underlaying device ' \ f'MTU is to small ({underlay_mtu} bytes)') -- cgit v1.2.3 From 9b268012252a4fe6f253177830abde950e2773b5 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Fri, 25 Sep 2020 20:57:37 +0200 Subject: ifconfig: T2912: add helper to verify interface min/max supported MTU Currently the MTU size of an interface is only checked when entered via CLI but if the interface supportes the configured MTU at all is not verified at all. New helper functions get_min_mtu(), get_max_mtu() and verify_mtu() have been added to provide a central API for validation. --- python/vyos/configverify.py | 21 +++++++++++++++++++++ python/vyos/ifconfig/interface.py | 4 ++-- 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/python/vyos/configverify.py b/python/vyos/configverify.py index afa6c7f06..6e5ba1df0 100644 --- a/python/vyos/configverify.py +++ b/python/vyos/configverify.py @@ -23,6 +23,27 @@ from vyos import ConfigError +def verify_mtu(config): + """ + Common helper function used by interface implementations to perform + recurring validation if the specified MTU can be used by the underlaying + hardware. + """ + from vyos.ifconfig import Interface + if 'mtu' in config: + mtu = int(config['mtu']) + + tmp = Interface(config['ifname']) + min_mtu = tmp.get_min_mtu() + max_mtu = tmp.get_max_mtu() + + if mtu < min_mtu: + raise ConfigError(f'Interface MTU too low, ' \ + f'minimum supported MTU is {min_mtu}!') + if mtu > max_mtu: + raise ConfigError(f'Interface MTU too high, ' \ + f'maximum supported MTU is {max_mtu}!') + def verify_vrf(config): """ Common helper function used by interface implementations to perform diff --git a/python/vyos/ifconfig/interface.py b/python/vyos/ifconfig/interface.py index a0f0ffe04..d200fc7a8 100644 --- a/python/vyos/ifconfig/interface.py +++ b/python/vyos/ifconfig/interface.py @@ -299,7 +299,7 @@ class Interface(Control): >>> Interface('eth0').get_min_mtu() '60' """ - return self.get_interface('min_mtu') + return int(self.get_interface('min_mtu')) def get_max_mtu(self): """ @@ -310,7 +310,7 @@ class Interface(Control): >>> Interface('eth0').get_max_mtu() '9000' """ - return self.get_interface('max_mtu') + return int(self.get_interface('max_mtu')) def get_mtu(self): """ -- cgit v1.2.3 From 3abeef7e4bf6b59b8e58b31600b536a5a07d981e Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Fri, 25 Sep 2020 20:58:59 +0200 Subject: ethernet: T2912: verify() that hardware supports specified MTU value Check the hardware if MTU value is supported at all. --- src/conf_mode/interfaces-ethernet.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/conf_mode/interfaces-ethernet.py b/src/conf_mode/interfaces-ethernet.py index a8df64cce..5468c7bda 100755 --- a/src/conf_mode/interfaces-ethernet.py +++ b/src/conf_mode/interfaces-ethernet.py @@ -20,11 +20,12 @@ from sys import exit from vyos.config import Config from vyos.configdict import get_interface_dict -from vyos.configverify import verify_interface_exists -from vyos.configverify import verify_dhcpv6 from vyos.configverify import verify_address -from vyos.configverify import verify_vrf +from vyos.configverify import verify_dhcpv6 +from vyos.configverify import verify_interface_exists +from vyos.configverify import verify_mtu from vyos.configverify import verify_vlan_config +from vyos.configverify import verify_vrf from vyos.ifconfig import EthernetIf from vyos import ConfigError from vyos import airbag @@ -57,6 +58,7 @@ def verify(ethernet): if ethernet.get('speed', None) != 'auto': raise ConfigError('If duplex is hardcoded, speed must be hardcoded, too') + verify_mtu(ethernet) verify_dhcpv6(ethernet) verify_address(ethernet) verify_vrf(ethernet) -- cgit v1.2.3 From fe87871f3bf53c0f03b7ec82ae8d4735a429b25d Mon Sep 17 00:00:00 2001 From: kroy Date: Fri, 25 Sep 2020 16:22:54 -0500 Subject: T2926: Missing import --- src/conf_mode/snmp.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/conf_mode/snmp.py b/src/conf_mode/snmp.py index e9806ef47..117bf0274 100755 --- a/src/conf_mode/snmp.py +++ b/src/conf_mode/snmp.py @@ -22,7 +22,7 @@ from vyos.config import Config from vyos.configverify import verify_vrf from vyos.snmpv3_hashgen import plaintext_to_md5, plaintext_to_sha1, random from vyos.template import render -from vyos.util import call +from vyos.util import call, chmod_755 from vyos.validate import is_ipv4, is_addr_assigned from vyos.version import get_version_data from vyos import ConfigError, airbag -- cgit v1.2.3 From dfa949c5b758e2954ed5c6ad455fe586965cd156 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sat, 26 Sep 2020 10:06:27 +0200 Subject: smoketest: platform: check for required virtio/vmxnet drivers --- smoketest/scripts/system/test_kernel_options.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/smoketest/scripts/system/test_kernel_options.py b/smoketest/scripts/system/test_kernel_options.py index 6316521f6..043567c4f 100755 --- a/smoketest/scripts/system/test_kernel_options.py +++ b/smoketest/scripts/system/test_kernel_options.py @@ -42,6 +42,22 @@ class TestKernelModules(unittest.TestCase): tmp = re.findall(f'{option}=(y|m)', config) self.assertTrue(tmp) + def test_qemu_support(self): + """ The bond/lacp interface must be enabled in the OS Kernel """ + for option in ['CONFIG_VIRTIO_BLK', 'CONFIG_SCSI_VIRTIO', + 'CONFIG_VIRTIO_NET', 'CONFIG_VIRTIO_CONSOLE', + 'CONFIG_VIRTIO', 'CONFIG_VIRTIO_PCI', + 'CONFIG_VIRTIO_BALLOON', 'CONFIG_CRYPTO_DEV_VIRTIO', + 'CONFIG_X86_PLATFORM_DEVICES']: + tmp = re.findall(f'{option}=(y|m)', config) + self.assertTrue(tmp) + + def test_vmware_support(self): + """ The bond/lacp interface must be enabled in the OS Kernel """ + for option in ['CONFIG_VMXNET3']: + tmp = re.findall(f'{option}=(y|m)', config) + self.assertTrue(tmp) + if __name__ == '__main__': unittest.main() -- cgit v1.2.3 From 5db3d63160670c796ed74a170862c367048d89bb Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sat, 26 Sep 2020 12:00:06 +0200 Subject: ifconfig: mtu: disallow MTU < 1280 bytes when IPv6 is enabled on the interface Using an MTU less then the required 1280 bytes (as per RFC) on an interface where IPv6 is not explicitly disabled by: - set interfaces ethernet eth1 ipv6 address no-default-link-local - not having any other IPv6 address configured Will now trigger a commit error via verify() instead of raising FileNotFoundError! --- python/vyos/configverify.py | 30 ++++++++++++++++++++++++++++++ src/conf_mode/interfaces-bonding.py | 2 ++ src/conf_mode/interfaces-bridge.py | 2 ++ src/conf_mode/interfaces-ethernet.py | 3 +++ src/conf_mode/interfaces-geneve.py | 2 ++ src/conf_mode/interfaces-l2tpv3.py | 2 ++ src/conf_mode/interfaces-macsec.py | 2 ++ src/conf_mode/interfaces-pppoe.py | 2 ++ src/conf_mode/interfaces-vxlan.py | 2 ++ src/conf_mode/interfaces-wireguard.py | 2 ++ 10 files changed, 49 insertions(+) diff --git a/python/vyos/configverify.py b/python/vyos/configverify.py index 6e5ba1df0..944fc4294 100644 --- a/python/vyos/configverify.py +++ b/python/vyos/configverify.py @@ -44,6 +44,36 @@ def verify_mtu(config): raise ConfigError(f'Interface MTU too high, ' \ f'maximum supported MTU is {max_mtu}!') +def verify_mtu_ipv6(config): + """ + Common helper function used by interface implementations to perform + recurring validation if the specified MTU can be used when IPv6 is + configured on the interface. IPv6 requires a 1280 bytes MTU. + """ + from vyos.validate import is_ipv6 + from vyos.util import vyos_dict_search + # IPv6 minimum required link mtu + 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}!' + + if not vyos_dict_search('ipv6.address.no_default_link_local', config): + raise ConfigError('link-local ' + error_msg) + + for address in (vyos_dict_search('address', config) or []): + if address in ['dhcpv6'] or is_ipv6(address): + raise ConfigError(error_msg) + + if vyos_dict_search('ipv6.address.autoconf', config): + raise ConfigError(error_msg) + + if vyos_dict_search('ipv6.address.eui64', config): + raise ConfigError(error_msg) + + def verify_vrf(config): """ Common helper function used by interface implementations to perform diff --git a/src/conf_mode/interfaces-bonding.py b/src/conf_mode/interfaces-bonding.py index aece2a04b..9763620ac 100755 --- a/src/conf_mode/interfaces-bonding.py +++ b/src/conf_mode/interfaces-bonding.py @@ -28,6 +28,7 @@ from vyos.configverify import verify_address from vyos.configverify import verify_bridge_delete from vyos.configverify import verify_dhcpv6 from vyos.configverify import verify_source_interface +from vyos.configverify import verify_mtu_ipv6 from vyos.configverify import verify_vlan_config from vyos.configverify import verify_vrf from vyos.ifconfig import BondIf @@ -141,6 +142,7 @@ def verify(bond): raise ConfigError('Option primary - mode dependency failed, not' 'supported in mode {mode}!'.format(**bond)) + verify_mtu_ipv6(bond) verify_address(bond) verify_dhcpv6(bond) verify_vrf(bond) diff --git a/src/conf_mode/interfaces-bridge.py b/src/conf_mode/interfaces-bridge.py index 485decb17..4ac9c8963 100755 --- a/src/conf_mode/interfaces-bridge.py +++ b/src/conf_mode/interfaces-bridge.py @@ -25,6 +25,7 @@ from vyos.configdict import node_changed from vyos.configdict import is_member from vyos.configdict import is_source_interface from vyos.configverify import verify_dhcpv6 +from vyos.configverify import verify_mtu_ipv6 from vyos.configverify import verify_vrf from vyos.ifconfig import BridgeIf from vyos.validate import has_address_configured @@ -95,6 +96,7 @@ def verify(bridge): if 'deleted' in bridge: return None + verify_mtu_ipv6(bridge) verify_dhcpv6(bridge) verify_vrf(bridge) diff --git a/src/conf_mode/interfaces-ethernet.py b/src/conf_mode/interfaces-ethernet.py index 5468c7bda..1f622c003 100755 --- a/src/conf_mode/interfaces-ethernet.py +++ b/src/conf_mode/interfaces-ethernet.py @@ -24,6 +24,7 @@ from vyos.configverify import verify_address from vyos.configverify import verify_dhcpv6 from vyos.configverify import verify_interface_exists from vyos.configverify import verify_mtu +from vyos.configverify import verify_mtu_ipv6 from vyos.configverify import verify_vlan_config from vyos.configverify import verify_vrf from vyos.ifconfig import EthernetIf @@ -42,6 +43,7 @@ def get_config(config=None): conf = Config() base = ['interfaces', 'ethernet'] ethernet = get_interface_dict(conf, base) + return ethernet def verify(ethernet): @@ -59,6 +61,7 @@ def verify(ethernet): raise ConfigError('If duplex is hardcoded, speed must be hardcoded, too') verify_mtu(ethernet) + verify_mtu_ipv6(ethernet) verify_dhcpv6(ethernet) verify_address(ethernet) verify_vrf(ethernet) diff --git a/src/conf_mode/interfaces-geneve.py b/src/conf_mode/interfaces-geneve.py index af7c121f4..979a5612e 100755 --- a/src/conf_mode/interfaces-geneve.py +++ b/src/conf_mode/interfaces-geneve.py @@ -22,6 +22,7 @@ from netifaces import interfaces from vyos.config import Config from vyos.configdict import get_interface_dict from vyos.configverify import verify_address +from vyos.configverify import verify_mtu_ipv6 from vyos.configverify import verify_bridge_delete from vyos.ifconfig import GeneveIf from vyos import ConfigError @@ -47,6 +48,7 @@ def verify(geneve): verify_bridge_delete(geneve) return None + verify_mtu_ipv6(geneve) verify_address(geneve) if 'remote' not in geneve: diff --git a/src/conf_mode/interfaces-l2tpv3.py b/src/conf_mode/interfaces-l2tpv3.py index 2653ff19c..1118143e4 100755 --- a/src/conf_mode/interfaces-l2tpv3.py +++ b/src/conf_mode/interfaces-l2tpv3.py @@ -24,6 +24,7 @@ from vyos.configdict import get_interface_dict from vyos.configdict import leaf_node_changed from vyos.configverify import verify_address from vyos.configverify import verify_bridge_delete +from vyos.configverify import verify_mtu_ipv6 from vyos.ifconfig import L2TPv3If from vyos.util import check_kmod from vyos.validate import is_addr_assigned @@ -80,6 +81,7 @@ def verify(l2tpv3): raise ConfigError('L2TPv3 local-ip address ' '"{local_ip}" is not configured!'.format(**l2tpv3)) + verify_mtu_ipv6(l2tpv3) verify_address(l2tpv3) return None diff --git a/src/conf_mode/interfaces-macsec.py b/src/conf_mode/interfaces-macsec.py index a224c540e..0a20a121b 100755 --- a/src/conf_mode/interfaces-macsec.py +++ b/src/conf_mode/interfaces-macsec.py @@ -27,6 +27,7 @@ from vyos.util import call from vyos.configverify import verify_vrf from vyos.configverify import verify_address from vyos.configverify import verify_bridge_delete +from vyos.configverify import verify_mtu_ipv6 from vyos.configverify import verify_source_interface from vyos import ConfigError from vyos import airbag @@ -71,6 +72,7 @@ def verify(macsec): verify_source_interface(macsec) verify_vrf(macsec) + verify_mtu_ipv6(macsec) verify_address(macsec) if not (('security' in macsec) and diff --git a/src/conf_mode/interfaces-pppoe.py b/src/conf_mode/interfaces-pppoe.py index 1b4b9e4ee..ee3b142c8 100755 --- a/src/conf_mode/interfaces-pppoe.py +++ b/src/conf_mode/interfaces-pppoe.py @@ -24,6 +24,7 @@ from vyos.config import Config from vyos.configdict import get_interface_dict from vyos.configverify import verify_source_interface from vyos.configverify import verify_vrf +from vyos.configverify import verify_mtu_ipv6 from vyos.template import render from vyos.util import call from vyos import ConfigError @@ -57,6 +58,7 @@ def verify(pppoe): verify_source_interface(pppoe) verify_vrf(pppoe) + verify_mtu_ipv6(pppoe) if {'connect_on_demand', 'vrf'} <= set(pppoe): raise ConfigError('On-demand dialing and VRF can not be used at the same time') diff --git a/src/conf_mode/interfaces-vxlan.py b/src/conf_mode/interfaces-vxlan.py index 850ea28d7..002f40aef 100755 --- a/src/conf_mode/interfaces-vxlan.py +++ b/src/conf_mode/interfaces-vxlan.py @@ -23,6 +23,7 @@ from vyos.config import Config from vyos.configdict import get_interface_dict from vyos.configverify import verify_address from vyos.configverify import verify_bridge_delete +from vyos.configverify import verify_mtu_ipv6 from vyos.configverify import verify_source_interface from vyos.ifconfig import VXLANIf, Interface from vyos import ConfigError @@ -77,6 +78,7 @@ def verify(vxlan): raise ConfigError('VXLAN has a 50 byte overhead, underlaying device ' \ f'MTU is to small ({underlay_mtu} bytes)') + verify_mtu_ipv6(vxlan) verify_address(vxlan) return None diff --git a/src/conf_mode/interfaces-wireguard.py b/src/conf_mode/interfaces-wireguard.py index e7c22da1a..d5800264f 100755 --- a/src/conf_mode/interfaces-wireguard.py +++ b/src/conf_mode/interfaces-wireguard.py @@ -27,6 +27,7 @@ from vyos.configdict import leaf_node_changed from vyos.configverify import verify_vrf from vyos.configverify import verify_address 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 import ConfigError @@ -71,6 +72,7 @@ def verify(wireguard): verify_bridge_delete(wireguard) return None + verify_mtu_ipv6(wireguard) verify_address(wireguard) verify_vrf(wireguard) -- cgit v1.2.3 From f0400b7e24944de75fef8cdd0d7eb016b224b9c7 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sat, 26 Sep 2020 12:38:19 +0200 Subject: ifconfig: mtu: T2928: remove bridge mtu check as our bridge interface has no mtu --- src/conf_mode/interfaces-bridge.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/conf_mode/interfaces-bridge.py b/src/conf_mode/interfaces-bridge.py index 4ac9c8963..485decb17 100755 --- a/src/conf_mode/interfaces-bridge.py +++ b/src/conf_mode/interfaces-bridge.py @@ -25,7 +25,6 @@ from vyos.configdict import node_changed from vyos.configdict import is_member from vyos.configdict import is_source_interface from vyos.configverify import verify_dhcpv6 -from vyos.configverify import verify_mtu_ipv6 from vyos.configverify import verify_vrf from vyos.ifconfig import BridgeIf from vyos.validate import has_address_configured @@ -96,7 +95,6 @@ def verify(bridge): if 'deleted' in bridge: return None - verify_mtu_ipv6(bridge) verify_dhcpv6(bridge) verify_vrf(bridge) -- cgit v1.2.3 From f325d869515e008b09a8f2de7a4bda81dffd1158 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sat, 26 Sep 2020 12:49:04 +0200 Subject: xml: interface-definitions: fix wrong file extension of inlcude files Files which are included by others whouls have the ".xml.i" extension. --- .../include/accel-client-ipv6-pool.xml.i | 59 ++++++++++ .../include/accel-client-ipv6-pool.xml.in | 59 ---------- .../include/accel-name-server.xml.i | 18 +++ .../include/accel-name-server.xml.in | 18 --- .../include/accel-radius-additions.xml.i | 125 +++++++++++++++++++++ .../include/accel-radius-additions.xml.in | 125 --------------------- interface-definitions/service_ipoe-server.xml.in | 6 +- interface-definitions/service_pppoe-server.xml.in | 6 +- interface-definitions/vpn_l2tp.xml.in | 4 +- interface-definitions/vpn_pptp.xml.in | 2 +- interface-definitions/vpn_sstp.xml.in | 6 +- 11 files changed, 214 insertions(+), 214 deletions(-) create mode 100644 interface-definitions/include/accel-client-ipv6-pool.xml.i delete mode 100644 interface-definitions/include/accel-client-ipv6-pool.xml.in create mode 100644 interface-definitions/include/accel-name-server.xml.i delete mode 100644 interface-definitions/include/accel-name-server.xml.in create mode 100644 interface-definitions/include/accel-radius-additions.xml.i delete mode 100644 interface-definitions/include/accel-radius-additions.xml.in diff --git a/interface-definitions/include/accel-client-ipv6-pool.xml.i b/interface-definitions/include/accel-client-ipv6-pool.xml.i new file mode 100644 index 000000000..455ada6ef --- /dev/null +++ b/interface-definitions/include/accel-client-ipv6-pool.xml.i @@ -0,0 +1,59 @@ + + + Pool of client IPv6 addresses + + + + + Pool of addresses used to assign to clients + + ipv6net + IPv6 address and prefix length + + + + + + + + + Prefix length used for individual client + + <48-128> + Client prefix length (default: 64) + + + + + + + + + + + Subnet used to delegate prefix through DHCPv6-PD (RFC3633) + + ipv6net + IPv6 address and prefix length + + + + + + + + + Prefix length delegated to client + + <32-64> + Delegated prefix length + + + + + + + + + + diff --git a/interface-definitions/include/accel-client-ipv6-pool.xml.in b/interface-definitions/include/accel-client-ipv6-pool.xml.in deleted file mode 100644 index 455ada6ef..000000000 --- a/interface-definitions/include/accel-client-ipv6-pool.xml.in +++ /dev/null @@ -1,59 +0,0 @@ - - - Pool of client IPv6 addresses - - - - - Pool of addresses used to assign to clients - - ipv6net - IPv6 address and prefix length - - - - - - - - - Prefix length used for individual client - - <48-128> - Client prefix length (default: 64) - - - - - - - - - - - Subnet used to delegate prefix through DHCPv6-PD (RFC3633) - - ipv6net - IPv6 address and prefix length - - - - - - - - - Prefix length delegated to client - - <32-64> - Delegated prefix length - - - - - - - - - - diff --git a/interface-definitions/include/accel-name-server.xml.i b/interface-definitions/include/accel-name-server.xml.i new file mode 100644 index 000000000..82ed6771d --- /dev/null +++ b/interface-definitions/include/accel-name-server.xml.i @@ -0,0 +1,18 @@ + + + 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/accel-name-server.xml.in b/interface-definitions/include/accel-name-server.xml.in deleted file mode 100644 index 82ed6771d..000000000 --- a/interface-definitions/include/accel-name-server.xml.in +++ /dev/null @@ -1,18 +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/accel-radius-additions.xml.i b/interface-definitions/include/accel-radius-additions.xml.i new file mode 100644 index 000000000..e37b68514 --- /dev/null +++ b/interface-definitions/include/accel-radius-additions.xml.i @@ -0,0 +1,125 @@ + + + + + + + Accounting port + + 1-65535 + Numeric IP port (default: 1813) + + + + + + + + + Mark server unavailable for <n> seconds on failure + + 0-600 + Fail time penalty + + + + + Fail time must be between 0 and 600 seconds + + + + + + + Timeout in seconds to wait response from RADIUS server + + 1-60 + Timeout in seconds + + + + + Timeout must be between 1 and 60 seconds + + + + + Timeout for Interim-Update packets, terminate session afterwards (default 3 seconds) + + 0-60 + Timeout in seconds, 0 to keep active + + + + + Timeout must be between 0 and 60 seconds + + + + + Number of tries to send Access-Request/Accounting-Request queries + + 1-20 + Maximum tries + + + + + Maximum tries must be between 1 and 20 + + + + + NAS-Identifier attribute sent to RADIUS + + + + + NAS-IP-Address attribute sent to RADIUS + + + + + ipv4 + NAS-IP-Address attribute + + + + + + Dynamic Authorization Extension/Change of Authorization server + + + + + IP address for Dynamic Authorization Extension server (DM/CoA) + + + + + ipv4 + IPv4 address for aynamic authorization server + + + + + + Port for Dynamic Authorization Extension server (DM/CoA) + + number + TCP port + + + + + + + + + Shared secret for Dynamic Authorization Extension server + + + + + + diff --git a/interface-definitions/include/accel-radius-additions.xml.in b/interface-definitions/include/accel-radius-additions.xml.in deleted file mode 100644 index e37b68514..000000000 --- a/interface-definitions/include/accel-radius-additions.xml.in +++ /dev/null @@ -1,125 +0,0 @@ - - - - - - - Accounting port - - 1-65535 - Numeric IP port (default: 1813) - - - - - - - - - Mark server unavailable for <n> seconds on failure - - 0-600 - Fail time penalty - - - - - Fail time must be between 0 and 600 seconds - - - - - - - Timeout in seconds to wait response from RADIUS server - - 1-60 - Timeout in seconds - - - - - Timeout must be between 1 and 60 seconds - - - - - Timeout for Interim-Update packets, terminate session afterwards (default 3 seconds) - - 0-60 - Timeout in seconds, 0 to keep active - - - - - Timeout must be between 0 and 60 seconds - - - - - Number of tries to send Access-Request/Accounting-Request queries - - 1-20 - Maximum tries - - - - - Maximum tries must be between 1 and 20 - - - - - NAS-Identifier attribute sent to RADIUS - - - - - NAS-IP-Address attribute sent to RADIUS - - - - - ipv4 - NAS-IP-Address attribute - - - - - - Dynamic Authorization Extension/Change of Authorization server - - - - - IP address for Dynamic Authorization Extension server (DM/CoA) - - - - - ipv4 - IPv4 address for aynamic authorization server - - - - - - Port for Dynamic Authorization Extension server (DM/CoA) - - number - TCP port - - - - - - - - - Shared secret for Dynamic Authorization Extension server - - - - - - diff --git a/interface-definitions/service_ipoe-server.xml.in b/interface-definitions/service_ipoe-server.xml.in index 9ee5d5156..ee09d01d6 100644 --- a/interface-definitions/service_ipoe-server.xml.in +++ b/interface-definitions/service_ipoe-server.xml.in @@ -111,8 +111,8 @@ - #include - #include + #include + #include Client authentication methods @@ -198,7 +198,7 @@ #include - #include + #include diff --git a/interface-definitions/service_pppoe-server.xml.in b/interface-definitions/service_pppoe-server.xml.in index 605f47b37..64fd6e4ef 100644 --- a/interface-definitions/service_pppoe-server.xml.in +++ b/interface-definitions/service_pppoe-server.xml.in @@ -109,7 +109,7 @@ #include #include - #include + #include @@ -200,8 +200,8 @@ - #include - #include + #include + #include interface(s) to listen on diff --git a/interface-definitions/vpn_l2tp.xml.in b/interface-definitions/vpn_l2tp.xml.in index 702ef8b5a..4de28d2bd 100644 --- a/interface-definitions/vpn_l2tp.xml.in +++ b/interface-definitions/vpn_l2tp.xml.in @@ -36,7 +36,7 @@ - #include + #include L2TP Network Server (LNS) @@ -203,7 +203,7 @@ - #include + #include Description for L2TP remote-access settings diff --git a/interface-definitions/vpn_pptp.xml.in b/interface-definitions/vpn_pptp.xml.in index 032455b4d..f37c9bd01 100644 --- a/interface-definitions/vpn_pptp.xml.in +++ b/interface-definitions/vpn_pptp.xml.in @@ -153,7 +153,7 @@ #include - #include + #include diff --git a/interface-definitions/vpn_sstp.xml.in b/interface-definitions/vpn_sstp.xml.in index f0c93b882..5da2f8f24 100644 --- a/interface-definitions/vpn_sstp.xml.in +++ b/interface-definitions/vpn_sstp.xml.in @@ -96,7 +96,7 @@ #include - #include + #include @@ -207,8 +207,8 @@ - #include - #include + #include + #include #include -- cgit v1.2.3 From 374724be64728101c262fcac1579beece63ee651 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sat, 26 Sep 2020 13:10:51 +0200 Subject: Debian: adding vyos-configd made this package architecture specific There is a shim which is compiled by GCC per target architecture. --- debian/control | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/debian/control b/debian/control index 0777ecc52..ebcfc6c43 100644 --- a/debian/control +++ b/debian/control @@ -18,7 +18,7 @@ Build-Depends: Standards-Version: 3.9.6 Package: vyos-1x -Architecture: all +Architecture: amd64 arm64 Depends: accel-ppp, beep, @@ -117,7 +117,7 @@ Description: VyOS configuration scripts and data VyOS configuration scripts, interface definitions, and everything Package: vyos-1x-vmware -Architecture: amd64 i386 +Architecture: amd64 Depends: vyos-1x, open-vm-tools -- cgit v1.2.3