Attached is a filtered debdiff, excluding tests and ovs and nm-cloud related code (which is not buit in Debian)

git diff debian/1.52.0-6..debian/1.52.1-1 | filterdiff -x "*/src/tests/*" -x "*/contrib/fedora/*" -x "*src/nm-cloud-setup/*" -x "*/src/core/devices/ovs/*"


diff --git a/NEWS b/NEWS
index 1e50ee0cf2..b47b76b865 100644
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,14 @@
+=============================================
+NetworkManager-1.52.1
+Overview of changes since NetworkManager-1.52
+=============================================
+* Fail early if we cannot get current FEC
+  (Forward Error Correction) value.
+* Allow reapplying ovs-bridge and ovs-port properties.
+* When activating a WireGuard connection to an IPv6 endpoint, now
+  NetworkManager creates firewall rules to ensure that the incoming
+  packets are not dropped by kernel reverse path filtering.
+
 =============================================
 NetworkManager-1.52
 Overview of changes since NetworkManager-1.50
diff --git a/config.h.meson b/config.h.meson
index e59160dec5..9ae88b26bf 100644
--- a/config.h.meson
+++ b/config.h.meson
@@ -67,6 +67,9 @@
 /* Define to path of iptables binary */
 #mesondefine IPTABLES_PATH
 
+/* Define to path of ip6tables binary */
+#mesondefine IP6TABLES_PATH
+
 /* Define to path of nft binary */
 #mesondefine NFT_PATH
 
diff --git a/debian/changelog b/debian/changelog
index 78613a2474..cbd4bc3e60 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,10 @@
+network-manager (1.52.1-1) unstable; urgency=medium
+
+  * New upstream version 1.52.1
+  * Explicitly specify the path for ip6tables
+
+ -- Michael Biebl <bi...@debian.org>  Tue, 15 Jul 2025 12:21:02 +0200
+
 network-manager (1.52.0-6) unstable; urgency=medium
 
   [ Lukas Märdian ]
diff --git a/debian/rules b/debian/rules
index f222c09b28..1279622896 100755
--- a/debian/rules
+++ b/debian/rules
@@ -22,6 +22,7 @@ override_dh_auto_configure:
                -Dmodprobe=/usr/sbin/modprobe \
                -Ddhcpcd=false \
                -Diptables=/usr/sbin/iptables \
+               -Dip6tables=/usr/sbin/ip6tables \
                -Dnft=/usr/sbin/nft \
                -Ddnsmasq=/usr/sbin/dnsmasq \
                
-Dpolkit_agent_helper_1=/usr/lib/policykit-1/polkit-agent-helper-1 \
diff --git a/man/NetworkManager.conf.xml b/man/NetworkManager.conf.xml
index 4652887905..eb8f18e76a 100644
--- a/man/NetworkManager.conf.xml
+++ b/man/NetworkManager.conf.xml
@@ -1933,7 +1933,7 @@ interface-name:vboxnet*,except:interface-name:vboxnet2
           
<literal>"uuid:83037490-1d17-4986-a397-01f1db3a7fc2"</literal></para></listitem>
         </varlistentry>
         <varlistentry>
-          <term>id=ID</term>
+          <term>id:ID</term>
           <listitem><para>Match the connection by name.</para></listitem>
         </varlistentry>
         <varlistentry>
diff --git a/meson.build b/meson.build
index 52bd072b0c..847f72b556 100644
--- a/meson.build
+++ b/meson.build
@@ -5,7 +5,7 @@ project(
 # NOTE: When incrementing version also add corresponding
 #       NM_VERSION_x_y_z macros in
 #       "src/libnm-core-public/nm-version-macros.h.in"
-  version: '1.52.0',
+  version: '1.52.1',
   license: 'GPL2+',
   default_options: [
     'buildtype=debugoptimized',
@@ -723,6 +723,7 @@ default_paths = ['/sbin', '/usr/sbin']
 
 # 0: cmdline option, 1: paths, 2: fallback
 progs = [['iptables', default_paths, '/usr/sbin/iptables'],
+         ['ip6tables', default_paths, '/usr/sbin/ip6tables'],
          ['nft',      default_paths, '/usr/sbin/nft'],
          ['dnsmasq',  default_paths, ''],
          ['modprobe', default_paths, '/sbin/modprobe']
@@ -1125,6 +1126,7 @@ endif
 output += '\n'
 output += '  jansson: ' + jansson_msg + '\n'
 output += '  iptables: ' + config_h.get('IPTABLES_PATH') + '\n'
+output += '  ip6tables: ' + config_h.get('IP6TABLES_PATH') + '\n'
 output += '  nft: ' + config_h.get('NFT_PATH') + '\n'
 output += '  modprobe: ' + config_h.get('MODPROBE_PATH') + '\n'
 output += '  modemmanager-1: ' + enable_modem_manager.to_string() + '\n'
diff --git a/meson_options.txt b/meson_options.txt
index 4bc11aa08b..fe696aaf16 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -6,6 +6,7 @@ option('dbus_conf_dir', type: 'string', value: '', description: 
'where D-Bus sys
 option('kernel_firmware_dir', type: 'string', value: '/lib/firmware', 
description: 'where kernel firmware directory is (default is /lib/firmware)')
 option('runtime_dir', type: 'string', value: '', description: 'Directory for 
transient runtime state [default: LOCALSTATEDIR/run or /run]')
 option('iptables', type: 'string', value: '', description: 'path to iptables')
+option('ip6tables', type: 'string', value: '', description: 'path to 
ip6tables')
 option('nft', type: 'string', value: '', description: 'path to nft')
 option('dnsmasq', type: 'string', value: '', description: 'path to dnsmasq')
 option('modprobe', type: 'string', value: '', description: 'path to modprobe')
diff --git a/src/core/devices/nm-device-6lowpan.c 
b/src/core/devices/nm-device-6lowpan.c
index 3dabcb9bdc..61b1e4eee8 100644
--- a/src/core/devices/nm-device-6lowpan.c
+++ b/src/core/devices/nm-device-6lowpan.c
@@ -137,14 +137,6 @@ link_changed(NMDevice *device, const NMPlatformLink 
*pllink)
     nm_device_parent_set_ifindex(device, parent);
 }
 
-static gboolean
-is_available(NMDevice *device, NMDeviceCheckDevAvailableFlags flags)
-{
-    if (!nm_device_parent_get_device(device))
-        return FALSE;
-    return 
NM_DEVICE_CLASS(nm_device_6lowpan_parent_class)->is_available(device, flags);
-}
-
 static gboolean
 complete_connection(NMDevice            *device,
                     NMConnection        *connection,
@@ -237,7 +229,6 @@ nm_device_6lowpan_class_init(NMDevice6LowpanClass *klass)
     device_class->get_generic_capabilities               = 
get_generic_capabilities;
     device_class->get_configured_mtu                     = 
nm_device_get_configured_mtu_for_wired;
     device_class->link_changed                           = link_changed;
-    device_class->is_available                           = is_available;
     device_class->parent_changed_notify                  = 
parent_changed_notify;
     device_class->update_connection                      = update_connection;
 }
diff --git a/src/core/devices/nm-device-ipvlan.c 
b/src/core/devices/nm-device-ipvlan.c
index 00a1b57937..abeb82f794 100644
--- a/src/core/devices/nm-device-ipvlan.c
+++ b/src/core/devices/nm-device-ipvlan.c
@@ -221,16 +221,6 @@ get_generic_capabilities(NMDevice *device)
 
 /*****************************************************************************/
 
-static gboolean
-is_available(NMDevice *device, NMDeviceCheckDevAvailableFlags flags)
-{
-    if (!nm_device_parent_get_device(device))
-        return FALSE;
-    return 
NM_DEVICE_CLASS(nm_device_ipvlan_parent_class)->is_available(device, flags);
-}
-
-/*****************************************************************************/
-
 static gboolean
 check_connection_compatible(NMDevice     *device,
                             NMConnection *connection,
@@ -376,7 +366,6 @@ nm_device_ipvlan_class_init(NMDeviceIpvlanClass *klass)
     device_class->check_connection_compatible = check_connection_compatible;
     device_class->create_and_realize          = create_and_realize;
     device_class->get_generic_capabilities    = get_generic_capabilities;
-    device_class->is_available                = is_available;
     device_class->link_changed                = link_changed;
     device_class->update_connection           = update_connection;
 
diff --git a/src/core/devices/nm-device-macsec.c 
b/src/core/devices/nm-device-macsec.c
index 89a0672097..2ff1eeb30a 100644
--- a/src/core/devices/nm-device-macsec.c
+++ b/src/core/devices/nm-device-macsec.c
@@ -683,14 +683,6 @@ get_generic_capabilities(NMDevice *dev)
 
 /******************************************************************/
 
-static gboolean
-is_available(NMDevice *device, NMDeviceCheckDevAvailableFlags flags)
-{
-    if (!nm_device_parent_get_device(device))
-        return FALSE;
-    return 
NM_DEVICE_CLASS(nm_device_macsec_parent_class)->is_available(device, flags);
-}
-
 static gboolean
 create_and_realize(NMDevice              *device,
                    NMConnection          *connection,
@@ -903,7 +895,6 @@ nm_device_macsec_class_init(NMDeviceMacsecClass *klass)
     device_class->deactivate               = deactivate;
     device_class->get_generic_capabilities = get_generic_capabilities;
     device_class->link_changed             = link_changed;
-    device_class->is_available             = is_available;
     device_class->parent_changed_notify    = parent_changed_notify;
     device_class->state_changed            = device_state_changed;
     device_class->get_configured_mtu       = 
nm_device_get_configured_mtu_wired_parent;
diff --git a/src/core/devices/nm-device-macvlan.c 
b/src/core/devices/nm-device-macvlan.c
index 9501e8f15c..c5bcc91ad1 100644
--- a/src/core/devices/nm-device-macvlan.c
+++ b/src/core/devices/nm-device-macvlan.c
@@ -270,16 +270,6 @@ get_generic_capabilities(NMDevice *device)
 
 /*****************************************************************************/
 
-static gboolean
-is_available(NMDevice *device, NMDeviceCheckDevAvailableFlags flags)
-{
-    if (!nm_device_parent_get_device(device))
-        return FALSE;
-    return 
NM_DEVICE_CLASS(nm_device_macvlan_parent_class)->is_available(device, flags);
-}
-
-/*****************************************************************************/
-
 static gboolean
 check_connection_compatible(NMDevice     *device,
                             NMConnection *connection,
@@ -508,7 +498,6 @@ nm_device_macvlan_class_init(NMDeviceMacvlanClass *klass)
     device_class->create_and_realize                     = create_and_realize;
     device_class->get_generic_capabilities               = 
get_generic_capabilities;
     device_class->get_configured_mtu    = 
nm_device_get_configured_mtu_wired_parent;
-    device_class->is_available          = is_available;
     device_class->link_changed          = link_changed;
     device_class->parent_changed_notify = parent_changed_notify;
     device_class->update_connection     = update_connection;
diff --git a/src/core/devices/nm-device-vlan.c 
b/src/core/devices/nm-device-vlan.c
index 59a429ca7f..9d03e33704 100644
--- a/src/core/devices/nm-device-vlan.c
+++ b/src/core/devices/nm-device-vlan.c
@@ -292,16 +292,6 @@ get_generic_capabilities(NMDevice *device)
 
 /*****************************************************************************/
 
-static gboolean
-is_available(NMDevice *device, NMDeviceCheckDevAvailableFlags flags)
-{
-    if (!nm_device_parent_get_device(device))
-        return FALSE;
-    return NM_DEVICE_CLASS(nm_device_vlan_parent_class)->is_available(device, 
flags);
-}
-
-/*****************************************************************************/
-
 static gboolean
 check_connection_compatible(NMDevice     *device,
                             NMConnection *connection,
@@ -561,7 +551,6 @@ nm_device_vlan_class_init(NMDeviceVlanClass *klass)
     device_class->act_stage1_prepare_set_hwaddr_ethernet = TRUE;
     device_class->act_stage1_prepare                     = act_stage1_prepare;
     device_class->get_configured_mtu    = 
nm_device_get_configured_mtu_wired_parent;
-    device_class->is_available          = is_available;
     device_class->parent_changed_notify = parent_changed_notify;
 
     device_class->check_connection_compatible = check_connection_compatible;
diff --git a/src/core/devices/nm-device-wireguard.c 
b/src/core/devices/nm-device-wireguard.c
index 4a08192e03..299e3b30e8 100644
--- a/src/core/devices/nm-device-wireguard.c
+++ b/src/core/devices/nm-device-wireguard.c
@@ -23,6 +23,7 @@
 #include "nm-active-connection.h"
 #include "nm-act-request.h"
 #include "dns/nm-dns-manager.h"
+#include "nm-firewall-utils.h"
 
 #define _NMLOG_DEVICE_TYPE NMDeviceWireGuard
 #include "nm-device-logging.h"
@@ -1207,6 +1208,40 @@ skip:
     *out_allowed_ips_data = g_steal_pointer(&allowed_ips);
 }
 
+static void
+_configure_firewall(NMDeviceWireGuard *self, NMConnection *connection, int 
addr_family, gboolean up)
+{
+    NMDeviceWireGuardPrivate *priv = NM_DEVICE_WIREGUARD_GET_PRIVATE(self);
+    const char               *ip_iface;
+    NMSettingIPConfig        *ip_config;
+
+    ip_iface = nm_device_get_ip_iface(NM_DEVICE(self));
+
+    nm_assert(ip_iface);
+
+    switch (addr_family) {
+    case AF_INET:
+        if (!priv->auto_default_route_enabled_4)
+            return;
+
+        ip_config = nm_connection_get_setting_ip4_config(connection);
+        break;
+    case AF_INET6:
+        if (!priv->auto_default_route_enabled_6)
+            return;
+
+        ip_config = nm_connection_get_setting_ip6_config(connection);
+        break;
+    default:
+        nm_assert_not_reached();
+    }
+
+    nm_assert(ip_config);
+    nm_assert(priv->auto_default_route_fwmark);
+
+    nm_firewall_config_set_wg_rule(ip_iface, ip_config, 
priv->auto_default_route_fwmark, up);
+}
+
 /*****************************************************************************/
 
 static void
@@ -1300,6 +1335,18 @@ create_and_realize(NMDevice              *device,
     return TRUE;
 }
 
+static void
+deactivate(NMDevice *device)
+{
+    NMDeviceWireGuard *self       = NM_DEVICE_WIREGUARD(device);
+    NMConnection      *connection = 
nm_device_get_applied_connection(NM_DEVICE(self));
+
+    if (connection) {
+        _configure_firewall(self, connection, AF_INET, FALSE);
+        _configure_firewall(self, connection, AF_INET6, FALSE);
+    }
+}
+
 /*****************************************************************************/
 
 static void
@@ -1768,6 +1815,10 @@ act_stage3_ip_config(NMDevice *device, int addr_family)
     nm_auto_unref_l3cd const NML3ConfigData *l3cd = NULL;
 
     l3cd = _get_dev2_ip_config(NM_DEVICE_WIREGUARD(device), addr_family);
+    _configure_firewall(NM_DEVICE_WIREGUARD(device),
+                        nm_device_get_applied_connection(device),
+                        addr_family,
+                        TRUE);
     nm_device_devip_set_state(device, addr_family, NM_DEVICE_IP_STATE_READY, 
l3cd);
 }
 
@@ -1866,6 +1917,10 @@ reapply_connection(NMDevice *device, NMConnection 
*con_old, NMConnection *con_ne
 
     if (state >= NM_DEVICE_STATE_CONFIG) {
         priv->auto_default_route_refresh = TRUE;
+
+        _configure_firewall(self, con_old, AF_INET, FALSE);
+        _configure_firewall(self, con_old, AF_INET6, FALSE);
+
         link_config(NM_DEVICE_WIREGUARD(device), "reapply", 
LINK_CONFIG_MODE_REAPPLY, NULL);
     }
 
@@ -2018,6 +2073,7 @@ nm_device_wireguard_class_init(NMDeviceWireGuardClass 
*klass)
 
     device_class->state_changed                                 = 
device_state_changed;
     device_class->create_and_realize                            = 
create_and_realize;
+    device_class->deactivate                                    = deactivate;
     device_class->act_stage2_config                             = 
act_stage2_config;
     device_class->act_stage2_config_also_for_external_or_assume = TRUE;
     device_class->act_stage3_ip_config                          = 
act_stage3_ip_config;
diff --git a/src/core/devices/nm-device.c b/src/core/devices/nm-device.c
index e310a9c680..2f2f25a5b8 100644
--- a/src/core/devices/nm-device.c
+++ b/src/core/devices/nm-device.c
@@ -603,6 +603,7 @@ typedef struct _NMDevicePrivate {
 
     bool is_attached : 1;
 
+    bool device_link_carrier_changed_down : 1;
     bool device_link_changed_down : 1;
 
     bool concheck_rp_filter_checked : 1;
@@ -2759,22 +2760,23 @@ _ethtool_fec_set(NMDevice         *self,
 
     g_hash_table_iter_init(&iter, hash);
     while (g_hash_table_iter_next(&iter, (gpointer *) &name, (gpointer *) 
&variant)) {
-        NMEthtoolID ethtool_id = nm_ethtool_id_get_by_name(name);
-
-        if (!nm_ethtool_id_is_fec(ethtool_id))
-            continue;
-
-        nm_assert(g_variant_is_of_type(variant, G_VARIANT_TYPE_UINT32));
-        fec_mode = g_variant_get_uint32(variant);
+        if (nm_ethtool_id_is_fec(nm_ethtool_id_get_by_name(name))) {
+            nm_assert(g_variant_is_of_type(variant, G_VARIANT_TYPE_UINT32));
+            fec_mode = g_variant_get_uint32(variant);
+            break;
+        }
     }
 
-    nm_platform_ethtool_get_fec_mode(platform, ethtool_state->ifindex, 
&old_fec_mode);
-
     /* The NM_SETTING_ETHTOOL_FEC_MODE_NONE is query only value, hence do 
nothing. */
     if (!fec_mode || fec_mode == NM_SETTING_ETHTOOL_FEC_MODE_NONE) {
         return;
     }
 
+    if (!nm_platform_ethtool_get_fec_mode(platform, ethtool_state->ifindex, 
&old_fec_mode)) {
+        _LOGW(LOGD_DEVICE, "ethtool: failure setting FEC %d: cannot get 
current value", fec_mode);
+        return;
+    }
+
     if (!nm_platform_ethtool_set_fec_mode(platform, ethtool_state->ifindex, 
fec_mode))
         _LOGW(LOGD_DEVICE, "ethtool: failure setting FEC %d", fec_mode);
     else {
@@ -7123,6 +7125,9 @@ nm_device_controller_release_port(NMDevice           
*self,
                                      NM_UNMANAGED_IS_PORT,
                                      NM_UNMAN_FLAG_OP_FORGET,
                                      NM_DEVICE_STATE_REASON_REMOVED);
+
+    /* Once the port is detached, unmanaged-external-down might change */
+    _dev_unmanaged_check_external_down(self, FALSE, FALSE);
 }
 
 /*****************************************************************************/
@@ -7558,10 +7563,12 @@ device_link_changed(gpointer user_data)
     gboolean                        carrier_was_up;
     gboolean                        update_unmanaged_specs = FALSE;
     gboolean                        got_hw_addr            = FALSE, 
had_hw_addr;
+    gboolean                        carrier_seen_down      = 
priv->device_link_carrier_changed_down;
     gboolean                        seen_down              = 
priv->device_link_changed_down;
 
-    priv->device_link_changed_id   = 0;
-    priv->device_link_changed_down = FALSE;
+    priv->device_link_changed_id           = 0;
+    priv->device_link_changed_down         = FALSE;
+    priv->device_link_carrier_changed_down = FALSE;
 
     ifindex = nm_device_get_ifindex(self);
     if (ifindex <= 0)
@@ -7712,7 +7719,8 @@ device_link_changed(gpointer user_data)
         if (priv->state >= NM_DEVICE_STATE_IP_CONFIG && priv->state <= 
NM_DEVICE_STATE_ACTIVATED
             && !nm_device_managed_type_is_external(self))
             nm_device_l3cfg_commit(self, NM_L3_CFG_COMMIT_TYPE_REAPPLY, FALSE);
-
+    }
+    if (priv->carrier && (!carrier_was_up || carrier_seen_down)) {
         /* If the device is active without a carrier (probably because it is
          * tagged for carrier ignore) ensure that when the carrier appears we
          * renew DHCP leases and such.
@@ -7803,6 +7811,8 @@ link_changed_cb(NMPlatform     *platform,
     priv = NM_DEVICE_GET_PRIVATE(self);
 
     if (ifindex == nm_device_get_ifindex(self)) {
+        if (!(pllink->n_ifi_flags & IFF_LOWER_UP))
+            priv->device_link_carrier_changed_down = TRUE;
         if (!(pllink->n_ifi_flags & IFF_UP))
             priv->device_link_changed_down = TRUE;
         if (!priv->device_link_changed_id) {
@@ -8814,6 +8824,9 @@ nm_device_controller_add_port(NMDevice *self, NMDevice 
*port, gboolean configure
     } else
         g_return_val_if_fail(port_priv->controller == self, FALSE);
 
+    /* Once the port is attached, unmanaged-external-down might change */
+    _dev_unmanaged_check_external_down(self, TRUE, FALSE);
+
     nm_device_queue_recheck_assume(self);
     nm_device_queue_recheck_assume(port);
 
@@ -13417,6 +13430,8 @@ _dev_ipsharedx_cleanup(NMDevice *self, int addr_family)
         nm_clear_l3cd(&priv->ipshared_data_4.v4.l3cd);
 
         _dev_l3_register_l3cds_set_one(self, L3_CONFIG_DATA_TYPE_SHARED_4, 
NULL, FALSE);
+    } else {
+        _dev_l3_register_l3cds_set_one(self, L3_CONFIG_DATA_TYPE_PD_6, NULL, 
FALSE);
     }
 
     _dev_ipsharedx_set_state(self, addr_family, NM_DEVICE_IP_STATE_NONE);
@@ -15085,8 +15100,8 @@ respawn_ping_cb(gpointer user_data)
     nm_clear_g_source_inst(&ping_op->watch);
 
     if (!spawn_ping_for_operation(self, ping_op)) {
-        cleanup_ping_operation(ping_op);
         priv->ping_operations = g_list_remove(priv->ping_operations, ping_op);
+        cleanup_ping_operation(ping_op);
 
         if (g_list_length(priv->ping_operations) == 0) {
             ip_check_pre_up(self);
@@ -15129,7 +15144,6 @@ ip_check_ping_watch_cb(GPid pid, int status, gpointer 
user_data)
 
     if (success) {
         if (ping_op->ping_addresses_require_all) {
-            cleanup_ping_operation(ping_op);
             priv->ping_operations = g_list_remove(priv->ping_operations, 
ping_op);
             if (g_list_length(priv->ping_operations) == 0) {
                 _LOGD(ping_op->log_domain,
@@ -15139,6 +15153,7 @@ ip_check_ping_watch_cb(GPid pid, int status, gpointer 
user_data)
                     nm_clear_g_source_inst(&priv->ping_timeout);
                 ip_check_pre_up(self);
             }
+            cleanup_ping_operation(ping_op);
         } else {
             nm_assert(priv->ping_operations);
 
diff --git a/src/core/dns/nm-dns-dnsconfd.c b/src/core/dns/nm-dns-dnsconfd.c
index 63b3060f3d..c17fb9cf44 100644
--- a/src/core/dns/nm-dns-dnsconfd.c
+++ b/src/core/dns/nm-dns-dnsconfd.c
@@ -71,6 +71,15 @@ typedef enum {
 
 /*****************************************************************************/
 
+static void
+dnsconfd_change_plugin_state(NMDnsDnsconfd *self, DnsconfdPluginState 
new_state)
+{
+    NMDnsDnsconfdPrivate *priv = NM_DNS_DNSCONFD_GET_PRIVATE(self);
+
+    priv->plugin_state = new_state;
+    _nm_dns_plugin_update_pending_maybe_changed(NM_DNS_PLUGIN(self));
+}
+
 static void
 dnsconfd_serial_changed(NMDnsDnsconfd *self, guint new_serial)
 {
@@ -78,12 +87,10 @@ dnsconfd_serial_changed(NMDnsDnsconfd *self, guint 
new_serial)
     priv->present_configuration_serial = new_serial;
     if (priv->plugin_state == DNSCONFD_PLUGIN_WAIT_SERIAL
         && priv->awaited_configuration_serial == new_serial) {
-        priv->plugin_state = DNSCONFD_PLUGIN_IDLE;
+        dnsconfd_change_plugin_state(self, DNSCONFD_PLUGIN_IDLE);
         /* Update finished, serials match */
         _LOGT("serials match, update finished");
     }
-
-    _nm_dns_plugin_update_pending_maybe_changed(NM_DNS_PLUGIN(self));
 }
 
 static void
@@ -132,6 +139,12 @@ dnsconfd_serial_retrieval_done(GObject *source_object, 
GAsyncResult *res, gpoint
     self = user_data;
     priv = NM_DNS_DNSCONFD_GET_PRIVATE(self);
 
+    if (!response) {
+        _LOGW("dnsconfd serial retrieval failed: %s", error->message);
+        dnsconfd_change_plugin_state(self, DNSCONFD_PLUGIN_IDLE);
+        return;
+    }
+
     nm_clear_g_cancellable(&priv->serial_cancellable);
 
     g_variant_get(response, "(v)", &new_serial_variant);
@@ -201,8 +214,11 @@ dnsconfd_update_done(GObject *source_object, GAsyncResult 
*res, gpointer user_da
 
     nm_clear_g_cancellable(&priv->update_cancellable);
 
-    if (!response)
+    if (!response) {
         _LOGW("dnsconfd update failed: %s", error->message);
+        dnsconfd_change_plugin_state(self, DNSCONFD_PLUGIN_IDLE);
+        return;
+    }
 
     /* By using &s we will get pointer to char data contained
      * in variant and thus no freing of dnsconfd_message is required */
@@ -210,8 +226,7 @@ dnsconfd_update_done(GObject *source_object, GAsyncResult 
*res, gpointer user_da
 
     if (!awaited_serial) {
         _LOGW("dnsconfd refused update: %s", dnsconfd_message);
-        priv->plugin_state = DNSCONFD_PLUGIN_IDLE;
-        _nm_dns_plugin_update_pending_maybe_changed(NM_DNS_PLUGIN(self));
+        dnsconfd_change_plugin_state(self, DNSCONFD_PLUGIN_IDLE);
         return;
     }
 
@@ -220,14 +235,12 @@ dnsconfd_update_done(GObject *source_object, GAsyncResult 
*res, gpointer user_da
 
     if (priv->awaited_configuration_serial == 
priv->present_configuration_serial) {
         /* Serials match, update finished */
-        priv->plugin_state = DNSCONFD_PLUGIN_IDLE;
+        dnsconfd_change_plugin_state(self, DNSCONFD_PLUGIN_IDLE);
         _LOGT("after update serials match");
     } else {
-        priv->plugin_state = DNSCONFD_PLUGIN_WAIT_SERIAL;
+        dnsconfd_change_plugin_state(self, DNSCONFD_PLUGIN_WAIT_SERIAL);
         _LOGT("after update serials don't match, waiting");
     }
-
-    _nm_dns_plugin_update_pending_maybe_changed(NM_DNS_PLUGIN(self));
 }
 
 static gboolean
@@ -478,8 +491,7 @@ name_owner_changed(NMDnsDnsconfd *self, const char 
*name_owner)
             || priv->plugin_state == DNSCONFD_PLUGIN_WAIT_SERIAL) {
             /* We were waiting for either serial or confirmation of update and 
name
              * disappeared, thus we need to retransmit */
-            priv->plugin_state = DNSCONFD_PLUGIN_WAIT_CONNECT;
-            _nm_dns_plugin_update_pending_maybe_changed(NM_DNS_PLUGIN(self));
+            dnsconfd_change_plugin_state(self, DNSCONFD_PLUGIN_WAIT_CONNECT);
         }
         return;
     }
@@ -490,15 +502,13 @@ name_owner_changed(NMDnsDnsconfd *self, const char 
*name_owner)
     if (!subscribe_serial(self)) {
         /* This means that in time between new name and subscribe serial call
          * we lost the name again thus wait again */
-        priv->plugin_state = DNSCONFD_PLUGIN_WAIT_CONNECT;
+        dnsconfd_change_plugin_state(self, DNSCONFD_PLUGIN_WAIT_CONNECT);
         _LOGT("subscription failed, waiting to connect");
     } else {
-        priv->plugin_state = DNSCONFD_PLUGIN_WAIT_UPDATE_DONE;
+        dnsconfd_change_plugin_state(self, DNSCONFD_PLUGIN_WAIT_UPDATE_DONE);
         _LOGT("sending update and waiting for its finish");
         send_dnsconfd_update(self);
     }
-
-    _nm_dns_plugin_update_pending_maybe_changed(NM_DNS_PLUGIN(self));
 }
 
 static void
@@ -695,18 +705,16 @@ update(NMDnsPlugin             *plugin,
     /* We need to consider only whether we are connected, because newer update 
call
      * overrides the old one */
     if (all_connected == CONNECTION_FAIL) {
-        priv->plugin_state = DNSCONFD_PLUGIN_IDLE;
+        dnsconfd_change_plugin_state(self, DNSCONFD_PLUGIN_IDLE);
         _LOGT("failed to connect");
     } else if (all_connected == CONNECTION_WAIT) {
-        priv->plugin_state = DNSCONFD_PLUGIN_WAIT_CONNECT;
+        dnsconfd_change_plugin_state(self, DNSCONFD_PLUGIN_WAIT_CONNECT);
         _LOGT("not connected, waiting to connect");
     } else {
-        priv->plugin_state = DNSCONFD_PLUGIN_WAIT_UPDATE_DONE;
+        dnsconfd_change_plugin_state(self, DNSCONFD_PLUGIN_WAIT_UPDATE_DONE);
         _LOGT("connected, waiting for update to finish");
     }
 
-    _nm_dns_plugin_update_pending_maybe_changed(plugin);
-
     if (all_connected == CONNECTION_FAIL) {
         nm_utils_error_set(error,
                            NM_UTILS_ERROR_UNKNOWN,
diff --git a/src/core/nm-bond-manager.c b/src/core/nm-bond-manager.c
index 2f7fe36c16..116cb73744 100644
--- a/src/core/nm-bond-manager.c
+++ b/src/core/nm-bond-manager.c
@@ -882,7 +882,7 @@ nm_bond_manager_send_arp(int                 bond_ifindex,
         .sll_protocol = htons(ETH_P_ARP),
         .sll_ifindex  = bond_ifindex,
     };
-    ARPPacket         data;
+    ARPPacket         data = {0};
     const guint8     *hwaddr;
     gsize             hwaddrlen    = 0;
     nm_auto_close int sockfd       = -1;
@@ -940,6 +940,7 @@ nm_bond_manager_send_arp(int                 bond_ifindex,
         data.op = htons(ARP_OP_GARP);
         memcpy(data.s_addr, hwaddr, hwaddrlen);
         memcpy(data.s_hw_addr, hwaddr, hwaddrlen);
+        memset(data.d_hw_addr, 0xff, ETH_ALEN);
         for (int i = 0; i < addrs_len; i++) {
             const in_addr_t tmp_addr = addrs_array[i];
 
diff --git a/src/core/nm-connectivity.c b/src/core/nm-connectivity.c
index 22e6c0d5eb..2aa22331ea 100644
--- a/src/core/nm-connectivity.c
+++ b/src/core/nm-connectivity.c
@@ -25,6 +25,8 @@
 
 #define HEADER_STATUS_ONLINE "X-NetworkManager-Status: online\r\n"
 
+#define SD_RESOLVED_DNS ((guint64) (1LL << 0))
+
 /*****************************************************************************/
 
 static NM_UTILS_LOOKUP_STR_DEFINE(_state_to_string,
@@ -950,9 +952,6 @@ systemd_resolved_resolve_cb(GObject *object, GAsyncResult 
*res, gpointer user_da
 
     do_curl_request(cb_data, nm_str_buf_get_str(&strbuf_hosts));
 }
-#endif
-
-#define SD_RESOLVED_DNS ((guint64) (1LL << 0))
 
 static NMConnectivityState
 check_platform_config(NMConnectivity *self,
@@ -1013,6 +1012,7 @@ check_platform_config(NMConnectivity *self,
     NM_SET_OUT(reason, NULL);
     return NM_CONNECTIVITY_UNKNOWN;
 }
+#endif
 
 NMConnectivityCheckHandle *
 nm_connectivity_check_start(NMConnectivity             *self,
diff --git a/src/core/nm-firewall-utils.c b/src/core/nm-firewall-utils.c
index b496061247..60f40228db 100644
--- a/src/core/nm-firewall-utils.c
+++ b/src/core/nm-firewall-utils.c
@@ -8,6 +8,7 @@
 
 #include "nm-firewall-utils.h"
 
+#include "libnm-core-aux-intern/nm-libnm-core-utils.h"
 #include "libnm-glib-aux/nm-str-buf.h"
 #include "libnm-glib-aux/nm-io-utils.h"
 #include "libnm-platform/nm-platform.h"
@@ -127,7 +128,7 @@ _share_iptables_subnet_to_str(char      buf[static 
_SHARE_IPTABLES_SUBNET_TO_STR
 }
 
 static char *
-_share_iptables_get_name(gboolean is_iptables_chain, const char *prefix, const 
char *ip_iface)
+_iptables_get_name(gboolean is_iptables_chain, const char *prefix, const char 
*ip_iface)
 {
     NMStrBuf strbuf = NM_STR_BUF_INIT(NM_UTILS_GET_NEXT_REALLOC_SIZE_40, 
FALSE);
     gsize    ip_iface_len;
@@ -179,13 +180,13 @@ _share_iptables_get_name(gboolean is_iptables_chain, 
const char *prefix, const c
 /*****************************************************************************/
 
 static gboolean
-_share_iptables_call_v(const char *const *argv)
+_iptables_call_v(const char *const *argv)
 {
     gs_free_error GError *error    = NULL;
     gs_free char         *argv_str = NULL;
     int                   status;
 
-    nm_log_dbg(LOGD_SHARING, "iptables: %s", (argv_str = g_strjoinv(" ", (char 
**) argv)));
+    nm_log_dbg(LOGD_FIREWALL, "iptables: %s", (argv_str = g_strjoinv(" ", 
(char **) argv)));
 
     if (!g_spawn_sync("/",
                       (char **) argv,
@@ -197,7 +198,7 @@ _share_iptables_call_v(const char *const *argv)
                       NULL,
                       &status,
                       &error)) {
-        nm_log_warn(LOGD_SHARING,
+        nm_log_warn(LOGD_FIREWALL,
                     "iptables: error executing command %s: %s",
                     argv[0],
                     error->message);
@@ -205,20 +206,24 @@ _share_iptables_call_v(const char *const *argv)
     }
 
     if (!g_spawn_check_exit_status(status, &error)) {
-        nm_log_warn(LOGD_SHARING, "iptables: command %s failed: %s", argv[0], 
error->message);
+        nm_log_warn(LOGD_FIREWALL, "iptables: command %s failed: %s", argv[0], 
error->message);
         return FALSE;
     }
 
     return TRUE;
 }
 
-#define _share_iptables_call(...) \
-    _share_iptables_call_v(NM_MAKE_STRV("" IPTABLES_PATH "", "--wait", "2", 
__VA_ARGS__))
+#define _ipxtables_call(family, ...)                                           
        \
+    _iptables_call_v(                                                          
        \
+        NM_MAKE_STRV((family == AF_INET ? "" IPTABLES_PATH "" : "" 
IP6TABLES_PATH ""), \
+                     "--wait",                                                 
        \
+                     "2",                                                      
        \
+                     __VA_ARGS__))
 
 static gboolean
 _share_iptables_chain_op(const char *table, const char *chain, const char *op)
 {
-    return _share_iptables_call("--table", table, op, chain);
+    return _ipxtables_call(AF_INET, "--table", table, op, chain);
 }
 
 static gboolean
@@ -244,24 +249,25 @@ _share_iptables_set_masquerade_sync(gboolean up, const 
char *ip_iface, in_addr_t
     char          str_subnet[_SHARE_IPTABLES_SUBNET_TO_STR_LEN];
     gs_free char *comment_name = NULL;
 
-    comment_name = _share_iptables_get_name(FALSE, "nm-shared", ip_iface);
+    comment_name = _iptables_get_name(FALSE, "nm-shared", ip_iface);
 
     _share_iptables_subnet_to_str(str_subnet, addr, plen);
-    _share_iptables_call("--table",
-                         "nat",
-                         up ? "--insert" : "--delete",
-                         "POSTROUTING",
-                         "--source",
-                         str_subnet,
-                         "!",
-                         "--destination",
-                         str_subnet,
-                         "--jump",
-                         "MASQUERADE",
-                         "-m",
-                         "comment",
-                         "--comment",
-                         comment_name);
+    _ipxtables_call(AF_INET,
+                    "--table",
+                    "nat",
+                    up ? "--insert" : "--delete",
+                    "POSTROUTING",
+                    "--source",
+                    str_subnet,
+                    "!",
+                    "--destination",
+                    str_subnet,
+                    "--jump",
+                    "MASQUERADE",
+                    "-m",
+                    "comment",
+                    "--comment",
+                    comment_name);
 }
 
 static void
@@ -297,70 +303,76 @@ _share_iptables_set_shared_chains_add(const char 
*chain_input,
     _share_iptables_chain_add("filter", chain_input);
 
     for (i = 0; i < (int) G_N_ELEMENTS(input_params); i++) {
-        _share_iptables_call("--table",
-                             "filter",
-                             "--append",
-                             chain_input,
-                             "--protocol",
-                             input_params[i][0],
-                             "--destination-port",
-                             input_params[i][1],
-                             "--jump",
-                             "ACCEPT");
+        _ipxtables_call(AF_INET,
+                        "--table",
+                        "filter",
+                        "--append",
+                        chain_input,
+                        "--protocol",
+                        input_params[i][0],
+                        "--destination-port",
+                        input_params[i][1],
+                        "--jump",
+                        "ACCEPT");
     }
 
     _share_iptables_chain_add("filter", chain_forward);
 
-    _share_iptables_call("--table",
-                         "filter",
-                         "--append",
-                         chain_forward,
-                         "--destination",
-                         str_subnet,
-                         "--out-interface",
-                         ip_iface,
-                         "--match",
-                         "state",
-                         "--state",
-                         "ESTABLISHED,RELATED",
-                         "--jump",
-                         "ACCEPT");
-    _share_iptables_call("--table",
-                         "filter",
-                         "--append",
-                         chain_forward,
-                         "--source",
-                         str_subnet,
-                         "--in-interface",
-                         ip_iface,
-                         "--jump",
-                         "ACCEPT");
-    _share_iptables_call("--table",
-                         "filter",
-                         "--append",
-                         chain_forward,
-                         "--in-interface",
-                         ip_iface,
-                         "--out-interface",
-                         ip_iface,
-                         "--jump",
-                         "ACCEPT");
-    _share_iptables_call("--table",
-                         "filter",
-                         "--append",
-                         chain_forward,
-                         "--out-interface",
-                         ip_iface,
-                         "--jump",
-                         "REJECT");
-    _share_iptables_call("--table",
-                         "filter",
-                         "--append",
-                         chain_forward,
-                         "--in-interface",
-                         ip_iface,
-                         "--jump",
-                         "REJECT");
+    _ipxtables_call(AF_INET,
+                    "--table",
+                    "filter",
+                    "--append",
+                    chain_forward,
+                    "--destination",
+                    str_subnet,
+                    "--out-interface",
+                    ip_iface,
+                    "--match",
+                    "state",
+                    "--state",
+                    "ESTABLISHED,RELATED",
+                    "--jump",
+                    "ACCEPT");
+    _ipxtables_call(AF_INET,
+                    "--table",
+                    "filter",
+                    "--append",
+                    chain_forward,
+                    "--source",
+                    str_subnet,
+                    "--in-interface",
+                    ip_iface,
+                    "--jump",
+                    "ACCEPT");
+    _ipxtables_call(AF_INET,
+                    "--table",
+                    "filter",
+                    "--append",
+                    chain_forward,
+                    "--in-interface",
+                    ip_iface,
+                    "--out-interface",
+                    ip_iface,
+                    "--jump",
+                    "ACCEPT");
+    _ipxtables_call(AF_INET,
+                    "--table",
+                    "filter",
+                    "--append",
+                    chain_forward,
+                    "--out-interface",
+                    ip_iface,
+                    "--jump",
+                    "REJECT");
+    _ipxtables_call(AF_INET,
+                    "--table",
+                    "filter",
+                    "--append",
+                    chain_forward,
+                    "--in-interface",
+                    ip_iface,
+                    "--jump",
+                    "REJECT");
 }
 
 static void
@@ -377,36 +389,38 @@ _share_iptables_set_shared_sync(gboolean up, const char 
*ip_iface, in_addr_t add
     gs_free char *chain_input   = NULL;
     gs_free char *chain_forward = NULL;
 
-    comment_name  = _share_iptables_get_name(FALSE, "nm-shared", ip_iface);
-    chain_input   = _share_iptables_get_name(TRUE, "nm-sh-in", ip_iface);
-    chain_forward = _share_iptables_get_name(TRUE, "nm-sh-fw", ip_iface);
+    comment_name  = _iptables_get_name(FALSE, "nm-shared", ip_iface);
+    chain_input   = _iptables_get_name(TRUE, "nm-sh-in", ip_iface);
+    chain_forward = _iptables_get_name(TRUE, "nm-sh-fw", ip_iface);
 
     if (up)
         _share_iptables_set_shared_chains_add(chain_input, chain_forward, 
ip_iface, addr, plen);
 
-    _share_iptables_call("--table",
-                         "filter",
-                         up ? "--insert" : "--delete",
-                         "INPUT",
-                         "--in-interface",
-                         ip_iface,
-                         "--jump",
-                         chain_input,
-                         "-m",
-                         "comment",
-                         "--comment",
-                         comment_name);
-
-    _share_iptables_call("--table",
-                         "filter",
-                         up ? "--insert" : "--delete",
-                         "FORWARD",
-                         "--jump",
-                         chain_forward,
-                         "-m",
-                         "comment",
-                         "--comment",
-                         comment_name);
+    _ipxtables_call(AF_INET,
+                    "--table",
+                    "filter",
+                    up ? "--insert" : "--delete",
+                    "INPUT",
+                    "--in-interface",
+                    ip_iface,
+                    "--jump",
+                    chain_input,
+                    "-m",
+                    "comment",
+                    "--comment",
+                    comment_name);
+
+    _ipxtables_call(AF_INET,
+                    "--table",
+                    "filter",
+                    up ? "--insert" : "--delete",
+                    "FORWARD",
+                    "--jump",
+                    chain_forward,
+                    "-m",
+                    "comment",
+                    "--comment",
+                    comment_name);
 
     if (!up)
         _share_iptables_set_shared_chains_delete(chain_input, chain_forward);
@@ -460,19 +474,19 @@ _fw_nft_call_communicate_cb(GObject *source, GAsyncResult 
*result, gpointer user
         /* on any error, the process might still be running. We need to abort 
it in
          * the background... */
         if (!nm_utils_error_is_cancelled(error)) {
-            nm_log_dbg(LOGD_SHARING,
+            nm_log_dbg(LOGD_FIREWALL,
                        "firewall: nft[%s]: communication failed: %s. Kill 
process",
                        call_data->identifier,
                        error->message);
         } else if (!call_data->timeout_source) {
-            nm_log_dbg(LOGD_SHARING,
-                       "firewall: ntf[%s]: communication timed out. Kill 
process",
+            nm_log_dbg(LOGD_FIREWALL,
+                       "firewall: nft[%s]: communication timed out. Kill 
process",
                        call_data->identifier);
             nm_clear_error(&error);
             nm_utils_error_set(&error, NM_UTILS_ERROR_UNKNOWN, "timeout 
communicating with nft");
         } else {
-            nm_log_dbg(LOGD_SHARING,
-                       "firewall: ntf[%s]: communication cancelled. Kill 
process",
+            nm_log_dbg(LOGD_FIREWALL,
+                       "firewall: nft[%s]: communication cancelled. Kill 
process",
                        call_data->identifier);
         }
 
@@ -485,7 +499,7 @@ _fw_nft_call_communicate_cb(GObject *source, GAsyncResult 
*result, gpointer user
             nm_g_subprocess_terminate_in_background(call_data->subprocess, 
200);
         }
     } else if (g_subprocess_get_successful(call_data->subprocess)) {
-        nm_log_dbg(LOGD_SHARING, "firewall: nft[%s]: command successful", 
call_data->identifier);
+        nm_log_dbg(LOGD_FIREWALL, "firewall: nft[%s]: command successful", 
call_data->identifier);
     } else {
         char          buf[NM_UTILS_GET_PROCESS_EXIT_STATUS_BUF_LEN];
         gs_free char *ss_stdout    = NULL;
@@ -498,7 +512,7 @@ _fw_nft_call_communicate_cb(GObject *source, GAsyncResult 
*result, gpointer user
 
         nm_utils_get_process_exit_status_desc_buf(status, buf, sizeof(buf));
 
-        nm_log_warn(LOGD_SHARING,
+        nm_log_warn(LOGD_FIREWALL,
                     "firewall: nft[%s]: command %s:%s%s%s%s%s%s%s",
                     call_data->identifier,
                     buf,
@@ -534,7 +548,7 @@ _fw_nft_call_cancelled_cb(GCancellable *cancellable, 
gpointer user_data)
     if (call_data->cancellable_id == 0)
         return;
 
-    nm_log_dbg(LOGD_SHARING, "firewall: nft[%s]: operation cancelled", 
call_data->identifier);
+    nm_log_dbg(LOGD_FIREWALL, "firewall: nft[%s]: operation cancelled", 
call_data->identifier);
 
     nm_clear_g_signal_handler(g_task_get_cancellable(call_data->task), 
&call_data->cancellable_id);
     nm_clear_g_cancellable(&call_data->intern_cancellable);
@@ -546,7 +560,7 @@ _fw_nft_call_timeout_cb(gpointer user_data)
     FwNftCallData *call_data = user_data;
 
     nm_clear_g_source_inst(&call_data->timeout_source);
-    nm_log_dbg(LOGD_SHARING,
+    nm_log_dbg(LOGD_FIREWALL,
                "firewall: nft[%s]: cancel operation after timeout",
                call_data->identifier);
 
@@ -573,7 +587,7 @@ nm_firewall_nft_call(GBytes             *stdin_buf,
         .timeout_source = NULL,
     };
 
-    nm_log_trace(LOGD_SHARING,
+    nm_log_trace(LOGD_FIREWALL,
                  "firewall: nft: call command: [ '%s' ]",
                  nm_utils_buf_utf8safe_escape_bytes(stdin_buf,
                                                     
NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_CTRL,
@@ -585,7 +599,7 @@ nm_firewall_nft_call(GBytes             *stdin_buf,
                                                           call_data,
                                                           NULL);
         if (call_data->cancellable_id == 0) {
-            nm_log_dbg(LOGD_SHARING, "firewall: nft: already cancelled");
+            nm_log_dbg(LOGD_FIREWALL, "firewall: nft: already cancelled");
             nm_utils_error_set_cancelled(&error, FALSE, NULL);
             _fw_nft_call_data_free(call_data, g_steal_pointer(&error));
             return;
@@ -602,14 +616,14 @@ nm_firewall_nft_call(GBytes             *stdin_buf,
                                                          &error);
 
     if (!call_data->subprocess) {
-        nm_log_dbg(LOGD_SHARING, "firewall: nft: spawning nft failed: %s", 
error->message);
+        nm_log_dbg(LOGD_FIREWALL, "firewall: nft: spawning nft failed: %s", 
error->message);
         _fw_nft_call_data_free(call_data, g_steal_pointer(&error));
         return;
     }
 
     call_data->identifier = 
g_strdup(g_subprocess_get_identifier(call_data->subprocess));
 
-    nm_log_dbg(LOGD_SHARING, "firewall: nft[%s]: communicate with nft", 
call_data->identifier);
+    nm_log_dbg(LOGD_FIREWALL, "firewall: nft[%s]: communicate with nft", 
call_data->identifier);
 
     nm_shutdown_wait_obj_register_object(call_data->task, "nft-call");
 
@@ -691,7 +705,7 @@ _fw_nft_set_shared_construct(gboolean up, const char 
*ip_iface, in_addr_t addr,
     gs_free char            *table_name = NULL;
     char                     str_subnet[_SHARE_IPTABLES_SUBNET_TO_STR_LEN];
 
-    table_name = _share_iptables_get_name(FALSE, "nm-shared", ip_iface);
+    table_name = _iptables_get_name(FALSE, "nm-shared", ip_iface);
 
     _share_iptables_subnet_to_str(str_subnet, addr, plen);
 
@@ -756,6 +770,141 @@ _fw_nft_set_shared_construct(gboolean up, const char 
*ip_iface, in_addr_t addr,
     return nm_str_buf_finalize_to_gbytes(&strbuf);
 }
 
+static GBytes *
+_fw_nft_wg_default_construct(const char        *ip_iface,
+                             NMSettingIPConfig *ip_config,
+                             int                fwmark,
+                             gboolean           up)
+{
+    nm_auto_str_buf NMStrBuf strbuf = 
NM_STR_BUF_INIT(NM_UTILS_GET_NEXT_REALLOC_SIZE_1000, FALSE);
+    gs_free char            *table_name = NULL;
+    const char              *family_str;
+
+    table_name = _iptables_get_name(FALSE, "nm-wg", ip_iface);
+    family_str = nm_setting_ip_config_get_addr_family(ip_config) == AF_INET ? 
"ip" : "ip6";
+
+    _fw_nft_append_cmd_table(&strbuf, family_str, table_name, up);
+
+    if (up) {
+        guint n_addresses = nm_setting_ip_config_get_num_addresses(ip_config);
+
+        if (n_addresses) {
+            _append(&strbuf, "add chain %s %s preraw {", family_str, 
table_name);
+
+            for (guint i = 0; i < n_addresses; i++) {
+                NMIPAddress *addr = 
nm_setting_ip_config_get_address(ip_config, i);
+
+                _append(&strbuf,
+                        " iifname != \"%s\" "
+                        " %s daddr %s "
+                        " fib saddr type != local "
+                        "drop;",
+                        ip_iface,
+                        family_str,
+                        nm_ip_address_get_address(addr));
+            }
+
+            _append(&strbuf, "};");
+        }
+
+        _append(&strbuf,
+                "add chain %s %s premangle {"
+                " type filter hook prerouting priority mangle; policy accept; "
+                " meta l4proto udp meta mark set ct mark; "
+                "};",
+                family_str,
+                table_name);
+
+        _append(&strbuf,
+                "add chain %s %s postmangle {"
+                " type filter hook postrouting priority mangle; policy accept; 
"
+                " meta l4proto udp mark 0x%08x ct mark set meta mark; "
+                "};",
+                family_str,
+                table_name,
+                fwmark);
+    }
+
+    return nm_str_buf_finalize_to_gbytes(&strbuf);
+}
+
+static void
+_fw_iptables_wg_configure(const char        *ip_iface,
+                          NMSettingIPConfig *ip_config,
+                          int                fwmark,
+                          gboolean           up)
+{
+    gs_free char *comment_name = NULL;
+    char          fwmark_str[11];
+    int           family      = 
nm_setting_ip_config_get_addr_family(ip_config);
+    guint         n_addresses = 
nm_setting_ip_config_get_num_addresses(ip_config);
+
+    comment_name = _iptables_get_name(FALSE, "nm-wg", ip_iface);
+    g_snprintf(fwmark_str, sizeof(fwmark_str), "%" G_GUINT32_FORMAT, fwmark);
+
+    nm_assert(strlen(fwmark_str) > 0);
+
+    for (guint i = 0; i < n_addresses; i++) {
+        NMIPAddress *addr = nm_setting_ip_config_get_address(ip_config, i);
+
+        _ipxtables_call(family,
+                        "--table",
+                        "raw",
+                        up ? "--insert" : "--delete",
+                        "PREROUTING",
+                        "!",
+                        "--in-interface",
+                        ip_iface,
+                        "--destination",
+                        nm_ip_address_get_address(addr),
+                        "--match",
+                        "addrtype",
+                        "!",
+                        "--src-type",
+                        "LOCAL",
+                        "-j",
+                        "DROP",
+                        "-m",
+                        "comment",
+                        "--comment",
+                        comment_name);
+    }
+
+    _ipxtables_call(family,
+                    "--table",
+                    "mangle",
+                    up ? "--insert" : "--delete",
+                    "POSTROUTING",
+                    "--match",
+                    "mark",
+                    "--mark",
+                    fwmark_str,
+                    "--protocol",
+                    "udp",
+                    "--jump",
+                    "CONNMARK",
+                    "--save-mark",
+                    "-m",
+                    "comment",
+                    "--comment",
+                    comment_name);
+
+    _ipxtables_call(family,
+                    "--table",
+                    "mangle",
+                    up ? "--insert" : "--delete",
+                    "PREROUTING",
+                    "--protocol",
+                    "udp",
+                    "--jump",
+                    "CONNMARK",
+                    "--restore-mark",
+                    "-m",
+                    "comment",
+                    "--comment",
+                    comment_name);
+}
+
 /*****************************************************************************/
 
 GBytes *
@@ -1046,6 +1195,31 @@ nm_firewall_config_free(NMFirewallConfig *self)
 }
 
 /*****************************************************************************/
+void
+nm_firewall_config_set_wg_rule(const char        *ifname,
+                               NMSettingIPConfig *ip_config,
+                               int                fwmark,
+                               gboolean           up)
+{
+    switch (nm_firewall_utils_get_backend()) {
+    case NM_FIREWALL_BACKEND_NFTABLES:
+    {
+        gs_unref_bytes GBytes *stdin_buf = NULL;
+
+        stdin_buf = _fw_nft_wg_default_construct(ifname, ip_config, fwmark, 
up);
+        _fw_nft_call_sync(stdin_buf, NULL);
+        break;
+    }
+    case NM_FIREWALL_BACKEND_IPTABLES:
+        _fw_iptables_wg_configure(ifname, ip_config, fwmark, up);
+        break;
+    case NM_FIREWALL_BACKEND_NONE:
+        break;
+    default:
+        nm_assert_not_reached();
+        break;
+    }
+}
 
 void
 nm_firewall_config_apply_sync(NMFirewallConfig *self, gboolean up)
@@ -1124,7 +1298,7 @@ again:
         if (!g_atomic_int_compare_and_exchange(&backend, 
NM_FIREWALL_BACKEND_UNKNOWN, b))
             goto again;
 
-        nm_log_dbg(LOGD_SHARING,
+        nm_log_dbg(LOGD_FIREWALL,
                    "firewall: use %s backend%s%s%s%s%s%s%s",
                    FirewallBackends[b - 1].name,
                    NM_PRINT_FMT_QUOTED(FirewallBackends[b - 1].path,
diff --git a/src/core/nm-firewall-utils.h b/src/core/nm-firewall-utils.h
index 9f13a5127e..4df33d11db 100644
--- a/src/core/nm-firewall-utils.h
+++ b/src/core/nm-firewall-utils.h
@@ -24,6 +24,11 @@ NMFirewallConfig *nm_firewall_config_new_shared(const char 
*ip_iface, in_addr_t
 
 void nm_firewall_config_free(NMFirewallConfig *self);
 
+void nm_firewall_config_set_wg_rule(const char        *ifname,
+                                    NMSettingIPConfig *ip_config,
+                                    int                fwmark,
+                                    gboolean           up);
+
 void nm_firewall_config_apply_sync(NMFirewallConfig *self, gboolean up);
 
 /*****************************************************************************/
diff --git a/src/core/nm-ip-config.c b/src/core/nm-ip-config.c
index eb0ec9aa03..975ae20d45 100644
--- a/src/core/nm-ip-config.c
+++ b/src/core/nm-ip-config.c
@@ -826,7 +826,7 @@ _handle_l3cd_changed(NMIPConfig *self, const NML3ConfigData 
*l3cd)
     if (v_i != v_i_old)
         changed_params[n_changed_params++] = 
obj_properties_ip[PROP_IP_DNS_PRIORITY];
 
-    strarr_old = nm_l3_config_data_get_dns_options(l3cd_old, addr_family, 
&len);
+    strarr_old = nm_l3_config_data_get_dns_options(l3cd_old, addr_family, 
&len_old);
     strarr     = nm_l3_config_data_get_dns_options(priv->l3cd, addr_family, 
&len);
     if (!nm_strv_equal_n(strarr, len, strarr_old, len_old))
         changed_params[n_changed_params++] = 
obj_properties_ip[PROP_IP_DNS_OPTIONS];
diff --git a/src/core/tests/test-core.c b/src/core/tests/test-core.c
index 2ff41f0067..e08296c20f 100644
--- a/src/core/tests/test-core.c
+++ b/src/core/tests/test-core.c
@@ -36,6 +36,7 @@ test_config_h(void)
     G_STMT_END
 
     ABSOLUTE_PATH(IPTABLES_PATH);
+    ABSOLUTE_PATH(IP6TABLES_PATH);
     ABSOLUTE_PATH(NFT_PATH);
 }
 
diff --git a/src/core/vpn/nm-vpn-connection.c b/src/core/vpn/nm-vpn-connection.c
index d0607160cd..22364ef9dc 100644
--- a/src/core/vpn/nm-vpn-connection.c
+++ b/src/core/vpn/nm-vpn-connection.c
@@ -1899,7 +1899,7 @@ _dbus_signal_config_cb(NMVpnConnection *self, GVariant 
*dict)
     _LOGD("config: reply received (IPv4:%s(%s), IPv6:%s(%s))",
           priv->ip_data_4.enabled ? "on" : "off",
           priv->ip_data_4.method_auto ? "auto" : "disabled",
-          priv->ip_data_4.enabled ? "on" : "off",
+          priv->ip_data_6.enabled ? "on" : "off",
           priv->ip_data_6.method_auto ? "auto" : "disabled");
 
     if (!priv->ip_data_4.method_auto)
diff --git a/src/libnm-client-impl/nm-ip-config.c 
b/src/libnm-client-impl/nm-ip-config.c
index ad5f08f814..ef3dcca1a5 100644
--- a/src/libnm-client-impl/nm-ip-config.c
+++ b/src/libnm-client-impl/nm-ip-config.c
@@ -180,6 +180,7 @@ _notify_update_prop_nameservers(NMClient               
*client,
                             goto next;
                         nameserver = g_steal_pointer(&val_str);
                     } else if (nm_streq(key, "uri")) {
+                        g_free(nameserver);
                         nameserver = g_variant_dup_string(val, NULL);
                     }
 next:
diff --git a/src/libnm-client-public/nm-secret-agent-old.h 
b/src/libnm-client-public/nm-secret-agent-old.h
index ca7bfa4cc9..6cf94a6d51 100644
--- a/src/libnm-client-public/nm-secret-agent-old.h
+++ b/src/libnm-client-public/nm-secret-agent-old.h
@@ -39,13 +39,13 @@ typedef struct {
  * note that this object will be unrefed after the callback has returned, use
  * g_object_ref()/g_object_unref() if you want to use this object after the 
callback
  * has returned
- * @secrets: the #GVariant of type %NM_VARIANT_TYPE_CONNECTION containing the 
requested
+ * @secrets: (nullable): the #GVariant of type %NM_VARIANT_TYPE_CONNECTION 
containing the requested
  * secrets (as created by nm_connection_to_dbus() for example).  Each key in 
@secrets
  * should be the name of a #NMSetting object (like "802-11-wireless-security")
  * and each value should be an %NM_VARIANT_TYPE_SETTING variant.  The sub-dicts
  * map string:value, where the string is the setting property name (like "psk")
  * and the value is the secret
- * @error: if the secrets request failed, give a descriptive error here
+ * @error: (nullable): if the secrets request failed, give a descriptive error 
here
  * @user_data: caller-specific data to be passed to the function
  *
  * Called as a result of a request by NM to retrieve secrets.  When the
@@ -90,7 +90,7 @@ typedef void 
(*NMSecretAgentOldGetSecretsFunc)(NMSecretAgentOld *agent,
  * note that this object will be unrefed after the callback has returned, use
  * g_object_ref()/g_object_unref() if you want to use this object after the 
callback
  * has returned
- * @error: if the saving secrets failed, give a descriptive error here
+ * @error: (nullable): if the saving secrets failed, give a descriptive error 
here
  * @user_data: caller-specific data to be passed to the function
  *
  * Called as a result of a request by NM to save secrets.  When the
@@ -109,7 +109,7 @@ typedef void 
(*NMSecretAgentOldSaveSecretsFunc)(NMSecretAgentOld *agent,
  * note that this object will be unrefed after the callback has returned, use
  * g_object_ref()/g_object_unref() if you want to use this object after the 
callback
  * has returned
- * @error: if the deleting secrets failed, give a descriptive error here
+ * @error: (nullable): if the deleting secrets failed, give a descriptive 
error here
  * @user_data: caller-specific data to be passed to the function
  *
  * Called as a result of a request by NM to delete secrets.  When the
diff --git a/src/libnm-core-impl/nm-meta-setting-base-impl.c 
b/src/libnm-core-impl/nm-meta-setting-base-impl.c
index 37cb61f176..b9dcf34f61 100644
--- a/src/libnm-core-impl/nm-meta-setting-base-impl.c
+++ b/src/libnm-core-impl/nm-meta-setting-base-impl.c
@@ -431,7 +431,7 @@ const NMMetaSettingInfo nm_meta_setting_infos[] = {
     [NM_META_SETTING_TYPE_OVS_DPDK] =
         {
             .meta_type         = NM_META_SETTING_TYPE_OVS_DPDK,
-            .setting_priority  = NM_SETTING_PRIORITY_HW_BASE,
+            .setting_priority  = NM_SETTING_PRIORITY_AUX,
             .setting_name      = NM_SETTING_OVS_DPDK_SETTING_NAME,
             .get_setting_gtype = nm_setting_ovs_dpdk_get_type,
         },
@@ -459,7 +459,7 @@ const NMMetaSettingInfo nm_meta_setting_infos[] = {
     [NM_META_SETTING_TYPE_OVS_PATCH] =
         {
             .meta_type         = NM_META_SETTING_TYPE_OVS_PATCH,
-            .setting_priority  = NM_SETTING_PRIORITY_HW_BASE,
+            .setting_priority  = NM_SETTING_PRIORITY_AUX,
             .setting_name      = NM_SETTING_OVS_PATCH_SETTING_NAME,
             .get_setting_gtype = nm_setting_ovs_patch_get_type,
         },
@@ -656,9 +656,7 @@ const NMMetaSettingType nm_meta_setting_types_by_priority[] 
= {
     NM_META_SETTING_TYPE_MACSEC,
     NM_META_SETTING_TYPE_MACVLAN,
     NM_META_SETTING_TYPE_OVS_BRIDGE,
-    NM_META_SETTING_TYPE_OVS_DPDK,
     NM_META_SETTING_TYPE_OVS_INTERFACE,
-    NM_META_SETTING_TYPE_OVS_PATCH,
     NM_META_SETTING_TYPE_OVS_PORT,
     NM_META_SETTING_TYPE_TEAM,
     NM_META_SETTING_TYPE_TUN,
@@ -688,8 +686,10 @@ const NMMetaSettingType 
nm_meta_setting_types_by_priority[] = {
     NM_META_SETTING_TYPE_ETHTOOL,
     NM_META_SETTING_TYPE_LINK,
     NM_META_SETTING_TYPE_MATCH,
+    NM_META_SETTING_TYPE_OVS_DPDK,
     NM_META_SETTING_TYPE_OVS_EXTERNAL_IDS,
     NM_META_SETTING_TYPE_OVS_OTHER_CONFIG,
+    NM_META_SETTING_TYPE_OVS_PATCH,
     NM_META_SETTING_TYPE_PPP,
     NM_META_SETTING_TYPE_PPPOE,
     NM_META_SETTING_TYPE_TEAM_PORT,
diff --git a/src/libnm-core-impl/nm-setting-macvlan.c 
b/src/libnm-core-impl/nm-setting-macvlan.c
index 2adecdfe6c..41d41a539a 100644
--- a/src/libnm-core-impl/nm-setting-macvlan.c
+++ b/src/libnm-core-impl/nm-setting-macvlan.c
@@ -251,7 +251,7 @@ nm_setting_macvlan_class_init(NMSettingMacvlanClass *klass)
     /**
      * NMSettingMacvlan:promiscuous:
      *
-     * Whether the interface should be put in promiscuous mode.
+     * Whether the parent interface should be put in promiscuous mode (true by 
default).
      *
      * Since: 1.2
      **/
diff --git a/src/libnmc-setting/nm-meta-setting-base-impl.c 
b/src/libnmc-setting/nm-meta-setting-base-impl.c
index 37cb61f176..b9dcf34f61 100644
--- a/src/libnmc-setting/nm-meta-setting-base-impl.c
+++ b/src/libnmc-setting/nm-meta-setting-base-impl.c
@@ -431,7 +431,7 @@ const NMMetaSettingInfo nm_meta_setting_infos[] = {
     [NM_META_SETTING_TYPE_OVS_DPDK] =
         {
             .meta_type         = NM_META_SETTING_TYPE_OVS_DPDK,
-            .setting_priority  = NM_SETTING_PRIORITY_HW_BASE,
+            .setting_priority  = NM_SETTING_PRIORITY_AUX,
             .setting_name      = NM_SETTING_OVS_DPDK_SETTING_NAME,
             .get_setting_gtype = nm_setting_ovs_dpdk_get_type,
         },
@@ -459,7 +459,7 @@ const NMMetaSettingInfo nm_meta_setting_infos[] = {
     [NM_META_SETTING_TYPE_OVS_PATCH] =
         {
             .meta_type         = NM_META_SETTING_TYPE_OVS_PATCH,
-            .setting_priority  = NM_SETTING_PRIORITY_HW_BASE,
+            .setting_priority  = NM_SETTING_PRIORITY_AUX,
             .setting_name      = NM_SETTING_OVS_PATCH_SETTING_NAME,
             .get_setting_gtype = nm_setting_ovs_patch_get_type,
         },
@@ -656,9 +656,7 @@ const NMMetaSettingType nm_meta_setting_types_by_priority[] 
= {
     NM_META_SETTING_TYPE_MACSEC,
     NM_META_SETTING_TYPE_MACVLAN,
     NM_META_SETTING_TYPE_OVS_BRIDGE,
-    NM_META_SETTING_TYPE_OVS_DPDK,
     NM_META_SETTING_TYPE_OVS_INTERFACE,
-    NM_META_SETTING_TYPE_OVS_PATCH,
     NM_META_SETTING_TYPE_OVS_PORT,
     NM_META_SETTING_TYPE_TEAM,
     NM_META_SETTING_TYPE_TUN,
@@ -688,8 +686,10 @@ const NMMetaSettingType 
nm_meta_setting_types_by_priority[] = {
     NM_META_SETTING_TYPE_ETHTOOL,
     NM_META_SETTING_TYPE_LINK,
     NM_META_SETTING_TYPE_MATCH,
+    NM_META_SETTING_TYPE_OVS_DPDK,
     NM_META_SETTING_TYPE_OVS_EXTERNAL_IDS,
     NM_META_SETTING_TYPE_OVS_OTHER_CONFIG,
+    NM_META_SETTING_TYPE_OVS_PATCH,
     NM_META_SETTING_TYPE_PPP,
     NM_META_SETTING_TYPE_PPPOE,
     NM_META_SETTING_TYPE_TEAM_PORT,
diff --git a/src/libnmc-setting/nm-meta-setting-desc.c 
b/src/libnmc-setting/nm-meta-setting-desc.c
index e35db06eca..fcb2d041d2 100644
--- a/src/libnmc-setting/nm-meta-setting-desc.c
+++ b/src/libnmc-setting/nm-meta-setting-desc.c
@@ -9133,12 +9133,7 @@ const NMMetaSettingInfoEditor 
nm_meta_setting_infos_editor[] = {
             NM_META_SETTING_VALID_PART_ITEM (WIRED,                 FALSE),
         ),
     ),
-    SETTING_INFO (OVS_DPDK,
-        .valid_parts = NM_META_SETTING_VALID_PARTS (
-            NM_META_SETTING_VALID_PART_ITEM (CONNECTION,            TRUE),
-            NM_META_SETTING_VALID_PART_ITEM (OVS_DPDK,              TRUE),
-        ),
-    ),
+    SETTING_INFO (OVS_DPDK),
     SETTING_INFO_EMPTY (OVS_OTHER_CONFIG),
     SETTING_INFO_EMPTY (OVS_EXTERNAL_IDS),
     SETTING_INFO (OVS_INTERFACE,
@@ -9153,12 +9148,7 @@ const NMMetaSettingInfoEditor 
nm_meta_setting_infos_editor[] = {
             NM_META_SETTING_VALID_PART_ITEM (ETHTOOL,               FALSE),
         ),
     ),
-    SETTING_INFO (OVS_PATCH,
-        .valid_parts = NM_META_SETTING_VALID_PARTS (
-            NM_META_SETTING_VALID_PART_ITEM (CONNECTION,            TRUE),
-            NM_META_SETTING_VALID_PART_ITEM (OVS_PATCH,             TRUE),
-        ),
-    ),
+    SETTING_INFO (OVS_PATCH),
     SETTING_INFO (OVS_PORT,
         .valid_parts = NM_META_SETTING_VALID_PARTS (
             NM_META_SETTING_VALID_PART_ITEM (CONNECTION,            TRUE),
diff --git a/src/libnmc-setting/settings-docs.h.in 
b/src/libnmc-setting/settings-docs.h.in
index dd719afad6..3bae49b8b5 100644
--- a/src/libnmc-setting/settings-docs.h.in
+++ b/src/libnmc-setting/settings-docs.h.in
@@ -286,7 +286,7 @@
 #define DESCRIBE_DOC_NM_SETTING_MACSEC_VALIDATION N_("Specifies the validation 
mode for incoming frames.")
 #define DESCRIBE_DOC_NM_SETTING_MACVLAN_MODE N_("The macvlan mode, which 
specifies the communication mechanism between multiple macvlans on the same 
lower device.")
 #define DESCRIBE_DOC_NM_SETTING_MACVLAN_PARENT N_("If given, specifies the 
parent interface name or parent connection UUID from which this MAC-VLAN 
interface should be created.  If this property is not specified, the connection 
must contain an \"802-3-ethernet\" setting with a \"mac-address\" property.")
-#define DESCRIBE_DOC_NM_SETTING_MACVLAN_PROMISCUOUS N_("Whether the interface 
should be put in promiscuous mode.")
+#define DESCRIBE_DOC_NM_SETTING_MACVLAN_PROMISCUOUS N_("Whether the parent 
interface should be put in promiscuous mode (true by default).")
 #define DESCRIBE_DOC_NM_SETTING_MACVLAN_TAP N_("Whether the interface should 
be a MACVTAP.")
 #define DESCRIBE_DOC_NM_SETTING_MATCH_DRIVER N_("A list of driver names to 
match. Each element is a shell wildcard pattern. See 
NMSettingMatch:interface-name for how special characters '|', '&', '!' and '\\' 
are used for optional and mandatory matches and inverting the pattern.")
 #define DESCRIBE_DOC_NM_SETTING_MATCH_INTERFACE_NAME N_("A list of interface 
names to match. Each element is a shell wildcard pattern. An element can be 
prefixed with a pipe symbol (|) or an ampersand (&). The former means that the 
element is optional and the latter means that it is mandatory. If there are any 
optional elements, than the match evaluates to true if at least one of the 
optional element matches (logical OR). If there are any mandatory elements, 
then they all must match (logical AND). By default, an element is optional. 
This means that an element \"foo\" behaves the same as \"|foo\". An element can 
also be inverted with exclamation mark (!) between the pipe symbol (or the 
ampersand) and before the pattern. Note that \"!foo\" is a shortcut for the 
mandatory match \"&!foo\". Finally, a backslash can be used at the beginning of 
the element (after the optional special characters) to escape the start of the 
pattern. For example, \"&\\!a\" is an mandatory match for literally \"!a\".")
diff --git a/src/n-dhcp4/src/n-dhcp4-c-probe.c 
b/src/n-dhcp4/src/n-dhcp4-c-probe.c
index 7a6def340c..58d61e72ba 100644
--- a/src/n-dhcp4/src/n-dhcp4-c-probe.c
+++ b/src/n-dhcp4/src/n-dhcp4-c-probe.c
@@ -1140,8 +1140,6 @@ int 
n_dhcp4_client_probe_transition_decline(NDhcp4ClientProbe *probe, NDhcp4Inco
                 r = n_dhcp4_c_connection_send_request(&probe->connection, 
request, ns_now);
                 if (r)
                         return r;
-                else
-                        request = NULL; /* consumed */
 
                 n_dhcp4_client_lease_unlink(probe->current_lease);
                 probe->current_lease = 
n_dhcp4_client_lease_unref(probe->current_lease);
@@ -1346,7 +1344,6 @@ int n_dhcp4_client_probe_release(NDhcp4ClientProbe 
*probe) {
 
         probe->state = N_DHCP4_CLIENT_PROBE_STATE_INIT;
         n_dhcp4_client_lease_unlink(probe->current_lease);
-        request_out = NULL;
 
         return 0;
 }
diff --git a/src/nm-initrd-generator/nmi-cmdline-reader.c 
b/src/nm-initrd-generator/nmi-cmdline-reader.c
index d6dc1fcb7c..5389748d82 100644
--- a/src/nm-initrd-generator/nmi-cmdline-reader.c
+++ b/src/nm-initrd-generator/nmi-cmdline-reader.c
@@ -299,33 +299,44 @@ get_word(char **argument, const char separator)
 {
     char *word;
     int   nest = 0;
+    char *last_ch;
+    char *first_close = NULL;
 
     if (*argument == NULL)
         return NULL;
 
-    if (**argument == '[') {
-        nest++;
-        (*argument)++;
-    }
-
-    word = *argument;
+    word = last_ch = *argument;
 
     while (**argument != '\0') {
-        if (nest && **argument == ']') {
-            **argument = '\0';
-            (*argument)++;
-            nest--;
-            continue;
-        }
-
         if (nest == 0 && **argument == separator) {
             **argument = '\0';
             (*argument)++;
             break;
         }
+        if (**argument == '[') {
+            nest++;
+        } else if (nest && **argument == ']') {
+            nest--;
+            if (!first_close && nest == 0)
+                first_close = *argument;
+        }
+
+        last_ch = *argument;
         (*argument)++;
     }
 
+    /* If the word is surrounded with the nesting symbols [], strip them so we 
return
+     * the inner content only.
+     * If there were nesting symbols but embracing only part of the inner 
content, don't
+     * remove them. Example:
+     *    Remove [] in get_word("[fc08::1]:other_token", ":")
+     *    Don't remove [] in get_word("ip6=[fc08::1]:other_token", ":")
+     */
+    if (*word == '[' && *last_ch == ']' && last_ch == first_close) {
+        word++;
+        *last_ch = '\0';
+    }
+
     return *word ? word : NULL;
 }
 
@@ -533,7 +544,7 @@ reader_parse_ip(Reader *reader, const char *sysfs_dir, char 
*argument)
     NMSettingConnection           *s_con;
     NMSettingIPConfig             *s_ip4 = NULL, *s_ip6 = NULL;
     gs_unref_hashtable GHashTable *ibft = NULL;
-    const char                    *tmp;
+    char                          *tmp;
     const char                    *tmp2;
     const char                    *tmp3;
     const char                    *kind;
@@ -578,15 +589,25 @@ reader_parse_ip(Reader *reader, const char *sysfs_dir, 
char *argument)
             kind       = tmp3;
         } else {
             /* 
<client-IP>:[<peer>]:<gateway-IP>:<netmask>:<client_hostname>:<kind> */
-            client_ip = tmp;
+
+            /* note: split here address and prefix to normalize IPs defined as
+             * [dead::beef]/64. Latter parsing would fail due to the '[]'. */
+            client_ip = get_word(&tmp, '/');
+
             if (client_ip) {
-                client_ip_family = get_ip_address_family(client_ip, TRUE);
+                client_ip_family = get_ip_address_family(client_ip, FALSE);
                 if (client_ip_family == AF_UNSPEC) {
                     _LOGW(LOGD_CORE, "Invalid IP address '%s'.", client_ip);
                     return;
                 }
             }
 
+            if (!nm_str_is_empty(tmp)) {
+                gboolean is_ipv4 = client_ip_family == AF_INET;
+
+                client_ip_prefix = _nm_utils_ascii_str_to_int64(tmp, 10, 0, 
is_ipv4 ? 32 : 128, -1);
+            }
+
             peer            = tmp2;
             gateway_ip      = get_word(&argument, ':');
             netmask         = get_word(&argument, ':');
@@ -661,11 +682,7 @@ reader_parse_ip(Reader *reader, const char *sysfs_dir, 
char *argument)
         NMIPAddress *address = NULL;
         NMIPAddr     addr;
 
-        if (nm_inet_parse_with_prefix_bin(client_ip_family,
-                                          client_ip,
-                                          NULL,
-                                          &addr,
-                                          client_ip_prefix == -1 ? 
&client_ip_prefix : NULL)) {
+        if (nm_inet_parse_bin(client_ip_family, client_ip, NULL, &addr)) {
             if (client_ip_prefix == -1) {
                 switch (client_ip_family) {
                 case AF_INET:
@@ -905,14 +922,25 @@ reader_parse_controller(Reader     *reader,
 
         opts = get_word(&argument, ':');
         while (opts && *opts) {
-            gs_free_error GError *error = NULL;
-            char                 *opt;
-            const char           *opt_name;
-
+            gs_free_error GError             *error = NULL;
+            char                             *tmp;
+            const char                       *opt_name;
+            char                             *opt;
+            const char                       *opt_value;
+            nm_auto_unref_ptrarray GPtrArray *opt_values     = 
g_ptr_array_new();
+            gs_free char                     *opt_normalized = NULL;
+
+            opt_name = get_word(&opts, '=');
             opt      = get_word(&opts, ',');
-            opt_name = get_word(&opt, '=');
 
-            if (!_nm_setting_bond_validate_option(opt_name, opt, &error)) {
+            /* Normalize: convert ';' to ',' and remove '[]' from IPv6 
addresses */
+            tmp = opt;
+            while ((opt_value = get_word(&tmp, ';')))
+                g_ptr_array_add(opt_values, (gpointer) opt_value);
+            g_ptr_array_add(opt_values, NULL);
+            opt_normalized = g_strjoinv(",", (char **) opt_values->pdata);
+
+            if (!_nm_setting_bond_validate_option(opt_name, opt_normalized, 
&error)) {
                 _LOGW(LOGD_CORE,
                       "Ignoring invalid bond option: %s%s%s = %s%s%s: %s",
                       NM_PRINT_FMT_QUOTE_STRING(opt_name),
@@ -920,7 +948,7 @@ reader_parse_controller(Reader     *reader,
                       error->message);
                 continue;
             }
-            nm_setting_bond_add_option(s_bond, opt_name, opt);
+            nm_setting_bond_add_option(s_bond, opt_name, opt_normalized);
         }
 
         mtu = get_word(&argument, ':');
diff --git a/src/nm-initrd-generator/tests/test-cmdline-reader.c 
b/src/nm-initrd-generator/tests/test-cmdline-reader.c
index a0100764ca..cd7b1069b6 100644
--- a/src/nm-initrd-generator/tests/test-cmdline-reader.c
+++ b/src/nm-initrd-generator/tests/test-cmdline-reader.c
@@ -597,7 +597,7 @@ static void
 test_if_ip6_manual(void)
 {
     gs_unref_hashtable GHashTable *connections = NULL;
-    const char *const             *ARGV = 
NM_MAKE_STRV("ip=[2001:0db8::02]/64::[2001:0db8::01]::"
+    const char *const             *ARGV = 
NM_MAKE_STRV("ip=[2001:0db8::02]/56::[2001:0db8::01]::"
                                                        
"hostname0.example.com:eth4::[2001:0db8::53]");
     NMConnection                  *connection;
     NMSettingIPConfig             *s_ip4;
@@ -633,7 +633,7 @@ test_if_ip6_manual(void)
     ip_addr = nm_setting_ip_config_get_address(s_ip6, 0);
     g_assert(ip_addr);
     g_assert_cmpstr(nm_ip_address_get_address(ip_addr), ==, "2001:db8::2");
-    g_assert_cmpint(nm_ip_address_get_prefix(ip_addr), ==, 64);
+    g_assert_cmpint(nm_ip_address_get_prefix(ip_addr), ==, 56);
     g_assert_cmpstr(nm_setting_ip_config_get_gateway(s_ip6), ==, 
"2001:db8::1");
     g_assert_cmpstr(nm_setting_ip_config_get_dhcp_hostname(s_ip6), ==, NULL);
 }
@@ -975,8 +975,8 @@ static void
 test_bond(void)
 {
     gs_unref_hashtable GHashTable *connections = NULL;
-    const char *const             *ARGV        = 
NM_MAKE_STRV("rd.route=192.0.2.53::bong0",
-                                           
"bond=bong0:eth0,eth1:mode=balance-rr:9000",
+    const char *const             *ARGV        = 
NM_MAKE_STRV("rd.route=192.0.2.53::bond0",
+                                           
"bond=bond0:eth0,eth1:mode=balance-rr:9000",
                                            "nameserver=203.0.113.53");
     NMConnection                  *connection;
     NMSettingConnection           *s_con;
@@ -990,12 +990,12 @@ test_bond(void)
     connections = _parse_cons(ARGV);
     g_assert_cmpint(g_hash_table_size(connections), ==, 3);
 
-    connection = g_hash_table_lookup(connections, "bong0");
+    connection = g_hash_table_lookup(connections, "bond0");
     nmtst_assert_connection_verifies_without_normalization(connection);
     g_assert_cmpstr(nm_connection_get_connection_type(connection),
                     ==,
                     NM_SETTING_BOND_SETTING_NAME);
-    g_assert_cmpstr(nm_connection_get_id(connection), ==, "bong0");
+    g_assert_cmpstr(nm_connection_get_id(connection), ==, "bond0");
     controller_uuid = nm_connection_get_uuid(connection);
     g_assert(controller_uuid);
 
@@ -1162,6 +1162,118 @@ test_bond_ip(void)
                     NM_CONNECTION_MULTI_CONNECT_SINGLE);
 }
 
+static void
+test_bond_ip6_option(void)
+{
+    /* Test that IPv6 addresses within [] are parsed fine in different 
positions */
+
+    gs_unref_hashtable GHashTable *connections = NULL;
+    const char *const             *ARGV =
+        
NM_MAKE_STRV("bond=bond0:eth0,eth1:arp_interval=100,ns_ip6_target=[fc08::1]",
+                     
"bond=bond1:eth2,eth3:arp_interval=100,ns_ip6_target=[fc08::1]:9000",
+                     
"bond=bond2:eth4,eth5:ns_ip6_target=[fc08::1],arp_interval=100");
+    NMConnection  *connection;
+    NMSettingBond *s_bond;
+
+    connections = _parse_cons(ARGV);
+    g_assert_cmpint(g_hash_table_size(connections), ==, 9);
+
+    connection = g_hash_table_lookup(connections, "bond0");
+    nmtst_assert_connection_verifies_without_normalization(connection);
+    s_bond = nm_connection_get_setting_bond(connection);
+    g_assert(s_bond);
+    g_assert_cmpint(nm_setting_bond_get_num_options(s_bond), ==, 3);
+    g_assert_cmpstr(nm_setting_bond_get_option_by_name(s_bond, 
"ns_ip6_target"), ==, "fc08::1");
+
+    connection = g_hash_table_lookup(connections, "bond1");
+    nmtst_assert_connection_verifies_without_normalization(connection);
+    s_bond = nm_connection_get_setting_bond(connection);
+    g_assert(s_bond);
+    g_assert_cmpint(nm_setting_bond_get_num_options(s_bond), ==, 3);
+    g_assert_cmpstr(nm_setting_bond_get_option_by_name(s_bond, 
"ns_ip6_target"), ==, "fc08::1");
+
+    connection = g_hash_table_lookup(connections, "bond2");
+    nmtst_assert_connection_verifies_without_normalization(connection);
+    s_bond = nm_connection_get_setting_bond(connection);
+    g_assert(s_bond);
+    g_assert_cmpint(nm_setting_bond_get_num_options(s_bond), ==, 3);
+    g_assert_cmpstr(nm_setting_bond_get_option_by_name(s_bond, 
"ns_ip6_target"), ==, "fc08::1");
+}
+
+static void
+test_bond_multi_values_option(void)
+{
+    /* Test that semicolon-separated multi-valued options are parsed fine in 
different positions */
+
+    gs_unref_hashtable GHashTable *connections = NULL;
+    const char *const             *ARGV =
+        
NM_MAKE_STRV("bond=bond0:eth0,eth1:arp_interval=100,ns_ip6_target=[fc08::1];[fc08::2]",
+                     
"bond=bond1:eth2,eth3:arp_interval=100,ns_ip6_target=[fc08::1];[fc08::2]:9000",
+                     
"bond=bond2:eth4,eth5:ns_ip6_target=[fc08::1];[fc08::2],arp_interval=100",
+                     
"bond=bond3:eth6,eth7:arp_interval=100,arp_ip_target=10.0.0.1;10.0.0.2",
+                     
"bond=bond4:eth8,eth9:arp_interval=100,arp_ip_target=10.0.0.1;10.0.0.2:9000",
+                     
"bond=bond5:eth10,eth11:arp_ip_target=10.0.0.1;10.0.0.2,arp_interval=100");
+    NMConnection  *connection;
+    NMSettingBond *s_bond;
+
+    connections = _parse_cons(ARGV);
+    g_assert_cmpint(g_hash_table_size(connections), ==, 18);
+
+    connection = g_hash_table_lookup(connections, "bond0");
+    nmtst_assert_connection_verifies_without_normalization(connection);
+    s_bond = nm_connection_get_setting_bond(connection);
+    g_assert(s_bond);
+    g_assert_cmpint(nm_setting_bond_get_num_options(s_bond), ==, 3);
+    g_assert_cmpstr(nm_setting_bond_get_option_by_name(s_bond, 
"ns_ip6_target"),
+                    ==,
+                    "fc08::1,fc08::2");
+
+    connection = g_hash_table_lookup(connections, "bond1");
+    nmtst_assert_connection_verifies_without_normalization(connection);
+    s_bond = nm_connection_get_setting_bond(connection);
+    g_assert(s_bond);
+    g_assert_cmpint(nm_setting_bond_get_num_options(s_bond), ==, 3);
+    g_assert_cmpstr(nm_setting_bond_get_option_by_name(s_bond, 
"ns_ip6_target"),
+                    ==,
+                    "fc08::1,fc08::2");
+
+    connection = g_hash_table_lookup(connections, "bond2");
+    nmtst_assert_connection_verifies_without_normalization(connection);
+    s_bond = nm_connection_get_setting_bond(connection);
+    g_assert(s_bond);
+    g_assert_cmpint(nm_setting_bond_get_num_options(s_bond), ==, 3);
+    g_assert_cmpstr(nm_setting_bond_get_option_by_name(s_bond, 
"ns_ip6_target"),
+                    ==,
+                    "fc08::1,fc08::2");
+
+    connection = g_hash_table_lookup(connections, "bond3");
+    nmtst_assert_connection_verifies_without_normalization(connection);
+    s_bond = nm_connection_get_setting_bond(connection);
+    g_assert(s_bond);
+    g_assert_cmpint(nm_setting_bond_get_num_options(s_bond), ==, 3);
+    g_assert_cmpstr(nm_setting_bond_get_option_by_name(s_bond, 
"arp_ip_target"),
+                    ==,
+                    "10.0.0.1,10.0.0.2");
+
+    connection = g_hash_table_lookup(connections, "bond4");
+    nmtst_assert_connection_verifies_without_normalization(connection);
+    s_bond = nm_connection_get_setting_bond(connection);
+    g_assert(s_bond);
+    g_assert_cmpint(nm_setting_bond_get_num_options(s_bond), ==, 3);
+    g_assert_cmpstr(nm_setting_bond_get_option_by_name(s_bond, 
"arp_ip_target"),
+                    ==,
+                    "10.0.0.1,10.0.0.2");
+
+    connection = g_hash_table_lookup(connections, "bond5");
+    nmtst_assert_connection_verifies_without_normalization(connection);
+    s_bond = nm_connection_get_setting_bond(connection);
+    g_assert(s_bond);
+    g_assert_cmpint(nm_setting_bond_get_num_options(s_bond), ==, 3);
+    g_assert_cmpstr(nm_setting_bond_get_option_by_name(s_bond, 
"arp_ip_target"),
+                    ==,
+                    "10.0.0.1,10.0.0.2");
+}
+
 static void
 test_bond_default(void)
 {
@@ -2701,6 +2813,8 @@ main(int argc, char **argv)
     g_test_add_func("/initrd/cmdline/bootdev", test_bootdev);
     g_test_add_func("/initrd/cmdline/bond", test_bond);
     g_test_add_func("/initrd/cmdline/bond/ip", test_bond_ip);
+    g_test_add_func("/initrd/cmdline/bond/ip6-option", test_bond_ip6_option);
+    g_test_add_func("/initrd/cmdline/bond/multi-values-option", 
test_bond_multi_values_option);
     g_test_add_func("/initrd/cmdline/bond/default", test_bond_default);
     g_test_add_func("/initrd/cmdline/team", test_team);
     g_test_add_func("/initrd/cmdline/vlan", test_vlan);
diff --git a/src/nmcli/gen-metadata-nm-settings-nmcli.xml.in 
b/src/nmcli/gen-metadata-nm-settings-nmcli.xml.in
index 9aa1751e7e..77e6c4278a 100644
--- a/src/nmcli/gen-metadata-nm-settings-nmcli.xml.in
+++ b/src/nmcli/gen-metadata-nm-settings-nmcli.xml.in
@@ -629,7 +629,7 @@
                   alias="type"
                   nmcli-description="Base type of the connection. For 
hardware-dependent connections, should contain the setting name of the 
hardware-type specific setting (ie, &quot;802-3-ethernet&quot; or 
&quot;802-11-wireless&quot; or &quot;bluetooth&quot;, etc), and for 
non-hardware dependent connections like VPN or otherwise, should contain the 
setting name of that setting type (ie, &quot;vpn&quot; or &quot;bridge&quot;, 
etc)."
                   format="string"
-                  values="6lowpan, 802-11-olpc-mesh, 802-11-wireless, 
802-3-ethernet, adsl, bluetooth, bond, bridge, cdma, dummy, generic, gsm, hsr, 
infiniband, ip-tunnel, ipvlan, loopback, macsec, macvlan, ovs-bridge, ovs-dpdk, 
ovs-interface, ovs-patch, ovs-port, pppoe, team, tun, veth, vlan, vpn, vrf, 
vxlan, wifi-p2p, wimax, wireguard, wpan" />
+                  values="6lowpan, 802-11-olpc-mesh, 802-11-wireless, 
802-3-ethernet, adsl, bluetooth, bond, bridge, cdma, dummy, generic, gsm, hsr, 
infiniband, ip-tunnel, ipvlan, loopback, macsec, macvlan, ovs-bridge, 
ovs-interface, ovs-port, pppoe, team, tun, veth, vlan, vpn, vrf, vxlan, 
wifi-p2p, wimax, wireguard, wpan" />
         <property name="interface-name"
                   alias="ifname"
                   nmcli-description="The name of the network interface this 
connection is bound to. If not set, then the connection can be attached to any 
interface of the appropriate type (subject to restrictions imposed by other 
settings). For software devices this specifies the name of the created device. 
For connection types where interface names cannot easily be made persistent 
(e.g. mobile broadband or USB Ethernet), this property should not be used. 
Setting this property restricts the interfaces a connection can be used with, 
and if interface names change or are reordered the connection may be applied to 
the wrong interface."
@@ -1698,7 +1698,7 @@
                   format="choice (NMSettingMacvlanMode)"
                   values="vepa (1), bridge (2), private (3), passthru (4), 
source (5)" />
         <property name="promiscuous"
-                  nmcli-description="Whether the interface should be put in 
promiscuous mode."
+                  nmcli-description="Whether the parent interface should be 
put in promiscuous mode (true by default)."
                   format="boolean"
                   values="true/yes/on, false/no/off" />
         <property name="tap"
diff --git a/tools/test-networkmanager-service.py 
b/tools/test-networkmanager-service.py
index 8a42633204..8d39aef78e 100755
--- a/tools/test-networkmanager-service.py
+++ b/tools/test-networkmanager-service.py
@@ -868,7 +868,7 @@ class Device(ExportedObj):
         self.activation_state_change_delay_ms = 50
         self.hwaddr = hwaddr is None if "" else hwaddr
 
-        self.prp_state = NM.DeviceState.UNAVAILABLE
+        self.prp_state = NM.DeviceState.DISCONNECTED
 
         if devtype == NM.DeviceType.MODEM:
             udi = "/org/freedesktop/ModemManager1/Modem/0"

Attachment: OpenPGP_signature.asc
Description: OpenPGP digital signature

Reply via email to