From 805fad5649f96616ce6c176a46c572f2c6710e2c Mon Sep 17 00:00:00 2001 From: Rahul Rajesh Date: Mon, 20 Apr 2026 16:23:20 -0400 Subject: [PATCH] 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 --- src/core/devices/nm-device.c | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/src/core/devices/nm-device.c b/src/core/devices/nm-device.c index f8b0dad9c8..0995b4bdc6 100644 --- a/src/core/devices/nm-device.c +++ b/src/core/devices/nm-device.c @@ -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,