Add new virtual function nm_device_unmanage_on_quit() to determine
whether to unmanage the device on shutdown.
This allows Wi-Fi devices to always be unmanaged. We want that to
reset the initial MAC address.
This allows the user to disable MAC address randomization during
scanning for Wi-Fi networks, which is done by default.
For one, this allows the user to disable the randomization for whatever
reason.
Also, together with configuring the per-connection setting
wifi.cloned-mac-address=preserve, this allows to disable NetworkManager
to modify the MAC address of the interface. This may allow the user
to set the MAC address outside of NetworkManager without NetworkManager
interfering.
'wireless.mac-address-randomization' broke 'wireless.cloned-mac-address',
because we would always set 'PreassocMacAddr=1'. The reason is that
supplicant would set 'wpa_s->mac_addr_changed' during scanning, and
later during association it would either set a random MAC address or
reset the permanent MAC address [1].
Anyway, 'wireless.mac-address-randomization' conflicts with
'wireless.cloned-mac-address'. Instead of letting supplicant set the
MAC address, manage the MAC addresses entirely from NetworkManager.
Supplicant should not touch it.
[1] https://w1.fi/cgit/hostap/tree/wpa_supplicant/wpa_supplicant.c?id=f885b8e97cf39b56fe7ca6577890f2d20df7ae08#n1663
When a user want to explicitly spoof the MAC address, a failure
to do so should fail activation. For one, failing to do so may
be a security problem. In any case, if user asks to configure the
interface in a certain way and we fail to do so that shall result
in a failure to activate.
Extend the "ethernet.cloned-mac-address" and "wifi.cloned-mac-address"
settings. Instead of specifying an explicit MAC address, the additional
special values "permanent", "preserve", "random", "random-bia", "stable" and
"stable-bia" are supported.
"permanent" means to use the permanent hardware address. Previously that
was the default if no explict cloned-mac-address was set. The default is
thus still "permanent", but it can be overwritten by global
configuration.
"preserve" means not to configure the MAC address when activating the
device. That was actually the default behavior before introducing MAC
address handling with commit 1b49f941a6.
"random" and "random-bia" use a randomized MAC address for each
connection. "stable" and "stable-bia" use a generated, stable
address based on some token. The "bia" suffix says to generate a
burned-in address. The stable method by default uses as token the
connection UUID, but the token can be explicitly choosen via
"stable:<TOKEN>" and "stable-bia:<TOKEN>".
On a D-Bus level, the "cloned-mac-address" is a bytestring and thus
cannot express the new forms. It is replaced by the new
"assigned-mac-address" field. For the GObject property, libnm's API,
nmcli, keyfile, etc. the old name "cloned-mac-address" is still used.
Deprecating the old field seems more complicated then just extending
the use of the existing "cloned-mac-address" field, although the name
doesn't match well with the extended meaning.
There is some overlap with the "wifi.mac-address-randomization" setting.
https://bugzilla.gnome.org/show_bug.cgi?id=705545https://bugzilla.gnome.org/show_bug.cgi?id=708820https://bugzilla.gnome.org/show_bug.cgi?id=758301
Note that usually for infiniband we cannot get a permanent MAC address
via ethtool. Thus, nm_device_get_permanent_hw_address() will return the
current address due to fallback_fake=TRUE.
VLAN and MACVLAN devices consider an ethernet.mac-address setting
to find the parent device. This setting shall be the permanent MAC
address of the device, not the current.
`man nm-settings` says about ethernet.mac-address:
If specified, this connection will only apply to the Ethernet device
whose permanent MAC address matches.
Using the current, possibly non-permanent MAC address doesn't really
make sense.
Also, NM_DEVICE_HW_ADDRESS used to be writable and was set by NMDeviceBt
to the bdaddr. That is wrong, because bdaddr should not be the current
address, but the permanent one.
When we were able to read a MAC address previously, we would not expect
a failure the next time.
Say a failure happens. Still, we should not clear the MAC address,
because we also determine hw_addr_len based on that address. And
hw_addr_perm and hw_addr_initial have the same length. When we allow
hw_addr to be reset (and possibly reset to a different address length),
we somehow have to re-fresh also the permanent and initial MAC address.
Just don't allow for that complexity, when it's not even clear what such
a scenario would mean and what do to in that case.
Both NMDeviceEthernet and NMDeviceWifi have a property "perm-hw-address".
As the hw_addr_perm property is tracked in the parent NMDevice class,
let it also implement the GObject property.
Then it knows better when to emit a notification about property
changes.
While a device is realized, we only want to read the permanent
MAC address once. If that fails, we fallback to the current MAC
address. Thus, we want the permanent address be stable until
the device unrealizes.
While we want to fallback to the current MAC address, in some cases
the caller wants to know whether this was a "real" permanent MAC
address as read via ethtool.
For example, when matching an ethernet device against ethernet.mac-address
property, the fake (current) address should not be used in such case.
Next I will add two more fields. Being able to efficiently grep the code
is important.
I want to be able to grep for "->hw_addr" or "\<hw_addr" to find
related stuff.
Unfortunately, prefixes often result in backward English names, e.g.
hw_addr_set/hw_addr_get. I still prefer that over get_hw_addr/set_hw_addr
though.
Previously, we would only once read the initial hardware address during
device realization.
When a device activates, NetworkManager always sets the MAC address as configured
in the cloned-mac-address setting -- or, if unspecified -- it falls back
to use the permanent hardware-address instead.
Later, when deactivating the device, the MAC address is reset to the
"inital MAC address".
This patch changes, that the "initial MAC address" is re-read every time
before activating the device, contrary to reading it once in the
beginning.
This allows for a user to first start NetworkManager and later change the
MAC address of the device. When activating the device, NM will reset the
MAC address for the time the device is active. But when disconnecting,
it resets to the user-changed value, not the value when NM was started.
https://bugzilla.gnome.org/show_bug.cgi?id=708820
The current MAC address is part of NMPlatformLink in the platform cache.
When it changes, we must update the device's current value.
Also, the MAC address of NMDeviceEthernet is exposed on D-Bus. That
property should show the currently configured MAC address, not a state
that was read some time in the past.
Also, nm_device_hw_addr_set() compares the current MAC address before
resetting it. If that field is out-of-date, nm_device_hw_addr_set() will
behave wrongly.
NMDeviceEthernet had some special handling in link_changed() that would
re-read the MAC addresses and possibly bring up the interface. Move that
code to the parent device.
hw-addr is a constuct-only property. We should not do complex stuff in the property
setter before the object is sufficiently initialized. For example, the logging
macros access nm_device_get_iface(), which might be unset at that early
point.
Instead, initialize hw_addr and hw_addr_len later, at the end of the constructor()
function.
Also, ensure that @hw_addr_len is zero iff @hw_addr is unset.
Also, ensure that we always log a message when changing/setting the
hardware address -- except when clearing it during unrealize. It's
implicit that unrealize clears the hardware address.
Also, give all related logging messages a "hw-addr:" prefix.
Accessing the platform cache might anytime yield unexpected results.
E.g. the link could be gone, or the ifindex could even be replaced
by a different interface (yes, that can happen when moving links
between network namespaces).
It's not clear how to handle such a case at runtime. It seems wrong to
me to just error out. Still, such case might happen under normal
conditions, so it's wrong to just warn and proceed.
When the entire NMSettingWired setting is missing, it should be treated
exactly the same as each property having the default/unset value.
Otherwise, adding a NMSettingWired setting only to set (say) MTU,
would result in different behavior. Although effectively the
"cloned-mac-address" shall be in both cases the same.
There was no leak here, because we would only call
nm_device_update_initial_hw_address() when @initial_hw_addr is unset.
However, still clear it to make it more robust against later changes.
_nm_utils_hwaddr_to_dbus() would serialize invalid hardware addresses
as "'cloned-mac-address': <@ay []>".
An empty array is treated the same as no hardware address set,
so we should not serialize it in the first place.
This is a change in behavior on how the connection is exported
on D-Bus, but it should not have any bad consequences.
We need this as we later want to deprecate the 'cloned-mac-address'
D-Bus field and overwrite it via a 'assigned-mac-address' field.
In this case, the "<@ay []>" is interfering. While it could be worked
around by treating an empty MAC address as "unset", fix it instead
and just not serialize it.
When modifying an existing ifcfg-rh file, we always want to enforce
the absense of a certain setting. That is done, by calling svSetValue()
with a value of NULL.
Same for writing MTU value.
When comparing settings, nm_setting_compare() performs a complicated
logic, which basically serializes each GObject property to a GVariant
for the D-Bus representation.
That is wrong for example for ipv4.addresses, which don't contain
address labels. That is, the GObject property is called "addresses",
but the D-Bus field "addresses" cannot encode every information
and thus comparison fails. Instead, it would have to look into
"address-data".
Traditionally, we have virtual functions like compare_property() per
NMSetting to do the comparison. That comparison is based on the GObject
properties. I think that is wrong, because we should have a generic
concept of what a property is, independent from GObject properties.
With libnm, we added NMSettingProperty, which indeed is such an
GObject independent representation to define properties.
However, it is not used thoroughly, instead compare_property() is a hack
of special cases, overloads from NMSettingProperty, overloads of
compare_property(), and default behavior based on GParamSpec.
This should be cleaned up.
For now, just hack it by handle the properties with the problems
explicitly.
The Network_ID for generating RFC 7217 stable privacy IPv6 addresses
is by default the UUID of the connection.
Alternatively, prefer "connection.stable-id" as Network_ID to generate
the stable addresses. This allows to configure a set of connections that
all use the same Network_ID for generating stable addresses.
Note that the stable-id and the UUID do no overlap, that is two
connections
[connection]
uuid=uuid1
stable-id=
and
[connection]
uuid=uuid2
stable-id=uuid1
generate distinct addresses.
This new property be used as token to generate stable-ids instead
of the connection's UUID.
Later, this will be used by ipv6.addr-gen-mode=stable-privacy,
ethernet.cloned-mac-address=stable, and wifi.cloned-mac-address=stable
setting. Those generate stable addresses based on the connection's
UUID, but allow to use the stable-id instead.
This allows multiple connections to generate the same addresses
-- on the same machine, because in the above cases a machine
dependant key is also hashed.
NetworkManager.conf already contains several per-device settings,
that is, settings that have a device-spec as argument.
main.ignore-carrier
main.no-auto-default
main.assume-ipv6ll-only
keyfile.unmanged-devices
Optimally, these settings should be moved to the new [device*]
section.
For now, only move main.ignore-carrier there. For the others
it may not make sense to do so:
- main.no-auto-default: is already merged with internal state
from /var/lib/NetworkManager/no-auto-default.state. While
NMConfig's write API would be fine to also persist and merge
the no-auto-default setting, we'd still have to read the old
file too. Thus, deprecating this setting gets quite cumbersome
to still handle the old state file.
Also, it seems a less useful setting to configure in the
global configuration aside setting main.no-auto-default=*.
- main.assume-ipv6ll-only: one day, I hope that we no longer
assume connections at all, and this setting becomes entirely
obsolete.
- keyfile.unmanged-devices: this sets NM_UNMANAGED_USER_SETTINGS,
which cannot be overruled via D-Bus. For a future device.managed
setting we want it it to be overwritable via D-Bus by an explicit
user action. Thus, a device.managed property should have a different
semantic, this should be more like a device.unmanaged-force setting,
which could be done.
Add a new [device*] section to NetworkManager.conf. This works similar
like the default connection settings in [connection*].
This will allow us to express per-device configuration in NetworkManager.conf
in our familar style.
Later, via NMConfig's write API it will be possible to make settings
accessible via D-Bus and persist them in NetworkManager-intern.conf.
This way, the user can both edit configuration snippets and modify
them via D-Bus, and also support installing default configuration
from the package.
In a way, a [device*] setting is similar to networkd's link files.
The match options is all encoded in the match-device specs.
One difference is, that the resulting setting can be merged together
by multiple section by partially overwriting them. This makes it
more flexible and allows for example to drop a configuration snippet
that only sets one property, while the rest can be merged from different
snippets.