From 081e23996feb60ad903caf8b0a4587f5dacc69bf Mon Sep 17 00:00:00 2001
From: Christian Poessinger <christian@poessinger.com>
Date: Mon, 20 Sep 2021 21:50:56 +0200
Subject: vyos.ifconfig: get_mac_synthetic() must generate a stable "MAC"

Commit b7d30137b1 ("vyos.ifconfig: provide generic get_mac_synthetic() method")
provided a common helper to generate MAC addresses used by EUI64 addresses for
interfaces not having a layer2 interface (WireGuard or ip tunnel).

The problem is that every call to the helper always yielded a new MAC address.
This becomes problematic when IPv6 link-local addresses are generated and
modified on the interface as multiple link-local (fe80::/64) addresses can
easily be added to the interface leaving ... a mess.

This commit changes the way how the "synthetic" MAC is generated, we generate a
UUID which is stable as it is based on the interface name. We take out the last
48 bits of the UUID and form the "MAC" address.
---
 python/vyos/ifconfig/interface.py | 14 ++++++++++----
 1 file changed, 10 insertions(+), 4 deletions(-)

diff --git a/python/vyos/ifconfig/interface.py b/python/vyos/ifconfig/interface.py
index 963f47c89..0256ca5df 100755
--- a/python/vyos/ifconfig/interface.py
+++ b/python/vyos/ifconfig/interface.py
@@ -27,6 +27,8 @@ from netifaces import ifaddresses
 # this is not the same as socket.AF_INET/INET6
 from netifaces import AF_INET
 from netifaces import AF_INET6
+from uuid import uuid3
+from uuid import NAMESPACE_DNS
 
 from vyos import ConfigError
 from vyos.configdict import list_diff
@@ -56,7 +58,6 @@ from vyos.ifconfig import Section
 
 from netaddr import EUI
 from netaddr import mac_unix_expanded
-from random import getrandbits
 
 class Interface(Control):
     # This is the class which will be used to create
@@ -458,9 +459,14 @@ class Interface(Control):
         >>> Interface('eth0').get_mac()
         '00:50:ab:cd:ef:00'
         """
-        # we choose 40 random bytes for the MAC address, this gives
-        # us e.g. EUI('00-EA-EE-D6-A3-C8') or EUI('00-41-B9-0D-F2-2A')
-        tmp = EUI(getrandbits(48)).value
+        # calculate a UUID based on the interface name - this is as predictable
+        # as an interface MAC address and thus can be used in the same way
+        tmp = uuid3(NAMESPACE_DNS, self.ifname)
+        # take the last 48 bits from the UUID string
+        tmp = str(tmp).split('-')[-1]
+        # Convert pseudo random string into EUI format which now represents a
+        # MAC address
+        tmp = EUI(tmp).value
         # set locally administered bit in MAC address
         tmp |= 0xf20000000000
         # convert integer to "real" MAC address representation
-- 
cgit v1.2.3