device, dhcp: cleanup DHCP before devices removed

* Add _dev_ipdhcpx_cleanup in __set_state_full in DEACTIVATING STATE
  before STATE_CHANGED signal is emitted to ensure DHCP RELEASE
  packet is sent.
* Set a flag after dhcp cleanup is done and check that flag is false
  before calling dhcp cleanup function.

Assisted-by: Cursor with Claude Opus 4.5
This commit is contained in:
Rahul Rajesh 2026-04-20 16:23:20 -04:00
parent 9303996b44
commit 805fad5649

View file

@ -801,6 +801,8 @@ typedef struct _NMDevicePrivate {
bool needs_ip6_subnet : 1;
bool dhcp_cleanup_done : 1;
NMOptionBool promisc_reset;
GVariant *ports_variant; /* Array of port devices D-Bus path */
@ -9272,10 +9274,13 @@ nm_device_removed(NMDevice *self, gboolean unconfigure_ip_config)
g_return_if_fail(NM_IS_DEVICE(self));
_dev_ipdhcpx_cleanup(self, AF_INET, TRUE, FALSE);
_dev_ipdhcpx_cleanup(self, AF_INET6, TRUE, FALSE);
priv = NM_DEVICE_GET_PRIVATE(self);
if (!priv->dhcp_cleanup_done) {
_dev_ipdhcpx_cleanup(self, AF_INET, TRUE, FALSE);
_dev_ipdhcpx_cleanup(self, AF_INET6, TRUE, FALSE);
priv->dhcp_cleanup_done = TRUE;
}
if (priv->controller) {
/* this is called when something externally messes with the port or during shut-down.
* Release the port from controller, but don't touch the device. */
@ -14271,7 +14276,8 @@ _cleanup_ip_pre(NMDevice *self, int addr_family, CleanupType cleanup_type, gbool
_dev_ipdev_cleanup(self, AF_UNSPEC);
_dev_ipdev_cleanup(self, addr_family);
_dev_ipdhcpx_cleanup(self, addr_family, !preserve_dhcp || !keep_reapply, FALSE);
if (!priv->dhcp_cleanup_done)
_dev_ipdhcpx_cleanup(self, addr_family, !preserve_dhcp || !keep_reapply, FALSE);
if (IS_IPv4) {
priv->ipll_data_4.v4.mode = NM_SETTING_IP4_LL_DISABLED;
@ -18002,6 +18008,7 @@ _set_state_full(NMDevice *self, NMDeviceState state, NMDeviceStateReason reason,
break;
case NM_DEVICE_STATE_PREPARE:
nm_device_update_initial_hw_address(self);
priv->dhcp_cleanup_done = FALSE;
break;
case NM_DEVICE_STATE_NEED_AUTH:
if (old_state > NM_DEVICE_STATE_NEED_AUTH) {
@ -18029,6 +18036,17 @@ _set_state_full(NMDevice *self, NMDeviceState state, NMDeviceStateReason reason,
(guint32) state,
(guint32) old_state,
(guint32) reason);
/* In case where an internal OVS ports holds a DHCP lease, and
* we need to send a RELEASE packet, we must cleanup DHCP before
* the STATE_CHANGD signal is emitted so that the internal port
* is not detached from the controller OVS Port and removed from
* the kernel link by ovs-vswitchd */
if (state == NM_DEVICE_STATE_DEACTIVATING && !priv->dhcp_cleanup_done) {
_dev_ipdhcpx_cleanup(self, AF_INET, TRUE, FALSE);
_dev_ipdhcpx_cleanup(self, AF_INET6, TRUE, FALSE);
priv->dhcp_cleanup_done = TRUE;
}
g_signal_emit(self,
signals[STATE_CHANGED],
0,