summary refs log tree commit diff
path: root/src/core/devices
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/devices')
-rw-r--r--src/core/devices/nm-device-hsr.c36
-rw-r--r--src/core/devices/nm-device-utils.c6
-rw-r--r--src/core/devices/nm-device.c265
-rw-r--r--src/core/devices/nm-device.h23
-rw-r--r--src/core/devices/ovs/nm-ovsdb.c32
5 files changed, 256 insertions, 106 deletions
diff --git a/src/core/devices/nm-device-hsr.c b/src/core/devices/nm-device-hsr.c
index 59454ee3..e3368a5b 100644
--- a/src/core/devices/nm-device-hsr.c
+++ b/src/core/devices/nm-device-hsr.c
@@ -116,29 +116,51 @@ create_and_realize(NMDevice              *device,
                    const NMPlatformLink **out_plink,
                    GError               **error)
 {
-    const char      *iface = nm_device_get_iface(device);
-    NMSettingHsr    *s_hsr;
-    NMPlatformLnkHsr lnk = {};
-    int              r;
+    const char        *iface   = nm_device_get_iface(device);
+    nm_auto_free char *err_msg = NULL;
+    NMSettingHsr      *s_hsr;
+    NMPlatformLnkHsr   lnk = {};
+    int                r   = 0;
 
     s_hsr = _nm_connection_get_setting(connection, NM_TYPE_SETTING_HSR);
+
     nm_assert(s_hsr);
 
     if (nm_setting_hsr_get_port1(s_hsr) != NULL)
         lnk.port1 = nm_platform_link_get_ifindex(NM_PLATFORM_GET, nm_setting_hsr_get_port1(s_hsr));
     if (nm_setting_hsr_get_port2(s_hsr) != NULL)
         lnk.port2 = nm_platform_link_get_ifindex(NM_PLATFORM_GET, nm_setting_hsr_get_port2(s_hsr));
-    lnk.multicast_spec = nm_setting_hsr_get_multicast_spec(s_hsr);
-    lnk.prp            = nm_setting_hsr_get_prp(s_hsr);
+    if (nm_setting_hsr_get_interlink(s_hsr) != NULL) {
+        const char *ifname  = nm_setting_hsr_get_interlink(s_hsr);
+        int         ifindex = nm_platform_link_get_ifindex(NM_PLATFORM_GET, ifname);
+
+        if (ifindex <= 0) {
+            err_msg = g_strdup_printf("interlink port '%s' does not exist", ifname);
+            goto out;
+        }
+
+        lnk.interlink = ifindex;
+    }
+
+    lnk.multicast_spec   = nm_setting_hsr_get_multicast_spec(s_hsr);
+    lnk.prp              = nm_setting_hsr_get_prp(s_hsr);
+    lnk.protocol_version = nm_setting_hsr_get_protocol_version(s_hsr);
+
     r = nm_platform_link_hsr_add(nm_device_get_platform(device), iface, &lnk, out_plink);
+
     if (r < 0) {
+        err_msg = g_strdup(nm_strerror(r) ?: "unknown");
+    }
+
+out:
+    if (err_msg) {
         g_set_error(error,
                     NM_DEVICE_ERROR,
                     NM_DEVICE_ERROR_CREATION_FAILED,
                     "Failed to create HSR interface '%s' for '%s': %s",
                     iface,
                     nm_connection_get_id(connection),
-                    nm_strerror(r));
+                    err_msg);
         return FALSE;
     }
 
diff --git a/src/core/devices/nm-device-utils.c b/src/core/devices/nm-device-utils.c
index 9fd7ac9d..a78499fb 100644
--- a/src/core/devices/nm-device-utils.c
+++ b/src/core/devices/nm-device-utils.c
@@ -135,13 +135,15 @@ NM_UTILS_LOOKUP_STR_DEFINE(
     NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_UNMANAGED_LINK_NOT_INIT,
                              "unmanaged-link-not-init"),
     NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_UNMANAGED_QUITTING, "unmanaged-quitting"),
-    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_UNMANAGED_SLEEPING, "unmanaged-sleeping"),
+    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_UNMANAGED_MANAGER_DISABLED,
+                             "unmanaged-nm-disabled"),
     NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_UNMANAGED_USER_CONF, "unmanaged-user-conf"),
     NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_UNMANAGED_USER_EXPLICIT,
                              "unmanaged-user-explicit"),
     NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_UNMANAGED_USER_SETTINGS,
                              "unmanaged-user-settings"),
-    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_UNMANAGED_USER_UDEV, "unmanaged-user-udev"), );
+    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_UNMANAGED_USER_UDEV, "unmanaged-user-udev"),
+    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_NETWORKING_OFF, "networking-off"), );
 
 NM_UTILS_LOOKUP_STR_DEFINE(nm_device_mtu_source_to_string,
                            NMDeviceMtuSource,
diff --git a/src/core/devices/nm-device.c b/src/core/devices/nm-device.c
index 79b1076d..6bba982a 100644
--- a/src/core/devices/nm-device.c
+++ b/src/core/devices/nm-device.c
@@ -692,6 +692,8 @@ typedef struct _NMDevicePrivate {
 
     IPDevStateData ipdev_data_unspec;
 
+    gulong sharing_ipv4_changed_id;
+
     struct {
         /* If we set the addrgenmode6, this records the previously set value. */
         guint8 previous_mode_val;
@@ -779,7 +781,6 @@ typedef struct _NMDevicePrivate {
     char     *prop_ip_iface; /* IP interface D-Bus property */
     GList    *ping_operations;
     GSource  *ping_timeout;
-    bool      refresh_forwarding_done : 1;
 } NMDevicePrivate;
 
 G_DEFINE_ABSTRACT_TYPE(NMDevice, nm_device, NM_TYPE_DBUS_OBJECT)
@@ -867,6 +868,8 @@ static void _dev_ipshared4_spawn_dnsmasq(NMDevice *self);
 
 static void _dev_ipshared6_start(NMDevice *self);
 
+static void _dev_ipforwarding4_start(NMDevice *self, int addr_family);
+
 static void
 _cleanup_ip_pre(NMDevice *self, int addr_family, CleanupType cleanup_type, gboolean preserve_dhcp);
 
@@ -2131,8 +2134,8 @@ _prop_get_ipvx_dhcp_send_hostname(NMDevice *self, int addr_family)
     return send_hostname_v2;
 }
 
-NMSettingIPConfigForwarding
-nm_device_get_ipv4_forwarding(NMDevice *self)
+static NMSettingIPConfigForwarding
+_prop_get_ipv4_forwarding(NMDevice *self)
 {
     NMSettingIPConfig          *s_ip;
     NMSettingIPConfigForwarding forwarding;
@@ -3778,7 +3781,7 @@ nm_device_assume_state_reset(NMDevice *self)
 
 /*****************************************************************************/
 
-char *
+static char *
 nm_device_sysctl_ip_conf_get(NMDevice *self, int addr_family, const char *property)
 {
     const char *ifname;
@@ -6645,7 +6648,7 @@ concheck_update_state(NMDevice           *self,
     }
 }
 
-const char *
+static const char *
 nm_device_get_effective_ip_config_method(NMDevice *self, int addr_family)
 {
     NMDeviceClass *klass;
@@ -8639,6 +8642,8 @@ nm_device_unrealize(NMDevice *self, gboolean remove_resources, GError **error)
 
     g_object_thaw_notify(G_OBJECT(self));
 
+    nm_device_managed_type_set(self, NM_DEVICE_MANAGED_TYPE_REMOVED);
+
     nm_device_set_unmanaged_flags(self, NM_UNMANAGED_PLATFORM_INIT, TRUE);
 
     nm_device_set_unmanaged_flags(self,
@@ -10394,6 +10399,43 @@ sriov_params_cb(GError *error, gpointer user_data)
     nm_device_activate_schedule_stage1_device_prepare(self, FALSE);
 }
 
+static gboolean
+sriov_gen_platform_vfs(NMDevice       *self,
+                       NMSettingSriov *s_sriov,
+                       NMPlatformVF ***plat_vfs_out,
+                       GError        **error)
+{
+    nm_auto_freev NMPlatformVF **plat_vfs = NULL;
+    guint                        num;
+
+    nm_assert(s_sriov);
+    nm_assert(plat_vfs_out && !*plat_vfs_out);
+
+    num      = nm_setting_sriov_get_num_vfs(s_sriov);
+    plat_vfs = g_new0(NMPlatformVF *, num + 1);
+
+    for (int i = 0; i < num; i++) {
+        NMSriovVF            *vf    = nm_setting_sriov_get_vf(s_sriov, i);
+        gs_free_error GError *local = NULL;
+
+        plat_vfs[i] = sriov_vf_config_to_platform(self, vf, &local);
+
+        if (!plat_vfs[i]) {
+            g_set_error(error,
+                        local->domain,
+                        local->code,
+                        "VF '%s' is invalid: %s",
+                        nm_utils_sriov_vf_to_str(vf, FALSE, NULL),
+                        local->message);
+            return FALSE;
+        }
+    }
+
+    *plat_vfs_out = g_steal_pointer(&plat_vfs);
+
+    return TRUE;
+}
+
 /*
  * activate_stage1_device_prepare
  *
@@ -10440,10 +10482,7 @@ activate_stage1_device_prepare(NMDevice *self)
         if (s_sriov && nm_device_has_capability(self, NM_DEVICE_CAP_SRIOV)) {
             nm_auto_freev NMPlatformVF **plat_vfs = NULL;
             gs_free_error GError        *error    = NULL;
-            NMSriovVF                   *vf;
             NMTernary                    autoprobe;
-            guint                        num;
-            guint                        i;
 
             autoprobe = nm_setting_sriov_get_autoprobe_drivers(s_sriov);
             if (autoprobe == NM_TERNARY_DEFAULT) {
@@ -10456,21 +10495,12 @@ activate_stage1_device_prepare(NMDevice *self)
                     NM_OPTION_BOOL_TRUE);
             }
 
-            num      = nm_setting_sriov_get_num_vfs(s_sriov);
-            plat_vfs = g_new0(NMPlatformVF *, num + 1);
-            for (i = 0; i < num; i++) {
-                vf          = nm_setting_sriov_get_vf(s_sriov, i);
-                plat_vfs[i] = sriov_vf_config_to_platform(self, vf, &error);
-                if (!plat_vfs[i]) {
-                    _LOGE(LOGD_DEVICE,
-                          "failed to apply SR-IOV VF '%s': %s",
-                          nm_utils_sriov_vf_to_str(vf, FALSE, NULL),
-                          error->message);
-                    nm_device_state_changed(self,
-                                            NM_DEVICE_STATE_FAILED,
-                                            NM_DEVICE_STATE_REASON_SRIOV_CONFIGURATION_FAILED);
-                    return;
-                }
+            if (!sriov_gen_platform_vfs(self, s_sriov, &plat_vfs, &error)) {
+                _LOGE(LOGD_DEVICE, "cannot parse the VF list: %s", error->message);
+                nm_device_state_changed(self,
+                                        NM_DEVICE_STATE_FAILED,
+                                        NM_DEVICE_STATE_REASON_SRIOV_CONFIGURATION_FAILED);
+                return;
             }
 
             /* When changing the number of VFs the kernel can block
@@ -13139,17 +13169,12 @@ activate_stage3_ip_config_for_addr_family(NMDevice *self, int addr_family)
         goto out_devip;
 
     if (IS_IPv4) {
-        NMSettingIPConfigForwarding ipv4_forwarding = nm_device_get_ipv4_forwarding(self);
-
-        if (NM_IN_SET(ipv4_forwarding,
-                      NM_SETTING_IP_CONFIG_FORWARDING_NO,
-                      NM_SETTING_IP_CONFIG_FORWARDING_YES)) {
-            nm_device_sysctl_ip_conf_set(self, AF_INET, "forwarding", ipv4_forwarding ? "1" : "0");
-        }
         priv->ipll_data_4.v4.mode = _prop_get_ipv4_link_local(self);
         if (priv->ipll_data_4.v4.mode == NM_SETTING_IP4_LL_ENABLED)
             _dev_ipll4_start(self);
 
+        _dev_ipforwarding4_start(self, addr_family);
+
         if (nm_streq(priv->ipv4_method, NM_SETTING_IP4_CONFIG_METHOD_AUTO))
             _dev_ipdhcpx_start(self, AF_INET);
         else if (nm_streq(priv->ipv4_method, NM_SETTING_IP4_CONFIG_METHOD_LINK_LOCAL)) {
@@ -13469,15 +13494,21 @@ nm_device_activate_schedule_stage3_ip_config(NMDevice *self, gboolean do_sync)
 static void
 _dev_ipsharedx_set_state(NMDevice *self, int addr_family, NMDeviceIPState state)
 {
-    NMDevicePrivate *priv    = NM_DEVICE_GET_PRIVATE(self);
-    const int        IS_IPv4 = NM_IS_IPv4(addr_family);
+    NMDevicePrivate *priv      = NM_DEVICE_GET_PRIVATE(self);
+    const int        IS_IPv4   = NM_IS_IPv4(addr_family);
+    NMDeviceIPState  old_state = priv->ipshared_data_x[IS_IPv4].state;
 
-    if (priv->ipshared_data_x[IS_IPv4].state != state) {
+    if (old_state != state) {
         _LOGD_ipshared(addr_family,
                        "set state %s (was %s)",
                        nm_device_ip_state_to_string(state),
-                       nm_device_ip_state_to_string(priv->ipshared_data_x[IS_IPv4].state));
+                       nm_device_ip_state_to_string(old_state));
         priv->ipshared_data_x[IS_IPv4].state = state;
+
+        if (old_state == NM_DEVICE_IP_STATE_READY || state == NM_DEVICE_IP_STATE_READY)
+            nm_manager_update_shared_connection(NM_MANAGER_GET,
+                                                addr_family,
+                                                state == NM_DEVICE_IP_STATE_READY);
     }
 }
 
@@ -13772,6 +13803,106 @@ _dev_ipshared6_start(NMDevice *self)
 
 /*****************************************************************************/
 
+/**
+ * Set the device's forwarding to the specified value. If %NM_TERNARY_DEFAULT is specified,
+ * it's set to the kernel's default, otherwise it's set to the specific value.
+ */
+static void
+_dev_ipforwarding4_set(NMDevice *self, NMTernary val)
+{
+    gs_free const char *default_forwarding = NULL;
+    gs_free const char *current_forwarding = NULL;
+    const char         *val_str;
+
+    if (val != NM_TERNARY_DEFAULT) {
+        val_str = val ? "1" : "0";
+    } else {
+        default_forwarding = nm_platform_sysctl_get(
+            nm_device_get_platform(self),
+            NMP_SYSCTL_PATHID_ABSOLUTE("/proc/sys/net/ipv4/conf/default/forwarding"));
+
+        if (!default_forwarding) {
+            _LOGW(LOGD_DEVICE,
+                  "error setting IPv4 forwarding: can't read default forwarding value: %s",
+                  nm_strerror_native(errno));
+            return; /* Non fatal */
+        }
+
+        val_str = default_forwarding;
+    }
+
+    current_forwarding = nm_device_sysctl_ip_conf_get(self, AF_INET, "forwarding");
+    if (nm_streq0(current_forwarding, val_str))
+        return;
+
+    if (!nm_device_sysctl_ip_conf_set(self, AF_INET, "forwarding", val_str))
+        _LOGW(LOGD_DEVICE,
+              "error setting IPv4 forwarding to '%s': %s",
+              val_str,
+              nm_strerror_native(errno));
+}
+
+static void
+_dev_ipforwarding4_auto_cb(NMManager *manager, gboolean sharing_ipv4, gpointer data)
+{
+    NMDevice *self = NM_DEVICE(data);
+
+    _dev_ipforwarding4_set(self, sharing_ipv4 ? NM_TERNARY_TRUE : NM_TERNARY_DEFAULT);
+}
+
+static void
+_dev_ipforwarding4_start(NMDevice *self, int addr_family)
+{
+    NMDevicePrivate            *priv            = NM_DEVICE_GET_PRIVATE(self);
+    NMSettingIPConfigForwarding ipv4_forwarding = _prop_get_ipv4_forwarding(self);
+    NMTernary                   new_forwarding  = NM_TERNARY_DEFAULT;
+
+    /* IPv6 per-interface forwarding not supported yet */
+    if (addr_family != AF_INET)
+        return;
+
+    if (nm_streq(priv->ipv4_method, NM_SETTING_IP4_CONFIG_METHOD_SHARED)) {
+        new_forwarding = NM_TERNARY_TRUE;
+    } else if (ipv4_forwarding == NM_SETTING_IP_CONFIG_FORWARDING_YES) {
+        new_forwarding = NM_TERNARY_TRUE;
+    } else if (ipv4_forwarding == NM_SETTING_IP_CONFIG_FORWARDING_NO) {
+        new_forwarding = NM_TERNARY_FALSE;
+    } else if (ipv4_forwarding == NM_SETTING_IP_CONFIG_FORWARDING_AUTO) {
+        if (nm_manager_get_sharing_ipv4(NM_MANAGER_GET))
+            new_forwarding = NM_TERNARY_TRUE;
+        else
+            new_forwarding = NM_TERNARY_DEFAULT;
+
+        if (!priv->sharing_ipv4_changed_id)
+            priv->sharing_ipv4_changed_id = g_signal_connect(NM_MANAGER_GET,
+                                                             NM_MANAGER_SHARING_IPV4_CHANGED,
+                                                             G_CALLBACK(_dev_ipforwarding4_auto_cb),
+                                                             self);
+    } else {
+        nm_assert_not_reached();
+    }
+
+    _dev_ipforwarding4_set(self, new_forwarding);
+}
+
+static void
+_dev_ipforwarding_cleanup(NMDevice *self, int addr_family, CleanupType cleanup_type)
+{
+    NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self);
+
+    if (!NM_IS_IPv4(addr_family))
+        return;
+
+    nm_clear_g_signal_handler(NM_MANAGER_GET, &priv->sharing_ipv4_changed_id);
+
+    if (NM_IN_SET(cleanup_type, CLEANUP_TYPE_DECONFIGURE, CLEANUP_TYPE_KEEP_REAPPLY)) {
+        /* Deconfigure by restoring kernel's default */
+        _dev_ipforwarding4_set(self, NM_TERNARY_DEFAULT);
+    }
+}
+
+/*****************************************************************************/
+
 static void
 act_request_set(NMDevice *self, NMActRequest *act_request)
 {
@@ -13884,6 +14015,8 @@ _cleanup_ip_pre(NMDevice *self, int addr_family, CleanupType cleanup_type, gbool
     NMDevicePrivate *priv         = NM_DEVICE_GET_PRIVATE(self);
     gboolean         keep_reapply = (cleanup_type == CLEANUP_TYPE_KEEP_REAPPLY);
 
+    _dev_ipforwarding_cleanup(self, addr_family, cleanup_type);
+
     _dev_ipsharedx_cleanup(self, addr_family);
 
     _dev_ipdev_cleanup(self, AF_UNSPEC);
@@ -14066,7 +14199,8 @@ can_reapply_change(NMDevice   *self,
         return nm_device_hash_check_invalid_keys(diffs,
                                                  NM_SETTING_SRIOV_SETTING_NAME,
                                                  error,
-                                                 NM_SETTING_SRIOV_PRESERVE_ON_DOWN);
+                                                 NM_SETTING_SRIOV_PRESERVE_ON_DOWN,
+                                                 NM_SETTING_SRIOV_VFS);
     }
 
 out_fail:
@@ -14244,9 +14378,35 @@ check_and_reapply_connection(NMDevice            *self,
 
     nm_device_link_properties_set(self, TRUE);
 
-    if (priv->state >= NM_DEVICE_STATE_CONFIG)
+    if (priv->state >= NM_DEVICE_STATE_CONFIG) {
+        GHashTable *sriov_diff;
+
         lldp_setup(self, NM_TERNARY_DEFAULT);
 
+        sriov_diff = nm_g_hash_table_lookup(diffs, NM_SETTING_SRIOV_SETTING_NAME);
+
+        if (sriov_diff && nm_g_hash_table_lookup(sriov_diff, NM_SETTING_SRIOV_VFS)) {
+            nm_auto_freev NMPlatformVF **plat_vfs = NULL;
+            NMSettingSriov              *s_sriov;
+
+            s_sriov = (NMSettingSriov *) nm_connection_get_setting(applied, NM_TYPE_SETTING_SRIOV);
+
+            if (s_sriov) {
+                gs_free_error GError *local = NULL;
+
+                if (!sriov_gen_platform_vfs(self, s_sriov, &plat_vfs, &local)
+                    || !nm_platform_link_set_sriov_vfs(nm_device_get_platform(self),
+                                                       priv->ifindex,
+                                                       (const NMPlatformVF *const *) plat_vfs)) {
+                    _LOGE(LOGD_DEVICE,
+                          "failed to reapply SRIOV VFs%s%s",
+                          local ? ": " : "",
+                          local ? local->message : "");
+                }
+            }
+        }
+    }
+
     if (priv->state >= NM_DEVICE_STATE_IP_CONFIG) {
         /* Allow reapply of MTU */
         priv->mtu_source = NM_DEVICE_MTU_SOURCE_NONE;
@@ -15613,7 +15773,7 @@ nm_device_get_firmware_missing(NMDevice *self)
 
 NM_UTILS_FLAGS2STR_DEFINE(nm_unmanaged_flags2str,
                           NMUnmanagedFlags,
-                          NM_UTILS_FLAGS2STR(NM_UNMANAGED_SLEEPING, "sleeping"),
+                          NM_UTILS_FLAGS2STR(NM_UNMANAGED_MANAGER_DISABLED, "nm-disabled"),
                           NM_UTILS_FLAGS2STR(NM_UNMANAGED_QUITTING, "quitting"),
                           NM_UTILS_FLAGS2STR(NM_UNMANAGED_PLATFORM_INIT, "platform-init"),
                           NM_UTILS_FLAGS2STR(NM_UNMANAGED_USER_EXPLICIT, "user-explicit"),
@@ -15677,8 +15837,8 @@ unmanaged_flags_to_reason(NMUnmanagedFlags flags)
     /* Even if there are multiple flags, we can only return one reason.
      * Return the most important reason.
      */
-    if (NM_FLAGS_HAS(flags, NM_UNMANAGED_SLEEPING))
-        return NM_DEVICE_STATE_REASON_UNMANAGED_SLEEPING;
+    if (NM_FLAGS_HAS(flags, NM_UNMANAGED_MANAGER_DISABLED))
+        return NM_DEVICE_STATE_REASON_UNMANAGED_MANAGER_DISABLED;
     if (NM_FLAGS_HAS(flags, NM_UNMANAGED_QUITTING))
         return NM_DEVICE_STATE_REASON_UNMANAGED_QUITTING;
     if (NM_FLAGS_HAS(flags, NM_UNMANAGED_USER_SETTINGS))
@@ -16923,8 +17083,6 @@ _cleanup_generic_post(NMDevice *self, NMDeviceStateReason reason, CleanupType cl
     priv->v4_route_table_all_sync_before = FALSE;
     priv->v6_route_table_all_sync_before = FALSE;
 
-    priv->refresh_forwarding_done = FALSE;
-
     priv->mtu_force_set_done = FALSE;
 
     priv->needs_ip6_subnet = FALSE;
@@ -16970,7 +17128,6 @@ nm_device_cleanup(NMDevice *self, NMDeviceStateReason reason, CleanupType cleanu
     NMDevicePrivate *priv;
     NMDeviceClass   *klass = NM_DEVICE_GET_CLASS(self);
     int              ifindex;
-    gint32           default_forwarding_v4;
 
     g_return_if_fail(NM_IS_DEVICE(self));
 
@@ -16993,17 +17150,6 @@ nm_device_cleanup(NMDevice *self, NMDeviceStateReason reason, CleanupType cleanu
         nm_device_sysctl_ip_conf_set(self, AF_INET6, "use_tempaddr", "0");
     }
 
-    /* Restoring the device's forwarding to the sysctl default is necessary because
-     * `refresh_forwarding()` only updates forwarding on activated devices. */
-    default_forwarding_v4 = nm_platform_sysctl_get_int32(
-        nm_device_get_platform(self),
-        NMP_SYSCTL_PATHID_ABSOLUTE("/proc/sys/net/ipv4/conf/default/forwarding"),
-        0);
-    nm_device_sysctl_ip_conf_set(self,
-                                 AF_INET,
-                                 "forwarding",
-                                 default_forwarding_v4 == 1 ? "1" : "0");
-
     /* Call device type-specific deactivation */
     if (klass->deactivate)
         klass->deactivate(self);
@@ -18947,19 +19093,6 @@ nm_device_get_hostname_from_dns_lookup(NMDevice *self, int addr_family, gboolean
     return nm_assert_unreachable_val(NULL);
 }
 
-gboolean
-nm_device_get_refresh_forwarding_done(NMDevice *self)
-{
-    return NM_DEVICE_GET_PRIVATE(self)->refresh_forwarding_done;
-}
-
-void
-nm_device_set_refresh_forwarding_done(NMDevice *self, gboolean is_refresh_forwarding_done)
-{
-    NMDevicePrivate *priv         = NM_DEVICE_GET_PRIVATE(self);
-    priv->refresh_forwarding_done = is_refresh_forwarding_done;
-}
-
 /*****************************************************************************/
 
 static const char *
diff --git a/src/core/devices/nm-device.h b/src/core/devices/nm-device.h
index 9a663c2a..8632944a 100644
--- a/src/core/devices/nm-device.h
+++ b/src/core/devices/nm-device.h
@@ -581,7 +581,8 @@ void nm_device_copy_ip6_dns_config(NMDevice *self, NMDevice *from_device);
 /**
  * NMUnmanagedFlags:
  * @NM_UNMANAGED_NONE: placeholder value
- * @NM_UNMANAGED_SLEEPING: %TRUE when unmanaged because NM is sleeping.
+ * @NM_UNMANAGED_MANAGER_DISABLED: %TRUE when unmanaged because NM is disabled.
+ *   Currently, this happens when sleeping or with networking disabled.
  * @NM_UNMANAGED_QUITTING: %TRUE when unmanaged because NM is shutting down.
  * @NM_UNMANAGED_PLATFORM_INIT: %TRUE when unmanaged because platform link not
  *   yet initialized. Unrealized device are also unmanaged for this reason.
@@ -610,11 +611,11 @@ typedef enum {
 
     /* these flags are authoritative. If one of them is set,
      * the device cannot be managed. */
-    NM_UNMANAGED_SLEEPING      = (1LL << 0),
-    NM_UNMANAGED_QUITTING      = (1LL << 1),
-    NM_UNMANAGED_PLATFORM_INIT = (1LL << 2),
-    NM_UNMANAGED_USER_EXPLICIT = (1LL << 3),
-    NM_UNMANAGED_USER_SETTINGS = (1LL << 4),
+    NM_UNMANAGED_MANAGER_DISABLED = (1LL << 0),
+    NM_UNMANAGED_QUITTING         = (1LL << 1),
+    NM_UNMANAGED_PLATFORM_INIT    = (1LL << 2),
+    NM_UNMANAGED_USER_EXPLICIT    = (1LL << 3),
+    NM_UNMANAGED_USER_SETTINGS    = (1LL << 4),
 
     /* These flags can be non-effective and be overwritten
      * by other flags. */
@@ -852,14 +853,4 @@ void nm_routing_rules_sync(NMConnection *applied_connection,
                            NMDevice *self,
                            NMNetns  *netns);
 
-NMSettingIPConfigForwarding nm_device_get_ipv4_forwarding(NMDevice *self);
-
-const char *nm_device_get_effective_ip_config_method(NMDevice *self, int addr_family);
-
-char *nm_device_sysctl_ip_conf_get(NMDevice *self, int addr_family, const char *property);
-
-gboolean nm_device_get_refresh_forwarding_done(NMDevice *self);
-
-void nm_device_set_refresh_forwarding_done(NMDevice *self, gboolean is_refresh_forwarding_done);
-
 #endif /* __NETWORKMANAGER_DEVICE_H__ */
diff --git a/src/core/devices/ovs/nm-ovsdb.c b/src/core/devices/ovs/nm-ovsdb.c
index 164bfd35..1c9484c4 100644
--- a/src/core/devices/ovs/nm-ovsdb.c
+++ b/src/core/devices/ovs/nm-ovsdb.c
@@ -1460,40 +1460,42 @@ _delete_interface(NMOvsdb *self, json_t *params, const char *ifname)
                 json_array_append_new(new_interfaces, json_pack("[s,s]", "uuid", interface_uuid));
             }
 
-            if (num_nm_interfaces == 0) {
-                /* The port no longer has any NM interface. Don't add it to "new_ports" and set
-                 * ports_changed=TRUE, so that it will be deleted. */
+            if (interfaces_changed && num_nm_interfaces == 0) {
+                /* We are deleting the last nm-interface of this port. Don't add it to "new_ports"
+                 * and set ports_changed=TRUE, so that it will be deleted. */
                 ports_changed = TRUE;
             } else {
+                /* Keep this port: it's still alive, or it's unrelated to the deleted interface */
+                json_array_append_new(new_ports, json_pack("[s,s]", "uuid", port_uuid));
+                if (ovs_port->connection_uuid)
+                    num_nm_ports++;
+
                 if (interfaces_changed) {
-                    /* An interface needs to be deleted from this port */
+                    /* This port is still alive, but an interface needs to be deleted from it */
                     _expect_port_interfaces(params, ovs_port->name, interfaces);
                     _set_port_interfaces(params, ovs_port->name, new_interfaces);
                 }
-                /* The port is still alive */
-                json_array_append_new(new_ports, json_pack("[s,s]", "uuid", port_uuid));
-                if (ovs_port->connection_uuid)
-                    num_nm_ports++;
             }
         }
 
-        if (num_nm_ports == 0) {
-            /* The bridge no longer has any NM port. Don't add it to "new_bridges" and set
-             * bridges_changed=TRUE, so that it will be deleted. */
+        if (ports_changed && num_nm_ports == 0) {
+            /* We are deleting the last nm-port of this bridge. Don't add it to "new_bridges"
+             * and set bridges_changed=TRUE, so that it will be deleted. */
             bridges_changed = TRUE;
         } else {
+            /* Keep this bridge: it's still alive, or it's unrelated to the deleted interface */
+            json_array_append_new(new_bridges, json_pack("[s,s]", "uuid", ovs_bridge->bridge_uuid));
+
             if (ports_changed) {
-                /* A port needs to be deleted from this bridge */
+                /* This bridge is still alive, but a port needs to be deleted from it */
                 _expect_bridge_ports(params, ovs_bridge->name, ports);
                 _set_bridge_ports(params, ovs_bridge->name, new_ports);
             }
-            /* The bridge is still alive */
-            json_array_append_new(new_bridges, json_pack("[s,s]", "uuid", ovs_bridge->bridge_uuid));
         }
     }
 
     if (bridges_changed) {
-        /* A port needs to be deleted from this bridge */
+        /* A bridge needs to be deleted */
         _expect_ovs_bridges(params, priv->db_uuid, bridges);
         _set_ovs_bridges(params, priv->db_uuid, new_bridges);
     }