summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristian Breunig <christian@breunig.cc>2025-09-13 13:16:40 +0200
committerChristian Breunig <christian@breunig.cc>2025-09-13 13:16:40 +0200
commitf08a5700e725a2ed4c53320b310167826953215d (patch)
tree9dd1483553bcdc380a956f2596185da463ff95b5
parent6a47b699577904eb3e5866c27ab2813058cc9ed9 (diff)
downloadvyos-1x-f08a5700e725a2ed4c53320b310167826953215d.tar.gz
vyos-1x-f08a5700e725a2ed4c53320b310167826953215d.zip
dhcpv6: T7646: restore missing default route after upgrade
Commit de44c6aef249 ("interface: T7379: do not request SLAAC default route when only DHCPv6 is set") introduced a behavioral change while addressing an earlier issue fixed in e9fb2078d5ea ("interface: T7375: SLAAC assigned address is not cleared when removing SLAAC"). This change caused unintended connectivity loss after upgrading to VyOS 1.4.3. The underlying reason is that VyOS now requires explicit configuration to determine how IPv6 addressing and routing should be handled, rather than making assumptions: Requesting a DHCPv6 address alone will correctly assign an address. However, since the IPv6 default route is typically advertised via SLAAC (and not DHCPv6), SLAAC must also be explicitly enabled to receive the default route. Historically, this distinction was implicit and did not require additional configuration. To preserve backward compatibility, a configuration migrator has been added. It inspects existing configurations that only request a DHCPv6 address and automatically appends the required CLI node to also enable SLAAC, ensuring that the default route is restored after upgrade.
-rw-r--r--interface-definitions/include/version/interfaces-version.xml.i2
-rw-r--r--smoketest/config-tests/basic-ipv640
-rw-r--r--smoketest/configs/basic-ipv6142
-rw-r--r--src/migration-scripts/interfaces/32-to-3351
-rw-r--r--src/migration-scripts/interfaces/33-to-3440
5 files changed, 255 insertions, 20 deletions
diff --git a/interface-definitions/include/version/interfaces-version.xml.i b/interface-definitions/include/version/interfaces-version.xml.i
index 2915b318e..28564b3d7 100644
--- a/interface-definitions/include/version/interfaces-version.xml.i
+++ b/interface-definitions/include/version/interfaces-version.xml.i
@@ -1,3 +1,3 @@
<!-- include start from include/version/interfaces-version.xml.i -->
-<syntaxVersion component='interfaces' version='33'></syntaxVersion>
+<syntaxVersion component='interfaces' version='34'></syntaxVersion>
<!-- include end -->
diff --git a/smoketest/config-tests/basic-ipv6 b/smoketest/config-tests/basic-ipv6
new file mode 100644
index 000000000..f217b5289
--- /dev/null
+++ b/smoketest/config-tests/basic-ipv6
@@ -0,0 +1,40 @@
+set interfaces ethernet eth0 vif 5 address 'dhcpv6'
+set interfaces ethernet eth0 vif 5 ipv6 address autoconf
+set interfaces ethernet eth0 vif-s 10 address 'dhcpv6'
+set interfaces ethernet eth0 vif-s 10 ipv6 address autoconf
+set interfaces ethernet eth0 vif-s 10 vif-c 20 address 'dhcpv6'
+set interfaces ethernet eth0 vif-s 10 vif-c 20 ipv6 address autoconf
+set interfaces ethernet eth1 duplex 'auto'
+set interfaces ethernet eth1 speed 'auto'
+set interfaces ethernet eth1 vif 50
+set interfaces ethernet eth1 vif-s 60 vif-c 70
+set interfaces ethernet eth2 duplex 'auto'
+set interfaces ethernet eth2 speed 'auto'
+set interfaces loopback lo
+set interfaces pppoe pppoe0 authentication password 'vyos'
+set interfaces pppoe pppoe0 authentication username 'vyos'
+set interfaces pppoe pppoe0 dhcpv6-options pd 0 interface eth1.50 address '1'
+set interfaces pppoe pppoe0 dhcpv6-options pd 0 interface eth1.50 sla-id '0'
+set interfaces pppoe pppoe0 dhcpv6-options pd 0 interface eth1.60 address '1'
+set interfaces pppoe pppoe0 dhcpv6-options pd 0 interface eth1.60 sla-id '1'
+set interfaces pppoe pppoe0 dhcpv6-options pd 0 interface eth1.60.70 address '1'
+set interfaces pppoe pppoe0 dhcpv6-options pd 0 interface eth1.60.70 sla-id '1'
+set interfaces pppoe pppoe0 dhcpv6-options pd 0 length '56'
+set interfaces pppoe pppoe0 no-peer-dns
+set interfaces pppoe pppoe0 source-interface 'eth2'
+set service ntp allow-client address '2001:db8::/32'
+set service ntp server time1.vyos.net
+set service ntp server time2.vyos.net
+set service router-advert interface eth1.50 prefix ::/64 preferred-lifetime '2700'
+set service router-advert interface eth1.50 prefix ::/64 valid-lifetime '5400'
+set service router-advert interface eth1.60 prefix ::/64
+set service router-advert interface eth1.60.70 prefix ::/64
+set system config-management commit-revisions '15'
+set system domain-name 'vyos.net'
+set system host-name 'ipv6-r1'
+set system login user vyos authentication encrypted-password '$6$2Ta6TWHd/U$NmrX0x9kexCimeOcYK1MfhMpITF9ELxHcaBU/znBq.X2ukQOj61fVI2UYP/xBzP4QtiTcdkgs7WOQMHWsRymO/'
+set system login user vyos authentication plaintext-password ''
+set system name-server '2001:db8::1'
+set system name-server '2001:db8::2'
+set system syslog local facility all level 'debug'
+set system console device ttyS0 speed '115200'
diff --git a/smoketest/configs/basic-ipv6 b/smoketest/configs/basic-ipv6
new file mode 100644
index 000000000..c1b87733e
--- /dev/null
+++ b/smoketest/configs/basic-ipv6
@@ -0,0 +1,142 @@
+interfaces {
+ ethernet eth0 {
+ duplex auto
+ offload {
+ gro
+ gso
+ sg
+ tso
+ }
+ speed auto
+ vif 5 {
+ address dhcpv6
+ }
+ vif-s 10 {
+ address dhcpv6
+ vif-c 20 {
+ address dhcpv6
+ }
+ }
+ }
+ ethernet eth1 {
+ duplex auto
+ speed auto
+ vif 50 {
+ }
+ vif-s 60 {
+ vif-c 70 {
+ }
+ }
+ }
+ ethernet eth2 {
+ duplex auto
+ speed auto
+ }
+ loopback lo {
+ }
+ pppoe pppoe0 {
+ authentication {
+ password vyos
+ user vyos
+ }
+ default-route force
+ dhcpv6-options {
+ pd 0 {
+ interface eth1.50 {
+ address 1
+ sla-id 0
+ }
+ interface eth1.60 {
+ address 1
+ sla-id 1
+ }
+ interface eth1.60.70 {
+ address 1
+ sla-id 1
+ }
+ length 56
+ }
+ }
+ ipv6 {
+ address {
+ autoconf
+ }
+ }
+ no-peer-dns
+ source-interface eth2
+ }
+}
+service {
+ router-advert {
+ interface eth1.50 {
+ prefix ::/64 {
+ preferred-lifetime 2700
+ valid-lifetime 5400
+ }
+ }
+ interface eth1.60 {
+ prefix ::/64 {
+ }
+ }
+ interface eth1.60.70 {
+ prefix ::/64 {
+ }
+ }
+ }
+}
+system {
+ config-management {
+ commit-revisions 15
+ }
+ conntrack {
+ modules {
+ ftp
+ h323
+ nfs
+ pptp
+ sqlnet
+ tftp
+ }
+ }
+ console {
+ device ttyS0 {
+ speed 115200
+ }
+ }
+ domain-name vyos.net
+ host-name ipv6-r1
+ login {
+ user vyos {
+ authentication {
+ encrypted-password $6$2Ta6TWHd/U$NmrX0x9kexCimeOcYK1MfhMpITF9ELxHcaBU/znBq.X2ukQOj61fVI2UYP/xBzP4QtiTcdkgs7WOQMHWsRymO/
+ plaintext-password ""
+ }
+ }
+ }
+ name-server 2001:db8::1
+ name-server 2001:db8::2
+ ntp {
+ allow-clients {
+ address 2001:db8::/32
+ }
+ server time1.vyos.net {
+ }
+ server time2.vyos.net {
+ }
+ }
+ syslog {
+ global {
+ facility all {
+ level debug
+ }
+ facility protocols {
+ level debug
+ }
+ }
+ }
+ time-zone Europe/Berlin
+}
+
+// Warning: Do not remove the following line.
+// vyos-config-version: "broadcast-relay@1:cluster@1:config-management@1:conntrack@3:conntrack-sync@2:container@1:dhcp-relay@2:dhcp-server@6:dhcpv6-server@1:dns-forwarding@3:firewall@5:https@2:interfaces@22:ipoe-server@1:ipsec@5:isis@1:l2tp@3:lldp@1:mdns@1:nat@5:ntp@1:pppoe-server@5:pptp@2:qos@1:quagga@8:rpki@1:salt@1:snmp@2:ssh@2:sstp@3:system@21:vrrp@2:vyos-accel-ppp@2:wanloadbalance@3:webproxy@2:zone-policy@1"
+// Release version: 1.3.4
diff --git a/src/migration-scripts/interfaces/32-to-33 b/src/migration-scripts/interfaces/32-to-33
index ccda04d0c..af7b7f497 100644
--- a/src/migration-scripts/interfaces/32-to-33
+++ b/src/migration-scripts/interfaces/32-to-33
@@ -13,28 +13,41 @@
#
# 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
+# T7646: restore behavior of IPv6 default route if only dhcpv6 was defined but
+# not "ipv6 address autoconf"
-base = ['interfaces', 'wireless']
+from vyos.configtree import ConfigTree
def migrate(config: ConfigTree) -> None:
- if not config.exists(base):
- # Nothing to do
- return
+ for type in config.list_nodes(['interfaces']):
+ for interface in config.list_nodes(['interfaces', type]):
+ iface_base_path = ['interfaces', type, interface]
+ dhcpv6_addr_path = iface_base_path + ['address', 'dhcpv6']
+ autoconf_path = iface_base_path + ['ipv6', 'address', 'autoconf']
+ if config.exists(dhcpv6_addr_path) and not config.exists(autoconf_path):
+ config.set(autoconf_path)
+
+ vif_path = iface_base_path + ['vif']
+ if config.exists(vif_path):
+ for vif in config.list_nodes(vif_path):
+ vif_dhcpv6_addr_path = vif_path + [vif, 'address']
+ vif_autoconf_path = vif_path + [vif, 'ipv6', 'address', 'autoconf']
+ if config.exists(vif_dhcpv6_addr_path) and not config.exists(vif_autoconf_path):
+ config.set(vif_autoconf_path)
- 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)
+ vif_s_path = iface_base_path + ['vif-s']
+ if config.exists(vif_s_path):
+ for vif_s in config.list_nodes(vif_s_path):
+ vif_s_dhcpv6_addr_path = vif_s_path + [vif_s, 'address']
+ vif_s_autoconf_path = vif_s_path + [vif_s, 'ipv6', 'address', 'autoconf']
+ if config.exists(vif_s_dhcpv6_addr_path) and not config.exists(vif_s_autoconf_path):
+ config.set(vif_s_autoconf_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
+ vif_c_path = iface_base_path + ['vif-s', vif_s, 'vif-c']
+ if config.exists(vif_c_path):
+ for vif_c in config.list_nodes(vif_c_path):
+ vif_c_dhcpv6_addr_path = vif_c_path + [vif_c, 'address']
+ vif_c_autoconf_path = vif_c_path + [vif_c, 'ipv6', 'address', 'autoconf']
+ if config.exists(vif_c_dhcpv6_addr_path) and not config.exists(vif_c_autoconf_path):
+ config.set(vif_c_autoconf_path)
diff --git a/src/migration-scripts/interfaces/33-to-34 b/src/migration-scripts/interfaces/33-to-34
new file mode 100644
index 000000000..ccda04d0c
--- /dev/null
+++ b/src/migration-scripts/interfaces/33-to-34
@@ -0,0 +1,40 @@
+#!/usr/bin/env python3
+#
+# Copyright VyOS maintainers and contributors <maintainers@vyos.io>
+#
+# 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