From 0b6eb4f601dd9717b478ff38e8d4ab4fcd878b15 Mon Sep 17 00:00:00 2001
From: Jernej Jakob <jernej.jakob@gmail.com>
Date: Wed, 22 Apr 2020 17:08:44 +0200
Subject: interfaces: T2362: split set_ipv6_eui64_address into add and del
 functions

---
 python/vyos/ifconfig/interface.py           | 41 +++++++++++------------------
 src/conf_mode/interfaces-bonding.py         | 23 ++++++++++++----
 src/conf_mode/interfaces-bridge.py          | 23 ++++++++++++----
 src/conf_mode/interfaces-ethernet.py        | 23 ++++++++++++----
 src/conf_mode/interfaces-l2tpv3.py          | 12 +++++----
 src/conf_mode/interfaces-openvpn.py         | 26 +++++++++++++++---
 src/conf_mode/interfaces-pseudo-ethernet.py | 23 ++++++++++++----
 src/conf_mode/interfaces-vxlan.py           | 12 +++++----
 src/conf_mode/interfaces-wireless.py        | 23 ++++++++++++----
 9 files changed, 141 insertions(+), 65 deletions(-)

diff --git a/python/vyos/ifconfig/interface.py b/python/vyos/ifconfig/interface.py
index 62c30dbf7..a156eddd9 100644
--- a/python/vyos/ifconfig/interface.py
+++ b/python/vyos/ifconfig/interface.py
@@ -1,4 +1,4 @@
-# Copyright 2019 VyOS maintainers and contributors <maintainers@vyos.io>
+# Copyright 2019-2020 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
@@ -419,39 +419,28 @@ class Interface(Control):
         """
         return self.set_interface('ipv6_autoconf', autoconf)
 
-    def set_ipv6_eui64_address(self, prefix):
+    def add_ipv6_eui64_address(self, prefix):
         """
         Extended Unique Identifier (EUI), as per RFC2373, allows a host to
-        assign iteslf a unique IPv6 address based on a given IPv6 prefix.
+        assign itself a unique IPv6 address based on a given IPv6 prefix.
 
-        If prefix is passed address is assigned, if prefix is '' address is
-        removed from interface.
+        Calculate the EUI64 from the interface's MAC, then assign it
+        with the given prefix to the interface.
         """
-        # if prefix is an empty string convert it to None so mac2eui64 works
-        # as expected
-        if not prefix:
-            prefix = None
 
         eui64 = mac2eui64(self.get_mac(), prefix)
+        prefixlen = prefix.split('/')[1]
+        self.add_addr(f'{eui64}/{prefixlen}')
 
-        if not prefix:
-            # if prefix is empty - thus removed - we need to walk through all
-            # interface IPv6 addresses and find the one with the calculated
-            # EUI-64 identifier. The address is then removed
-            for addr in self.get_addr():
-                addr_wo_prefix = addr.split('/')[0]
-                if is_ipv6(addr_wo_prefix):
-                    if eui64 in IPv6Address(addr_wo_prefix).exploded:
-                        self.del_addr(addr)
-
-            return None
+    def del_ipv6_eui64_address(self, prefix):
+        """
+        Delete the address based on the interface's MAC-based EUI64
+        combined with the prefix address.
+        """
+        eui64 = mac2eui64(self.get_mac(), prefix)
+        prefixlen = prefix.split('/')[1]
+        self.del_addr(f'{eui64}/{prefixlen}')
 
-        # calculate and add EUI-64 IPv6 address
-        if IPv6Network(prefix):
-            # we also need to take the subnet length into account
-            prefix = prefix.split('/')[1]
-            eui64 = f'{eui64}/{prefix}'
-            self.add_addr(eui64 )
 
     def set_ipv6_forwarding(self, forwarding):
         """
diff --git a/src/conf_mode/interfaces-bonding.py b/src/conf_mode/interfaces-bonding.py
index 380457772..267a78870 100755
--- a/src/conf_mode/interfaces-bonding.py
+++ b/src/conf_mode/interfaces-bonding.py
@@ -1,6 +1,6 @@
 #!/usr/bin/env python3
 #
-# Copyright (C) 2019 VyOS maintainers and contributors
+# Copyright (C) 2019-2020 VyOS maintainers and contributors
 #
 # This program is free software; you can redistribute it and/or modify
 # it under the terms of the GNU General Public License version 2 or later as
@@ -52,7 +52,8 @@ default_config_data = {
     'ip_proxy_arp': 0,
     'ip_proxy_arp_pvlan': 0,
     'ipv6_autoconf': 0,
-    'ipv6_eui64_prefix': '',
+    'ipv6_eui64_prefix': [],
+    'ipv6_eui64_prefix_remove': [],
     'ipv6_forwarding': 1,
     'ipv6_dup_addr_detect': 1,
     'is_bridge_member': False,
@@ -204,7 +205,13 @@ def get_config():
 
     # Get prefix for IPv6 addressing based on MAC address (EUI-64)
     if conf.exists('ipv6 address eui64'):
-        bond['ipv6_eui64_prefix'] = conf.return_value('ipv6 address eui64')
+        bond['ipv6_eui64_prefix'].append(conf.return_value('ipv6 address eui64'))
+
+    # Determine currently effective EUI64 address - to determine which
+    # address is no longer valid and needs to be removed
+    eff_addr = conf.return_effective_value('ipv6 address eui64')
+    if eff_addr and eff_addr not in bond['ipv6_eui64_prefix']:
+        bond['ipv6_eui64_prefix_remove'].append(eff_addr)
 
     # Disable IPv6 forwarding on this interface
     if conf.exists('ipv6 disable-forwarding'):
@@ -434,17 +441,23 @@ def apply(bond):
         b.set_proxy_arp_pvlan(bond['ip_proxy_arp_pvlan'])
         # IPv6 address autoconfiguration
         b.set_ipv6_autoconf(bond['ipv6_autoconf'])
-        # IPv6 EUI-based address
-        b.set_ipv6_eui64_address(bond['ipv6_eui64_prefix'])
         # IPv6 forwarding
         b.set_ipv6_forwarding(bond['ipv6_forwarding'])
         # IPv6 Duplicate Address Detection (DAD) tries
         b.set_ipv6_dad_messages(bond['ipv6_dup_addr_detect'])
 
+        # Delete old IPv6 EUI64 addresses before changing MAC
+        for addr in bond['ipv6_eui64_prefix_remove']:
+            b.del_ipv6_eui64_address(addr)
+
         # Change interface MAC address
         if bond['mac']:
             b.set_mac(bond['mac'])
 
+        # Add IPv6 EUI-based addresses
+        for addr in bond['ipv6_eui64_prefix']:
+            b.add_ipv6_eui64_address(addr)
+
         # Maximum Transmission Unit (MTU)
         b.set_mtu(bond['mtu'])
 
diff --git a/src/conf_mode/interfaces-bridge.py b/src/conf_mode/interfaces-bridge.py
index 93c6db97e..8826c6996 100755
--- a/src/conf_mode/interfaces-bridge.py
+++ b/src/conf_mode/interfaces-bridge.py
@@ -1,6 +1,6 @@
 #!/usr/bin/env python3
 #
-# Copyright (C) 2019 VyOS maintainers and contributors
+# Copyright (C) 2019-2020 VyOS maintainers and contributors
 #
 # This program is free software; you can redistribute it and/or modify
 # it under the terms of the GNU General Public License version 2 or later as
@@ -47,7 +47,8 @@ default_config_data = {
     'ip_enable_arp_announce': 0,
     'ip_enable_arp_ignore': 0,
     'ipv6_autoconf': 0,
-    'ipv6_eui64_prefix': '',
+    'ipv6_eui64_prefix': [],
+    'ipv6_eui64_prefix_remove': [],
     'ipv6_forwarding': 1,
     'ipv6_dup_addr_detect': 1,
     'igmp_querier': 0,
@@ -162,7 +163,13 @@ def get_config():
 
     # Get prefix for IPv6 addressing based on MAC address (EUI-64)
     if conf.exists('ipv6 address eui64'):
-        bridge['ipv6_eui64_prefix'] = conf.return_value('ipv6 address eui64')
+        bridge['ipv6_eui64_prefix'].append(conf.return_value('ipv6 address eui64'))
+
+    # Determine currently effective EUI64 address - to determine which
+    # address is no longer valid and needs to be removed
+    eff_addr = conf.return_effective_value('ipv6 address eui64')
+    if eff_addr and eff_addr not in bridge['ipv6_eui64_prefix']:
+        bridge['ipv6_eui64_prefix_remove'].append(eff_addr)
 
     # Disable IPv6 forwarding on this interface
     if conf.exists('ipv6 disable-forwarding'):
@@ -283,8 +290,6 @@ def apply(bridge):
         br.set_arp_ignore(bridge['ip_enable_arp_ignore'])
         # IPv6 address autoconfiguration
         br.set_ipv6_autoconf(bridge['ipv6_autoconf'])
-        # IPv6 EUI-based address
-        br.set_ipv6_eui64_address(bridge['ipv6_eui64_prefix'])
         # IPv6 forwarding
         br.set_ipv6_forwarding(bridge['ipv6_forwarding'])
         # IPv6 Duplicate Address Detection (DAD) tries
@@ -318,10 +323,18 @@ def apply(bridge):
         # assign/remove VRF
         br.set_vrf(bridge['vrf'])
 
+        # Delete old IPv6 EUI64 addresses before changing MAC
+        for addr in bridge['ipv6_eui64_prefix_remove']:
+            br.del_ipv6_eui64_address(addr)
+
         # Change interface MAC address
         if bridge['mac']:
             br.set_mac(bridge['mac'])
 
+        # Add IPv6 EUI-based addresses
+        for addr in bridge['ipv6_eui64_prefix']:
+            br.add_ipv6_eui64_address(addr)
+
         # remove interface from bridge
         for intf in bridge['member_remove']:
             br.del_port(intf)
diff --git a/src/conf_mode/interfaces-ethernet.py b/src/conf_mode/interfaces-ethernet.py
index 5a977d797..7f4762e84 100755
--- a/src/conf_mode/interfaces-ethernet.py
+++ b/src/conf_mode/interfaces-ethernet.py
@@ -1,6 +1,6 @@
 #!/usr/bin/env python3
 #
-# Copyright (C) 2019 VyOS maintainers and contributors
+# Copyright (C) 2019-2020 VyOS maintainers and contributors
 #
 # This program is free software; you can redistribute it and/or modify
 # it under the terms of the GNU General Public License version 2 or later as
@@ -49,7 +49,8 @@ default_config_data = {
     'ip_proxy_arp': 0,
     'ip_proxy_arp_pvlan': 0,
     'ipv6_autoconf': 0,
-    'ipv6_eui64_prefix': '',
+    'ipv6_eui64_prefix': [],
+    'ipv6_eui64_prefix_remove': [],
     'ipv6_forwarding': 1,
     'ipv6_dup_addr_detect': 1,
     'intf': '',
@@ -177,7 +178,13 @@ def get_config():
 
     # Get prefix for IPv6 addressing based on MAC address (EUI-64)
     if conf.exists('ipv6 address eui64'):
-        eth['ipv6_eui64_prefix'] = conf.return_value('ipv6 address eui64')
+        eth['ipv6_eui64_prefix'].append(conf.return_value('ipv6 address eui64'))
+
+    # Determine currently effective EUI64 address - to determine which
+    # address is no longer valid and needs to be removed
+    eff_addr = conf.return_effective_value('ipv6 address eui64')
+    if eff_addr and eff_addr not in eth['ipv6_eui64_prefix']:
+        eth['ipv6_eui64_prefix_remove'].append(eff_addr)
 
     # Disable IPv6 forwarding on this interface
     if conf.exists('ipv6 disable-forwarding'):
@@ -336,13 +343,15 @@ def apply(eth):
         e.set_proxy_arp_pvlan(eth['ip_proxy_arp_pvlan'])
         # IPv6 address autoconfiguration
         e.set_ipv6_autoconf(eth['ipv6_autoconf'])
-        # IPv6 EUI-based address
-        e.set_ipv6_eui64_address(eth['ipv6_eui64_prefix'])
         # IPv6 forwarding
         e.set_ipv6_forwarding(eth['ipv6_forwarding'])
         # IPv6 Duplicate Address Detection (DAD) tries
         e.set_ipv6_dad_messages(eth['ipv6_dup_addr_detect'])
 
+        # Delete old IPv6 EUI64 addresses before changing MAC
+        for addr in eth['ipv6_eui64_prefix_remove']:
+            e.del_ipv6_eui64_address(addr)
+
         # Change interface MAC address - re-set to real hardware address (hw-id)
         # if custom mac is removed
         if eth['mac']:
@@ -350,6 +359,10 @@ def apply(eth):
         elif eth['hw_id']:
             e.set_mac(eth['hw_id'])
 
+        # Add IPv6 EUI-based addresses
+        for addr in eth['ipv6_eui64_prefix']:
+            e.add_ipv6_eui64_address(addr)
+
         # Maximum Transmission Unit (MTU)
         e.set_mtu(eth['mtu'])
 
diff --git a/src/conf_mode/interfaces-l2tpv3.py b/src/conf_mode/interfaces-l2tpv3.py
index 8312d6f37..3698f3e12 100755
--- a/src/conf_mode/interfaces-l2tpv3.py
+++ b/src/conf_mode/interfaces-l2tpv3.py
@@ -1,6 +1,6 @@
 #!/usr/bin/env python3
 #
-# Copyright (C) 2019 VyOS maintainers and contributors
+# Copyright (C) 2019-2020 VyOS maintainers and contributors
 #
 # This program is free software; you can redistribute it and/or modify
 # it under the terms of the GNU General Public License version 2 or later as
@@ -36,7 +36,7 @@ default_config_data = {
     'local_port': 5000,
     'intf': '',
     'ipv6_autoconf': 0,
-    'ipv6_eui64_prefix': '',
+    'ipv6_eui64_prefix': [],
     'ipv6_forwarding': 1,
     'ipv6_dup_addr_detect': 1,
     'is_bridge_member': False,
@@ -115,7 +115,7 @@ def get_config():
 
     # Get prefix for IPv6 addressing based on MAC address (EUI-64)
     if conf.exists('ipv6 address eui64'):
-        l2tpv3['ipv6_eui64_prefix'] = conf.return_value('ipv6 address eui64')
+        l2tpv3['ipv6_eui64_prefix'].append(conf.return_value('ipv6 address eui64'))
 
     # Disable IPv6 forwarding on this interface
     if conf.exists('ipv6 disable-forwarding'):
@@ -225,8 +225,6 @@ def apply(l2tpv3):
         l.set_mtu(l2tpv3['mtu'])
         # IPv6 address autoconfiguration
         l.set_ipv6_autoconf(l2tpv3['ipv6_autoconf'])
-        # IPv6 EUI-based address
-        l.set_ipv6_eui64_address(l2tpv3['ipv6_eui64_prefix'])
         # IPv6 forwarding
         l.set_ipv6_forwarding(l2tpv3['ipv6_forwarding'])
         # IPv6 Duplicate Address Detection (DAD) tries
@@ -238,6 +236,10 @@ def apply(l2tpv3):
         for addr in l2tpv3['address']:
             l.add_addr(addr)
 
+        # IPv6 EUI-based addresses
+        for addr in l2tpv3['ipv6_eui64_prefix']:
+            l.add_ipv6_eui64_address(addr)
+
         # As the interface is always disabled first when changing parameters
         # we will only re-enable the interface if it is not  administratively
         # disabled
diff --git a/src/conf_mode/interfaces-openvpn.py b/src/conf_mode/interfaces-openvpn.py
index 708ac8f91..99eb8d6ca 100755
--- a/src/conf_mode/interfaces-openvpn.py
+++ b/src/conf_mode/interfaces-openvpn.py
@@ -50,7 +50,8 @@ default_config_data = {
     'hash': '',
     'intf': '',
     'ipv6_autoconf': 0,
-    'ipv6_eui64_prefix': '',
+    'ipv6_eui64_prefix': [],
+    'ipv6_eui64_prefix_remove': [],
     'ipv6_forwarding': 1,
     'ipv6_dup_addr_detect': 1,
     'ipv6_local_address': [],
@@ -316,7 +317,13 @@ def get_config():
 
     # Get prefix for IPv6 addressing based on MAC address (EUI-64)
     if conf.exists('ipv6 address eui64'):
-        openvpn['ipv6_eui64_prefix'] = conf.return_value('ipv6 address eui64')
+        openvpn['ipv6_eui64_prefix'].append(conf.return_value('ipv6 address eui64'))
+
+    # Determine currently effective EUI64 address - to determine which
+    # address is no longer valid and needs to be removed
+    eff_addr = conf.return_effective_value('ipv6 address eui64')
+    if eff_addr and eff_addr not in openvpn['ipv6_eui64_prefix']:
+        openvpn['ipv6_eui64_prefix_remove'].append(eff_addr)
 
     # Disable IPv6 forwarding on this interface
     if conf.exists('ipv6 disable-forwarding'):
@@ -1043,13 +1050,24 @@ def apply(openvpn):
         o.set_alias(openvpn['description'])
         # IPv6 address autoconfiguration
         o.set_ipv6_autoconf(openvpn['ipv6_autoconf'])
-        # IPv6 EUI-based address
-        o.set_ipv6_eui64_address(openvpn['ipv6_eui64_prefix'])
         # IPv6 forwarding
         o.set_ipv6_forwarding(openvpn['ipv6_forwarding'])
         # IPv6 Duplicate Address Detection (DAD) tries
         o.set_ipv6_dad_messages(openvpn['ipv6_dup_addr_detect'])
 
+        # IPv6 EUI-based addresses - only in TAP mode (TUN's have no MAC)
+        # If MAC has changed, old EUI64 addresses won't get deleted,
+        # but this isn't easy to solve, so leave them.
+        # This is even more difficult as openvpn uses a random MAC for the
+        # initial interface creation, unless set by 'lladdr'.
+        # NOTE: right now the interface is always deleted. For future
+        # compatibility when tap's are not deleted, leave the del_ in
+        if openvpn['mode'] == 'tap':
+            for addr in openvpn['ipv6_eui64_prefix_remove']:
+                o.del_ipv6_eui64_address(addr)
+            for addr in openvpn['ipv6_eui64_prefix']:
+                o.add_ipv6_eui64_address(addr)
+
     except:
         pass
 
diff --git a/src/conf_mode/interfaces-pseudo-ethernet.py b/src/conf_mode/interfaces-pseudo-ethernet.py
index d5f308ed3..6f194f814 100755
--- a/src/conf_mode/interfaces-pseudo-ethernet.py
+++ b/src/conf_mode/interfaces-pseudo-ethernet.py
@@ -1,6 +1,6 @@
 #!/usr/bin/env python3
 #
-# Copyright (C) 2019 VyOS maintainers and contributors
+# Copyright (C) 2019-2020 VyOS maintainers and contributors
 #
 # This program is free software; you can redistribute it and/or modify
 # it under the terms of the GNU General Public License version 2 or later as
@@ -48,7 +48,8 @@ default_config_data = {
     'ip_proxy_arp': 0,
     'ip_proxy_arp_pvlan': 0,
     'ipv6_autoconf': 0,
-    'ipv6_eui64_prefix': '',
+    'ipv6_eui64_prefix': [],
+    'ipv6_eui64_prefix_remove': [],
     'ipv6_forwarding': 1,
     'ipv6_dup_addr_detect': 1,
     'is_bridge_member': False,
@@ -159,7 +160,13 @@ def get_config():
 
     # Get prefix for IPv6 addressing based on MAC address (EUI-64)
     if conf.exists('ipv6 address eui64'):
-        peth['ipv6_eui64_prefix'] = conf.return_value('ipv6 address eui64')
+        peth['ipv6_eui64_prefix'].append(conf.return_value('ipv6 address eui64'))
+
+    # Determine currently effective EUI64 address - to determine which
+    # address is no longer valid and needs to be removed
+    eff_addr = conf.return_effective_value('ipv6 address eui64')
+    if eff_addr and eff_addr not in peth['ipv6_eui64_prefix']:
+        peth['ipv6_eui64_prefix_remove'].append(eff_addr)
 
     # Disable IPv6 forwarding on this interface
     if conf.exists('ipv6 disable-forwarding'):
@@ -306,8 +313,6 @@ def apply(peth):
     p.set_proxy_arp_pvlan(peth['ip_proxy_arp_pvlan'])
     # IPv6 address autoconfiguration
     p.set_ipv6_autoconf(peth['ipv6_autoconf'])
-    # IPv6 EUI-based address
-    p.set_ipv6_eui64_address(peth['ipv6_eui64_prefix'])
     # IPv6 forwarding
     p.set_ipv6_forwarding(peth['ipv6_forwarding'])
     # IPv6 Duplicate Address Detection (DAD) tries
@@ -316,10 +321,18 @@ def apply(peth):
     # assign/remove VRF
     p.set_vrf(peth['vrf'])
 
+    # Delete old IPv6 EUI64 addresses before changing MAC
+    for addr in peth['ipv6_eui64_prefix_remove']:
+        p.del_ipv6_eui64_address(addr)
+
     # Change interface MAC address
     if peth['mac']:
         p.set_mac(peth['mac'])
 
+    # Add IPv6 EUI-based addresses
+    for addr in peth['ipv6_eui64_prefix']:
+        p.add_ipv6_eui64_address(addr)
+
     # Change interface mode
     p.set_mode(peth['mode'])
 
diff --git a/src/conf_mode/interfaces-vxlan.py b/src/conf_mode/interfaces-vxlan.py
index d238ddb57..5ed19ddf2 100755
--- a/src/conf_mode/interfaces-vxlan.py
+++ b/src/conf_mode/interfaces-vxlan.py
@@ -1,6 +1,6 @@
 #!/usr/bin/env python3
 #
-# Copyright (C) 2019 VyOS maintainers and contributors
+# Copyright (C) 2019-2020 VyOS maintainers and contributors
 #
 # This program is free software; you can redistribute it and/or modify
 # it under the terms of the GNU General Public License version 2 or later as
@@ -39,7 +39,7 @@ default_config_data = {
     'ip_enable_arp_ignore': 0,
     'ip_proxy_arp': 0,
     'ipv6_autoconf': 0,
-    'ipv6_eui64_prefix': '',
+    'ipv6_eui64_prefix': [],
     'ipv6_forwarding': 1,
     'ipv6_dup_addr_detect': 1,
     'is_bridge_member': False,
@@ -118,7 +118,7 @@ def get_config():
 
     # Get prefix for IPv6 addressing based on MAC address (EUI-64)
     if conf.exists('ipv6 address eui64'):
-        vxlan['ipv6_eui64_prefix'] = conf.return_value('ipv6 address eui64')
+        vxlan['ipv6_eui64_prefix'].append(conf.return_value('ipv6 address eui64'))
 
     # Disable IPv6 forwarding on this interface
     if conf.exists('ipv6 disable-forwarding'):
@@ -238,8 +238,6 @@ def apply(vxlan):
         v.set_proxy_arp(vxlan['ip_proxy_arp'])
         # IPv6 address autoconfiguration
         v.set_ipv6_autoconf(vxlan['ipv6_autoconf'])
-        # IPv6 EUI-based address
-        v.set_ipv6_eui64_address(vxlan['ipv6_eui64_prefix'])
         # IPv6 forwarding
         v.set_ipv6_forwarding(vxlan['ipv6_forwarding'])
         # IPv6 Duplicate Address Detection (DAD) tries
@@ -251,6 +249,10 @@ def apply(vxlan):
         for addr in vxlan['address']:
             v.add_addr(addr)
 
+        # IPv6 EUI-based addresses
+        for addr in vxlan['ipv6_eui64_prefix']:
+            v.add_ipv6_eui64_address(addr)
+
         # As the VXLAN interface is always disabled first when changing
         # parameters we will only re-enable the interface if it is not
         # administratively disabled
diff --git a/src/conf_mode/interfaces-wireless.py b/src/conf_mode/interfaces-wireless.py
index 42842f9bd..8f893fa8a 100755
--- a/src/conf_mode/interfaces-wireless.py
+++ b/src/conf_mode/interfaces-wireless.py
@@ -1,6 +1,6 @@
 #!/usr/bin/env python3
 #
-# Copyright (C) 2019 VyOS maintainers and contributors
+# Copyright (C) 2019-2020 VyOS maintainers and contributors
 #
 # This program is free software; you can redistribute it and/or modify
 # it under the terms of the GNU General Public License version 2 or later as
@@ -88,7 +88,8 @@ default_config_data = {
     'ip_enable_arp_announce': 0,
     'ip_enable_arp_ignore': 0,
     'ipv6_autoconf': 0,
-    'ipv6_eui64_prefix': '',
+    'ipv6_eui64_prefix': [],
+    'ipv6_eui64_prefix_remove': [],
     'ipv6_forwarding': 1,
     'ipv6_dup_addr_detect': 1,
     'is_bridge_member': False,
@@ -370,7 +371,13 @@ def get_config():
 
     # Get prefix for IPv6 addressing based on MAC address (EUI-64)
     if conf.exists('ipv6 address eui64'):
-        wifi['ipv6_eui64_prefix'] = conf.return_value('ipv6 address eui64')
+        wifi['ipv6_eui64_prefix'].append(conf.return_value('ipv6 address eui64'))
+
+    # Determine currently effective EUI64 address - to determine which
+    # address is no longer valid and needs to be removed
+    eff_addr = conf.return_effective_value('ipv6 address eui64')
+    if eff_addr and eff_addr not in wifi['ipv6_eui64_prefix']:
+        wifi['ipv6_eui64_prefix_remove'].append(eff_addr)
 
     # ARP enable ignore
     if conf.exists('ip enable-arp-ignore'):
@@ -696,6 +703,10 @@ def apply(wifi):
         # ignore link state changes
         w.set_link_detect(wifi['disable_link_detect'])
 
+        # Delete old IPv6 EUI64 addresses before changing MAC
+        for addr in wifi['ipv6_eui64_prefix_remove']:
+            w.del_ipv6_eui64_address(addr)
+
         # Change interface MAC address - re-set to real hardware address (hw-id)
         # if custom mac is removed
         if wifi['mac']:
@@ -703,6 +714,10 @@ def apply(wifi):
         elif wifi['hw_id']:
             w.set_mac(wifi['hw_id'])
 
+        # Add IPv6 EUI-based addresses
+        for addr in wifi['ipv6_eui64_prefix']:
+            w.add_ipv6_eui64_address(addr)
+
         # configure ARP filter configuration
         w.set_arp_filter(wifi['ip_disable_arp_filter'])
         # configure ARP accept
@@ -713,8 +728,6 @@ def apply(wifi):
         w.set_arp_ignore(wifi['ip_enable_arp_ignore'])
         # IPv6 address autoconfiguration
         w.set_ipv6_autoconf(wifi['ipv6_autoconf'])
-        # IPv6 EUI-based address
-        w.set_ipv6_eui64_address(wifi['ipv6_eui64_prefix'])
         # IPv6 forwarding
         w.set_ipv6_forwarding(wifi['ipv6_forwarding'])
         # IPv6 Duplicate Address Detection (DAD) tries
-- 
cgit v1.2.3


From 9f26522388972eb6537446a5030c806a5e1b68ff Mon Sep 17 00:00:00 2001
From: Jernej Jakob <jernej.jakob@gmail.com>
Date: Wed, 22 Apr 2020 18:57:50 +0200
Subject: interfaces: vlan: T2362: add IPv6 EUI64 address to VLAN scripts

---
 python/vyos/configdict.py    | 12 ++++++++++++
 python/vyos/ifconfig_vlan.py |  8 ++++++++
 2 files changed, 20 insertions(+)

diff --git a/python/vyos/configdict.py b/python/vyos/configdict.py
index 24fe174d2..0d315f146 100644
--- a/python/vyos/configdict.py
+++ b/python/vyos/configdict.py
@@ -125,6 +125,8 @@ def vlan_to_dict(conf):
         'ip_enable_arp_ignore': 0,
         'ip_proxy_arp': 0,
         'ipv6_autoconf': 0,
+        'ipv6_eui64_prefix': [],
+        'ipv6_eui64_prefix_remove': [],
         'ipv6_forwarding': 1,
         'ipv6_dup_addr_detect': 1,
         'ingress_qos': '',
@@ -199,6 +201,16 @@ def vlan_to_dict(conf):
     if conf.exists('ipv6 address autoconf'):
         vlan['ipv6_autoconf'] = 1
 
+    # Get prefix for IPv6 addressing based on MAC address (EUI-64)
+    if conf.exists('ipv6 address eui64'):
+        vlan['ipv6_eui64_prefix'].append(conf.return_value('ipv6 address eui64'))
+
+    # Determine currently effective EUI64 address - to determine which
+    # address is no longer valid and needs to be removed
+    eff_addr = conf.return_effective_value('ipv6 address eui64')
+    if eff_addr and eff_addr not in vlan['ipv6_eui64_prefix']:
+        vlan['ipv6_eui64_prefix_remove'].append(eff_addr)
+
     # Disable IPv6 forwarding on this interface
     if conf.exists('ipv6 disable-forwarding'):
         vlan['ipv6_forwarding'] = 0
diff --git a/python/vyos/ifconfig_vlan.py b/python/vyos/ifconfig_vlan.py
index 899fd17da..ee009f7f9 100644
--- a/python/vyos/ifconfig_vlan.py
+++ b/python/vyos/ifconfig_vlan.py
@@ -66,10 +66,18 @@ def apply_vlan_config(vlan, config):
     # assign/remove VRF
     vlan.set_vrf(config['vrf'])
 
+    # Delete old IPv6 EUI64 addresses before changing MAC
+    for addr in config['ipv6_eui64_prefix_remove']:
+        vlan.del_ipv6_eui64_address(addr)
+
     # Change VLAN interface MAC address
     if config['mac']:
         vlan.set_mac(config['mac'])
 
+    # Add IPv6 EUI-based addresses
+    for addr in config['ipv6_eui64_prefix']:
+        vlan.add_ipv6_eui64_address(addr)
+
     # enable/disable VLAN interface
     if config['disable']:
         vlan.set_admin_state('down')
-- 
cgit v1.2.3


From 76feb7d154a452ba5475b9b6ee720a7a8f5491d5 Mon Sep 17 00:00:00 2001
From: Jernej Jakob <jernej.jakob@gmail.com>
Date: Wed, 22 Apr 2020 17:11:14 +0200
Subject: interfaces: T2362: add default IPv6 link-local address to make IPv6
 work

---
 python/vyos/configdict.py                   | 3 +++
 src/conf_mode/interfaces-bonding.py         | 3 +++
 src/conf_mode/interfaces-bridge.py          | 3 +++
 src/conf_mode/interfaces-ethernet.py        | 3 +++
 src/conf_mode/interfaces-l2tpv3.py          | 3 +++
 src/conf_mode/interfaces-openvpn.py         | 3 +++
 src/conf_mode/interfaces-pseudo-ethernet.py | 3 +++
 src/conf_mode/interfaces-vxlan.py           | 3 +++
 src/conf_mode/interfaces-wireless.py        | 3 +++
 9 files changed, 27 insertions(+)

diff --git a/python/vyos/configdict.py b/python/vyos/configdict.py
index 0d315f146..611f7dd6e 100644
--- a/python/vyos/configdict.py
+++ b/python/vyos/configdict.py
@@ -211,6 +211,9 @@ def vlan_to_dict(conf):
     if eff_addr and eff_addr not in vlan['ipv6_eui64_prefix']:
         vlan['ipv6_eui64_prefix_remove'].append(eff_addr)
 
+    # add the link-local by default to make IPv6 work
+    vlan['ipv6_eui64_prefix'].append('fe80::/64')
+
     # Disable IPv6 forwarding on this interface
     if conf.exists('ipv6 disable-forwarding'):
         vlan['ipv6_forwarding'] = 0
diff --git a/src/conf_mode/interfaces-bonding.py b/src/conf_mode/interfaces-bonding.py
index 267a78870..0ce376a16 100755
--- a/src/conf_mode/interfaces-bonding.py
+++ b/src/conf_mode/interfaces-bonding.py
@@ -213,6 +213,9 @@ def get_config():
     if eff_addr and eff_addr not in bond['ipv6_eui64_prefix']:
         bond['ipv6_eui64_prefix_remove'].append(eff_addr)
 
+    # add the link-local by default to make IPv6 work
+    bond['ipv6_eui64_prefix'].append('fe80::/64')
+
     # Disable IPv6 forwarding on this interface
     if conf.exists('ipv6 disable-forwarding'):
         bond['ipv6_forwarding'] = 0
diff --git a/src/conf_mode/interfaces-bridge.py b/src/conf_mode/interfaces-bridge.py
index 8826c6996..ba459f871 100755
--- a/src/conf_mode/interfaces-bridge.py
+++ b/src/conf_mode/interfaces-bridge.py
@@ -171,6 +171,9 @@ def get_config():
     if eff_addr and eff_addr not in bridge['ipv6_eui64_prefix']:
         bridge['ipv6_eui64_prefix_remove'].append(eff_addr)
 
+    # add the link-local by default to make IPv6 work
+    bridge['ipv6_eui64_prefix'].append('fe80::/64')
+
     # Disable IPv6 forwarding on this interface
     if conf.exists('ipv6 disable-forwarding'):
         bridge['ipv6_forwarding'] = 0
diff --git a/src/conf_mode/interfaces-ethernet.py b/src/conf_mode/interfaces-ethernet.py
index 7f4762e84..01a805e8b 100755
--- a/src/conf_mode/interfaces-ethernet.py
+++ b/src/conf_mode/interfaces-ethernet.py
@@ -186,6 +186,9 @@ def get_config():
     if eff_addr and eff_addr not in eth['ipv6_eui64_prefix']:
         eth['ipv6_eui64_prefix_remove'].append(eff_addr)
 
+    # add the link-local by default to make IPv6 work
+    eth['ipv6_eui64_prefix'].append('fe80::/64')
+
     # Disable IPv6 forwarding on this interface
     if conf.exists('ipv6 disable-forwarding'):
         eth['ipv6_forwarding'] = 0
diff --git a/src/conf_mode/interfaces-l2tpv3.py b/src/conf_mode/interfaces-l2tpv3.py
index 3698f3e12..45b618148 100755
--- a/src/conf_mode/interfaces-l2tpv3.py
+++ b/src/conf_mode/interfaces-l2tpv3.py
@@ -117,6 +117,9 @@ def get_config():
     if conf.exists('ipv6 address eui64'):
         l2tpv3['ipv6_eui64_prefix'].append(conf.return_value('ipv6 address eui64'))
 
+    # add the link-local by default to make IPv6 work
+    l2tpv3['ipv6_eui64_prefix'].append('fe80::/64')
+
     # Disable IPv6 forwarding on this interface
     if conf.exists('ipv6 disable-forwarding'):
         l2tpv3['ipv6_forwarding'] = 0
diff --git a/src/conf_mode/interfaces-openvpn.py b/src/conf_mode/interfaces-openvpn.py
index 99eb8d6ca..469793910 100755
--- a/src/conf_mode/interfaces-openvpn.py
+++ b/src/conf_mode/interfaces-openvpn.py
@@ -325,6 +325,9 @@ def get_config():
     if eff_addr and eff_addr not in openvpn['ipv6_eui64_prefix']:
         openvpn['ipv6_eui64_prefix_remove'].append(eff_addr)
 
+    # add the link-local by default to make IPv6 work
+    openvpn['ipv6_eui64_prefix'].append('fe80::/64')
+
     # Disable IPv6 forwarding on this interface
     if conf.exists('ipv6 disable-forwarding'):
         openvpn['ipv6_forwarding'] = 0
diff --git a/src/conf_mode/interfaces-pseudo-ethernet.py b/src/conf_mode/interfaces-pseudo-ethernet.py
index 6f194f814..d7863512e 100755
--- a/src/conf_mode/interfaces-pseudo-ethernet.py
+++ b/src/conf_mode/interfaces-pseudo-ethernet.py
@@ -168,6 +168,9 @@ def get_config():
     if eff_addr and eff_addr not in peth['ipv6_eui64_prefix']:
         peth['ipv6_eui64_prefix_remove'].append(eff_addr)
 
+    # add the link-local by default to make IPv6 work
+    peth['ipv6_eui64_prefix'].append('fe80::/64')
+
     # Disable IPv6 forwarding on this interface
     if conf.exists('ipv6 disable-forwarding'):
         peth['ipv6_forwarding'] = 0
diff --git a/src/conf_mode/interfaces-vxlan.py b/src/conf_mode/interfaces-vxlan.py
index 5ed19ddf2..69dbe32d8 100755
--- a/src/conf_mode/interfaces-vxlan.py
+++ b/src/conf_mode/interfaces-vxlan.py
@@ -120,6 +120,9 @@ def get_config():
     if conf.exists('ipv6 address eui64'):
         vxlan['ipv6_eui64_prefix'].append(conf.return_value('ipv6 address eui64'))
 
+    # add the link-local by default to make IPv6 work
+    vxlan['ipv6_eui64_prefix'].append('fe80::/64')
+
     # Disable IPv6 forwarding on this interface
     if conf.exists('ipv6 disable-forwarding'):
         vxlan['ipv6_forwarding'] = 0
diff --git a/src/conf_mode/interfaces-wireless.py b/src/conf_mode/interfaces-wireless.py
index 8f893fa8a..e7c14e3fb 100755
--- a/src/conf_mode/interfaces-wireless.py
+++ b/src/conf_mode/interfaces-wireless.py
@@ -379,6 +379,9 @@ def get_config():
     if eff_addr and eff_addr not in wifi['ipv6_eui64_prefix']:
         wifi['ipv6_eui64_prefix_remove'].append(eff_addr)
 
+    # add the link-local by default to make IPv6 work
+    wifi['ipv6_eui64_prefix'].append('fe80::/64')
+
     # ARP enable ignore
     if conf.exists('ip enable-arp-ignore'):
         wifi['ip_enable_arp_ignore'] = 1
-- 
cgit v1.2.3


From d6c08414d55eadd8232b693303f2b14bfe121c01 Mon Sep 17 00:00:00 2001
From: Jernej Jakob <jernej.jakob@gmail.com>
Date: Wed, 22 Apr 2020 17:12:25 +0200
Subject: interfaces: T2362: delete and re-add all EUI64 addresses if MAC has
 changed

---
 python/vyos/configdict.py                   | 3 +++
 src/conf_mode/interfaces-bonding.py         | 8 +++++++-
 src/conf_mode/interfaces-bridge.py          | 8 +++++++-
 src/conf_mode/interfaces-ethernet.py        | 8 +++++++-
 src/conf_mode/interfaces-pseudo-ethernet.py | 8 +++++++-
 src/conf_mode/interfaces-wireless.py        | 8 +++++++-
 6 files changed, 38 insertions(+), 5 deletions(-)

diff --git a/python/vyos/configdict.py b/python/vyos/configdict.py
index 611f7dd6e..71c85b36d 100644
--- a/python/vyos/configdict.py
+++ b/python/vyos/configdict.py
@@ -225,6 +225,9 @@ def vlan_to_dict(conf):
     # Media Access Control (MAC) address
     if conf.exists('mac'):
         vlan['mac'] = conf.return_value('mac')
+        # always recreate EUI64 addresses if mac is set
+        # I'm not sure how to check if a vlan interface exists or how to get its current mac.
+        vlan['ipv6_eui64_prefix_remove'] += vlan['ipv6_eui64_prefix']
 
     # Maximum Transmission Unit (MTU)
     if conf.exists('mtu'):
diff --git a/src/conf_mode/interfaces-bonding.py b/src/conf_mode/interfaces-bonding.py
index 0ce376a16..001da081c 100755
--- a/src/conf_mode/interfaces-bonding.py
+++ b/src/conf_mode/interfaces-bonding.py
@@ -20,7 +20,7 @@ from copy import deepcopy
 from sys import exit
 from netifaces import interfaces
 
-from vyos.ifconfig import BondIf
+from vyos.ifconfig import BondIf, Section
 from vyos.ifconfig_vlan import apply_vlan_config, verify_vlan_config
 from vyos.configdict import list_diff, vlan_to_dict
 from vyos.config import Config
@@ -228,6 +228,12 @@ def get_config():
     if conf.exists('mac'):
         bond['mac'] = conf.return_value('mac')
 
+    # Find out if MAC has changed - if so, we need to delete all IPv6 EUI64 addresses
+    # before re-adding them
+    if ( bond['mac'] and bond['intf'] in Section.interfaces(section='bonding')
+            and bond['mac'] != BondIf(bond['intf'], create=False).get_mac() ):
+        bond['ipv6_eui64_prefix_remove'] += bond['ipv6_eui64_prefix']
+
     # Bonding mode
     if conf.exists('mode'):
         act_mode = conf.return_value('mode')
diff --git a/src/conf_mode/interfaces-bridge.py b/src/conf_mode/interfaces-bridge.py
index ba459f871..2ad5ecf2b 100755
--- a/src/conf_mode/interfaces-bridge.py
+++ b/src/conf_mode/interfaces-bridge.py
@@ -20,7 +20,7 @@ from copy import deepcopy
 from sys import exit
 from netifaces import interfaces
 
-from vyos.ifconfig import BridgeIf
+from vyos.ifconfig import BridgeIf, Section
 from vyos.ifconfig.stp import STP
 from vyos.configdict import list_diff
 from vyos.config import Config
@@ -186,6 +186,12 @@ def get_config():
     if conf.exists('mac'):
         bridge['mac'] = conf.return_value('mac')
 
+    # Find out if MAC has changed - if so, we need to delete all IPv6 EUI64 addresses
+    # before re-adding them
+    if ( bridge['mac'] and bridge['intf'] in Section.interfaces(section='bridge')
+             and bridge['mac'] != BridgeIf(bridge['intf'], create=False).get_mac() ):
+        bridge['ipv6_eui64_prefix_remove'] += bridge['ipv6_eui64_prefix']
+
     # Interval at which neighbor bridges are removed
     if conf.exists('max-age'):
         bridge['max_age'] = int(conf.return_value('max-age'))
diff --git a/src/conf_mode/interfaces-ethernet.py b/src/conf_mode/interfaces-ethernet.py
index 01a805e8b..f620cc6ab 100755
--- a/src/conf_mode/interfaces-ethernet.py
+++ b/src/conf_mode/interfaces-ethernet.py
@@ -20,7 +20,7 @@ from sys import exit
 from copy import deepcopy
 from netifaces import interfaces
 
-from vyos.ifconfig import EthernetIf
+from vyos.ifconfig import EthernetIf, Section
 from vyos.ifconfig_vlan import apply_vlan_config, verify_vlan_config
 from vyos.configdict import list_diff, vlan_to_dict
 from vyos.config import Config
@@ -201,6 +201,12 @@ def get_config():
     if conf.exists('mac'):
         eth['mac'] = conf.return_value('mac')
 
+    # Find out if MAC has changed - if so, we need to delete all IPv6 EUI64 addresses
+    # before re-adding them
+    if ( eth['mac'] and eth['intf'] in Section.interfaces(section='ethernet')
+            and eth['mac'] != EthernetIf(eth['intf'], create=False).get_mac() ):
+        eth['ipv6_eui64_prefix_remove'] += eth['ipv6_eui64_prefix']
+
     # Maximum Transmission Unit (MTU)
     if conf.exists('mtu'):
         eth['mtu'] = int(conf.return_value('mtu'))
diff --git a/src/conf_mode/interfaces-pseudo-ethernet.py b/src/conf_mode/interfaces-pseudo-ethernet.py
index d7863512e..bf19e46b9 100755
--- a/src/conf_mode/interfaces-pseudo-ethernet.py
+++ b/src/conf_mode/interfaces-pseudo-ethernet.py
@@ -22,7 +22,7 @@ from netifaces import interfaces
 
 from vyos.config import Config
 from vyos.configdict import list_diff, vlan_to_dict
-from vyos.ifconfig import MACVLANIf
+from vyos.ifconfig import MACVLANIf, Section
 from vyos.ifconfig_vlan import apply_vlan_config, verify_vlan_config
 from vyos.validate import is_bridge_member
 from vyos import ConfigError
@@ -190,6 +190,12 @@ def get_config():
     if conf.exists(['mac']):
         peth['mac'] = conf.return_value(['mac'])
 
+    # Find out if MAC has changed - if so, we need to delete all IPv6 EUI64 addresses
+    # before re-adding them
+    if ( peth['mac'] and peth['intf'] in Section.interfaces(section='pseudo-ethernet')
+            and peth['mac'] != MACVLANIf(peth['intf'], create=False).get_mac() ):
+        peth['ipv6_eui64_prefix_remove'] += peth['ipv6_eui64_prefix']
+
     # MACvlan mode
     if conf.exists(['mode']):
         peth['mode'] = conf.return_value(['mode'])
diff --git a/src/conf_mode/interfaces-wireless.py b/src/conf_mode/interfaces-wireless.py
index e7c14e3fb..82a5a892a 100755
--- a/src/conf_mode/interfaces-wireless.py
+++ b/src/conf_mode/interfaces-wireless.py
@@ -25,7 +25,7 @@ from netaddr import EUI, mac_unix_expanded
 
 from vyos.config import Config
 from vyos.configdict import list_diff, vlan_to_dict
-from vyos.ifconfig import WiFiIf
+from vyos.ifconfig import WiFiIf, Section
 from vyos.ifconfig_vlan import apply_vlan_config, verify_vlan_config
 from vyos.template import render
 from vyos.util import chown, call
@@ -402,6 +402,12 @@ def get_config():
     if conf.exists('mac'):
         wifi['mac'] = conf.return_value('mac')
 
+    # Find out if MAC has changed - if so, we need to delete all IPv6 EUI64 addresses
+    # before re-adding them
+    if ( wifi['mac'] and wifi['intf'] in Section.interfaces(section='wireless')
+            and wifi['mac'] != WiFiIf(wifi['intf'], create=False).get_mac() ):
+        wifi['ipv6_eui64_prefix_remove'] += wifi['ipv6_eui64_prefix']
+
     # Maximum number of wireless radio stations
     if conf.exists('max-stations'):
         wifi['max_stations'] = conf.return_value('max-stations')
-- 
cgit v1.2.3


From 4940bea4f7b8574d30876fd3d0456e75f6f29877 Mon Sep 17 00:00:00 2001
From: Jernej Jakob <jernej.jakob@gmail.com>
Date: Wed, 22 Apr 2020 12:39:22 +0200
Subject: interfaces: T2362: allow setting multiple 'ipv6 address eui64'

---
 interface-definitions/include/ipv6-address.xml.i |  5 +++--
 python/vyos/configdict.py                        | 11 +++++------
 src/conf_mode/interfaces-bonding.py              | 11 +++++------
 src/conf_mode/interfaces-bridge.py               | 11 +++++------
 src/conf_mode/interfaces-ethernet.py             | 11 +++++------
 src/conf_mode/interfaces-l2tpv3.py               |  4 ++--
 src/conf_mode/interfaces-openvpn.py              | 12 ++++++------
 src/conf_mode/interfaces-pseudo-ethernet.py      | 11 +++++------
 src/conf_mode/interfaces-vxlan.py                |  4 ++--
 src/conf_mode/interfaces-wireless.py             | 11 +++++------
 10 files changed, 43 insertions(+), 48 deletions(-)

diff --git a/interface-definitions/include/ipv6-address.xml.i b/interface-definitions/include/ipv6-address.xml.i
index 507d5dcc1..ffc6ef933 100644
--- a/interface-definitions/include/ipv6-address.xml.i
+++ b/interface-definitions/include/ipv6-address.xml.i
@@ -8,14 +8,15 @@
     </leafNode>
     <leafNode name="eui64">
       <properties>
-        <help>ssign IPv6 address using EUI-64 based on MAC address</help>
+        <help>Prefix for IPv6 address with MAC-based EUI-64</help>
         <valueHelp>
           <format>ipv6net</format>
-          <description>IPv6 address and prefix length</description>
+          <description>IPv6 network and prefix length</description>
         </valueHelp>
         <constraint>
           <validator name="ipv6-prefix"/>
         </constraint>
+        <multi/>
       </properties>
     </leafNode>
   </children>
diff --git a/python/vyos/configdict.py b/python/vyos/configdict.py
index 71c85b36d..9ea89194f 100644
--- a/python/vyos/configdict.py
+++ b/python/vyos/configdict.py
@@ -201,15 +201,14 @@ def vlan_to_dict(conf):
     if conf.exists('ipv6 address autoconf'):
         vlan['ipv6_autoconf'] = 1
 
-    # Get prefix for IPv6 addressing based on MAC address (EUI-64)
+    # Get prefixes for IPv6 addressing based on MAC address (EUI-64)
     if conf.exists('ipv6 address eui64'):
-        vlan['ipv6_eui64_prefix'].append(conf.return_value('ipv6 address eui64'))
+        vlan['ipv6_eui64_prefix'] = conf.return_values('ipv6 address eui64')
 
-    # Determine currently effective EUI64 address - to determine which
+    # Determine currently effective EUI64 addresses - to determine which
     # address is no longer valid and needs to be removed
-    eff_addr = conf.return_effective_value('ipv6 address eui64')
-    if eff_addr and eff_addr not in vlan['ipv6_eui64_prefix']:
-        vlan['ipv6_eui64_prefix_remove'].append(eff_addr)
+    eff_addr = conf.return_effective_values('ipv6 address eui64')
+    vlan['ipv6_eui64_prefix_remove'] = list_diff(eff_addr, vlan['ipv6_eui64_prefix'])
 
     # add the link-local by default to make IPv6 work
     vlan['ipv6_eui64_prefix'].append('fe80::/64')
diff --git a/src/conf_mode/interfaces-bonding.py b/src/conf_mode/interfaces-bonding.py
index 001da081c..6693f3a13 100755
--- a/src/conf_mode/interfaces-bonding.py
+++ b/src/conf_mode/interfaces-bonding.py
@@ -203,15 +203,14 @@ def get_config():
     if conf.exists('ipv6 address autoconf'):
         bond['ipv6_autoconf'] = 1
 
-    # Get prefix for IPv6 addressing based on MAC address (EUI-64)
+    # Get prefixes for IPv6 addressing based on MAC address (EUI-64)
     if conf.exists('ipv6 address eui64'):
-        bond['ipv6_eui64_prefix'].append(conf.return_value('ipv6 address eui64'))
+        bond['ipv6_eui64_prefix'] = conf.return_values('ipv6 address eui64')
 
-    # Determine currently effective EUI64 address - to determine which
+    # Determine currently effective EUI64 addresses - to determine which
     # address is no longer valid and needs to be removed
-    eff_addr = conf.return_effective_value('ipv6 address eui64')
-    if eff_addr and eff_addr not in bond['ipv6_eui64_prefix']:
-        bond['ipv6_eui64_prefix_remove'].append(eff_addr)
+    eff_addr = conf.return_effective_values('ipv6 address eui64')
+    bond['ipv6_eui64_prefix_remove'] = list_diff(eff_addr, bond['ipv6_eui64_prefix'])
 
     # add the link-local by default to make IPv6 work
     bond['ipv6_eui64_prefix'].append('fe80::/64')
diff --git a/src/conf_mode/interfaces-bridge.py b/src/conf_mode/interfaces-bridge.py
index 2ad5ecf2b..d4470ef26 100755
--- a/src/conf_mode/interfaces-bridge.py
+++ b/src/conf_mode/interfaces-bridge.py
@@ -161,15 +161,14 @@ def get_config():
     if conf.exists('ipv6 address autoconf'):
         bridge['ipv6_autoconf'] = 1
 
-    # Get prefix for IPv6 addressing based on MAC address (EUI-64)
+    # Get prefixes for IPv6 addressing based on MAC address (EUI-64)
     if conf.exists('ipv6 address eui64'):
-        bridge['ipv6_eui64_prefix'].append(conf.return_value('ipv6 address eui64'))
+        bridge['ipv6_eui64_prefix'] = conf.return_values('ipv6 address eui64')
 
-    # Determine currently effective EUI64 address - to determine which
+    # Determine currently effective EUI64 addresses - to determine which
     # address is no longer valid and needs to be removed
-    eff_addr = conf.return_effective_value('ipv6 address eui64')
-    if eff_addr and eff_addr not in bridge['ipv6_eui64_prefix']:
-        bridge['ipv6_eui64_prefix_remove'].append(eff_addr)
+    eff_addr = conf.return_effective_values('ipv6 address eui64')
+    bridge['ipv6_eui64_prefix_remove'] = list_diff(eff_addr, bridge['ipv6_eui64_prefix'])
 
     # add the link-local by default to make IPv6 work
     bridge['ipv6_eui64_prefix'].append('fe80::/64')
diff --git a/src/conf_mode/interfaces-ethernet.py b/src/conf_mode/interfaces-ethernet.py
index f620cc6ab..db8e2cd3c 100755
--- a/src/conf_mode/interfaces-ethernet.py
+++ b/src/conf_mode/interfaces-ethernet.py
@@ -176,15 +176,14 @@ def get_config():
     if conf.exists('ipv6 address autoconf'):
         eth['ipv6_autoconf'] = 1
 
-    # Get prefix for IPv6 addressing based on MAC address (EUI-64)
+    # Get prefixes for IPv6 addressing based on MAC address (EUI-64)
     if conf.exists('ipv6 address eui64'):
-        eth['ipv6_eui64_prefix'].append(conf.return_value('ipv6 address eui64'))
+        eth['ipv6_eui64_prefix'] = conf.return_values('ipv6 address eui64')
 
-    # Determine currently effective EUI64 address - to determine which
+    # Determine currently effective EUI64 addresses - to determine which
     # address is no longer valid and needs to be removed
-    eff_addr = conf.return_effective_value('ipv6 address eui64')
-    if eff_addr and eff_addr not in eth['ipv6_eui64_prefix']:
-        eth['ipv6_eui64_prefix_remove'].append(eff_addr)
+    eff_addr = conf.return_effective_values('ipv6 address eui64')
+    eth['ipv6_eui64_prefix_remove'] = list_diff(eff_addr, eth['ipv6_eui64_prefix'])
 
     # add the link-local by default to make IPv6 work
     eth['ipv6_eui64_prefix'].append('fe80::/64')
diff --git a/src/conf_mode/interfaces-l2tpv3.py b/src/conf_mode/interfaces-l2tpv3.py
index 45b618148..a18cc6161 100755
--- a/src/conf_mode/interfaces-l2tpv3.py
+++ b/src/conf_mode/interfaces-l2tpv3.py
@@ -113,9 +113,9 @@ def get_config():
     if conf.exists('ipv6 address autoconf'):
         l2tpv3['ipv6_autoconf'] = 1
 
-    # Get prefix for IPv6 addressing based on MAC address (EUI-64)
+    # Get prefixes for IPv6 addressing based on MAC address (EUI-64)
     if conf.exists('ipv6 address eui64'):
-        l2tpv3['ipv6_eui64_prefix'].append(conf.return_value('ipv6 address eui64'))
+        l2tpv3['ipv6_eui64_prefix'] = conf.return_values('ipv6 address eui64')
 
     # add the link-local by default to make IPv6 work
     l2tpv3['ipv6_eui64_prefix'].append('fe80::/64')
diff --git a/src/conf_mode/interfaces-openvpn.py b/src/conf_mode/interfaces-openvpn.py
index 469793910..668dcabb4 100755
--- a/src/conf_mode/interfaces-openvpn.py
+++ b/src/conf_mode/interfaces-openvpn.py
@@ -25,6 +25,7 @@ from time import sleep
 from shutil import rmtree
 
 from vyos.config import Config
+from vyos.configdict import list_diff
 from vyos.ifconfig import VTunIf
 from vyos.template import render
 from vyos.util import call, chown, chmod_600, chmod_755
@@ -315,15 +316,14 @@ def get_config():
     if conf.exists('ipv6 address autoconf'):
         openvpn['ipv6_autoconf'] = 1
 
-    # Get prefix for IPv6 addressing based on MAC address (EUI-64)
+    # Get prefixes for IPv6 addressing based on MAC address (EUI-64)
     if conf.exists('ipv6 address eui64'):
-        openvpn['ipv6_eui64_prefix'].append(conf.return_value('ipv6 address eui64'))
+        openvpn['ipv6_eui64_prefix'] = conf.return_values('ipv6 address eui64')
 
-    # Determine currently effective EUI64 address - to determine which
+    # Determine currently effective EUI64 addresses - to determine which
     # address is no longer valid and needs to be removed
-    eff_addr = conf.return_effective_value('ipv6 address eui64')
-    if eff_addr and eff_addr not in openvpn['ipv6_eui64_prefix']:
-        openvpn['ipv6_eui64_prefix_remove'].append(eff_addr)
+    eff_addr = conf.return_effective_values('ipv6 address eui64')
+    openvpn['ipv6_eui64_prefix_remove'] = list_diff(eff_addr, openvpn['ipv6_eui64_prefix'])
 
     # add the link-local by default to make IPv6 work
     openvpn['ipv6_eui64_prefix'].append('fe80::/64')
diff --git a/src/conf_mode/interfaces-pseudo-ethernet.py b/src/conf_mode/interfaces-pseudo-ethernet.py
index bf19e46b9..2f86a3bea 100755
--- a/src/conf_mode/interfaces-pseudo-ethernet.py
+++ b/src/conf_mode/interfaces-pseudo-ethernet.py
@@ -158,15 +158,14 @@ def get_config():
     if conf.exists('ipv6 address autoconf'):
         peth['ipv6_autoconf'] = 1
 
-    # Get prefix for IPv6 addressing based on MAC address (EUI-64)
+    # Get prefixes for IPv6 addressing based on MAC address (EUI-64)
     if conf.exists('ipv6 address eui64'):
-        peth['ipv6_eui64_prefix'].append(conf.return_value('ipv6 address eui64'))
+        peth['ipv6_eui64_prefix'] = conf.return_values('ipv6 address eui64')
 
-    # Determine currently effective EUI64 address - to determine which
+    # Determine currently effective EUI64 addresses - to determine which
     # address is no longer valid and needs to be removed
-    eff_addr = conf.return_effective_value('ipv6 address eui64')
-    if eff_addr and eff_addr not in peth['ipv6_eui64_prefix']:
-        peth['ipv6_eui64_prefix_remove'].append(eff_addr)
+    eff_addr = conf.return_effective_values('ipv6 address eui64')
+    peth['ipv6_eui64_prefix_remove'] = list_diff(eff_addr, peth['ipv6_eui64_prefix'])
 
     # add the link-local by default to make IPv6 work
     peth['ipv6_eui64_prefix'].append('fe80::/64')
diff --git a/src/conf_mode/interfaces-vxlan.py b/src/conf_mode/interfaces-vxlan.py
index 69dbe32d8..3ff051eed 100755
--- a/src/conf_mode/interfaces-vxlan.py
+++ b/src/conf_mode/interfaces-vxlan.py
@@ -116,9 +116,9 @@ def get_config():
     if conf.exists('ipv6 address autoconf'):
         vxlan['ipv6_autoconf'] = 1
 
-    # Get prefix for IPv6 addressing based on MAC address (EUI-64)
+    # Get prefixes for IPv6 addressing based on MAC address (EUI-64)
     if conf.exists('ipv6 address eui64'):
-        vxlan['ipv6_eui64_prefix'].append(conf.return_value('ipv6 address eui64'))
+        vxlan['ipv6_eui64_prefix'] = conf.return_values('ipv6 address eui64')
 
     # add the link-local by default to make IPv6 work
     vxlan['ipv6_eui64_prefix'].append('fe80::/64')
diff --git a/src/conf_mode/interfaces-wireless.py b/src/conf_mode/interfaces-wireless.py
index 82a5a892a..b25a094e2 100755
--- a/src/conf_mode/interfaces-wireless.py
+++ b/src/conf_mode/interfaces-wireless.py
@@ -369,15 +369,14 @@ def get_config():
     if conf.exists('ipv6 address autoconf'):
         wifi['ipv6_autoconf'] = 1
 
-    # Get prefix for IPv6 addressing based on MAC address (EUI-64)
+    # Get prefixes for IPv6 addressing based on MAC address (EUI-64)
     if conf.exists('ipv6 address eui64'):
-        wifi['ipv6_eui64_prefix'].append(conf.return_value('ipv6 address eui64'))
+        wifi['ipv6_eui64_prefix'] = conf.return_values('ipv6 address eui64')
 
-    # Determine currently effective EUI64 address - to determine which
+    # Determine currently effective EUI64 addresses - to determine which
     # address is no longer valid and needs to be removed
-    eff_addr = conf.return_effective_value('ipv6 address eui64')
-    if eff_addr and eff_addr not in wifi['ipv6_eui64_prefix']:
-        wifi['ipv6_eui64_prefix_remove'].append(eff_addr)
+    eff_addr = conf.return_effective_values('ipv6 address eui64')
+    wifi['ipv6_eui64_prefix_remove'] = list_diff(eff_addr, wifi['ipv6_eui64_prefix'])
 
     # add the link-local by default to make IPv6 work
     wifi['ipv6_eui64_prefix'].append('fe80::/64')
-- 
cgit v1.2.3


From c87a5a1e1f52cddf113f427ea902f45a2e2a8445 Mon Sep 17 00:00:00 2001
From: Jernej Jakob <jernej.jakob@gmail.com>
Date: Wed, 22 Apr 2020 12:41:07 +0200
Subject: interfaces: T2362: add node to delete the default IPv6 link-local
 address

---
 interface-definitions/include/ipv6-address.xml.i | 6 ++++++
 python/vyos/configdict.py                        | 8 ++++++--
 src/conf_mode/interfaces-bonding.py              | 8 ++++++--
 src/conf_mode/interfaces-bridge.py               | 8 ++++++--
 src/conf_mode/interfaces-ethernet.py             | 8 ++++++--
 src/conf_mode/interfaces-l2tpv3.py               | 6 ++++--
 src/conf_mode/interfaces-openvpn.py              | 8 ++++++--
 src/conf_mode/interfaces-pseudo-ethernet.py      | 8 ++++++--
 src/conf_mode/interfaces-vxlan.py                | 6 ++++--
 src/conf_mode/interfaces-wireless.py             | 8 ++++++--
 10 files changed, 56 insertions(+), 18 deletions(-)

diff --git a/interface-definitions/include/ipv6-address.xml.i b/interface-definitions/include/ipv6-address.xml.i
index ffc6ef933..34f54e4c1 100644
--- a/interface-definitions/include/ipv6-address.xml.i
+++ b/interface-definitions/include/ipv6-address.xml.i
@@ -19,5 +19,11 @@
         <multi/>
       </properties>
     </leafNode>
+    <leafNode name="no-default-link-local">
+      <properties>
+        <help>Remove the default link-local address from the interface</help>
+        <valueless/>
+      </properties>
+    </leafNode>
   </children>
 </node>
diff --git a/python/vyos/configdict.py b/python/vyos/configdict.py
index 9ea89194f..2ce8a795f 100644
--- a/python/vyos/configdict.py
+++ b/python/vyos/configdict.py
@@ -210,8 +210,12 @@ def vlan_to_dict(conf):
     eff_addr = conf.return_effective_values('ipv6 address eui64')
     vlan['ipv6_eui64_prefix_remove'] = list_diff(eff_addr, vlan['ipv6_eui64_prefix'])
 
-    # add the link-local by default to make IPv6 work
-    vlan['ipv6_eui64_prefix'].append('fe80::/64')
+    # Remove the default link-local address if set.
+    if conf.exists('ipv6 address no-default-link-local'):
+        vlan['ipv6_eui64_prefix_remove'].append('fe80::/64')
+    else:
+        # add the link-local by default to make IPv6 work
+        vlan['ipv6_eui64_prefix'].append('fe80::/64')
 
     # Disable IPv6 forwarding on this interface
     if conf.exists('ipv6 disable-forwarding'):
diff --git a/src/conf_mode/interfaces-bonding.py b/src/conf_mode/interfaces-bonding.py
index 6693f3a13..4aef486b4 100755
--- a/src/conf_mode/interfaces-bonding.py
+++ b/src/conf_mode/interfaces-bonding.py
@@ -212,8 +212,12 @@ def get_config():
     eff_addr = conf.return_effective_values('ipv6 address eui64')
     bond['ipv6_eui64_prefix_remove'] = list_diff(eff_addr, bond['ipv6_eui64_prefix'])
 
-    # add the link-local by default to make IPv6 work
-    bond['ipv6_eui64_prefix'].append('fe80::/64')
+    # Remove the default link-local address if set.
+    if conf.exists('ipv6 address no-default-link-local'):
+        bond['ipv6_eui64_prefix_remove'].append('fe80::/64')
+    else:
+        # add the link-local by default to make IPv6 work
+        bond['ipv6_eui64_prefix'].append('fe80::/64')
 
     # Disable IPv6 forwarding on this interface
     if conf.exists('ipv6 disable-forwarding'):
diff --git a/src/conf_mode/interfaces-bridge.py b/src/conf_mode/interfaces-bridge.py
index d4470ef26..da49415f7 100755
--- a/src/conf_mode/interfaces-bridge.py
+++ b/src/conf_mode/interfaces-bridge.py
@@ -170,8 +170,12 @@ def get_config():
     eff_addr = conf.return_effective_values('ipv6 address eui64')
     bridge['ipv6_eui64_prefix_remove'] = list_diff(eff_addr, bridge['ipv6_eui64_prefix'])
 
-    # add the link-local by default to make IPv6 work
-    bridge['ipv6_eui64_prefix'].append('fe80::/64')
+    # Remove the default link-local address if set.
+    if conf.exists('ipv6 address no-default-link-local'):
+        bridge['ipv6_eui64_prefix_remove'].append('fe80::/64')
+    else:
+        # add the link-local by default to make IPv6 work
+        bridge['ipv6_eui64_prefix'].append('fe80::/64')
 
     # Disable IPv6 forwarding on this interface
     if conf.exists('ipv6 disable-forwarding'):
diff --git a/src/conf_mode/interfaces-ethernet.py b/src/conf_mode/interfaces-ethernet.py
index db8e2cd3c..43d97916d 100755
--- a/src/conf_mode/interfaces-ethernet.py
+++ b/src/conf_mode/interfaces-ethernet.py
@@ -185,8 +185,12 @@ def get_config():
     eff_addr = conf.return_effective_values('ipv6 address eui64')
     eth['ipv6_eui64_prefix_remove'] = list_diff(eff_addr, eth['ipv6_eui64_prefix'])
 
-    # add the link-local by default to make IPv6 work
-    eth['ipv6_eui64_prefix'].append('fe80::/64')
+    # Remove the default link-local address if set.
+    if conf.exists('ipv6 address no-default-link-local'):
+        eth['ipv6_eui64_prefix_remove'].append('fe80::/64')
+    else:
+        # add the link-local by default to make IPv6 work
+        eth['ipv6_eui64_prefix'].append('fe80::/64')
 
     # Disable IPv6 forwarding on this interface
     if conf.exists('ipv6 disable-forwarding'):
diff --git a/src/conf_mode/interfaces-l2tpv3.py b/src/conf_mode/interfaces-l2tpv3.py
index a18cc6161..8c3a8279e 100755
--- a/src/conf_mode/interfaces-l2tpv3.py
+++ b/src/conf_mode/interfaces-l2tpv3.py
@@ -117,8 +117,10 @@ def get_config():
     if conf.exists('ipv6 address eui64'):
         l2tpv3['ipv6_eui64_prefix'] = conf.return_values('ipv6 address eui64')
 
-    # add the link-local by default to make IPv6 work
-    l2tpv3['ipv6_eui64_prefix'].append('fe80::/64')
+    # Remove the default link-local address if set.
+    if not conf.exists('ipv6 address no-default-link-local'):
+        # add the link-local by default to make IPv6 work
+        l2tpv3['ipv6_eui64_prefix'].append('fe80::/64')
 
     # Disable IPv6 forwarding on this interface
     if conf.exists('ipv6 disable-forwarding'):
diff --git a/src/conf_mode/interfaces-openvpn.py b/src/conf_mode/interfaces-openvpn.py
index 668dcabb4..029bc1d69 100755
--- a/src/conf_mode/interfaces-openvpn.py
+++ b/src/conf_mode/interfaces-openvpn.py
@@ -325,8 +325,12 @@ def get_config():
     eff_addr = conf.return_effective_values('ipv6 address eui64')
     openvpn['ipv6_eui64_prefix_remove'] = list_diff(eff_addr, openvpn['ipv6_eui64_prefix'])
 
-    # add the link-local by default to make IPv6 work
-    openvpn['ipv6_eui64_prefix'].append('fe80::/64')
+    # Remove the default link-local address if set.
+    if conf.exists('ipv6 address no-default-link-local'):
+        openvpn['ipv6_eui64_prefix_remove'].append('fe80::/64')
+    else:
+        # add the link-local by default to make IPv6 work
+        openvpn['ipv6_eui64_prefix'].append('fe80::/64')
 
     # Disable IPv6 forwarding on this interface
     if conf.exists('ipv6 disable-forwarding'):
diff --git a/src/conf_mode/interfaces-pseudo-ethernet.py b/src/conf_mode/interfaces-pseudo-ethernet.py
index 2f86a3bea..57b282291 100755
--- a/src/conf_mode/interfaces-pseudo-ethernet.py
+++ b/src/conf_mode/interfaces-pseudo-ethernet.py
@@ -167,8 +167,12 @@ def get_config():
     eff_addr = conf.return_effective_values('ipv6 address eui64')
     peth['ipv6_eui64_prefix_remove'] = list_diff(eff_addr, peth['ipv6_eui64_prefix'])
 
-    # add the link-local by default to make IPv6 work
-    peth['ipv6_eui64_prefix'].append('fe80::/64')
+    # Remove the default link-local address if set.
+    if conf.exists('ipv6 address no-default-link-local'):
+        peth['ipv6_eui64_prefix_remove'].append('fe80::/64')
+    else:
+        # add the link-local by default to make IPv6 work
+        peth['ipv6_eui64_prefix'].append('fe80::/64')
 
     # Disable IPv6 forwarding on this interface
     if conf.exists('ipv6 disable-forwarding'):
diff --git a/src/conf_mode/interfaces-vxlan.py b/src/conf_mode/interfaces-vxlan.py
index 3ff051eed..74eae4281 100755
--- a/src/conf_mode/interfaces-vxlan.py
+++ b/src/conf_mode/interfaces-vxlan.py
@@ -120,8 +120,10 @@ def get_config():
     if conf.exists('ipv6 address eui64'):
         vxlan['ipv6_eui64_prefix'] = conf.return_values('ipv6 address eui64')
 
-    # add the link-local by default to make IPv6 work
-    vxlan['ipv6_eui64_prefix'].append('fe80::/64')
+    # Remove the default link-local address if set.
+    if not conf.exists('ipv6 address no-default-link-local'):
+        # add the link-local by default to make IPv6 work
+        vxlan['ipv6_eui64_prefix'].append('fe80::/64')
 
     # Disable IPv6 forwarding on this interface
     if conf.exists('ipv6 disable-forwarding'):
diff --git a/src/conf_mode/interfaces-wireless.py b/src/conf_mode/interfaces-wireless.py
index b25a094e2..148a7f6e0 100755
--- a/src/conf_mode/interfaces-wireless.py
+++ b/src/conf_mode/interfaces-wireless.py
@@ -378,8 +378,12 @@ def get_config():
     eff_addr = conf.return_effective_values('ipv6 address eui64')
     wifi['ipv6_eui64_prefix_remove'] = list_diff(eff_addr, wifi['ipv6_eui64_prefix'])
 
-    # add the link-local by default to make IPv6 work
-    wifi['ipv6_eui64_prefix'].append('fe80::/64')
+    # Remove the default link-local address if set.
+    if conf.exists('ipv6 address no-default-link-local'):
+        wifi['ipv6_eui64_prefix_remove'].append('fe80::/64')
+    else:
+        # add the link-local by default to make IPv6 work
+        wifi['ipv6_eui64_prefix'].append('fe80::/64')
 
     # ARP enable ignore
     if conf.exists('ip enable-arp-ignore'):
-- 
cgit v1.2.3


From c6997f0ca98c628dd85f1789ce53e495e9a451bc Mon Sep 17 00:00:00 2001
From: Jernej Jakob <jernej.jakob@gmail.com>
Date: Wed, 22 Apr 2020 17:47:03 +0200
Subject: interfaces: T2362: sysctl to not generate IPv6 link-local addreses by
 default

---
 debian/vyos-1x.install                      |  1 +
 src/etc/sysctl.d/31-vyos-addr_gen_mode.conf | 14 ++++++++++++++
 2 files changed, 15 insertions(+)
 create mode 100644 src/etc/sysctl.d/31-vyos-addr_gen_mode.conf

diff --git a/debian/vyos-1x.install b/debian/vyos-1x.install
index dd8eebc0b..599f3f3f5 100644
--- a/debian/vyos-1x.install
+++ b/debian/vyos-1x.install
@@ -2,6 +2,7 @@ etc/dhcp
 etc/ppp
 etc/rsyslog.d
 etc/systemd
+etc/sysctl.d
 etc/udev
 etc/vyos
 lib/
diff --git a/src/etc/sysctl.d/31-vyos-addr_gen_mode.conf b/src/etc/sysctl.d/31-vyos-addr_gen_mode.conf
new file mode 100644
index 000000000..07a0d1584
--- /dev/null
+++ b/src/etc/sysctl.d/31-vyos-addr_gen_mode.conf
@@ -0,0 +1,14 @@
+### Added by vyos-1x ###
+#
+# addr_gen_mode - INTEGER
+# 	Defines how link-local and autoconf addresses are generated.
+#
+# 	0: generate address based on EUI64 (default)
+# 	1: do no generate a link-local address, use EUI64 for addresses generated
+# 	   from autoconf
+# 	2: generate stable privacy addresses, using the secret from
+# 	   stable_secret (RFC7217)
+# 	3: generate stable privacy addresses, using a random secret if unset
+#
+net.ipv6.conf.all.addr_gen_mode = 1
+net.ipv6.conf.default.addr_gen_mode = 1
-- 
cgit v1.2.3


From 2d1dddd1b381676743dc602a321f2fd3146adc08 Mon Sep 17 00:00:00 2001
From: Jernej Jakob <jernej.jakob@gmail.com>
Date: Wed, 22 Apr 2020 18:03:55 +0200
Subject: interfaces: bridge: T2362: correct order of adding/deleting EUI64
 addresses

---
 src/conf_mode/interfaces-bridge.py | 18 ++++++++++--------
 1 file changed, 10 insertions(+), 8 deletions(-)

diff --git a/src/conf_mode/interfaces-bridge.py b/src/conf_mode/interfaces-bridge.py
index da49415f7..2f92aae09 100755
--- a/src/conf_mode/interfaces-bridge.py
+++ b/src/conf_mode/interfaces-bridge.py
@@ -336,17 +336,10 @@ def apply(bridge):
         br.set_vrf(bridge['vrf'])
 
         # Delete old IPv6 EUI64 addresses before changing MAC
+        # (adding members to a fresh bridge changes its MAC too)
         for addr in bridge['ipv6_eui64_prefix_remove']:
             br.del_ipv6_eui64_address(addr)
 
-        # Change interface MAC address
-        if bridge['mac']:
-            br.set_mac(bridge['mac'])
-
-        # Add IPv6 EUI-based addresses
-        for addr in bridge['ipv6_eui64_prefix']:
-            br.add_ipv6_eui64_address(addr)
-
         # remove interface from bridge
         for intf in bridge['member_remove']:
             br.del_port(intf)
@@ -355,6 +348,15 @@ def apply(bridge):
         for member in bridge['member']:
             br.add_port(member['name'])
 
+        # Change interface MAC address
+        if bridge['mac']:
+            br.set_mac(bridge['mac'])
+
+        # Add IPv6 EUI-based addresses (must be done after adding the
+        # 1st bridge member or setting its MAC)
+        for addr in bridge['ipv6_eui64_prefix']:
+            br.add_ipv6_eui64_address(addr)
+
         # up/down interface
         if bridge['disable']:
             br.set_admin_state('down')
-- 
cgit v1.2.3