Help understanding MAC address allocation

I’m using a physical interface on top of a bridge, then I’m using a stand-alone dnsmasq instance to allocate IP’s for containers launched on that bridge. It works fine, except it seems that each time a container is restarted, it’s issues a new “random” mac address.

If I set the “hwaddr” attribute on the container’s “eth-1”, then I get a consistent mac address each time the container is restarted.

Is this intended behaviour? / Is there a way to automatically make mac addresses persistent, or do I need to manually set “hwaddr” for each container when i create it … or do I create a container then “override” the hwaddr? (I’m a little frightened of the last option as overriding network setting seems to set off my ZFS lock-up problem)

If you are a bit familiar with the Go language, you can start off here, incus/internal/server/instance/instance_utils.go at 07baf74cdab7e38823f89a2525edfde05631f74c · lxc/incus · GitHub and check which functions are calling this one.

The MAC address of a container is of the form 10:66:6a:xx:xx:xx, since the first three octets are reserved for virtual servers.

Hmm … I’m primarily a Python/JS/HTML developer, but I’ll take a look.

From what you say however, this looks to be problematic;

Inside my nice new shiny container I see;

# ifconfig eth0
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1374
        inet 10.2.0.254  netmask 255.255.0.0  broadcast 10.2.255.255
        ether 8a:0b:c8:76:08:b0  txqueuelen 1000  (Ethernet)
        RX packets 4  bytes 461 (461.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 7  bytes 421 (421.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

root@dnsmasq:~# ifconfig eth1
eth1: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1374
        inet 10.3.0.254  netmask 255.255.0.0  broadcast 10.3.255.255
        ether 3a:2f:6c:be:f8:c5  txqueuelen 1000  (Ethernet)
        RX packets 2  bytes 112 (112.0 B)
        RX errors 0  dropped 1  overruns 0  frame 0
        TX packets 6  bytes 379 (379.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

Neither appears to have a 10:66 prefix.
There is nothing set in the container config re; hwaddr.

Hmm. It would appear that if you create an interface on a managed bridge you do indeed get a prefix of 10:66:6a… , however if you create an interface on a physical network that backs onto a host bridge, you get a completely random mac.

Is this really intentional?

There are no fully random MACs. The first few octets identify the vendor that has requested the range. If you have an unknown MAC, you can go to https://macvendors.com/ and identify the vendor.

Incus and other virtualization projects would use the allocation from (I think) the Xen project, and more recently there’s another allocation that was added.

Ok, so I think I understand what you’re saying … but this is what is confusing me;

$ ssh gw2
Linux gw2 6.1.0-37-arm64 #1 SMP Debian 6.1.140-1 (2025-05-22) aarch64

The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.

Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
Last login: Thu Jul  3 14:16:16 2025 from aa.bb.cc.dd
root@gw2:~# incus shell demo
root@demo:~# ifconfig eth0
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1370
        inet6 fe80::70:c9ff:fe28:7157  prefixlen 64  scopeid 0x20<link>
        ether 02:70:c9:28:71:57  txqueuelen 1000  (Ethernet)
        RX packets 21  bytes 2558 (2.4 KiB)
        TX packets 15  bytes 2034 (1.9 KiB)

root@gw2:~# incus restart demo
root@gw2:~# incus shell demo
root@demo:~# ifconfig eth0
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1370
        inet6 fe80::ec2c:b5ff:fe10:5e45  prefixlen 64  scopeid 0x20<link>
        ether ee:2c:b5:10:5e:45  txqueuelen 1000  (Ethernet)
        RX packets 18  bytes 1548 (1.5 KiB)
        TX packets 12  bytes 1296 (1.2 KiB)

Now what you seem to be saying is that the mac address should always be generated according to a set of rules, and these rules should at least ensure a consistent prefix. (although I’m not sure whether you are saying the remainder might be random)

There’s nothing exotic going on here, this is a clean install, Debian 12, Zabbly 6.14 on a Hetzner cloud instance. Container is a Debian 12 cloud instance with a single device level attachment. No overrides.

If this is the case (!), surely after a restart the container should at least retain the same prefix … what am I not getting?

Have a look at MAC EE:2C:B5:10:5E:45 | MAC Address Lookup
This is a LAA, Locally Administered Address.

Your smartphone and other such devices would do LAA for privacy purposes. The privacy reason is that if a mobile device emits the same WiFi MAC address, then others will be able to identify specific devices. Hence, there is randomization.

Linux distributions also have this feature for MAC address randomization. Definitely the desktop Linux distributions when they use NetworkManager. Do the rabbit hole search as to why you get those LAA addresses there, and how you may just disable this feature. There’s an easy option in NetworkManager, you would need to search a bit with other networking stacks.

Ok, thanks, I think I’m starting to get it. For managed networks Incus assigns a MAC address according to the specified template, but for unmanaged networks the address comes from the OS, and the default from the OS appears to be to use LAA addresses. (which are essentially random but from a specific LAA pool, so they kinda ‘look’ totally random … :wink: )

So, assuming I understand this correctly (?) the thing I’m still a little fuzzy on is “why”. I get the addressing thing re; making the OS provide DHCP for unmanaged networks (although using Incus DHCP would still be nice), but I’m not sure I get the logic of handing off MAC generation to the OS. My ‘expectation’ was that given Incus was in control of MAC addresses and by default is responsible for assigning them (indeed if you put ‘hwaddr’ in an eth device stanza it still does) it seems surprising that in this instance it releases responsibility which (at least to ‘this’ user) introduces somewhat unexpected behavior.

I understand this feature from a mobile phone perspective, just not sure how it would apply to containers running services.

Ok, that was almost too easy!

So on a stock Debian 12 system;

vi /etc/udev/udev.conf

Insert;

MACAddressPolicy=persistent

Then;

systemctl restart systemd-udevd

And I appear to be sorted, MAC addresses for containers now seem to survive a restart! :slight_smile:

Many thanks for your help, I’m getting there slowly :grin:

1 Like