manager: Ensure DHCP interface delete first when daemon stop

Given linux bridge/bond holds DHCP config with
`ipv4.dhcp-send-release: ture` or `ipv6.dhcp-send-release: true`,
when stopping NetworkManager daemon, then NM daemon might
remove/deactivate physical interface first causing DHCP release packet
cannot be delivered.

To fix the issue, we sort the device deletion to let software device
that holds DHCP config to remove first.

Merge Request: https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/2379

Co-authored-by: Rahul Rajesh <rajeshrah22@gmail.com>
This commit is contained in:
Gris Ge 2026-03-19 15:42:47 +08:00 committed by Rahul Rajesh
parent 09784fcce3
commit 1747eb96d6

View file

@ -8146,6 +8146,27 @@ nm_manager_start(NMManager *self, GError **error)
return TRUE;
}
static int
compare_device_remove_order(const CList *a, const CList *b, const void *user_data)
{
NMDevice *dev_a = c_list_entry(a, NMDevice, devices_lst);
NMDevice *dev_b = c_list_entry(b, NMDevice, devices_lst);
gboolean a_has_dhcp =
nm_device_get_dhcp_config(dev_a, AF_INET) || nm_device_get_dhcp_config(dev_a, AF_INET6);
gboolean b_has_dhcp =
nm_device_get_dhcp_config(dev_b, AF_INET) || nm_device_get_dhcp_config(dev_b, AF_INET6);
gboolean a_is_software = nm_device_is_software(dev_a);
gboolean b_is_software = nm_device_is_software(dev_b);
/* priority: software AND dhcp first, then dhcp only
* then everything else,*/
uint a_score = a_has_dhcp ? (a_is_software ? 2 : 1) : 0;
uint b_score = b_has_dhcp ? (b_is_software ? 2 : 1) : 0;
return b_score - a_score;
}
void
nm_manager_stop(NMManager *self)
{
@ -8167,6 +8188,12 @@ nm_manager_stop(NMManager *self)
nm_dbus_manager_stop(nm_dbus_object_get_manager(NM_DBUS_OBJECT(self)));
/* When OVS internal interface or linux bridge holds DHCP, if we delete its
* physical interface first, then we cannot send out DHCP release request
* anymore. To fix that, we need to remove/deactivate software interfaces that
* holds DHCP config first.
*/
c_list_sort(&priv->devices_lst_head, compare_device_remove_order, NULL);
while ((device = c_list_first_entry(&priv->devices_lst_head, NMDevice, devices_lst)))
remove_device(self, device, TRUE);