summary refs log tree commit diff
path: root/src/core/devices
diff options
context:
space:
mode:
authorMichael Biebl <biebl@debian.org>2025-08-01 19:15:13 +0200
committerMichael Biebl <biebl@debian.org>2025-08-01 19:15:13 +0200
commit7e9091a5960f67a84787c03153324915220e4816 (patch)
treedc743de962532932ebc3b4ed3a36ddd093d97d68 /src/core/devices
parent582eb4472a0f3375042afb9026cbeb50e058a27d (diff)
New upstream version 1.54.0 upstream/1.54.0
Diffstat (limited to 'src/core/devices')
-rw-r--r--src/core/devices/nm-device-bond.c16
-rw-r--r--src/core/devices/nm-device-bridge.c5
-rw-r--r--src/core/devices/nm-device-hsr.c4
-rw-r--r--src/core/devices/nm-device-tun.c8
-rw-r--r--src/core/devices/nm-device-vrf.c2
-rw-r--r--src/core/devices/nm-device.c177
-rw-r--r--src/core/devices/nm-device.h12
-rw-r--r--src/core/devices/nm-lldp-listener.c2
-rw-r--r--src/core/devices/ovs/nm-device-ovs-bridge.c28
-rw-r--r--src/core/devices/ovs/nm-device-ovs-interface.c58
-rw-r--r--src/core/devices/ovs/nm-device-ovs-port.c28
-rw-r--r--src/core/devices/ovs/nm-ovs-factory.c27
-rw-r--r--src/core/devices/ovs/nm-ovsdb.c80
13 files changed, 319 insertions, 128 deletions
diff --git a/src/core/devices/nm-device-bond.c b/src/core/devices/nm-device-bond.c
index 53b32466..673d2361 100644
--- a/src/core/devices/nm-device-bond.c
+++ b/src/core/devices/nm-device-bond.c
@@ -137,13 +137,13 @@ _set_bond_attr(NMDevice *device, const char *attr, const char *value)
     return ret;
 }
 
-#define _set_bond_attr_take(device, attr, value)                            \
-    G_STMT_START                                                            \
-    {                                                                       \
-        gs_free char *_tmp = (value);                                       \
-                                                                            \
-        _set_bond_attr(device, NM_SETTING_BOND_OPTION_ARP_IP_TARGET, _tmp); \
-    }                                                                       \
+#define _set_bond_attr_take(device, attr, value) \
+    G_STMT_START                                 \
+    {                                            \
+        gs_free char *_tmp = (value);            \
+                                                 \
+        _set_bond_attr(device, attr, _tmp);      \
+    }                                            \
     G_STMT_END
 
 #define _set_bond_attr_printf(device, attr, fmt, ...) \
@@ -902,7 +902,7 @@ reapply_connection(NMDevice *device, NMConnection *con_old, NMConnection *con_ne
     mode  = _nm_setting_bond_mode_from_string(value);
     g_return_if_fail(mode != NM_BOND_MODE_UNKNOWN);
 
-    /* Below we set only the bond options that kernel allows to modify
+    /* Below we set only the bond options that the kernel allows modifying
      * while keeping the bond interface up */
 
     set_bond_arp_ip_targets(device, s_bond);
diff --git a/src/core/devices/nm-device-bridge.c b/src/core/devices/nm-device-bridge.c
index 7c34fde0..45cdd83f 100644
--- a/src/core/devices/nm-device-bridge.c
+++ b/src/core/devices/nm-device-bridge.c
@@ -735,6 +735,11 @@ merge_bridge_vlan_default_pvid(NMPlatformBridgeVlan *vlans, guint *num_vlans, gu
     gboolean              has_pvid = FALSE;
     guint                 i;
 
+    if (default_pvid == 0) {
+        /* default_pvid=0 means that the default PVID is disabled. No need to merge it. */
+        return vlans;
+    }
+
     for (i = 0; i < *num_vlans; i++) {
         if (vlans[i].pvid) {
             has_pvid = TRUE;
diff --git a/src/core/devices/nm-device-hsr.c b/src/core/devices/nm-device-hsr.c
index 8156fa77..59454ee3 100644
--- a/src/core/devices/nm-device-hsr.c
+++ b/src/core/devices/nm-device-hsr.c
@@ -94,8 +94,10 @@ update_properties(NMDevice *device)
     CHECK_PROPERTY_CHANGED(multicast_spec, PROP_MULTICAST_SPEC);
     CHECK_PROPERTY_CHANGED(prp, PROP_PRP);
 
-    if (!nm_ether_addr_equal(&priv->props.supervision_address, &props->supervision_address))
+    if (!nm_ether_addr_equal(&priv->props.supervision_address, &props->supervision_address)) {
+        priv->props.supervision_address = props->supervision_address;
         _notify(self, PROP_SUPERVISION_ADDRESS);
+    }
 
     g_object_thaw_notify((GObject *) device);
 }
diff --git a/src/core/devices/nm-device-tun.c b/src/core/devices/nm-device-tun.c
index faab86d0..b277c378 100644
--- a/src/core/devices/nm-device-tun.c
+++ b/src/core/devices/nm-device-tun.c
@@ -242,12 +242,14 @@ create_and_realize(NMDevice              *device,
         g_return_val_if_reached(FALSE);
     }
 
-    owner = _nm_utils_ascii_str_to_int64(nm_setting_tun_get_owner(s_tun), 10, 0, G_MAXINT32, -1);
+    owner =
+        _nm_utils_ascii_str_to_int64(nm_setting_tun_get_owner(s_tun), 10, 0, G_MAXUINT32 - 1, -1);
     if (owner != -1) {
         props.owner_valid = TRUE;
         props.owner       = owner;
     }
-    group = _nm_utils_ascii_str_to_int64(nm_setting_tun_get_group(s_tun), 10, 0, G_MAXINT32, -1);
+    group =
+        _nm_utils_ascii_str_to_int64(nm_setting_tun_get_group(s_tun), 10, 0, G_MAXUINT32 - 1, -1);
     if (group != -1) {
         props.group_valid = TRUE;
         props.group       = group;
@@ -278,7 +280,7 @@ _same_og(const char *str, gboolean og_valid, guint32 og_num)
 {
     gint64 v;
 
-    v = _nm_utils_ascii_str_to_int64(str, 10, 0, G_MAXINT32, -1);
+    v = _nm_utils_ascii_str_to_int64(str, 10, 0, G_MAXUINT32 - 1, -1);
     return (!og_valid && (v == (gint64) -1)) || (og_valid && (((guint32) v) == og_num));
 }
 
diff --git a/src/core/devices/nm-device-vrf.c b/src/core/devices/nm-device-vrf.c
index 7dfd6504..6d272a95 100644
--- a/src/core/devices/nm-device-vrf.c
+++ b/src/core/devices/nm-device-vrf.c
@@ -235,7 +235,7 @@ attach_port(NMDevice                  *device,
 
         _LOGI(LOGD_DEVICE, "attached VRF port %s", port_iface);
     } else
-        _LOGI(LOGD_BOND, "VRF port %s was attached", port_iface);
+        _LOGI(LOGD_DEVICE, "VRF port %s was attached", port_iface);
 
     return TRUE;
 }
diff --git a/src/core/devices/nm-device.c b/src/core/devices/nm-device.c
index 2f2f25a5..f6057e52 100644
--- a/src/core/devices/nm-device.c
+++ b/src/core/devices/nm-device.c
@@ -779,6 +779,7 @@ 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)
@@ -880,6 +881,7 @@ static void device_ifindex_changed_cb(NMManager *manager, NMDevice *device_chang
 static gboolean device_link_changed(gpointer user_data);
 static gboolean _get_maybe_ipv6_disabled(NMDevice *self);
 static void     deactivate_ready(NMDevice *self, NMDeviceStateReason reason);
+static void     carrier_disconnected_action_cancel(NMDevice *self);
 
 /*****************************************************************************/
 
@@ -1414,6 +1416,26 @@ _prop_get_connection_mdns(NMDevice *self)
                                                        NM_SETTING_CONNECTION_MDNS_DEFAULT);
 }
 
+static gboolean
+_prop_get_sriov_preserve_on_down(NMDevice *self, NMSettingSriov *s_sriov)
+{
+    NMSriovPreserveOnDown preserve;
+
+    g_return_val_if_fail(NM_IS_DEVICE(self), FALSE);
+    g_return_val_if_fail(NM_IS_SETTING_SRIOV(s_sriov), FALSE);
+
+    preserve = nm_setting_sriov_get_preserve_on_down(s_sriov);
+    if (NM_IN_SET(preserve, NM_SRIOV_PRESERVE_ON_DOWN_NO, NM_SRIOV_PRESERVE_ON_DOWN_YES))
+        return preserve;
+
+    return nm_config_data_get_connection_default_int64(NM_CONFIG_GET_DATA,
+                                                       NM_CON_DEFAULT("sriov.preserve-on-down"),
+                                                       self,
+                                                       NM_SRIOV_PRESERVE_ON_DOWN_NO,
+                                                       NM_SRIOV_PRESERVE_ON_DOWN_YES,
+                                                       NM_SRIOV_PRESERVE_ON_DOWN_NO);
+}
+
 static NMSettingConnectionLlmnr
 _prop_get_connection_llmnr(NMDevice *self)
 {
@@ -2109,6 +2131,33 @@ _prop_get_ipvx_dhcp_send_hostname(NMDevice *self, int addr_family)
     return send_hostname_v2;
 }
 
+NMSettingIPConfigForwarding
+nm_device_get_ipv4_forwarding(NMDevice *self)
+{
+    NMSettingIPConfig          *s_ip;
+    NMSettingIPConfigForwarding forwarding;
+
+    g_return_val_if_fail(NM_IS_DEVICE(self), NM_SETTING_IP_CONFIG_FORWARDING_AUTO);
+
+    s_ip = nm_device_get_applied_setting(self, NM_TYPE_SETTING_IP4_CONFIG);
+    if (s_ip)
+        forwarding = nm_setting_ip_config_get_forwarding(s_ip);
+    else
+        forwarding = NM_SETTING_IP_CONFIG_FORWARDING_DEFAULT;
+
+    if (forwarding == NM_SETTING_IP_CONFIG_FORWARDING_DEFAULT) {
+        forwarding =
+            nm_config_data_get_connection_default_int64(NM_CONFIG_GET_DATA,
+                                                        NM_CON_DEFAULT("ipv4.forwarding"),
+                                                        self,
+                                                        NM_SETTING_IP_CONFIG_FORWARDING_NO,
+                                                        NM_SETTING_IP_CONFIG_FORWARDING_AUTO,
+                                                        NM_SETTING_IP_CONFIG_FORWARDING_AUTO);
+    }
+
+    return forwarding;
+}
+
 static gboolean
 _prop_get_connection_ip_ping_addresses_require_all(NMDevice *self, NMSettingConnection *s_con)
 {
@@ -2717,7 +2766,7 @@ _ethtool_features_set(NMDevice         *self,
     if (nm_setting_ethtool_init_features(s_ethtool, ethtool_state->requested) == 0)
         return;
 
-    features = nm_platform_ethtool_get_link_features(platform, ethtool_state->ifindex);
+    features = nm_platform_ethtool_get_features(platform, ethtool_state->ifindex);
     if (!features) {
         _LOGW(LOGD_DEVICE, "ethtool: failure setting offload features (cannot read features)");
         return;
@@ -2836,9 +2885,9 @@ _ethtool_coalesce_set(NMDevice         *self,
             continue;
 
         if (!has_old) {
-            if (!nm_platform_ethtool_get_link_coalesce(platform,
-                                                       ethtool_state->ifindex,
-                                                       &coalesce_old)) {
+            if (!nm_platform_ethtool_get_coalesce(platform,
+                                                  ethtool_state->ifindex,
+                                                  &coalesce_old)) {
                 _LOGW(LOGD_DEVICE, "ethtool: failure getting coalesce settings (cannot read)");
                 return;
             }
@@ -2917,7 +2966,7 @@ _ethtool_ring_set(NMDevice         *self,
         nm_assert(g_variant_is_of_type(variant, G_VARIANT_TYPE_UINT32));
 
         if (!has_old) {
-            if (!nm_platform_ethtool_get_link_ring(platform, ethtool_state->ifindex, &ring_old)) {
+            if (!nm_platform_ethtool_get_ring(platform, ethtool_state->ifindex, &ring_old)) {
                 _LOGW(LOGD_DEVICE,
                       "ethtool: failure setting ring options (cannot read existing setting)");
                 return;
@@ -3013,9 +3062,9 @@ _ethtool_channels_set(NMDevice         *self,
         nm_assert(g_variant_is_of_type(variant, G_VARIANT_TYPE_UINT32));
 
         if (!has_old) {
-            if (!nm_platform_ethtool_get_link_channels(platform,
-                                                       ethtool_state->ifindex,
-                                                       &channels_old)) {
+            if (!nm_platform_ethtool_get_channels(platform,
+                                                  ethtool_state->ifindex,
+                                                  &channels_old)) {
                 _LOGW(LOGD_DEVICE,
                       "ethtool: failure setting channels options (cannot read existing setting)");
                 return;
@@ -3132,7 +3181,7 @@ _ethtool_pause_set(NMDevice         *self,
         nm_assert(g_variant_is_of_type(variant, G_VARIANT_TYPE_BOOLEAN));
 
         if (!has_old) {
-            if (!nm_platform_ethtool_get_link_pause(platform, ethtool_state->ifindex, &pause_old)) {
+            if (!nm_platform_ethtool_get_pause(platform, ethtool_state->ifindex, &pause_old)) {
                 _LOGW(LOGD_DEVICE,
                       "ethtool: failure setting pause options (cannot read "
                       "existing setting)");
@@ -3218,7 +3267,7 @@ _ethtool_eee_set(NMDevice         *self,
         nm_assert(g_variant_is_of_type(variant, G_VARIANT_TYPE_BOOLEAN));
 
         if (!has_old) {
-            if (!nm_platform_ethtool_get_link_eee(platform, ethtool_state->ifindex, &eee_old)) {
+            if (!nm_platform_ethtool_get_eee(platform, ethtool_state->ifindex, &eee_old)) {
                 _LOGW(LOGD_DEVICE,
                       "ethtool: failure setting eee options (cannot read "
                       "existing setting)");
@@ -3729,7 +3778,7 @@ nm_device_assume_state_reset(NMDevice *self)
 
 /*****************************************************************************/
 
-static char *
+char *
 nm_device_sysctl_ip_conf_get(NMDevice *self, int addr_family, const char *property)
 {
     const char *ifname;
@@ -4993,6 +5042,10 @@ _set_ifindex(NMDevice *self, int ifindex, gboolean is_ip_ifindex)
 
     ip_ifindex_new = nm_device_get_ip_ifindex(self);
 
+    /* the ifindex changed; forget about any carrier change event for
+     * the previous ifindex */
+    carrier_disconnected_action_cancel(self);
+
     if (priv->l3cfg) {
         if (ip_ifindex_new <= 0 || ip_ifindex_new != nm_l3cfg_get_ifindex(priv->l3cfg)) {
             const NML3ConfigData *l3cd_old;
@@ -6584,11 +6637,15 @@ concheck_update_state(NMDevice           *self,
 
     _notify(self, IS_IPv4 ? PROP_IP4_CONNECTIVITY : PROP_IP6_CONNECTIVITY);
 
-    if (priv->state == NM_DEVICE_STATE_ACTIVATED && !nm_device_managed_type_is_external(self))
+    /* State change could've affected the route metrics (removed the penalty
+     * once FULL connectivity is reached), redo the L3 configuration. */
+    if (priv->state > NM_DEVICE_STATE_IP_CONFIG && priv->state < NM_DEVICE_STATE_DEACTIVATING
+        && !nm_device_managed_type_is_external(self)) {
         _dev_l3_register_l3cds(self, priv->l3cfg, TRUE, NM_TERNARY_DEFAULT);
+    }
 }
 
-static const char *
+const char *
 nm_device_get_effective_ip_config_method(NMDevice *self, int addr_family)
 {
     NMDeviceClass *klass;
@@ -7725,9 +7782,7 @@ device_link_changed(gpointer user_data)
          * tagged for carrier ignore) ensure that when the carrier appears we
          * renew DHCP leases and such.
          */
-        if (priv->state == NM_DEVICE_STATE_ACTIVATED) {
-            nm_device_update_dynamic_ip_setup(self, "interface got carrier");
-        }
+        nm_device_update_dynamic_ip_setup(self, "interface got carrier");
     }
 
     if (update_unmanaged_specs)
@@ -8974,7 +9029,7 @@ nm_device_port_notify_attach_as_port(NMDevice *self, gboolean success)
 
             priv->is_attached = TRUE;
 
-            _notify(priv->controller, PROP_CONTROLLER);
+            _notify(self, PROP_CONTROLLER);
 
             nm_clear_pointer(&NM_DEVICE_GET_PRIVATE(priv->controller)->ports_variant,
                              g_variant_unref);
@@ -9053,7 +9108,7 @@ nm_device_port_notify_release(NMDevice           *self,
 
     priv->is_attached = FALSE;
 
-    _notify(priv->controller, PROP_CONTROLLER);
+    _notify(self, PROP_CONTROLLER);
 
     nm_clear_pointer(&NM_DEVICE_GET_PRIVATE(priv->controller)->ports_variant, g_variant_unref);
     nm_gobject_notify_together(priv->controller, PROP_PORTS, PROP_SLAVES);
@@ -9521,7 +9576,7 @@ nm_device_generate_connection(NMDevice *self,
                  NM_SETTING_CONNECTION_ID,
                  ifname,
                  NM_SETTING_CONNECTION_AUTOCONNECT,
-                 FALSE,
+                 TRUE,
                  NM_SETTING_CONNECTION_INTERFACE_NAME,
                  ifname,
                  NM_SETTING_CONNECTION_TIMESTAMP,
@@ -11335,6 +11390,13 @@ _dev_ipdhcpx_notify(NMDhcpClient *client, const NMDhcpClientNotifyData *notify_d
     switch (notify_data->notify_type) {
     case NM_DHCP_CLIENT_NOTIFY_TYPE_PREFIX_DELEGATED:
         nm_assert(!IS_IPv4);
+        if (notify_data->prefix_delegated.prefix->plen == 0
+            || notify_data->prefix_delegated.prefix->plen > 64) {
+            _LOGW_ipdhcp(addr_family,
+                         "ignoring invalid prefix-delegation with length %u",
+                         notify_data->prefix_delegated.prefix->plen);
+            return;
+        }
         /* Just re-emit. The device just contributes the prefix to the
          * pool in NMPolicy, which decides about subnet allocation
          * on the shared devices. */
@@ -13073,6 +13135,13 @@ 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);
@@ -13369,7 +13438,8 @@ activate_stage3_ip_config(NMDevice *self)
              * IPv6LL if this is not an assumed connection, since assumed connections
              * will already have IPv6 set up.
              */
-            if (!nm_device_managed_type_is_external_or_assume(self))
+            if ((priv->state <= NM_DEVICE_STATE_IP_CONFIG || priv->ip_data_6.do_reapply)
+                && !nm_device_managed_type_is_external_or_assume(self))
                 _dev_addrgenmode6_set(self, NM_IN6_ADDR_GEN_MODE_NONE);
 
             /* Re-enable IPv6 on the interface */
@@ -13505,19 +13575,6 @@ _dev_ipshared4_init(NMDevice *self)
     }
 
     if (nm_platform_sysctl_get_int32(nm_device_get_platform(self),
-                                     NMP_SYSCTL_PATHID_ABSOLUTE("/proc/sys/net/ipv4/ip_forward"),
-                                     -1)
-        == 1) {
-        /* nothing to do. */
-    } else if (!nm_platform_sysctl_set(nm_device_get_platform(self),
-                                       NMP_SYSCTL_PATHID_ABSOLUTE("/proc/sys/net/ipv4/ip_forward"),
-                                       "1")) {
-        errsv = errno;
-        _LOGW_ipshared(AF_INET, "error enabling IPv4 forwarding: %s", nm_strerror_native(errsv));
-        return FALSE;
-    }
-
-    if (nm_platform_sysctl_get_int32(nm_device_get_platform(self),
                                      NMP_SYSCTL_PATHID_ABSOLUTE("/proc/sys/net/ipv4/ip_dynaddr"),
                                      -1)
         == 1) {
@@ -13994,13 +14051,18 @@ can_reapply_change(NMDevice   *self,
         goto out_fail;
     }
 
-    if (NM_IN_STRSET(setting_name,
-                     NM_SETTING_OVS_EXTERNAL_IDS_SETTING_NAME,
-                     NM_SETTING_OVS_OTHER_CONFIG_SETTING_NAME)
-        && NM_DEVICE_GET_CLASS(self)->can_reapply_change_ovs_external_ids) {
-        /* TODO: this means, you cannot reapply changes to the external-ids for
-         * OVS system interfaces. */
-        return TRUE;
+    if (nm_streq(setting_name, NM_SETTING_BRIDGE_PORT_SETTING_NAME)) {
+        return nm_device_hash_check_invalid_keys(diffs,
+                                                 NM_SETTING_BRIDGE_PORT_SETTING_NAME,
+                                                 error,
+                                                 NM_SETTING_BRIDGE_PORT_VLANS);
+    }
+
+    if (nm_streq(setting_name, NM_SETTING_SRIOV_SETTING_NAME)) {
+        return nm_device_hash_check_invalid_keys(diffs,
+                                                 NM_SETTING_SRIOV_SETTING_NAME,
+                                                 error,
+                                                 NM_SETTING_SRIOV_PRESERVE_ON_DOWN);
     }
 
 out_fail:
@@ -16851,6 +16913,8 @@ _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;
@@ -16896,6 +16960,7 @@ 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));
 
@@ -16918,6 +16983,17 @@ 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);
@@ -17437,7 +17513,8 @@ _set_state_full(NMDevice *self, NMDeviceState state, NMDeviceStateReason reason,
             }
 
             if (priv->ifindex > 0
-                && (s_sriov = nm_device_get_applied_setting(self, NM_TYPE_SETTING_SRIOV))) {
+                && (s_sriov = nm_device_get_applied_setting(self, NM_TYPE_SETTING_SRIOV))
+                && (!_prop_get_sriov_preserve_on_down(self, s_sriov))) {
                 priv->sriov_reset_pending++;
                 sriov_op_queue(self,
                                0,
@@ -17492,7 +17569,8 @@ _set_state_full(NMDevice *self, NMDeviceState state, NMDeviceStateReason reason,
             nm_settings_connection_update_timestamp(sett_conn, (guint64) 0);
 
         if (priv->ifindex > 0
-            && (s_sriov = nm_device_get_applied_setting(self, NM_TYPE_SETTING_SRIOV))) {
+            && (s_sriov = nm_device_get_applied_setting(self, NM_TYPE_SETTING_SRIOV))
+            && (!_prop_get_sriov_preserve_on_down(self, s_sriov))) {
             priv->sriov_reset_pending++;
             sriov_op_queue(self,
                            0,
@@ -18613,7 +18691,7 @@ hostname_dns_lookup_callback(GObject *source, GAsyncResult *result, gpointer use
         gboolean valid;
 
         resolver->hostname = g_steal_pointer(&output);
-        valid              = nm_utils_validate_hostname(resolver->hostname);
+        valid              = nm_sd_dns_name_is_valid(resolver->hostname);
 
         _LOGD(LOGD_DNS,
               "hostname-from-dns: ipv%c resolver %s: lookup successful for %s, result %s%s%s%s",
@@ -18858,6 +18936,19 @@ 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 9b051ab4..9a663c2a 100644
--- a/src/core/devices/nm-device.h
+++ b/src/core/devices/nm-device.h
@@ -209,8 +209,6 @@ typedef struct _NMDeviceClass {
 
     bool act_stage1_prepare_set_hwaddr_ethernet : 1;
 
-    bool can_reapply_change_ovs_external_ids : 1;
-
     bool allow_autoconnect_on_external : 1;
 
     NMRfkillType rfkill_type : 4;
@@ -854,4 +852,14 @@ 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/nm-lldp-listener.c b/src/core/devices/nm-lldp-listener.c
index e08b379a..5c90911f 100644
--- a/src/core/devices/nm-lldp-listener.c
+++ b/src/core/devices/nm-lldp-listener.c
@@ -310,7 +310,7 @@ format_string(const guint8 *data, gsize len, gboolean allow_trim, char **out_to_
     if (len == 0)
         return NULL;
 
-    if (memchr(data, len, '\0'))
+    if (memchr(data, '\0', len))
         return NULL;
 
     return nm_utils_buf_utf8safe_escape(data,
diff --git a/src/core/devices/ovs/nm-device-ovs-bridge.c b/src/core/devices/ovs/nm-device-ovs-bridge.c
index c9b1f4c6..c9736eeb 100644
--- a/src/core/devices/ovs/nm-device-ovs-bridge.c
+++ b/src/core/devices/ovs/nm-device-ovs-bridge.c
@@ -162,6 +162,11 @@ can_reapply_change(NMDevice   *device,
                                                  NM_SETTING_OVS_BRIDGE_STP_ENABLE);
     }
 
+    if (NM_IN_STRSET(setting_name,
+                     NM_SETTING_OVS_EXTERNAL_IDS_SETTING_NAME,
+                     NM_SETTING_OVS_OTHER_CONFIG_SETTING_NAME))
+        return TRUE;
+
     return device_class->can_reapply_change(device, setting_name, s_old, s_new, diffs, error);
 }
 
@@ -194,16 +199,15 @@ nm_device_ovs_bridge_class_init(NMDeviceOvsBridgeClass *klass)
     device_class->connection_type_check_compatible = NM_SETTING_OVS_BRIDGE_SETTING_NAME;
     device_class->link_types                       = NM_DEVICE_DEFINE_LINK_TYPES();
 
-    device_class->is_controller                       = TRUE;
-    device_class->get_type_description                = get_type_description;
-    device_class->create_and_realize                  = create_and_realize;
-    device_class->unrealize                           = unrealize;
-    device_class->get_generic_capabilities            = get_generic_capabilities;
-    device_class->act_stage3_ip_config                = act_stage3_ip_config;
-    device_class->ready_for_ip_config                 = ready_for_ip_config;
-    device_class->attach_port                         = attach_port;
-    device_class->detach_port                         = detach_port;
-    device_class->can_reapply_change                  = can_reapply_change;
-    device_class->can_reapply_change_ovs_external_ids = TRUE;
-    device_class->reapply_connection                  = nm_device_ovs_reapply_connection;
+    device_class->is_controller            = TRUE;
+    device_class->get_type_description     = get_type_description;
+    device_class->create_and_realize       = create_and_realize;
+    device_class->unrealize                = unrealize;
+    device_class->get_generic_capabilities = get_generic_capabilities;
+    device_class->act_stage3_ip_config     = act_stage3_ip_config;
+    device_class->ready_for_ip_config      = ready_for_ip_config;
+    device_class->attach_port              = attach_port;
+    device_class->detach_port              = detach_port;
+    device_class->can_reapply_change       = can_reapply_change;
+    device_class->reapply_connection       = nm_device_ovs_reapply_connection;
 }
diff --git a/src/core/devices/ovs/nm-device-ovs-interface.c b/src/core/devices/ovs/nm-device-ovs-interface.c
index 512aa5c3..922ed345 100644
--- a/src/core/devices/ovs/nm-device-ovs-interface.c
+++ b/src/core/devices/ovs/nm-device-ovs-interface.c
@@ -16,6 +16,8 @@
 #include "nm-setting-ovs-bridge.h"
 #include "nm-setting-ovs-interface.h"
 #include "nm-setting-ovs-port.h"
+#include "nm-setting-ovs-external-ids.h"
+#include "nm-setting-ovs-other-config.h"
 
 #define _NMLOG_DEVICE_TYPE NMDeviceOvsInterface
 #include "devices/nm-device-logging.h"
@@ -631,6 +633,28 @@ can_update_from_platform_link(NMDevice *device, const NMPlatformLink *plink)
     return !plink || nm_device_get_state(device) != NM_DEVICE_STATE_DEACTIVATING;
 }
 
+static gboolean
+can_reapply_change(NMDevice   *device,
+                   const char *setting_name,
+                   NMSetting  *s_old,
+                   NMSetting  *s_new,
+                   GHashTable *diffs,
+                   GError    **error)
+{
+    NMDeviceClass *device_class = NM_DEVICE_CLASS(nm_device_ovs_interface_parent_class);
+
+    if (NM_IN_STRSET(setting_name,
+                     NM_SETTING_OVS_EXTERNAL_IDS_SETTING_NAME,
+                     NM_SETTING_OVS_OTHER_CONFIG_SETTING_NAME)) {
+        /* TODO: it's currently not possible to reapply those settings on OVS
+         * system interfaces because they have type != "ovs-interface" (e.g.
+         * "ethernet") */
+        return TRUE;
+    }
+
+    return device_class->can_reapply_change(device, setting_name, s_old, s_new, diffs, error);
+}
+
 /*****************************************************************************/
 
 static void
@@ -696,21 +720,21 @@ nm_device_ovs_interface_class_init(NMDeviceOvsInterfaceClass *klass)
     device_class->connection_type_check_compatible = NM_SETTING_OVS_INTERFACE_SETTING_NAME;
     device_class->link_types = NM_DEVICE_DEFINE_LINK_TYPES(NM_LINK_TYPE_OPENVSWITCH);
 
-    device_class->can_auto_connect                    = can_auto_connect;
-    device_class->can_update_from_platform_link       = can_update_from_platform_link;
-    device_class->deactivate                          = deactivate;
-    device_class->deactivate_async                    = deactivate_async;
-    device_class->get_type_description                = get_type_description;
-    device_class->create_and_realize                  = create_and_realize;
-    device_class->get_generic_capabilities            = get_generic_capabilities;
-    device_class->is_available                        = is_available;
-    device_class->check_connection_compatible         = check_connection_compatible;
-    device_class->link_changed                        = link_changed;
-    device_class->act_stage3_ip_config                = act_stage3_ip_config;
-    device_class->ready_for_ip_config                 = ready_for_ip_config;
-    device_class->can_unmanaged_external_down         = can_unmanaged_external_down;
-    device_class->set_platform_mtu                    = set_platform_mtu;
-    device_class->get_configured_mtu                  = nm_device_get_configured_mtu_for_wired;
-    device_class->can_reapply_change_ovs_external_ids = TRUE;
-    device_class->reapply_connection                  = nm_device_ovs_reapply_connection;
+    device_class->can_auto_connect              = can_auto_connect;
+    device_class->can_update_from_platform_link = can_update_from_platform_link;
+    device_class->deactivate                    = deactivate;
+    device_class->deactivate_async              = deactivate_async;
+    device_class->get_type_description          = get_type_description;
+    device_class->create_and_realize            = create_and_realize;
+    device_class->get_generic_capabilities      = get_generic_capabilities;
+    device_class->is_available                  = is_available;
+    device_class->check_connection_compatible   = check_connection_compatible;
+    device_class->link_changed                  = link_changed;
+    device_class->act_stage3_ip_config          = act_stage3_ip_config;
+    device_class->ready_for_ip_config           = ready_for_ip_config;
+    device_class->can_unmanaged_external_down   = can_unmanaged_external_down;
+    device_class->set_platform_mtu              = set_platform_mtu;
+    device_class->get_configured_mtu            = nm_device_get_configured_mtu_for_wired;
+    device_class->can_reapply_change            = can_reapply_change;
+    device_class->reapply_connection            = nm_device_ovs_reapply_connection;
 }
diff --git a/src/core/devices/ovs/nm-device-ovs-port.c b/src/core/devices/ovs/nm-device-ovs-port.c
index e9928548..0e3ef92c 100644
--- a/src/core/devices/ovs/nm-device-ovs-port.c
+++ b/src/core/devices/ovs/nm-device-ovs-port.c
@@ -16,6 +16,8 @@
 #include "nm-setting-connection.h"
 #include "nm-setting-ovs-port.h"
 #include "nm-setting-ovs-interface.h"
+#include "nm-setting-ovs-external-ids.h"
+#include "nm-setting-ovs-other-config.h"
 #include "nm-setting-wired.h"
 
 #define _NMLOG_DEVICE_TYPE NMDeviceOvsPort
@@ -279,6 +281,11 @@ can_reapply_change(NMDevice   *device,
                                                  NM_SETTING_OVS_PORT_TRUNKS);
     }
 
+    if (NM_IN_STRSET(setting_name,
+                     NM_SETTING_OVS_EXTERNAL_IDS_SETTING_NAME,
+                     NM_SETTING_OVS_OTHER_CONFIG_SETTING_NAME))
+        return TRUE;
+
     return device_class->can_reapply_change(device, setting_name, s_old, s_new, diffs, error);
 }
 
@@ -311,15 +318,14 @@ nm_device_ovs_port_class_init(NMDeviceOvsPortClass *klass)
     device_class->connection_type_check_compatible = NM_SETTING_OVS_PORT_SETTING_NAME;
     device_class->link_types                       = NM_DEVICE_DEFINE_LINK_TYPES();
 
-    device_class->is_controller                       = TRUE;
-    device_class->get_type_description                = get_type_description;
-    device_class->create_and_realize                  = create_and_realize;
-    device_class->get_generic_capabilities            = get_generic_capabilities;
-    device_class->act_stage3_ip_config                = act_stage3_ip_config;
-    device_class->ready_for_ip_config                 = ready_for_ip_config;
-    device_class->attach_port                         = attach_port;
-    device_class->detach_port                         = detach_port;
-    device_class->can_reapply_change                  = can_reapply_change;
-    device_class->can_reapply_change_ovs_external_ids = TRUE;
-    device_class->reapply_connection                  = nm_device_ovs_reapply_connection;
+    device_class->is_controller            = TRUE;
+    device_class->get_type_description     = get_type_description;
+    device_class->create_and_realize       = create_and_realize;
+    device_class->get_generic_capabilities = get_generic_capabilities;
+    device_class->act_stage3_ip_config     = act_stage3_ip_config;
+    device_class->ready_for_ip_config      = ready_for_ip_config;
+    device_class->attach_port              = attach_port;
+    device_class->detach_port              = detach_port;
+    device_class->can_reapply_change       = can_reapply_change;
+    device_class->reapply_connection       = nm_device_ovs_reapply_connection;
 }
diff --git a/src/core/devices/ovs/nm-ovs-factory.c b/src/core/devices/ovs/nm-ovs-factory.c
index 2ca1a0b5..4084356f 100644
--- a/src/core/devices/ovs/nm-ovs-factory.c
+++ b/src/core/devices/ovs/nm-ovs-factory.c
@@ -75,9 +75,6 @@ new_device_from_type(const char *name, NMDeviceType device_type)
     const char *type_desc;
     NMLinkType  link_type = NM_LINK_TYPE_NONE;
 
-    if (nm_manager_get_device(NM_MANAGER_GET, name, device_type))
-        return NULL;
-
     if (device_type == NM_DEVICE_TYPE_OVS_INTERFACE) {
         type      = NM_TYPE_DEVICE_OVS_INTERFACE;
         type_desc = "Open vSwitch Interface";
@@ -89,6 +86,7 @@ new_device_from_type(const char *name, NMDeviceType device_type)
         type      = NM_TYPE_DEVICE_OVS_BRIDGE;
         type_desc = "Open vSwitch Bridge";
     } else {
+        _LOGT(name, NULL, "Unrecognized link type: %d", device_type);
         return NULL;
     }
 
@@ -125,6 +123,11 @@ ovsdb_device_added(NMOvsdb         *ovsdb,
         return;
     }
 
+    if (nm_manager_get_device(NM_MANAGER_GET, name, device_type)) {
+        _LOGT(name, NULL, "Device already registered with manager, skipping.");
+        return;
+    }
+
     device = new_device_from_type(name, device_type);
     if (!device)
         return;
@@ -288,23 +291,31 @@ create_device(NMDeviceFactory      *self,
 {
     NMDeviceType device_type     = NM_DEVICE_TYPE_UNKNOWN;
     const char  *connection_type = NULL;
+    const char  *connection_uuid = NULL;
 
     if (g_strcmp0(iface, "ovs-system") == 0) {
         *out_ignore = TRUE;
         return NULL;
     }
 
-    if (connection)
+    if (connection) {
         connection_type = nm_connection_get_connection_type(connection);
+        connection_uuid = nm_connection_get_uuid(connection);
+    }
 
-    if (plink)
+    if (plink) {
+        _LOGD(iface, connection_uuid, "creating OVS interface (from a platform link)");
         device_type = NM_DEVICE_TYPE_OVS_INTERFACE;
-    else if (g_strcmp0(connection_type, NM_SETTING_OVS_INTERFACE_SETTING_NAME) == 0)
+    } else if (nm_streq0(connection_type, NM_SETTING_OVS_INTERFACE_SETTING_NAME)) {
+        _LOGD(iface, connection_uuid, "creating OVS interface (from setting)");
         device_type = NM_DEVICE_TYPE_OVS_INTERFACE;
-    else if (g_strcmp0(connection_type, NM_SETTING_OVS_PORT_SETTING_NAME) == 0)
+    } else if (nm_streq0(connection_type, NM_SETTING_OVS_PORT_SETTING_NAME)) {
+        _LOGD(iface, connection_uuid, "creating OVS port (from setting)");
         device_type = NM_DEVICE_TYPE_OVS_PORT;
-    else if (g_strcmp0(connection_type, NM_SETTING_OVS_BRIDGE_SETTING_NAME) == 0)
+    } else if (nm_streq0(connection_type, NM_SETTING_OVS_BRIDGE_SETTING_NAME)) {
+        _LOGD(iface, connection_uuid, "creating OVS bridge (from setting)");
         device_type = NM_DEVICE_TYPE_OVS_BRIDGE;
+    }
 
     return new_device_from_type(iface, device_type);
 }
diff --git a/src/core/devices/ovs/nm-ovsdb.c b/src/core/devices/ovs/nm-ovsdb.c
index 2dbc842a..164bfd35 100644
--- a/src/core/devices/ovs/nm-ovsdb.c
+++ b/src/core/devices/ovs/nm-ovsdb.c
@@ -900,15 +900,17 @@ _insert_interface(json_t       *params,
         s_ovs_patch = nm_connection_get_setting_ovs_patch(interface);
 
     if (s_ovs_dpdk) {
-        const char *devargs;
-        guint32     n_rxq;
-        guint32     n_rxq_desc;
-        guint32     n_txq_desc;
+        const char                  *devargs;
+        guint32                      n_rxq;
+        guint32                      n_rxq_desc;
+        guint32                      n_txq_desc;
+        NMSettingOvsDpdkLscInterrupt lsc_int;
 
         devargs    = nm_setting_ovs_dpdk_get_devargs(s_ovs_dpdk);
         n_rxq      = nm_setting_ovs_dpdk_get_n_rxq(s_ovs_dpdk);
         n_rxq_desc = nm_setting_ovs_dpdk_get_n_rxq_desc(s_ovs_dpdk);
         n_txq_desc = nm_setting_ovs_dpdk_get_n_txq_desc(s_ovs_dpdk);
+        lsc_int    = nm_setting_ovs_dpdk_get_lsc_interrupt(s_ovs_dpdk);
 
         dpdk_array = json_array();
 
@@ -930,6 +932,17 @@ _insert_interface(json_t       *params,
                 json_pack("[s,s]", "n_txq_desc", nm_sprintf_buf(sbuf, "%u", n_txq_desc)));
         }
 
+        switch (lsc_int) {
+        case NM_SETTING_OVS_DPDK_LSC_INTERRUPT_IGNORE:
+            break;
+        case NM_SETTING_OVS_DPDK_LSC_INTERRUPT_ENABLED:
+            json_array_append_new(dpdk_array, json_pack("[s,s]", "dpdk-lsc-interrupt", "true"));
+            break;
+        case NM_SETTING_OVS_DPDK_LSC_INTERRUPT_DISABLED:
+            json_array_append_new(dpdk_array, json_pack("[s,s]", "dpdk-lsc-interrupt", "false"));
+            break;
+        }
+
         json_array_append_new(options, dpdk_array);
 
     } else if (s_ovs_patch) {
@@ -1374,88 +1387,113 @@ _delete_interface(NMOvsdb *self, json_t *params, const char *ifname)
     nm_auto_decref_json json_t *bridges     = NULL;
     nm_auto_decref_json json_t *new_bridges = NULL;
     gboolean                    bridges_changed;
-    gboolean                    ports_changed;
-    gboolean                    interfaces_changed;
-    int                         pi;
-    int                         ii;
 
     bridges         = json_array();
     new_bridges     = json_array();
     bridges_changed = FALSE;
 
+    /* Loop over all bridges */
     g_hash_table_iter_init(&iter, priv->bridges);
     while (g_hash_table_iter_next(&iter, (gpointer) &ovs_bridge, NULL)) {
-        nm_auto_decref_json json_t *ports     = NULL;
-        nm_auto_decref_json json_t *new_ports = NULL;
+        nm_auto_decref_json json_t *ports         = NULL;
+        nm_auto_decref_json json_t *new_ports     = NULL;
+        guint                       num_nm_ports  = 0;
+        gboolean                    ports_changed = FALSE;
+        int                         pi;
 
-        ports         = json_array();
-        new_ports     = json_array();
-        ports_changed = FALSE;
+        ports     = json_array();
+        new_ports = json_array();
 
+        /* Add the bridge UUID to the list of known bridges for the "expect" condition */
         json_array_append_new(bridges, json_pack("[s,s]", "uuid", ovs_bridge->bridge_uuid));
 
+        if (!ovs_bridge->connection_uuid) {
+            /* Externally created, don't touch it */
+            json_array_append_new(new_bridges, json_pack("[s,s]", "uuid", ovs_bridge->bridge_uuid));
+            continue;
+        }
+
+        /* Loop over all bridge's ports */
         for (pi = 0; pi < ovs_bridge->ports->len; pi++) {
-            nm_auto_decref_json json_t *interfaces     = NULL;
-            nm_auto_decref_json json_t *new_interfaces = NULL;
+            nm_auto_decref_json json_t *interfaces         = NULL;
+            nm_auto_decref_json json_t *new_interfaces     = NULL;
+            guint                       num_nm_interfaces  = 0;
+            gboolean                    interfaces_changed = FALSE;
+            int                         ii;
 
             interfaces     = json_array();
             new_interfaces = json_array();
             port_uuid      = g_ptr_array_index(ovs_bridge->ports, pi);
             ovs_port       = g_hash_table_lookup(priv->ports, &port_uuid);
 
+            /* Add the port UUID to the list of known bridge port for the "expect" condition */
             json_array_append_new(ports, json_pack("[s,s]", "uuid", port_uuid));
 
-            interfaces_changed = FALSE;
-
             if (!ovs_port) {
                 /* This would be a violation of ovsdb's reference integrity (a bug). */
                 _LOGW("Unknown port '%s' in bridge '%s'", port_uuid, ovs_bridge->bridge_uuid);
                 continue;
             }
 
+            /* Loop over all port's interfaces */
             for (ii = 0; ii < ovs_port->interfaces->len; ii++) {
                 interface_uuid = g_ptr_array_index(ovs_port->interfaces, ii);
                 ovs_interface  = g_hash_table_lookup(priv->interfaces, &interface_uuid);
 
+                /* Add the interface UUID to the list of known port interfaces for the "expect" condition */
                 json_array_append_new(interfaces, json_pack("[s,s]", "uuid", interface_uuid));
 
                 if (ovs_interface) {
                     if (nm_streq(ovs_interface->name, ifname)) {
-                        /* skip the interface */
+                        /* We are deleting this interface, don't count it */
                         interfaces_changed = TRUE;
                         continue;
                     }
+                    if (ovs_interface->connection_uuid)
+                        num_nm_interfaces++;
                 } else {
                     /* This would be a violation of ovsdb's reference integrity (a bug). */
                     _LOGW("Unknown interface '%s' in port '%s'", interface_uuid, port_uuid);
                 }
 
+                /* Add the interface to the list of new interfaces to set on the port */
                 json_array_append_new(new_interfaces, json_pack("[s,s]", "uuid", interface_uuid));
             }
 
-            if (json_array_size(new_interfaces) == 0) {
+            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. */
                 ports_changed = TRUE;
             } else {
                 if (interfaces_changed) {
+                    /* An interface needs to be deleted from this port */
                     _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 (json_array_size(new_ports) == 0) {
+        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. */
             bridges_changed = TRUE;
         } else {
             if (ports_changed) {
+                /* A port needs to be deleted from this bridge */
                 _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 */
         _expect_ovs_bridges(params, priv->db_uuid, bridges);
         _set_ovs_bridges(params, priv->db_uuid, new_bridges);
     }
@@ -2211,7 +2249,7 @@ ovsdb_got_update(NMOvsdb *self, json_t *msg)
                                            ovs_bridge->connection_uuid,
                                            ""),
                       (strtmp1 = _strdict_to_string(ovs_bridge->external_ids)),
-                      (strtmp2 = _strdict_to_string(ovs_bridge->external_ids)));
+                      (strtmp2 = _strdict_to_string(ovs_bridge->other_config)));
             }
         } else {
             gs_free char *strtmp1 = NULL;