core: device: autoselect device match criteria in SetManaged()

Devices like veth without a permanent MAC address cannot be matched by
MAC. If using the BY_MAC flag in SetManaged(), the changes are not
effective for such kind of devices.

Add a BY_NAME flag, in addition to the BY_MAC one. If the client sets
one of them, it means to force this mode of matching. If none is
selected, the daemon will choose how to match, preferring matching by
MAC when possible, and by ifname when not possible.
This commit is contained in:
Íñigo Huguet 2026-02-25 12:30:50 +01:00
parent 8dc06c11de
commit d0fef16bcb
3 changed files with 59 additions and 15 deletions

View file

@ -14942,6 +14942,39 @@ typedef struct {
NMDeviceManagedFlags managed_flags;
} SetManagedData;
static gboolean
get_managed_by_mac(NMDevice *self, NMDeviceManagedFlags flags, GError **error)
{
gboolean is_fake_hwaddr;
if ((flags & NM_DEVICE_MANAGED_FLAGS_PERMANENT_BY_MAC)
&& (flags & NM_DEVICE_MANAGED_FLAGS_PERMANENT_BY_NAME)) {
g_set_error_literal(error,
NM_DEVICE_ERROR,
NM_DEVICE_ERROR_INVALID_ARGUMENT,
"cannot match both by 'mac' and by 'interface-name'");
return FALSE;
}
nm_device_get_permanent_hw_address_full(self, TRUE, &is_fake_hwaddr);
if ((flags & NM_DEVICE_MANAGED_FLAGS_PERMANENT_BY_MAC) && is_fake_hwaddr) {
g_set_error_literal(
error,
NM_DEVICE_ERROR,
NM_DEVICE_ERROR_INVALID_ARGUMENT,
"cannot match by 'mac': the device doesn't have a permanent MAC address");
return FALSE;
}
if (flags & NM_DEVICE_MANAGED_FLAGS_PERMANENT_BY_MAC)
return TRUE;
else if (flags & NM_DEVICE_MANAGED_FLAGS_PERMANENT_BY_NAME)
return FALSE;
else
return !is_fake_hwaddr;
}
/**
* set_managed:
* @self: the device
@ -14959,10 +14992,15 @@ static gboolean
set_managed(NMDevice *self, NMDeviceManaged managed, NMDeviceManagedFlags flags, GError **error)
{
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self);
gboolean managed_old, managed_new;
NMTernary managed_to_disk, managed_to_disk_old;
if (flags & NM_DEVICE_MANAGED_FLAGS_PERMANENT) {
NMTernary managed_to_disk, managed_to_disk_old;
gboolean by_mac;
by_mac = get_managed_by_mac(self, flags, error);
if (*error)
return FALSE;
managed_to_disk = managed == NM_DEVICE_MANAGED_RESET ? NM_TERNARY_DEFAULT : !!managed;
managed_to_disk_old = nm_config_get_device_managed(nm_manager_get_config(priv->manager),
nm_device_get_iface(self));
@ -14970,7 +15008,7 @@ set_managed(NMDevice *self, NMDeviceManaged managed, NMDeviceManagedFlags flags,
if (!nm_config_set_device_managed(nm_manager_get_config(priv->manager),
self,
managed_to_disk,
flags & NM_DEVICE_MANAGED_FLAGS_BY_MAC,
by_mac,
error))
return FALSE;
@ -14986,8 +15024,8 @@ set_managed(NMDevice *self, NMDeviceManaged managed, NMDeviceManagedFlags flags,
nm_config_set_device_managed(nm_manager_get_config(priv->manager),
self,
managed_to_disk_old,
flags & NM_DEVICE_MANAGED_FLAGS_BY_MAC,
error);
by_mac,
NULL);
g_set_error(error,
NM_DEVICE_ERROR,
NM_DEVICE_ERROR_FAILED,
@ -14999,6 +15037,8 @@ set_managed(NMDevice *self, NMDeviceManaged managed, NMDeviceManagedFlags flags,
}
if (flags & NM_DEVICE_MANAGED_FLAGS_RUNTIME) {
gboolean managed_old, managed_new;
if (managed == NM_DEVICE_MANAGED_RESET) {
nm_device_set_unmanaged_by_flags(self,
NM_UNMANAGED_USER_EXPLICIT,

View file

@ -1266,8 +1266,8 @@ typedef enum {
* @NM_DEVICE_MANAGED_FLAGS_NONE: no flag set.
* @NM_DEVICE_MANAGED_FLAGS_RUNTIME: to set the device managed state to the runtime value.
* @NM_DEVICE_MANAGED_FLAGS_PERMANENT: to set the device managed state to the permanent (on disk) value.
* @NM_DEVICE_MANAGED_FLAGS_BY_MAC: to match the device by MAC address, not by name.
* This option only makes sense together with %NM_DEVICE_MANAGED_FLAGS_PERMANENT.
* @NM_DEVICE_MANAGED_FLAGS_PERMANENT_BY_NAME: to match the device by name, not by MAC address.
* @NM_DEVICE_MANAGED_FLAGS_PERMANENT_BY_MAC: to match the device by MAC address, not by name.
* @NM_DEVICE_MANAGED_FLAGS_SET_ADMIN_STATE: to set the administrative state of the
* device to up if the managed state is %NM_DEVICE_MANAGED_YES, and down if the managed state
* is %NM_DEVICE_MANAGED_NO. If the flag is not set, the administrative state is not changed.
@ -1275,14 +1275,19 @@ typedef enum {
*
* Flags for the SetManaged() D-Bus call of a device and nm_device_set_managed_async().
*
* %NM_DEVICE_MANAGED_FLAGS_PERMANENT_BY_NAME and %NM_DEVICE_MANAGED_FLAGS_PERMANENT_BY_MAC
* are mutually exclusive, and they only make sense together with %NM_DEVICE_MANAGED_FLAGS_PERMANENT.
* If none is set, the matching criteria is selected automatically.
*
* Since: 1.58
*/
typedef enum /*< flags >*/ {
NM_DEVICE_MANAGED_FLAGS_NONE = 0,
NM_DEVICE_MANAGED_FLAGS_RUNTIME = 0x1,
NM_DEVICE_MANAGED_FLAGS_PERMANENT = 0x2,
NM_DEVICE_MANAGED_FLAGS_BY_MAC = 0x4,
NM_DEVICE_MANAGED_FLAGS_SET_ADMIN_STATE = 0x8,
NM_DEVICE_MANAGED_FLAGS_NONE = 0,
NM_DEVICE_MANAGED_FLAGS_RUNTIME = 0x1,
NM_DEVICE_MANAGED_FLAGS_PERMANENT = 0x2,
NM_DEVICE_MANAGED_FLAGS_PERMANENT_BY_NAME = 0x4,
NM_DEVICE_MANAGED_FLAGS_PERMANENT_BY_MAC = 0x8,
NM_DEVICE_MANAGED_FLAGS_SET_ADMIN_STATE = 0x10,
} NMDeviceManagedFlags;
/**

View file

@ -2903,10 +2903,9 @@ do_device_set(const NMCCommand *cmd, NmCli *nmc, int argc, const char *const *ar
nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
return;
} else if (perm) {
flags |= NM_DEVICE_MANAGED_FLAGS_RUNTIME | NM_DEVICE_MANAGED_FLAGS_PERMANENT
| NM_DEVICE_MANAGED_FLAGS_BY_MAC;
flags |= NM_DEVICE_MANAGED_FLAGS_RUNTIME | NM_DEVICE_MANAGED_FLAGS_PERMANENT;
} else if (perm_only) {
flags |= NM_DEVICE_MANAGED_FLAGS_PERMANENT | NM_DEVICE_MANAGED_FLAGS_BY_MAC;
flags |= NM_DEVICE_MANAGED_FLAGS_PERMANENT;
} else {
/* If --permanent/--permanent-only are missing, set only the runtime flag, as this
* is how it used to work when these options didn't exist. */