diff options
author | kumvijaya <kuvmijaya@gmail.com> | 2024-09-26 11:31:07 +0530 |
---|---|---|
committer | kumvijaya <kuvmijaya@gmail.com> | 2024-09-26 11:31:07 +0530 |
commit | a950059053f7394acfb453cc0d8194aa3dc721fa (patch) | |
tree | eb0acf278f649b5d1417e18e34d728efcd16e745 /src/migration-scripts/interfaces | |
parent | f0815f3e9b212f424f5adb0c572a71119ad4a8a0 (diff) | |
download | vyos-workflow-test-temp-a950059053f7394acfb453cc0d8194aa3dc721fa.tar.gz vyos-workflow-test-temp-a950059053f7394acfb453cc0d8194aa3dc721fa.zip |
T6732: added same as vyos 1x
Diffstat (limited to 'src/migration-scripts/interfaces')
33 files changed, 2129 insertions, 0 deletions
diff --git a/src/migration-scripts/interfaces/0-to-1 b/src/migration-scripts/interfaces/0-to-1 new file mode 100644 index 0000000..7c135e7 --- /dev/null +++ b/src/migration-scripts/interfaces/0-to-1 @@ -0,0 +1,113 @@ +# Copyright 2019-2024 VyOS maintainers and contributors <maintainers@vyos.io> +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library 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 +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this library. If not, see <http://www.gnu.org/licenses/>. + +# Change syntax of bridge interface +# - move interface based bridge-group to actual bridge (de-nest) +# - make stp and igmp-snooping nodes valueless +# https://vyos.dev/T1556 + +from vyos.configtree import ConfigTree + +def migrate_bridge(config, tree, intf): + # check if bridge-group exists + tree_bridge = tree + ['bridge-group'] + if config.exists(tree_bridge): + bridge = config.return_value(tree_bridge + ['bridge']) + # create new bridge member interface + config.set(base + [bridge, 'member', 'interface', intf]) + # format as tag node to avoid loading problems + config.set_tag(base + [bridge, 'member', 'interface']) + + # cost: migrate if configured + tree_cost = tree + ['bridge-group', 'cost'] + if config.exists(tree_cost): + cost = config.return_value(tree_cost) + # set new node + config.set(base + [bridge, 'member', 'interface', intf, 'cost'], value=cost) + + # priority: migrate if configured + tree_priority = tree + ['bridge-group', 'priority'] + if config.exists(tree_priority): + priority = config.return_value(tree_priority) + # set new node + config.set(base + [bridge, 'member', 'interface', intf, 'priority'], value=priority) + + # Delete the old bridge-group assigned to an interface + config.delete(tree_bridge) + + +def migrate(config: ConfigTree) -> None: + base = ['interfaces', 'bridge'] + + if not config.exists(base): + # Nothing to do + return + + # + # make stp and igmp-snooping nodes valueless + # + for br in config.list_nodes(base): + # STP: check if enabled + if config.exists(base + [br, 'stp']): + stp_val = config.return_value(base + [br, 'stp']) + # STP: delete node with old syntax + config.delete(base + [br, 'stp']) + # STP: set new node - if enabled + if stp_val == "true": + config.set(base + [br, 'stp'], value=None) + + # igmp-snooping: check if enabled + if config.exists(base + [br, 'igmp-snooping', 'querier']): + igmp_val = config.return_value(base + [br, 'igmp-snooping', 'querier']) + # igmp-snooping: delete node with old syntax + config.delete(base + [br, 'igmp-snooping', 'querier']) + # igmp-snooping: set new node - if enabled + if igmp_val == "enable": + config.set(base + [br, 'igmp', 'querier'], value=None) + + # + # move interface based bridge-group to actual bridge (de-nest) + # + bridge_types = ['bonding', 'ethernet', 'l2tpv3', 'openvpn', 'vxlan', 'wireless'] + for type in bridge_types: + if not config.exists(['interfaces', type]): + continue + + for interface in config.list_nodes(['interfaces', type]): + # check if bridge-group exists + bridge_group = ['interfaces', type, interface] + if config.exists(bridge_group + ['bridge-group']): + migrate_bridge(config, bridge_group, interface) + + # We also need to migrate VLAN interfaces + vlan_base = ['interfaces', type, interface, 'vif'] + if config.exists(vlan_base): + for vlan in config.list_nodes(vlan_base): + intf = "{}.{}".format(interface, vlan) + migrate_bridge(config, vlan_base + [vlan], intf) + + # And then we have service VLANs (vif-s) interfaces + vlan_base = ['interfaces', type, interface, 'vif-s'] + if config.exists(vlan_base): + for vif_s in config.list_nodes(vlan_base): + intf = "{}.{}".format(interface, vif_s) + migrate_bridge(config, vlan_base + [vif_s], intf) + + # Every service VLAN can have multiple customer VLANs (vif-c) + vlan_c = ['interfaces', type, interface, 'vif-s', vif_s, 'vif-c'] + if config.exists(vlan_c): + for vif_c in config.list_nodes(vlan_c): + intf = "{}.{}.{}".format(interface, vif_s, vif_c) + migrate_bridge(config, vlan_c + [vif_c], intf) diff --git a/src/migration-scripts/interfaces/1-to-2 b/src/migration-scripts/interfaces/1-to-2 new file mode 100644 index 0000000..ebf02b0 --- /dev/null +++ b/src/migration-scripts/interfaces/1-to-2 @@ -0,0 +1,59 @@ +# Copyright 2019-2024 VyOS maintainers and contributors <maintainers@vyos.io> +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library 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 +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this library. If not, see <http://www.gnu.org/licenses/>. + +# Change syntax of bond interface +# - move interface based bond-group to actual bond (de-nest) +# https://vyos.dev/T1614 + +from vyos.configtree import ConfigTree + +base = ['interfaces', 'bonding'] + +def migrate(config: ConfigTree) -> None: + if not config.exists(base): + # Nothing to do + return + + # + # move interface based bond-group to actual bond (de-nest) + # + for intf in config.list_nodes(['interfaces', 'ethernet']): + # check if bond-group exists + if config.exists(['interfaces', 'ethernet', intf, 'bond-group']): + # get configured bond interface + bond = config.return_value(['interfaces', 'ethernet', intf, 'bond-group']) + # delete old interface asigned (nested) bond group + config.delete(['interfaces', 'ethernet', intf, 'bond-group']) + # create new bond member interface + config.set(base + [bond, 'member', 'interface'], value=intf, replace=False) + + # + # some combinations were allowed in the past from a CLI perspective + # but the kernel overwrote them - remove from CLI to not confuse the users. + # In addition new consitency checks are in place so users can't repeat the + # mistake. One of those nice issues is https://vyos.dev/T532 + for bond in config.list_nodes(base): + if config.exists(base + [bond, 'arp-monitor', 'interval']) and config.exists(base + [bond, 'mode']): + mode = config.return_value(base + [bond, 'mode']) + if mode in ['802.3ad', 'transmit-load-balance', 'adaptive-load-balance']: + intvl = int(config.return_value(base + [bond, 'arp-monitor', 'interval'])) + if intvl > 0: + # this is not allowed and the linux kernel replies with: + # option arp_interval: mode dependency failed, not supported in mode 802.3ad(4) + # option arp_interval: mode dependency failed, not supported in mode balance-alb(6) + # option arp_interval: mode dependency failed, not supported in mode balance-tlb(5) + # + # so we simply disable arp_interval by setting it to 0 and miimon will take care about the link + config.set(base + [bond, 'arp-monitor', 'interval'], value='0') diff --git a/src/migration-scripts/interfaces/10-to-11 b/src/migration-scripts/interfaces/10-to-11 new file mode 100644 index 0000000..8a562f2 --- /dev/null +++ b/src/migration-scripts/interfaces/10-to-11 @@ -0,0 +1,38 @@ +# Copyright 2020-2024 VyOS maintainers and contributors <maintainers@vyos.io> +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library 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 +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this library. If not, see <http://www.gnu.org/licenses/>. + +# rename WWAN (wirelessmodem) serial interface from non persistent ttyUSB2 to +# a bus like name, e.g. "usb0b1.3p1.3" + +import os + +from vyos.configtree import ConfigTree + +base = ['interfaces', 'wirelessmodem'] + +def migrate(config: ConfigTree) -> None: + if not config.exists(base): + # Nothing to do + return + + for wwan in config.list_nodes(base): + if config.exists(base + [wwan, 'device']): + device = config.return_value(base + [wwan, 'device']) + + for root, dirs, files in os.walk('/dev/serial/by-bus'): + for file in files: + device_file = os.path.realpath(os.path.join(root, file)) + if os.path.basename(device_file) == device: + config.set(base + [wwan, 'device'], value=file, replace=True) diff --git a/src/migration-scripts/interfaces/11-to-12 b/src/migration-scripts/interfaces/11-to-12 new file mode 100644 index 0000000..132cecb --- /dev/null +++ b/src/migration-scripts/interfaces/11-to-12 @@ -0,0 +1,39 @@ +# Copyright 2020-2024 VyOS maintainers and contributors <maintainers@vyos.io> +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library 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 +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this library. If not, see <http://www.gnu.org/licenses/>. + +# - rename 'dhcpv6-options prefix-delegation' from single node to a new tag node +# 'dhcpv6-options pd 0' +# - delete 'sla-len' from CLI - value is calculated on demand + +from vyos.configtree import ConfigTree + +def migrate(config: ConfigTree) -> None: + for type in config.list_nodes(['interfaces']): + for interface in config.list_nodes(['interfaces', type]): + # cache current config tree + base_path = ['interfaces', type, interface, 'dhcpv6-options'] + old_base = base_path + ['prefix-delegation'] + new_base = base_path + ['pd'] + if config.exists(old_base): + config.set(new_base) + config.set_tag(new_base) + config.copy(old_base, new_base + ['0']) + config.delete(old_base) + + for pd in config.list_nodes(new_base): + for tmp in config.list_nodes(new_base + [pd, 'interface']): + sla_config = new_base + [pd, 'interface', tmp, 'sla-len'] + if config.exists(sla_config): + config.delete(sla_config) diff --git a/src/migration-scripts/interfaces/12-to-13 b/src/migration-scripts/interfaces/12-to-13 new file mode 100644 index 0000000..585deb8 --- /dev/null +++ b/src/migration-scripts/interfaces/12-to-13 @@ -0,0 +1,51 @@ +# Copyright 2020-2024 VyOS maintainers and contributors <maintainers@vyos.io> +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library 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 +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this library. If not, see <http://www.gnu.org/licenses/>. + +# - 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 vyos.configtree import ConfigTree + +def migrate(config: ConfigTree) -> None: + # + # 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']): + 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']) + + # + # 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') diff --git a/src/migration-scripts/interfaces/13-to-14 b/src/migration-scripts/interfaces/13-to-14 new file mode 100644 index 0000000..45d8e3b --- /dev/null +++ b/src/migration-scripts/interfaces/13-to-14 @@ -0,0 +1,42 @@ +# Copyright 2020-2024 VyOS maintainers and contributors <maintainers@vyos.io> +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library 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 +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this library. If not, see <http://www.gnu.org/licenses/>. + +# T3043: rename Wireless interface security mode 'both' to 'wpa+wpa2' +# T3043: move "system wifi-regulatory-domain" to indicidual wireless interface + +from vyos.configtree import ConfigTree + +def migrate(config: ConfigTree) -> None: + base = ['interfaces', 'wireless'] + + if not config.exists(base): + # Nothing to do + return + + country_code = '' + cc_cli = ['system', 'wifi-regulatory-domain'] + if config.exists(cc_cli): + country_code = config.return_value(cc_cli) + config.delete(cc_cli) + + for wifi in config.list_nodes(base): + sec_mode = base + [wifi, 'security', 'wpa', 'mode'] + if config.exists(sec_mode): + mode = config.return_value(sec_mode) + if mode == 'both': + config.set(sec_mode, value='wpa+wpa2', replace=True) + + if country_code: + config.set(base + [wifi, 'country-code'], value=country_code) diff --git a/src/migration-scripts/interfaces/14-to-15 b/src/migration-scripts/interfaces/14-to-15 new file mode 100644 index 0000000..d45d59b --- /dev/null +++ b/src/migration-scripts/interfaces/14-to-15 @@ -0,0 +1,38 @@ +# Copyright 2020-2024 VyOS maintainers and contributors <maintainers@vyos.io> +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library 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 +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this library. If not, see <http://www.gnu.org/licenses/>. + +# T3048: remove smp-affinity node from ethernet and use tuned instead + +from vyos.configtree import ConfigTree + +def migrate(config: ConfigTree) -> None: + base = ['interfaces', 'ethernet'] + + if not config.exists(base): + # Nothing to do + return + + migrate = False + for interface in config.list_nodes(base): + smp_base = base + [interface, 'smp-affinity'] + # if any one interface had smp-affinity configured manually, we will + # configure "system option performance" + if config.exists(smp_base): + if config.return_value(smp_base) != 'auto': + migrate = True + config.delete(smp_base) + + if migrate: + config.set(['system', 'options', 'performance'], value='throughput') diff --git a/src/migration-scripts/interfaces/15-to-16 b/src/migration-scripts/interfaces/15-to-16 new file mode 100644 index 0000000..c9abdb5 --- /dev/null +++ b/src/migration-scripts/interfaces/15-to-16 @@ -0,0 +1,30 @@ +# Copyright 2020-2024 VyOS maintainers and contributors <maintainers@vyos.io> +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library 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 +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this library. If not, see <http://www.gnu.org/licenses/>. + +# remove pppoe "ipv6 enable" option + +from vyos.configtree import ConfigTree + +def migrate(config: ConfigTree) -> None: + base = ['interfaces', 'pppoe'] + + if not config.exists(base): + # Nothing to do + return + + for interface in config.list_nodes(base): + ipv6_enable = base + [interface, 'ipv6', 'enable'] + if config.exists(ipv6_enable): + config.delete(ipv6_enable) diff --git a/src/migration-scripts/interfaces/16-to-17 b/src/migration-scripts/interfaces/16-to-17 new file mode 100644 index 0000000..7d241ac --- /dev/null +++ b/src/migration-scripts/interfaces/16-to-17 @@ -0,0 +1,33 @@ +# Copyright 2020-2024 VyOS maintainers and contributors <maintainers@vyos.io> +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library 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 +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this library. If not, see <http://www.gnu.org/licenses/>. + +# Command line migration of port mirroring +# https://vyos.dev/T3089 + +from vyos.configtree import ConfigTree + +def migrate(config: ConfigTree) -> None: + base = ['interfaces', 'ethernet'] + if not config.exists(base): + # Nothing to do + return + + for interface in config.list_nodes(base): + mirror_old_base = base + [interface, 'mirror'] + if config.exists(mirror_old_base): + intf = config.return_values(mirror_old_base) + if config.exists(mirror_old_base): + config.delete(mirror_old_base) + config.set(mirror_old_base + ['ingress'],intf[0]) diff --git a/src/migration-scripts/interfaces/17-to-18 b/src/migration-scripts/interfaces/17-to-18 new file mode 100644 index 0000000..f45695a --- /dev/null +++ b/src/migration-scripts/interfaces/17-to-18 @@ -0,0 +1,52 @@ +# Copyright 2020-2024 VyOS maintainers and contributors <maintainers@vyos.io> +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library 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 +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this library. If not, see <http://www.gnu.org/licenses/>. + +# T3043: Move "system wifi-regulatory-domain" to indicidual wireless interface. +# Country Code will be migratred from upper to lower case. +# T3140: Relax ethernet interface offload-options + +from vyos.configtree import ConfigTree + +def migrate(config: ConfigTree) -> None: + # T3140: Cleanup ethernet offload-options, remove on/off value and use + # valueless nodes instead. + eth_base = ['interfaces', 'ethernet'] + if config.exists(eth_base): + for eth in config.list_nodes(eth_base): + offload = eth_base + [eth, 'offload-options'] + if config.exists(offload): + mapping = { + 'generic-receive' : 'gro', + 'generic-segmentation' : 'gso', + 'scatter-gather' : 'sg', + 'tcp-segmentation' : 'tso', + 'udp-fragmentation' : 'ufo', + } + for k, v in mapping.items(): + if config.exists(offload + [k]): + tmp = config.return_value(offload + [k]) + if tmp == 'on': + config.set(eth_base + [eth, 'offload', v]) + + config.delete(offload) + + # T3043: WIFI country-code should be lower-case + wifi_base = ['interfaces', 'wireless'] + if config.exists(wifi_base): + for wifi in config.list_nodes(wifi_base): + ccode = wifi_base + [wifi, 'country-code'] + if config.exists(ccode): + tmp = config.return_value(ccode) + config.set(ccode, value=tmp.lower(), replace=True) diff --git a/src/migration-scripts/interfaces/18-to-19 b/src/migration-scripts/interfaces/18-to-19 new file mode 100644 index 0000000..ae1a07a --- /dev/null +++ b/src/migration-scripts/interfaces/18-to-19 @@ -0,0 +1,86 @@ +# Copyright 2021-2024 VyOS maintainers and contributors <maintainers@vyos.io> +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library 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 +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this library. If not, see <http://www.gnu.org/licenses/>. + +import os + +from vyos.configtree import ConfigTree + +def replace_nat_interfaces(config, old, new): + if not config.exists(['nat']): + return + for direction in ['destination', 'source']: + conf_direction = ['nat', direction, 'rule'] + if not config.exists(conf_direction): + return + for rule in config.list_nodes(conf_direction): + conf_rule = conf_direction + [rule] + if config.exists(conf_rule + ['inbound-interface']): + tmp = config.return_value(conf_rule + ['inbound-interface']) + if tmp == old: + config.set(conf_rule + ['inbound-interface'], value=new) + if config.exists(conf_rule + ['outbound-interface']): + tmp = config.return_value(conf_rule + ['outbound-interface']) + if tmp == old: + config.set(conf_rule + ['outbound-interface'], value=new) + + +def migrate(config: ConfigTree) -> None: + base = ['interfaces', 'wirelessmodem'] + if not config.exists(base): + # Nothing to do + return + + new_base = ['interfaces', 'wwan'] + config.set(new_base) + config.set_tag(new_base) + for old_interface in config.list_nodes(base): + # convert usb0b1.3p1.2 device identifier and extract 1.3 usb bus id + usb = config.return_value(base + [old_interface, 'device']) + device = usb.split('b')[-1] + busid = device.split('p')[0] + for new_interface in os.listdir('/sys/class/net'): + # we are only interested in interfaces starting with wwan + if not new_interface.startswith('wwan'): + continue + device = os.readlink(f'/sys/class/net/{new_interface}/device') + device = device.split(':')[0] + if busid in device: + config.copy(base + [old_interface], new_base + [new_interface]) + replace_nat_interfaces(config, old_interface, new_interface) + + config.delete(base) + + # Now that we have copied the old wirelessmodem interfaces to wwan + # we can start to migrate also individual config items. + for interface in config.list_nodes(new_base): + # we do no longer need the USB device name + config.delete(new_base + [interface, 'device']) + # set/unset DNS configuration + dns = new_base + [interface, 'no-peer-dns'] + if config.exists(dns): + config.delete(dns) + else: + config.set(['system', 'name-servers-dhcp'], value=interface, replace=False) + + # Backup distance is now handled by DHCP option "default-route-distance" + distance = dns = new_base + [interface, 'backup', 'distance'] + old_default_distance = '10' + if config.exists(distance): + old_default_distance = config.return_value(distance) + config.delete(distance) + config.set(new_base + [interface, 'dhcp-options', 'default-route-distance'], value=old_default_distance) + + # the new wwan interface use regular IP addressing + config.set(new_base + [interface, 'address'], value='dhcp') diff --git a/src/migration-scripts/interfaces/19-to-20 b/src/migration-scripts/interfaces/19-to-20 new file mode 100644 index 0000000..7ee6302 --- /dev/null +++ b/src/migration-scripts/interfaces/19-to-20 @@ -0,0 +1,41 @@ +# Copyright 2021-2024 VyOS maintainers and contributors <maintainers@vyos.io> +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library 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 +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this library. If not, see <http://www.gnu.org/licenses/>. + +from vyos.configtree import ConfigTree + +def migrate(config: ConfigTree) -> None: + for type in ['tunnel', 'l2tpv3']: + base = ['interfaces', type] + if not config.exists(base): + # Nothing to do + continue + + for interface in config.list_nodes(base): + # Migrate "interface tunnel <tunX> encapsulation gre-bridge" to gretap + encap_path = base + [interface, 'encapsulation'] + if type == 'tunnel' and config.exists(encap_path): + tmp = config.return_value(encap_path) + if tmp == 'gre-bridge': + config.set(encap_path, value='gretap') + + # Migrate "interface tunnel|l2tpv3 <interface> local-ip" to source-address + # Migrate "interface tunnel|l2tpv3 <interface> remote-ip" to remote + local_ip_path = base + [interface, 'local-ip'] + if config.exists(local_ip_path): + config.rename(local_ip_path, 'source-address') + + remote_ip_path = base + [interface, 'remote-ip'] + if config.exists(remote_ip_path): + config.rename(remote_ip_path, 'remote') diff --git a/src/migration-scripts/interfaces/2-to-3 b/src/migration-scripts/interfaces/2-to-3 new file mode 100644 index 0000000..695dcbf --- /dev/null +++ b/src/migration-scripts/interfaces/2-to-3 @@ -0,0 +1,40 @@ +# Copyright 2019-2024 VyOS maintainers and contributors <maintainers@vyos.io> +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library 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 +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this library. If not, see <http://www.gnu.org/licenses/>. + +# Change syntax of openvpn encryption settings +# - move cipher from encryption to encryption cipher +# https://vyos.dev/T1704 + +from vyos.configtree import ConfigTree + +base = ['interfaces', 'openvpn'] + +def migrate(config: ConfigTree) -> None: + + if not config.exists(base): + # Nothing to do + return + # + # move cipher from "encryption" to "encryption cipher" + # + for intf in config.list_nodes(['interfaces', 'openvpn']): + # Check if encryption is set + if config.exists(['interfaces', 'openvpn', intf, 'encryption']): + # Get cipher used + cipher = config.return_value(['interfaces', 'openvpn', intf, 'encryption']) + # Delete old syntax + config.delete(['interfaces', 'openvpn', intf, 'encryption']) + # Add new syntax to config + config.set(['interfaces', 'openvpn', intf, 'encryption', 'cipher'], value=cipher) diff --git a/src/migration-scripts/interfaces/20-to-21 b/src/migration-scripts/interfaces/20-to-21 new file mode 100644 index 0000000..0b68951 --- /dev/null +++ b/src/migration-scripts/interfaces/20-to-21 @@ -0,0 +1,107 @@ +# Copyright 2021-2024 VyOS maintainers and contributors <maintainers@vyos.io> +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library 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 +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this library. If not, see <http://www.gnu.org/licenses/>. + +# T3619: mirror Linux Kernel defaults for ethernet offloading options into VyOS +# CLI. See https://vyos.dev/T3619#102254 for all the details. +# T3787: Remove deprecated UDP fragmentation offloading option + +from vyos.ethtool import Ethtool +from vyos.configtree import ConfigTree +from vyos.utils.network import interface_exists + +base = ['interfaces', 'ethernet'] + +def migrate(config: ConfigTree) -> None: + if not config.exists(base): + return + + for ifname in config.list_nodes(base): + # Bail out early if interface vanished from system + if not interface_exists(ifname): + continue + + eth = Ethtool(ifname) + + # If GRO is enabled by the Kernel - we reflect this on the CLI. If GRO is + # enabled via CLI but not supported by the NIC - we remove it from the CLI + configured = config.exists(base + [ifname, 'offload', 'gro']) + enabled, fixed = eth.get_generic_receive_offload() + if configured and fixed: + config.delete(base + [ifname, 'offload', 'gro']) + elif enabled and not fixed: + config.set(base + [ifname, 'offload', 'gro']) + + # If GSO is enabled by the Kernel - we reflect this on the CLI. If GSO is + # enabled via CLI but not supported by the NIC - we remove it from the CLI + configured = config.exists(base + [ifname, 'offload', 'gso']) + enabled, fixed = eth.get_generic_segmentation_offload() + if configured and fixed: + config.delete(base + [ifname, 'offload', 'gso']) + elif enabled and not fixed: + config.set(base + [ifname, 'offload', 'gso']) + + # If LRO is enabled by the Kernel - we reflect this on the CLI. If LRO is + # enabled via CLI but not supported by the NIC - we remove it from the CLI + configured = config.exists(base + [ifname, 'offload', 'lro']) + enabled, fixed = eth.get_large_receive_offload() + if configured and fixed: + config.delete(base + [ifname, 'offload', 'lro']) + elif enabled and not fixed: + config.set(base + [ifname, 'offload', 'lro']) + + # If SG is enabled by the Kernel - we reflect this on the CLI. If SG is + # enabled via CLI but not supported by the NIC - we remove it from the CLI + configured = config.exists(base + [ifname, 'offload', 'sg']) + enabled, fixed = eth.get_scatter_gather() + if configured and fixed: + config.delete(base + [ifname, 'offload', 'sg']) + elif enabled and not fixed: + config.set(base + [ifname, 'offload', 'sg']) + + # If TSO is enabled by the Kernel - we reflect this on the CLI. If TSO is + # enabled via CLI but not supported by the NIC - we remove it from the CLI + configured = config.exists(base + [ifname, 'offload', 'tso']) + enabled, fixed = eth.get_tcp_segmentation_offload() + if configured and fixed: + config.delete(base + [ifname, 'offload', 'tso']) + elif enabled and not fixed: + config.set(base + [ifname, 'offload', 'tso']) + + # Remove deprecated UDP fragmentation offloading option + if config.exists(base + [ifname, 'offload', 'ufo']): + config.delete(base + [ifname, 'offload', 'ufo']) + + # Also while processing the interface configuration, not all adapters support + # changing the speed and duplex settings. If the desired speed and duplex + # values do not work for the NIC driver, we change them back to the default + # value of "auto" - which will be applied if the CLI node is deleted. + speed_path = base + [ifname, 'speed'] + duplex_path = base + [ifname, 'duplex'] + # speed and duplex must always be set at the same time if not set to "auto" + if config.exists(speed_path) and config.exists(duplex_path): + speed = config.return_value(speed_path) + duplex = config.return_value(duplex_path) + if speed != 'auto' and duplex != 'auto': + if not eth.check_speed_duplex(speed, duplex): + config.delete(speed_path) + config.delete(duplex_path) + + # Also while processing the interface configuration, not all adapters support + # changing disabling flow-control - or change this setting. If disabling + # flow-control is not supported by the NIC, we remove the setting from CLI + flow_control_path = base + [ifname, 'disable-flow-control'] + if config.exists(flow_control_path): + if not eth.check_flow_control(): + config.delete(flow_control_path) diff --git a/src/migration-scripts/interfaces/21-to-22 b/src/migration-scripts/interfaces/21-to-22 new file mode 100644 index 0000000..046eb10 --- /dev/null +++ b/src/migration-scripts/interfaces/21-to-22 @@ -0,0 +1,29 @@ +# Copyright 2021-2024 VyOS maintainers and contributors <maintainers@vyos.io> +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library 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 +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this library. If not, see <http://www.gnu.org/licenses/>. + +from vyos.configtree import ConfigTree + +def migrate(config: ConfigTree) -> None: + base = ['interfaces', 'tunnel'] + + if not config.exists(base): + return + + for interface in config.list_nodes(base): + path = base + [interface, 'dhcp-interface'] + if config.exists(path): + tmp = config.return_value(path) + config.delete(path) + config.set(base + [interface, 'source-interface'], value=tmp) diff --git a/src/migration-scripts/interfaces/22-to-23 b/src/migration-scripts/interfaces/22-to-23 new file mode 100644 index 0000000..31f7fa2 --- /dev/null +++ b/src/migration-scripts/interfaces/22-to-23 @@ -0,0 +1,40 @@ +# Copyright 2021-2024 VyOS maintainers and contributors <maintainers@vyos.io> +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library 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 +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this library. If not, see <http://www.gnu.org/licenses/>. + +# Deletes Wireguard peers if they have the same public key as the router has. + +from vyos.configtree import ConfigTree +from vyos.utils.network import is_wireguard_key_pair + +def migrate(config: ConfigTree) -> None: + base = ['interfaces', 'wireguard'] + if not config.exists(base): + # Nothing to do + return + + for interface in config.list_nodes(base): + if not config.exists(base + [interface, 'private-key']): + continue + private_key = config.return_value(base + [interface, 'private-key']) + interface_base = base + [interface] + if config.exists(interface_base + ['peer']): + for peer in config.list_nodes(interface_base + ['peer']): + peer_base = interface_base + ['peer', peer] + if not config.exists(peer_base + ['public-key']): + continue + peer_public_key = config.return_value(peer_base + ['public-key']) + if not config.exists(peer_base + ['disable']) \ + and is_wireguard_key_pair(private_key, peer_public_key): + config.set(peer_base + ['disable']) diff --git a/src/migration-scripts/interfaces/23-to-24 b/src/migration-scripts/interfaces/23-to-24 new file mode 100644 index 0000000..b72ceee --- /dev/null +++ b/src/migration-scripts/interfaces/23-to-24 @@ -0,0 +1,125 @@ +# Copyright 2021-2024 VyOS maintainers and contributors <maintainers@vyos.io> +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library 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 +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this library. If not, see <http://www.gnu.org/licenses/>. + +from vyos.configtree import ConfigTree + +def migrate_ospf(config, path, interface): + path = path + ['ospf'] + if config.exists(path): + new_base = ['protocols', 'ospf', 'interface'] + config.set(new_base) + config.set_tag(new_base) + config.copy(path, new_base + [interface]) + config.delete(path) + + # if "ip ospf" was the only setting, we can clean out the empty + # ip node afterwards + if len(config.list_nodes(path[:-1])) == 0: + config.delete(path[:-1]) + +def migrate_ospfv3(config, path, interface): + path = path + ['ospfv3'] + if config.exists(path): + new_base = ['protocols', 'ospfv3', 'interface'] + config.set(new_base) + config.set_tag(new_base) + config.copy(path, new_base + [interface]) + config.delete(path) + + # if "ipv6 ospfv3" was the only setting, we can clean out the empty + # ip node afterwards + if len(config.list_nodes(path[:-1])) == 0: + config.delete(path[:-1]) + +def migrate_rip(config, path, interface): + path = path + ['rip'] + if config.exists(path): + new_base = ['protocols', 'rip', 'interface'] + config.set(new_base) + config.set_tag(new_base) + config.copy(path, new_base + [interface]) + config.delete(path) + + # if "ip rip" was the only setting, we can clean out the empty + # ip node afterwards + if len(config.list_nodes(path[:-1])) == 0: + config.delete(path[:-1]) + +def migrate_ripng(config, path, interface): + path = path + ['ripng'] + if config.exists(path): + new_base = ['protocols', 'ripng', 'interface'] + config.set(new_base) + config.set_tag(new_base) + config.copy(path, new_base + [interface]) + config.delete(path) + + # if "ipv6 ripng" was the only setting, we can clean out the empty + # ip node afterwards + if len(config.list_nodes(path[:-1])) == 0: + config.delete(path[:-1]) + +def migrate(config: ConfigTree) -> None: + # + # Migrate "interface ethernet eth0 ip ospf" to "protocols ospf interface eth0" + # + for type in config.list_nodes(['interfaces']): + for interface in config.list_nodes(['interfaces', type]): + ip_base = ['interfaces', type, interface, 'ip'] + ipv6_base = ['interfaces', type, interface, 'ipv6'] + migrate_rip(config, ip_base, interface) + migrate_ripng(config, ipv6_base, interface) + migrate_ospf(config, ip_base, interface) + migrate_ospfv3(config, ipv6_base, interface) + + vif_path = ['interfaces', type, interface, 'vif'] + if config.exists(vif_path): + for vif in config.list_nodes(vif_path): + vif_ip_base = vif_path + [vif, 'ip'] + vif_ipv6_base = vif_path + [vif, 'ipv6'] + ifname = f'{interface}.{vif}' + + migrate_rip(config, vif_ip_base, ifname) + migrate_ripng(config, vif_ipv6_base, ifname) + migrate_ospf(config, vif_ip_base, ifname) + migrate_ospfv3(config, vif_ipv6_base, ifname) + + + vif_s_path = ['interfaces', type, interface, 'vif-s'] + if config.exists(vif_s_path): + for vif_s in config.list_nodes(vif_s_path): + vif_s_ip_base = vif_s_path + [vif_s, 'ip'] + vif_s_ipv6_base = vif_s_path + [vif_s, 'ipv6'] + + # vif-c interfaces MUST be migrated before their parent vif-s + # interface as the migrate_*() functions delete the path! + vif_c_path = ['interfaces', type, interface, 'vif-s', vif_s, 'vif-c'] + if config.exists(vif_c_path): + for vif_c in config.list_nodes(vif_c_path): + vif_c_ip_base = vif_c_path + [vif_c, 'ip'] + vif_c_ipv6_base = vif_c_path + [vif_c, 'ipv6'] + ifname = f'{interface}.{vif_s}.{vif_c}' + + migrate_rip(config, vif_c_ip_base, ifname) + migrate_ripng(config, vif_c_ipv6_base, ifname) + migrate_ospf(config, vif_c_ip_base, ifname) + migrate_ospfv3(config, vif_c_ipv6_base, ifname) + + + ifname = f'{interface}.{vif_s}' + migrate_rip(config, vif_s_ip_base, ifname) + migrate_ripng(config, vif_s_ipv6_base, ifname) + migrate_ospf(config, vif_s_ip_base, ifname) + migrate_ospfv3(config, vif_s_ipv6_base, ifname) diff --git a/src/migration-scripts/interfaces/24-to-25 b/src/migration-scripts/interfaces/24-to-25 new file mode 100644 index 0000000..9f8cc80 --- /dev/null +++ b/src/migration-scripts/interfaces/24-to-25 @@ -0,0 +1,41 @@ +# Copyright 2021-2024 VyOS maintainers and contributors <maintainers@vyos.io> +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library 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 +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this library. If not, see <http://www.gnu.org/licenses/>. + +# A VTI interface also requires an IPSec configuration - VyOS 1.2 supported +# having a VTI interface in the CLI but no IPSec configuration - drop VTI +# configuration if this is the case for VyOS 1.4 + +from vyos.configtree import ConfigTree + +def migrate(config: ConfigTree) -> None: + base = ['interfaces', 'vti'] + if not config.exists(base): + # Nothing to do + return + + ipsec_base = ['vpn', 'ipsec', 'site-to-site', 'peer'] + for interface in config.list_nodes(base): + found = False + if config.exists(ipsec_base): + for peer in config.list_nodes(ipsec_base): + if config.exists(ipsec_base + [peer, 'vti', 'bind']): + tmp = config.return_value(ipsec_base + [peer, 'vti', 'bind']) + if tmp == interface: + # Interface was found and we no longer need to search + # for it in our IPSec peers + found = True + break + if not found: + config.delete(base + [interface]) diff --git a/src/migration-scripts/interfaces/25-to-26 b/src/migration-scripts/interfaces/25-to-26 new file mode 100644 index 0000000..7a4032d --- /dev/null +++ b/src/migration-scripts/interfaces/25-to-26 @@ -0,0 +1,368 @@ +# Copyright 2021-2024 VyOS maintainers and contributors <maintainers@vyos.io> +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library 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 +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this library. If not, see <http://www.gnu.org/licenses/>. + +# Migrate Wireguard to store keys in CLI +# Migrate EAPoL to PKI configuration + +import os + +from vyos.configtree import ConfigTree +from vyos.pki import CERT_BEGIN +from vyos.pki import load_certificate +from vyos.pki import load_crl +from vyos.pki import load_dh_parameters +from vyos.pki import load_private_key +from vyos.pki import encode_certificate +from vyos.pki import encode_dh_parameters +from vyos.pki import encode_private_key +from vyos.pki import verify_crl +from vyos.utils.process import run + +def wrapped_pem_to_config_value(pem): + out = [] + for line in pem.strip().split("\n"): + if not line or line.startswith("-----") or line[0] == '#': + continue + out.append(line) + return "".join(out) + +def read_file_for_pki(config_auth_path): + full_path = os.path.join(AUTH_DIR, config_auth_path) + output = None + + if os.path.isfile(full_path): + if not os.access(full_path, os.R_OK): + run(f'sudo chmod 644 {full_path}') + + with open(full_path, 'r') as f: + output = f.read() + + return output + +AUTH_DIR = '/config/auth' +pki_base = ['pki'] + +def migrate(config: ConfigTree) -> None: + # OpenVPN + base = ['interfaces', 'openvpn'] + + if config.exists(base): + for interface in config.list_nodes(base): + x509_base = base + [interface, 'tls'] + pki_name = f'openvpn_{interface}' + + if config.exists(base + [interface, 'shared-secret-key-file']): + if not config.exists(pki_base + ['openvpn', 'shared-secret']): + config.set(pki_base + ['openvpn', 'shared-secret']) + config.set_tag(pki_base + ['openvpn', 'shared-secret']) + + key_file = config.return_value(base + [interface, 'shared-secret-key-file']) + key = read_file_for_pki(key_file) + key_pki_name = f'{pki_name}_shared' + + if key: + config.set(pki_base + ['openvpn', 'shared-secret', key_pki_name, 'key'], value=wrapped_pem_to_config_value(key)) + config.set(pki_base + ['openvpn', 'shared-secret', key_pki_name, 'version'], value='1') + config.set(base + [interface, 'shared-secret-key'], value=key_pki_name) + else: + print(f'Failed to migrate shared-secret-key on openvpn interface {interface}') + + config.delete(base + [interface, 'shared-secret-key-file']) + + if not config.exists(base + [interface, 'tls']): + continue + + if config.exists(base + [interface, 'tls', 'auth-file']): + if not config.exists(pki_base + ['openvpn', 'shared-secret']): + config.set(pki_base + ['openvpn', 'shared-secret']) + config.set_tag(pki_base + ['openvpn', 'shared-secret']) + + key_file = config.return_value(base + [interface, 'tls', 'auth-file']) + key = read_file_for_pki(key_file) + key_pki_name = f'{pki_name}_auth' + + if key: + config.set(pki_base + ['openvpn', 'shared-secret', key_pki_name, 'key'], value=wrapped_pem_to_config_value(key)) + config.set(pki_base + ['openvpn', 'shared-secret', key_pki_name, 'version'], value='1') + config.set(base + [interface, 'tls', 'auth-key'], value=key_pki_name) + else: + print(f'Failed to migrate auth-key on openvpn interface {interface}') + + config.delete(base + [interface, 'tls', 'auth-file']) + + if config.exists(base + [interface, 'tls', 'crypt-file']): + if not config.exists(pki_base + ['openvpn', 'shared-secret']): + config.set(pki_base + ['openvpn', 'shared-secret']) + config.set_tag(pki_base + ['openvpn', 'shared-secret']) + + key_file = config.return_value(base + [interface, 'tls', 'crypt-file']) + key = read_file_for_pki(key_file) + key_pki_name = f'{pki_name}_crypt' + + if key: + config.set(pki_base + ['openvpn', 'shared-secret', key_pki_name, 'key'], value=wrapped_pem_to_config_value(key)) + config.set(pki_base + ['openvpn', 'shared-secret', key_pki_name, 'version'], value='1') + config.set(base + [interface, 'tls', 'crypt-key'], value=key_pki_name) + else: + print(f'Failed to migrate crypt-key on openvpn interface {interface}') + + config.delete(base + [interface, 'tls', 'crypt-file']) + + ca_certs = {} + + if config.exists(x509_base + ['ca-cert-file']): + if not config.exists(pki_base + ['ca']): + config.set(pki_base + ['ca']) + config.set_tag(pki_base + ['ca']) + + cert_file = config.return_value(x509_base + ['ca-cert-file']) + cert_path = os.path.join(AUTH_DIR, cert_file) + + if os.path.isfile(cert_path): + if not os.access(cert_path, os.R_OK): + run(f'sudo chmod 644 {cert_path}') + + with open(cert_path, 'r') as f: + certs_str = f.read() + certs_data = certs_str.split(CERT_BEGIN) + index = 1 + for cert_data in certs_data[1:]: + cert = load_certificate(CERT_BEGIN + cert_data, wrap_tags=False) + + if cert: + ca_certs[f'{pki_name}_{index}'] = cert + cert_pem = encode_certificate(cert) + config.set(pki_base + ['ca', f'{pki_name}_{index}', 'certificate'], value=wrapped_pem_to_config_value(cert_pem)) + config.set(x509_base + ['ca-certificate'], value=f'{pki_name}_{index}', replace=False) + else: + print(f'Failed to migrate CA certificate on openvpn interface {interface}') + + index += 1 + else: + print(f'Failed to migrate CA certificate on openvpn interface {interface}') + + config.delete(x509_base + ['ca-cert-file']) + + if config.exists(x509_base + ['crl-file']): + if not config.exists(pki_base + ['ca']): + config.set(pki_base + ['ca']) + config.set_tag(pki_base + ['ca']) + + crl_file = config.return_value(x509_base + ['crl-file']) + crl_path = os.path.join(AUTH_DIR, crl_file) + crl = None + crl_ca_name = None + + if os.path.isfile(crl_path): + if not os.access(crl_path, os.R_OK): + run(f'sudo chmod 644 {crl_path}') + + with open(crl_path, 'r') as f: + crl_data = f.read() + crl = load_crl(crl_data, wrap_tags=False) + + for ca_name, ca_cert in ca_certs.items(): + if verify_crl(crl, ca_cert): + crl_ca_name = ca_name + break + + if crl and crl_ca_name: + crl_pem = encode_certificate(crl) + config.set(pki_base + ['ca', crl_ca_name, 'crl'], value=wrapped_pem_to_config_value(crl_pem)) + else: + print(f'Failed to migrate CRL on openvpn interface {interface}') + + config.delete(x509_base + ['crl-file']) + + if config.exists(x509_base + ['cert-file']): + if not config.exists(pki_base + ['certificate']): + config.set(pki_base + ['certificate']) + config.set_tag(pki_base + ['certificate']) + + cert_file = config.return_value(x509_base + ['cert-file']) + cert_path = os.path.join(AUTH_DIR, cert_file) + cert = None + + if os.path.isfile(cert_path): + if not os.access(cert_path, os.R_OK): + run(f'sudo chmod 644 {cert_path}') + + with open(cert_path, 'r') as f: + cert_data = f.read() + cert = load_certificate(cert_data, wrap_tags=False) + + if cert: + cert_pem = encode_certificate(cert) + config.set(pki_base + ['certificate', pki_name, 'certificate'], value=wrapped_pem_to_config_value(cert_pem)) + config.set(x509_base + ['certificate'], value=pki_name) + else: + print(f'Failed to migrate certificate on openvpn interface {interface}') + + config.delete(x509_base + ['cert-file']) + + if config.exists(x509_base + ['key-file']): + key_file = config.return_value(x509_base + ['key-file']) + key_path = os.path.join(AUTH_DIR, key_file) + key = None + + if os.path.isfile(key_path): + if not os.access(key_path, os.R_OK): + run(f'sudo chmod 644 {key_path}') + + with open(key_path, 'r') as f: + key_data = f.read() + key = load_private_key(key_data, passphrase=None, wrap_tags=False) + + if key: + key_pem = encode_private_key(key, passphrase=None) + config.set(pki_base + ['certificate', pki_name, 'private', 'key'], value=wrapped_pem_to_config_value(key_pem)) + else: + print(f'Failed to migrate private key on openvpn interface {interface}') + + config.delete(x509_base + ['key-file']) + + if config.exists(x509_base + ['dh-file']): + if not config.exists(pki_base + ['dh']): + config.set(pki_base + ['dh']) + config.set_tag(pki_base + ['dh']) + + dh_file = config.return_value(x509_base + ['dh-file']) + dh_path = os.path.join(AUTH_DIR, dh_file) + dh = None + + if os.path.isfile(dh_path): + if not os.access(dh_path, os.R_OK): + run(f'sudo chmod 644 {dh_path}') + + with open(dh_path, 'r') as f: + dh_data = f.read() + dh = load_dh_parameters(dh_data, wrap_tags=False) + + if dh: + dh_pem = encode_dh_parameters(dh) + config.set(pki_base + ['dh', pki_name, 'parameters'], value=wrapped_pem_to_config_value(dh_pem)) + config.set(x509_base + ['dh-params'], value=pki_name) + else: + print(f'Failed to migrate DH parameters on openvpn interface {interface}') + + config.delete(x509_base + ['dh-file']) + + # Wireguard + base = ['interfaces', 'wireguard'] + + if config.exists(base): + for interface in config.list_nodes(base): + private_key_path = base + [interface, 'private-key'] + + key_file = 'default' + if config.exists(private_key_path): + key_file = config.return_value(private_key_path) + + full_key_path = f'/config/auth/wireguard/{key_file}/private.key' + + if not os.path.exists(full_key_path): + print(f'Could not find wireguard private key for migration on interface "{interface}"') + continue + + with open(full_key_path, 'r') as f: + key_data = f.read().strip() + config.set(private_key_path, value=key_data) + + for peer in config.list_nodes(base + [interface, 'peer']): + config.rename(base + [interface, 'peer', peer, 'pubkey'], 'public-key') + + # Ethernet EAPoL + base = ['interfaces', 'ethernet'] + + if config.exists(base): + for interface in config.list_nodes(base): + if not config.exists(base + [interface, 'eapol']): + continue + + x509_base = base + [interface, 'eapol'] + pki_name = f'eapol_{interface}' + + if config.exists(x509_base + ['ca-cert-file']): + if not config.exists(pki_base + ['ca']): + config.set(pki_base + ['ca']) + config.set_tag(pki_base + ['ca']) + + cert_file = config.return_value(x509_base + ['ca-cert-file']) + cert_path = os.path.join(AUTH_DIR, cert_file) + cert = None + + if os.path.isfile(cert_path): + if not os.access(cert_path, os.R_OK): + run(f'sudo chmod 644 {cert_path}') + + with open(cert_path, 'r') as f: + cert_data = f.read() + cert = load_certificate(cert_data, wrap_tags=False) + + if cert: + cert_pem = encode_certificate(cert) + config.set(pki_base + ['ca', pki_name, 'certificate'], value=wrapped_pem_to_config_value(cert_pem)) + config.set(x509_base + ['ca-certificate'], value=pki_name) + else: + print(f'Failed to migrate CA certificate on eapol config for interface {interface}') + + config.delete(x509_base + ['ca-cert-file']) + + if config.exists(x509_base + ['cert-file']): + if not config.exists(pki_base + ['certificate']): + config.set(pki_base + ['certificate']) + config.set_tag(pki_base + ['certificate']) + + cert_file = config.return_value(x509_base + ['cert-file']) + cert_path = os.path.join(AUTH_DIR, cert_file) + cert = None + + if os.path.isfile(cert_path): + if not os.access(cert_path, os.R_OK): + run(f'sudo chmod 644 {cert_path}') + + with open(cert_path, 'r') as f: + cert_data = f.read() + cert = load_certificate(cert_data, wrap_tags=False) + + if cert: + cert_pem = encode_certificate(cert) + config.set(pki_base + ['certificate', pki_name, 'certificate'], value=wrapped_pem_to_config_value(cert_pem)) + config.set(x509_base + ['certificate'], value=pki_name) + else: + print(f'Failed to migrate certificate on eapol config for interface {interface}') + + config.delete(x509_base + ['cert-file']) + + if config.exists(x509_base + ['key-file']): + key_file = config.return_value(x509_base + ['key-file']) + key_path = os.path.join(AUTH_DIR, key_file) + key = None + + if os.path.isfile(key_path): + if not os.access(key_path, os.R_OK): + run(f'sudo chmod 644 {key_path}') + + with open(key_path, 'r') as f: + key_data = f.read() + key = load_private_key(key_data, passphrase=None, wrap_tags=False) + + if key: + key_pem = encode_private_key(key, passphrase=None) + config.set(pki_base + ['certificate', pki_name, 'private', 'key'], value=wrapped_pem_to_config_value(key_pem)) + else: + print(f'Failed to migrate private key on eapol config for interface {interface}') + + config.delete(x509_base + ['key-file']) diff --git a/src/migration-scripts/interfaces/26-to-27 b/src/migration-scripts/interfaces/26-to-27 new file mode 100644 index 0000000..3f58de0 --- /dev/null +++ b/src/migration-scripts/interfaces/26-to-27 @@ -0,0 +1,35 @@ +# Copyright 2022-2024 VyOS maintainers and contributors <maintainers@vyos.io> +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library 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 +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this library. If not, see <http://www.gnu.org/licenses/>. + +# T4384: pppoe: replace default-route CLI option with common CLI nodes already +# present for DHCP + +from vyos.configtree import ConfigTree + +def migrate(config: ConfigTree) -> None: + base = ['interfaces', 'pppoe'] + + if not config.exists(base): + return + + for ifname in config.list_nodes(base): + tmp_config = base + [ifname, 'default-route'] + if config.exists(tmp_config): + # Retrieve current config value + value = config.return_value(tmp_config) + # Delete old Config node + config.delete(tmp_config) + if value == 'none': + config.set(base + [ifname, 'no-default-route']) diff --git a/src/migration-scripts/interfaces/27-to-28 b/src/migration-scripts/interfaces/27-to-28 new file mode 100644 index 0000000..eb9363e --- /dev/null +++ b/src/migration-scripts/interfaces/27-to-28 @@ -0,0 +1,29 @@ +# Copyright 2023-2024 VyOS maintainers and contributors <maintainers@vyos.io> +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library 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 +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this library. If not, see <http://www.gnu.org/licenses/>. + +# T4995: pppoe, wwan, sstpc-client rename "authentication user" CLI node +# to "authentication username" + +from vyos.configtree import ConfigTree + +def migrate(config: ConfigTree) -> None: + for type in ['pppoe', 'sstpc-client', 'wwam']: + base = ['interfaces', type] + if not config.exists(base): + continue + for interface in config.list_nodes(base): + auth_base = base + [interface, 'authentication', 'user'] + if config.exists(auth_base): + config.rename(auth_base, 'username') diff --git a/src/migration-scripts/interfaces/28-to-29 b/src/migration-scripts/interfaces/28-to-29 new file mode 100644 index 0000000..886d49e --- /dev/null +++ b/src/migration-scripts/interfaces/28-to-29 @@ -0,0 +1,35 @@ +# Copyright 2023-2024 VyOS maintainers and contributors <maintainers@vyos.io> +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library 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 +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this library. If not, see <http://www.gnu.org/licenses/>. + +# T5034: tunnel: rename "multicast enable" CLI node to "enable-multicast" +# valueless node. + +from vyos.configtree import ConfigTree + +base = ['interfaces', 'tunnel'] + +def migrate(config: ConfigTree) -> None: + if not config.exists(base): + return + + for ifname in config.list_nodes(base): + multicast_base = base + [ifname, 'multicast'] + if config.exists(multicast_base): + tmp = config.return_value(multicast_base) + print(tmp) + # Delete old Config node + config.delete(multicast_base) + if tmp == 'enable': + config.set(base + [ifname, 'enable-multicast']) diff --git a/src/migration-scripts/interfaces/29-to-30 b/src/migration-scripts/interfaces/29-to-30 new file mode 100644 index 0000000..7b32d87 --- /dev/null +++ b/src/migration-scripts/interfaces/29-to-30 @@ -0,0 +1,30 @@ +# Copyright 2023-2024 VyOS maintainers and contributors <maintainers@vyos.io> +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library 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 +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this library. If not, see <http://www.gnu.org/licenses/>. + +# T5286: remove XDP support in favour of VPP + +from vyos.configtree import ConfigTree + +supports_xdp = ['bonding', 'ethernet'] + +def migrate(config: ConfigTree) -> None: + for if_type in supports_xdp: + base = ['interfaces', if_type] + if not config.exists(base): + continue + for interface in config.list_nodes(base): + if_base = base + [interface] + if config.exists(if_base + ['xdp']): + config.delete(if_base + ['xdp']) diff --git a/src/migration-scripts/interfaces/3-to-4 b/src/migration-scripts/interfaces/3-to-4 new file mode 100644 index 0000000..4e56200 --- /dev/null +++ b/src/migration-scripts/interfaces/3-to-4 @@ -0,0 +1,93 @@ +# Copyright 2019-2024 VyOS maintainers and contributors <maintainers@vyos.io> +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library 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 +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this library. If not, see <http://www.gnu.org/licenses/>. + +# Change syntax of wireless interfaces +# Migrate boolean nodes to valueless + +from vyos.configtree import ConfigTree + +base = ['interfaces', 'wireless'] + +def migrate(config: ConfigTree) -> None: + if not config.exists(base): + # Nothing to do + return + + for wifi in config.list_nodes(base): + # as converting a node to bool is always the same, we can script it + to_bool_nodes = ['capabilities ht 40MHz-incapable', + 'capabilities ht auto-powersave', + 'capabilities ht delayed-block-ack', + 'capabilities ht dsss-cck-40', + 'capabilities ht greenfield', + 'capabilities ht ldpc', + 'capabilities ht lsig-protection', + 'capabilities ht stbc tx', + 'capabilities require-ht', + 'capabilities require-vht', + 'capabilities vht antenna-pattern-fixed', + 'capabilities vht ldpc', + 'capabilities vht stbc tx', + 'capabilities vht tx-powersave', + 'capabilities vht vht-cf', + 'expunge-failing-stations', + 'isolate-stations'] + + for node in to_bool_nodes: + if config.exists(base + [wifi, node]): + tmp = config.return_value(base + [wifi, node]) + # delete old node + config.delete(base + [wifi, node]) + # set new node if it was enabled + if tmp == 'true': + # OLD CLI used camel casing in 40MHz-incapable which is + # not supported in the new backend. Convert all to lower-case + config.set(base + [wifi, node.lower()]) + + # Remove debug node + if config.exists(base + [wifi, 'debug']): + config.delete(base + [wifi, 'debug']) + + # RADIUS servers + if config.exists(base + [wifi, 'security', 'wpa', 'radius-server']): + for server in config.list_nodes(base + [wifi, 'security', 'wpa', 'radius-server']): + base_server = base + [wifi, 'security', 'wpa', 'radius-server', server] + + # Migrate RADIUS shared secret + if config.exists(base_server + ['secret']): + key = config.return_value(base_server + ['secret']) + # write new configuration node + config.set(base + [wifi, 'security', 'wpa', 'radius', 'server', server, 'key'], value=key) + # format as tag node + config.set_tag(base + [wifi, 'security', 'wpa', 'radius', 'server']) + + # Migrate RADIUS port + if config.exists(base_server + ['port']): + port = config.return_value(base_server + ['port']) + # write new configuration node + config.set(base + [wifi, 'security', 'wpa', 'radius', 'server', server, 'port'], value=port) + # format as tag node + config.set_tag(base + [wifi, 'security', 'wpa', 'radius', 'server']) + + # Migrate RADIUS accounting + if config.exists(base_server + ['accounting']): + port = config.return_value(base_server + ['accounting']) + # write new configuration node + config.set(base + [wifi, 'security', 'wpa', 'radius', 'server', server, 'accounting']) + # format as tag node + config.set_tag(base + [wifi, 'security', 'wpa', 'radius', 'server']) + + # delete old radius-server nodes + config.delete(base + [wifi, 'security', 'wpa', 'radius-server']) diff --git a/src/migration-scripts/interfaces/30-to-31 b/src/migration-scripts/interfaces/30-to-31 new file mode 100644 index 0000000..7e509dd --- /dev/null +++ b/src/migration-scripts/interfaces/30-to-31 @@ -0,0 +1,56 @@ +#!/usr/bin/env python3 +# Copyright 2023-2024 VyOS maintainers and contributors <maintainers@vyos.io> +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library 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 +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this library. If not, see <http://www.gnu.org/licenses/>. + +# T5254: Fixed changing ethernet when it is a bond member + +import json +from vyos.configtree import ConfigTree +from vyos.ifconfig import EthernetIf +from vyos.ifconfig import BondIf +from vyos.utils.dict import dict_to_paths_values + +base = ['interfaces', 'bonding'] + +def migrate(config: ConfigTree) -> None: + if not config.exists(base): + # Nothing to do + return + + for bond in config.list_nodes(base): + member_base = base + [bond, 'member', 'interface'] + if config.exists(member_base): + for interface in config.return_values(member_base): + if_base = ['interfaces', 'ethernet', interface] + if config.exists(if_base): + config_ethernet = json.loads(config.get_subtree(if_base).to_json()) + eth_dict_paths = dict_to_paths_values(config_ethernet) + for option_path, option_value in eth_dict_paths.items(): + # If option is allowed for changing then continue + converted_path = option_path.replace('-','_') + if converted_path in EthernetIf.get_bond_member_allowed_options(): + continue + # if option is inherited from bond then continue + if converted_path in BondIf.get_inherit_bond_options(): + continue + option_path_list = option_path.split('.') + config.delete(if_base + option_path_list) + del option_path_list[-1] + # delete empty node from config + while len(option_path_list) > 0: + if config.list_nodes(if_base + option_path_list): + break + config.delete(if_base + option_path_list) + del option_path_list[-1] diff --git a/src/migration-scripts/interfaces/31-to-32 b/src/migration-scripts/interfaces/31-to-32 new file mode 100644 index 0000000..24077ed --- /dev/null +++ b/src/migration-scripts/interfaces/31-to-32 @@ -0,0 +1,37 @@ +# Copyright 2023-2024 VyOS maintainers and contributors <maintainers@vyos.io> +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library 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 +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this library. If not, see <http://www.gnu.org/licenses/>. + +# T5671: change port to IANA assigned default port +# T5759: change default MTU 1450 -> 1500 + +from vyos.configtree import ConfigTree + +base = ['interfaces', 'vxlan'] + +def migrate(config: ConfigTree) -> None: + if not config.exists(base): + # Nothing to do + return + + for vxlan in config.list_nodes(base): + if config.exists(base + [vxlan, 'external']): + config.delete(base + [vxlan, 'external']) + config.set(base + [vxlan, 'parameters', 'external']) + + if not config.exists(base + [vxlan, 'port']): + config.set(base + [vxlan, 'port'], value='8472') + + if not config.exists(base + [vxlan, 'mtu']): + config.set(base + [vxlan, 'mtu'], value='1450') diff --git a/src/migration-scripts/interfaces/32-to-33 b/src/migration-scripts/interfaces/32-to-33 new file mode 100644 index 0000000..c7b1c5b --- /dev/null +++ b/src/migration-scripts/interfaces/32-to-33 @@ -0,0 +1,40 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2024 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 <http://www.gnu.org/licenses/>. +# +# T6318: WiFi country-code should be set system-wide instead of per-device + +from vyos.configtree import ConfigTree + +base = ['interfaces', 'wireless'] + +def migrate(config: ConfigTree) -> None: + if not config.exists(base): + # Nothing to do + return + + installed = False + for interface in config.list_nodes(base): + cc_path = base + [interface, 'country-code'] + if config.exists(cc_path): + tmp = config.return_value(cc_path) + config.delete(cc_path) + + # There can be only ONE wireless country-code per device, everything + # else makes no sense as a WIFI router can not operate in two + # different countries + if not installed: + config.set(['system', 'wireless', 'country-code'], value=tmp) + installed = True diff --git a/src/migration-scripts/interfaces/4-to-5 b/src/migration-scripts/interfaces/4-to-5 new file mode 100644 index 0000000..93fa7c3 --- /dev/null +++ b/src/migration-scripts/interfaces/4-to-5 @@ -0,0 +1,106 @@ +# Copyright 2019-2024 VyOS maintainers and contributors <maintainers@vyos.io> +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library 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 +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this library. If not, see <http://www.gnu.org/licenses/>. + +# De-nest PPPoE interfaces +# Migrate boolean nodes to valueless + +from vyos.configtree import ConfigTree + +def migrate_dialer(config, tree, intf): + for pppoe in config.list_nodes(tree): + # assemble string, 0 -> pppoe0 + new_base = ['interfaces', 'pppoe'] + pppoe_base = new_base + ['pppoe' + pppoe] + config.set(new_base) + # format as tag node to avoid loading problems + config.set_tag(new_base) + + # Copy the entire old node to the new one before migrating individual + # parts + config.copy(tree + [pppoe], pppoe_base) + + # Instead of letting the user choose between auto and none + # where auto is default, it makes more sesne to just offer + # an option to disable the default behavior (declutter CLI) + if config.exists(pppoe_base + ['name-server']): + tmp = config.return_value(pppoe_base + ['name-server']) + if tmp == "none": + config.set(pppoe_base + ['no-peer-dns']) + config.delete(pppoe_base + ['name-server']) + + # Migrate user-id and password nodes under an 'authentication' + # node + if config.exists(pppoe_base + ['user-id']): + user = config.return_value(pppoe_base + ['user-id']) + config.set(pppoe_base + ['authentication', 'user'], value=user) + config.delete(pppoe_base + ['user-id']) + + if config.exists(pppoe_base + ['password']): + pwd = config.return_value(pppoe_base + ['password']) + config.set(pppoe_base + ['authentication', 'password'], value=pwd) + config.delete(pppoe_base + ['password']) + + # remove enable-ipv6 node and rather place it under ipv6 node + if config.exists(pppoe_base + ['enable-ipv6']): + config.set(pppoe_base + ['ipv6', 'enable']) + config.delete(pppoe_base + ['enable-ipv6']) + + # Source interface migration + config.set(pppoe_base + ['source-interface'], value=intf) + + # Remove IPv6 router-advert nodes as this makes no sense on a + # client diale rinterface to send RAs back into the network + # https://vyos.dev/T2055 + ipv6_ra = pppoe_base + ['ipv6', 'router-advert'] + if config.exists(ipv6_ra): + config.delete(ipv6_ra) + +def migrate(config: ConfigTree) -> None: + pppoe_links = ['bonding', 'ethernet'] + + for link_type in pppoe_links: + if not config.exists(['interfaces', link_type]): + continue + + for interface in config.list_nodes(['interfaces', link_type]): + # check if PPPoE exists + base_if = ['interfaces', link_type, interface] + pppoe_if = base_if + ['pppoe'] + if config.exists(pppoe_if): + for dialer in config.list_nodes(pppoe_if): + migrate_dialer(config, pppoe_if, interface) + + # Delete old PPPoE interface + config.delete(pppoe_if) + + # bail out early if there are no VLAN interfaces to migrate + if not config.exists(base_if + ['vif']): + continue + + # Migrate PPPoE interfaces attached to a VLAN + for vlan in config.list_nodes(base_if + ['vif']): + vlan_if = base_if + ['vif', vlan] + pppoe_if = vlan_if + ['pppoe'] + if config.exists(pppoe_if): + for dialer in config.list_nodes(pppoe_if): + intf = "{}.{}".format(interface, vlan) + migrate_dialer(config, pppoe_if, intf) + + # Delete old PPPoE interface + config.delete(pppoe_if) + + # Add interface description that this is required for PPPoE + if not config.exists(vlan_if + ['description']): + config.set(vlan_if + ['description'], value='PPPoE link interface') diff --git a/src/migration-scripts/interfaces/5-to-6 b/src/migration-scripts/interfaces/5-to-6 new file mode 100644 index 0000000..44c32ba --- /dev/null +++ b/src/migration-scripts/interfaces/5-to-6 @@ -0,0 +1,114 @@ +# Copyright 202-2024 VyOS maintainers and contributors <maintainers@vyos.io> +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library 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 +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this library. If not, see <http://www.gnu.org/licenses/>. + +# Migrate IPv6 router advertisments from a nested interface configuration to +# a denested "service router-advert" + +from vyos.configtree import ConfigTree + +def copy_rtradv(c, old_base, interface): + base = ['service', 'router-advert', 'interface'] + + if c.exists(old_base): + if not c.exists(base): + c.set(base) + c.set_tag(base) + + # take the old node as a whole and copy it to new new path, + # additional migrations will be done afterwards + new_base = base + [interface] + c.copy(old_base, new_base) + c.delete(old_base) + + # cur-hop-limit has been renamed to hop-limit + if c.exists(new_base + ['cur-hop-limit']): + c.rename(new_base + ['cur-hop-limit'], 'hop-limit') + + bool_cleanup = ['managed-flag', 'other-config-flag'] + for bool in bool_cleanup: + if c.exists(new_base + [bool]): + tmp = c.return_value(new_base + [bool]) + c.delete(new_base + [bool]) + if tmp == 'true': + c.set(new_base + [bool]) + + # max/min interval moved to subnode + intervals = ['max-interval', 'min-interval'] + for interval in intervals: + if c.exists(new_base + [interval]): + tmp = c.return_value(new_base + [interval]) + c.delete(new_base + [interval]) + min_max = interval.split('-')[0] + c.set(new_base + ['interval', min_max], value=tmp) + + # cleanup boolean nodes in individual route + route_base = new_base + ['route'] + if c.exists(route_base): + for route in config.list_nodes(route_base): + if c.exists(route_base + [route, 'remove-route']): + tmp = c.return_value(route_base + [route, 'remove-route']) + c.delete(route_base + [route, 'remove-route']) + if tmp == 'false': + c.set(route_base + [route, 'no-remove-route']) + + # cleanup boolean nodes in individual prefix + prefix_base = new_base + ['prefix'] + if c.exists(prefix_base): + for prefix in config.list_nodes(prefix_base): + if c.exists(prefix_base + [prefix, 'autonomous-flag']): + tmp = c.return_value(prefix_base + [prefix, 'autonomous-flag']) + c.delete(prefix_base + [prefix, 'autonomous-flag']) + if tmp == 'false': + c.set(prefix_base + [prefix, 'no-autonomous-flag']) + + if c.exists(prefix_base + [prefix, 'on-link-flag']): + tmp = c.return_value(prefix_base + [prefix, 'on-link-flag']) + c.delete(prefix_base + [prefix, 'on-link-flag']) + if tmp == 'true': + c.set(prefix_base + [prefix, 'on-link-flag']) + + # router advertisement can be individually disabled per interface + # the node has been renamed from send-advert {true | false} to no-send-advert + if c.exists(new_base + ['send-advert']): + tmp = c.return_value(new_base + ['send-advert']) + c.delete(new_base + ['send-advert']) + if tmp == 'false': + c.set(new_base + ['no-send-advert']) + + # link-mtu advertisement was formerly disabled by setting its value to 0 + # ... this makes less sense - if it should not be send, just do not + # configure it + if c.exists(new_base + ['link-mtu']): + tmp = c.return_value(new_base + ['link-mtu']) + if tmp == '0': + c.delete(new_base + ['link-mtu']) + +def migrate(config: ConfigTree) -> None: + # list all individual interface types like dummy, ethernet and so on + for if_type in config.list_nodes(['interfaces']): + base_if_type = ['interfaces', if_type] + + # for every individual interface we need to check if there is an + # ipv6 ra configured ... and also for every VIF (VLAN) interface + for intf in config.list_nodes(base_if_type): + old_base = base_if_type + [intf, 'ipv6', 'router-advert'] + copy_rtradv(config, old_base, intf) + + vif_base = base_if_type + [intf, 'vif'] + if config.exists(vif_base): + for vif in config.list_nodes(vif_base): + old_base = vif_base + [vif, 'ipv6', 'router-advert'] + vlan_name = f'{intf}.{vif}' + copy_rtradv(config, old_base, vlan_name) diff --git a/src/migration-scripts/interfaces/6-to-7 b/src/migration-scripts/interfaces/6-to-7 new file mode 100644 index 0000000..e60121e --- /dev/null +++ b/src/migration-scripts/interfaces/6-to-7 @@ -0,0 +1,45 @@ +# Copyright 2020-2024 VyOS maintainers and contributors <maintainers@vyos.io> +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library 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 +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this library. If not, see <http://www.gnu.org/licenses/>. + +# Remove network provider name from CLI and rather use provider APN from CLI + +from vyos.configtree import ConfigTree + +def migrate(config: ConfigTree) -> None: + base = ['interfaces', 'wirelessmodem'] + + if not config.exists(base): + # Nothing to do + return + + # list all individual wwan/wireless modem interfaces + for i in config.list_nodes(base): + iface = base + [i] + + # only three carries have been supported in the past, thus + # this will be fairly simple \o/ - and only one (AT&T) did + # configure an APN + if config.exists(iface + ['network']): + network = config.return_value(iface + ['network']) + if network == "att": + apn = 'isp.cingular' + config.set(iface + ['apn'], value=apn) + + config.delete(iface + ['network']) + + # synchronize DNS configuration with PPPoE interfaces to have a + # uniform CLI experience + if config.exists(iface + ['no-dns']): + config.rename(iface + ['no-dns'], 'no-peer-dns') diff --git a/src/migration-scripts/interfaces/7-to-8 b/src/migration-scripts/interfaces/7-to-8 new file mode 100644 index 0000000..43ae320 --- /dev/null +++ b/src/migration-scripts/interfaces/7-to-8 @@ -0,0 +1,59 @@ +# Copyright 2020-2024 VyOS maintainers and contributors <maintainers@vyos.io> +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library 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 +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this library. If not, see <http://www.gnu.org/licenses/>. + +# Split WireGuard endpoint into address / port nodes to make use of common +# validators + +import os + +from vyos.configtree import ConfigTree +from vyos.utils.permission import chown +from vyos.utils.permission import chmod_750 + +def migrate_default_keys(): + kdir = r'/config/auth/wireguard' + if os.path.exists(f'{kdir}/private.key') and not os.path.exists(f'{kdir}/default/private.key'): + location = f'{kdir}/default' + if not os.path.exists(location): + os.makedirs(location) + + chown(location, 'root', 'vyattacfg') + chmod_750(location) + os.rename(f'{kdir}/private.key', f'{location}/private.key') + os.rename(f'{kdir}/public.key', f'{location}/public.key') + +def migrate(config: ConfigTree) -> None: + base = ['interfaces', 'wireguard'] + + migrate_default_keys() + + if not config.exists(base): + # Nothing to do + return + + # list all individual wireguard interface isntance + for i in config.list_nodes(base): + iface = base + [i] + for peer in config.list_nodes(iface + ['peer']): + base_peer = iface + ['peer', peer] + if config.exists(base_peer + ['endpoint']): + endpoint = config.return_value(base_peer + ['endpoint']) + address = endpoint.split(':')[0] + port = endpoint.split(':')[1] + # delete old node + config.delete(base_peer + ['endpoint']) + # setup new nodes + config.set(base_peer + ['address'], value=address) + config.set(base_peer + ['port'], value=port) diff --git a/src/migration-scripts/interfaces/8-to-9 b/src/migration-scripts/interfaces/8-to-9 new file mode 100644 index 0000000..bae1b34 --- /dev/null +++ b/src/migration-scripts/interfaces/8-to-9 @@ -0,0 +1,33 @@ +# Copyright 2020-2024 VyOS maintainers and contributors <maintainers@vyos.io> +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library 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 +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this library. If not, see <http://www.gnu.org/licenses/>. + +# Rename link nodes to source-interface for the following interface types: +# - vxlan +# - pseudo-ethernet + +from vyos.configtree import ConfigTree + +def migrate(config: ConfigTree) -> None: + for if_type in ['vxlan', 'pseudo-ethernet']: + base = ['interfaces', if_type] + if not config.exists(base): + # Nothing to do + continue + + # list all individual interface isntance + for i in config.list_nodes(base): + iface = base + [i] + if config.exists(iface + ['link']): + config.rename(iface + ['link'], 'source-interface') diff --git a/src/migration-scripts/interfaces/9-to-10 b/src/migration-scripts/interfaces/9-to-10 new file mode 100644 index 0000000..cdfd7d4 --- /dev/null +++ b/src/migration-scripts/interfaces/9-to-10 @@ -0,0 +1,45 @@ +# Copyright 2020-2024 VyOS maintainers and contributors <maintainers@vyos.io> +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library 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 +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this library. If not, see <http://www.gnu.org/licenses/>. + +# - rename CLI node 'dhcpv6-options delgate' to 'dhcpv6-options prefix-delegation +# interface' +# - rename CLI node 'interface-id' for prefix-delegation to 'address' as it +# represents the local interface IPv6 address assigned by DHCPv6-PD + +from vyos.configtree import ConfigTree + +def migrate(config: ConfigTree) -> None: + for intf_type in config.list_nodes(['interfaces']): + for intf in config.list_nodes(['interfaces', intf_type]): + # cache current config tree + base_path = ['interfaces', intf_type, intf, 'dhcpv6-options', + 'delegate'] + + if config.exists(base_path): + # cache new config tree + new_path = ['interfaces', intf_type, intf, 'dhcpv6-options', + 'prefix-delegation'] + if not config.exists(new_path): + config.set(new_path) + + # copy to new node + config.copy(base_path, new_path + ['interface']) + + # rename interface-id to address + for interface in config.list_nodes(new_path + ['interface']): + config.rename(new_path + ['interface', interface, 'interface-id'], 'address') + + # delete old noe + config.delete(base_path) |