MAC Address Spoofing in NetworkManager 1.4.0

The new NetworkManager release 1.4.0 adds new features to change the current MAC address of your Ethernet or Wi-Fi card. This is also called MAC address “spoofing” or “cloning”.

His name is Václav
Václav sleeps better with NetworkManager’s MAC address randomization

Previously NetworkManager 1.2.0 added MAC address randomization for Wi-Fi, as Lubomir explains in his blog post. He also explains why you may want to do that in the first place, so I skip the introduction. Suffice to say, some consider randomizing the MAC address an important feature to protect their privacy. Only be aware that for real™ privacy, more considerations come into play. The Tails distribution and Wikipedia have good reads on the subject.

1.2.0 relies on support from wpa_supplicant to configure a random MAC address. The problem is that it requires API which will only be part of the next major release 2.6 of the supplicant. Such a release does not yet exist to this date and thus virtually nobody is using this feature.

With NetworkManager 1.4.0, changing of the MAC address is done by NetworkManager itself, requiring no support from the supplicant. This allows also for more flexibility to generate “stable” addresses and the “generate-mac-address-mask”. Also, the same options are now available not only for Wi-Fi, but also Ethernet devices.

Tools like macchanger and macchiato are commonly used to change the MAC address of a device. They support flexible mechanisms to generate a random address, most of these options are now also supported by NetworkManager.

Randomization during Wi-Fi scanning

During Wi-Fi scanning, NetworkManager resets the MAC address frequently to a randomly generated address. This was already enabled by default in 1.2.0, but as said, users likely didn’t have the required support from wpa_supplicant.

This default behavior can be disabled with a global configuration option in NetworkManager.conf:

[device]
wifi.scan-rand-mac-address=no

Note that this is a per-device configuration value, because at the time of Wi-Fi scanning, no connection is yet activated. A “connection” in NetworkManager-speak is a profile, a bunch of settings.

Supported Modes

Since long, NetworkManager supports two connection properties “ethernet.cloned-mac-address” and “wifi.cloned-mac-address”. These settings take effect when activating the connection. They got extended in 1.4.0 and support the following values:

  • An explict MAC address: this was already supported before 1.4.0 and allows to spoof a specific MAC address.
  • “permanent”: use the permanent MAC address of the device. Before 1.4.0, the permanent MAC address was used if the “cloned-mac-address” property was left empty, thus it was the default.
  • “preserve”: don’t change the MAC address of the device upon activation.
  • “random”: generate a randomized value upon each connect.
  • “stable”: generate a stable, hashed MAC address.
  • NULL/unset: this is the default value which allows fallback to a globally configured default, see below. In case no global override exists, NetworkManager falls back to “permanent”, like it did before.

Update-2017-01-25: with 1.6 release and newer, the default value changed from “permanent” to “preserve” [commit],[bug].

Note that in the D-Bus API, the “cloned-mac-address” field is not a string and thus could not be extended in a backward compatible way. That is why on D-Bus there are new fields “ethernet.assigned-mac-address” and “wifi.assigned-mac-address” instead. On the other hand, in nmcli, libnm.so, and keyfile-format the properties are indeed called “cloned-mac-address”.

How to configure it?

It is likely that your favorite NetworkManager client does not expose these options in the UI. In that case, I would suggest to use nmcli to configure the per-connection settings:

$ nmcli connection show
NAME               UUID               TYPE            DEVICE
My Wi-Fi           fca8fc45-c47...    802-11-wireless --
...

$ nmcli connection show "My Wi-Fi"
...

$ nmcli connection modify "My Wi-Fi" \
        wifi.cloned-mac-address stable

$ nmcli connection up "My Wi-Fi"

$ ip link show
...

Stable MAC Address Generation

The “stable” method warrants more explanation. In a way it is similar to “random”, but instead it generates a stable, hashed value. This way every time the connection activates, the same address is generated. However, each connection generates a different address.

This is for example useful so that you get the same IP address from DHCP, which might not be the case with “random”. Or a captive-portal might remember your login-status based on the MAC address. With “random” you may be required to re-authenticate on every connect.

The “stable” mode still makes you easily recognizable when you re-connect to a previous network, but your hardware MAC address is hidden and tracking you across different networks may be harder (YMMV).

The stable address is generated by hashing a private key from /var/lib/NetworkManager/secret_key, the ifname of the device, and a stable-id. The stable-id by default is the UUID of the connection (“connection.uuid”), unless you configure the new property “connection.stable-id“. The latter allows you to have multiple connections that generate the same MAC address. Note that “connection.stable-id” property is also used when generating stable-privacy IPv6 addresses (“ipv6.addr-gen-mode”, RFC 7217).

Format of the MAC Address

The “random” and “stable” modes both generate a MAC address. By default, all 48 bits of the MAC address are scrambled except the following two bits. For one, the LSB of the first octet which must always be cleared to indicate a unicast MAC address. And then, the 2nd-LSB of the first octet is set to indicate a locally administered address — contrary to a burned-in address. This has the same effect as calling macchanger --random with respect to which bits are scrambled.

Which bits are scrambled is configurable by the per-connection properties “ethernet.generate-mac-address-mask” and “wifi.generate-mac-address-mask” [man]. During Wi-Fi scanning, the per-device property “wifi.scan-generate-mac-address-mask” is used instead [man].

The property works as follows. If the mask-setting contains one MAC address, that address is used as a mask. For example “FF:FF:FF:00:00:00” results in randomizing the lower 3 octets and use the vendor OUI of the device’s permanent MAC address. This is similar to macchanger --ending, except that NetworkManager uses the permanent MAC address of the device while macchanger preserves the OUI of the current address.

Update-2016-11-04: it doesn’t use the permanent MAC address, instead the “initial” MAC address, that is the current MAC address that was configured on the device outside of NetworkManager.

If after the initial mask a second MAC address follows, that address is used instead of the device’s permanent address. For example “FF:FF:FF:00:00:00 00:50:E4:00:00:00” sets the OUI to “00:50:E4” but randomizes the last 3 octets. Likewise, “02:00:00:00:00:00 00:00:00:00:00:00” scrambles all bits but clears the second LSB of the first octet, thus creating a burned-in address like macchanger --random --bia.

Actually, there can follow arbitrary many MAC addresses after the mask, in which case one will be chosen randomly. “02:00:00:00:00:00 00:00:00:00:00:00 02:00:00:00:00:00” will scramble all 47 bits — except the unicast bit which must be always cleared. This allows you to specify a list of OUIs.

Global Default Configuration

NetworkManager supports certain per-connection properties to fallback to a globally configured default value. By having “cloned-mac-address” or “generate-mac-address-mask” unset, it allows fallback to a value configured in NetworkManager.conf.

For example, I have a file /etc/NetworkManager/conf.d/30-mac-randomization.conf like:

[device-mac-randomization]
# "yes" is already the default for scanning
wifi.scan-rand-mac-address=yes

[connection-mac-randomization]
ethernet.cloned-mac-address=random
wifi.cloned-mac-address=random

which sets the default fallback to random. Only for a few selected connection profiles I explicitly switch the per-connection setting to “stable”.

Note: distributions and packages are advised to install configuration snippets to /usr/lib/NetworkManager/conf.d directory instead of /etc.

What’s missing?

What feature do you miss?

One idea would be to support some special “connection.stable-id”. This would allow to implement a “change daily” feature like Windows 10 has. Allowing for a stable-id “time: <date> <time> <period>” could have the effect to start at <date>-<time> and generate a new ID each <period> time. Say, “time: 2016-08-22 6:00:00 7d” could mean to generate a new ID every Monday at 6:00 a.m. Of course, the ID only gets re-generated upon activation of a connection.

Update-2017-01-25: since 1.6, NetworkManager supports dynamic stable-ids like "${BOOT}", "${CONNECTION}", "${RANDOM}" or any combination of these. This also affects RFC7217 stable privacy IPv4 addresses [commit], [example].

Another idea would be to allow a special keyword “preserve” in “generate-mac-address-mask”. It could be used to set a value like “FF:FF:FF:00:00:00 preserve” which should have the same effect as macchanger --ending and use the current MAC address instead of the permanent one.

If you have problems, questions or suggestions, meet us on mailing list, IRC (#nm on freenode) or check our documentation and our bugtracker.