about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorMichael Biebl <biebl@debian.org>2023-06-28 15:04:35 +0200
committerMichael Biebl <biebl@debian.org>2023-06-28 15:04:35 +0200
commit14b0f3a9dc9ea90d60a3b057350fd4d637dc021a (patch)
tree951db959a175b866d0839749ca5d62ab91bb30fb /src
parent150fe9eef8dd22307ee16687509acde616663982 (diff)
New upstream version 1.42.8 upstream/1.42.8
Diffstat (limited to 'src')
-rw-r--r--src/core/devices/nm-device-bond.c88
-rw-r--r--src/core/devices/nm-device.c24
-rw-r--r--src/core/devices/team/nm-device-team.c18
-rw-r--r--src/core/devices/wifi/nm-device-wifi-p2p.c4
-rw-r--r--src/core/nm-firewall-utils.c4
-rw-r--r--src/core/nm-l3-ipv6ll.c34
-rw-r--r--src/core/platform/nm-fake-platform.c28
-rw-r--r--src/core/platform/tests/test-link.c53
-rw-r--r--src/core/ppp/meson.build6
-rw-r--r--src/core/ppp/nm-ppp-manager.c6
-rw-r--r--src/core/ppp/nm-ppp-mgr.c2
-rw-r--r--src/core/ppp/nm-ppp-status.h37
-rw-r--r--src/core/ppp/nm-pppd-compat.c308
-rw-r--r--src/core/ppp/nm-pppd-compat.h64
-rw-r--r--src/core/ppp/nm-pppd-plugin.c198
-rw-r--r--src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c35
-rw-r--r--src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.c2
-rw-r--r--src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.h2
-rw-r--r--src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c36
-rw-r--r--src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-infiniband0 (renamed from src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-infiniband)0
-rw-r--r--src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-infiniband112
-rw-r--r--src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-infiniband212
-rw-r--r--src/core/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c85
-rw-r--r--src/libnm-base/nm-base.h1
-rw-r--r--src/libnm-client-impl/libnm.ver5
-rwxr-xr-xsrc/libnm-client-impl/tests/test-gir.py22
-rw-r--r--src/libnm-core-impl/gen-metadata-nm-settings-libnm-core.xml.in4
-rw-r--r--src/libnm-core-impl/nm-connection.c39
-rw-r--r--src/libnm-core-impl/nm-setting-bond-port.c50
-rw-r--r--src/libnm-core-impl/nm-setting-infiniband.c54
-rw-r--r--src/libnm-core-impl/tests/test-general.c62
-rw-r--r--src/libnm-core-intern/nm-core-internal.h2
-rw-r--r--src/libnm-core-public/nm-setting-bond-port.h4
-rw-r--r--src/libnm-core-public/nm-version-macros.h3
-rw-r--r--src/libnm-core-public/nm-version-macros.h.in1
-rw-r--r--src/libnm-core-public/nm-version.h6
-rw-r--r--src/libnm-glib-aux/nm-shared-utils.h8
-rw-r--r--src/libnm-platform/nm-linux-platform.c106
-rw-r--r--src/libnm-platform/nm-platform.c121
-rw-r--r--src/libnm-platform/nm-platform.h44
-rw-r--r--src/libnmc-setting/nm-meta-setting-desc.c6
-rw-r--r--src/libnmc-setting/settings-docs.h3
-rw-r--r--src/libnmc-setting/settings-docs.h.in3
-rw-r--r--src/nm-cloud-setup/main.c49
-rw-r--r--src/nm-cloud-setup/nm-cloud-setup-utils.c2
-rw-r--r--src/nm-cloud-setup/nmcs-provider-aliyun.c3
-rw-r--r--src/nmcli/gen-metadata-nm-settings-nmcli.xml.in5
47 files changed, 1336 insertions, 325 deletions
diff --git a/src/core/devices/nm-device-bond.c b/src/core/devices/nm-device-bond.c
index 0b0697e3..56c5ec50 100644
--- a/src/core/devices/nm-device-bond.c
+++ b/src/core/devices/nm-device-bond.c
@@ -227,24 +227,23 @@ controller_update_port_connection(NMDevice     *self,
                                   NMConnection *connection,
                                   GError      **error)
 {
-    NMSettingBondPort *s_port;
-    int                ifindex_port       = nm_device_get_ifindex(port);
-    NMConnection      *applied_connection = nm_device_get_applied_connection(self);
-    uint               queue_id           = NM_BOND_PORT_QUEUE_ID_DEF;
-    gs_free char      *queue_id_str       = NULL;
+    NMSettingBondPort    *s_port;
+    int                   ifindex_port       = nm_device_get_ifindex(port);
+    NMConnection         *applied_connection = nm_device_get_applied_connection(self);
+    const NMPlatformLink *pllink;
 
     g_return_val_if_fail(ifindex_port > 0, FALSE);
 
     s_port = _nm_connection_ensure_setting(connection, NM_TYPE_SETTING_BOND_PORT);
+    pllink = nm_platform_link_get(nm_device_get_platform(port), ifindex_port);
 
-    queue_id_str =
-        nm_platform_sysctl_slave_get_option(nm_device_get_platform(self), ifindex_port, "queue_id");
-    if (queue_id_str) {
-        queue_id =
-            _nm_utils_ascii_str_to_int64(queue_id_str, 10, 0, 65535, NM_BOND_PORT_QUEUE_ID_DEF);
-        g_object_set(s_port, NM_SETTING_BOND_PORT_QUEUE_ID, queue_id, NULL);
-    } else
-        _LOGW(LOGD_BOND, "failed to read bond port setting '%s'", NM_SETTING_BOND_PORT_QUEUE_ID);
+    if (pllink && pllink->port_kind == NM_PORT_KIND_BOND)
+        g_object_set(s_port,
+                     NM_SETTING_BOND_PORT_QUEUE_ID,
+                     pllink->port_data.bond.queue_id,
+                     NM_SETTING_BOND_PORT_PRIO,
+                     pllink->port_data.bond.prio,
+                     NULL);
 
     g_object_set(nm_connection_get_setting_connection(connection),
                  NM_SETTING_CONNECTION_MASTER,
@@ -600,23 +599,52 @@ act_stage1_prepare(NMDevice *device, NMDeviceStateReason *out_failure_reason)
 static void
 commit_port_options(NMDevice *bond_device, NMDevice *port, NMSettingBondPort *s_port)
 {
-    char queue_id_str[IFNAMSIZ + NM_STRLEN(":") + 5 + 100];
-
-    /*
-     * The queue-id of bond port is read only, we should modify bond interface using:
-     *    echo "eth1:2" > /sys/class/net/bond0/bonding/queue_id
-     * Kernel allows parital editing, so no need to care about other bond ports.
-     */
-    g_snprintf(queue_id_str,
-               sizeof(queue_id_str),
-               "%s:%" G_GUINT32_FORMAT,
-               nm_device_get_iface(port),
-               s_port ? nm_setting_bond_port_get_queue_id(s_port) : NM_BOND_PORT_QUEUE_ID_DEF);
-
-    nm_platform_sysctl_master_set_option(nm_device_get_platform(bond_device),
-                                         nm_device_get_ifindex(bond_device),
-                                         "queue_id",
-                                         queue_id_str);
+    NMBondMode     mode = NM_BOND_MODE_UNKNOWN;
+    const char    *value;
+    NMSettingBond *s_bond;
+    gint32         prio;
+    gboolean       prio_has;
+
+    s_bond = nm_device_get_applied_setting(bond_device, NM_TYPE_SETTING_BOND);
+    if (s_bond) {
+        value = nm_setting_bond_get_option_normalized(s_bond, NM_SETTING_BOND_OPTION_MODE);
+        mode  = _nm_setting_bond_mode_from_string(value);
+    }
+
+    prio = s_port ? nm_setting_bond_port_get_prio(s_port) : NM_BOND_PORT_PRIO_DEF;
+
+    if (prio != 0) {
+        /* The profile explicitly sets the priority. No matter what, we try to set it
+         * in netlink. */
+        prio_has = TRUE;
+    } else if (!NM_IN_SET(mode, NM_BOND_MODE_ACTIVEBACKUP, NM_BOND_MODE_TLB, NM_BOND_MODE_ALB)) {
+        /* The priority only is configurable with certain modes. If we don't have
+         * one of those modes, don't try to set the priority explicitly to zero. */
+        prio_has = FALSE;
+    } else if (nm_platform_kernel_support_get_full(
+                   NM_PLATFORM_KERNEL_SUPPORT_TYPE_IFLA_BOND_SLAVE_PRIO,
+                   FALSE)
+               == NM_OPTION_BOOL_TRUE) {
+        /* We can only detect support if we have it. We cannot detect lack of support if
+         * we don't have it.
+         *
+         * But we did explicitly detect support, so explicitly set the prio to zero. */
+        prio_has = TRUE;
+    } else {
+        /* We either have an unsuitable mode or didn't detect kernel support for the
+         * priority. Don't explicitly set priority to zero. It is already the default,
+         * so it shouldn't be necessary. */
+        prio_has = FALSE;
+    }
+
+    nm_platform_link_change(nm_device_get_platform(port),
+                            nm_device_get_ifindex(port),
+                            &((NMPlatformLinkBondPort){
+                                .queue_id = s_port ? nm_setting_bond_port_get_queue_id(s_port)
+                                                   : NM_BOND_PORT_QUEUE_ID_DEF,
+                                .prio     = prio_has ? prio : 0,
+                                .prio_has = prio_has,
+                            }));
 }
 
 static NMTernary
diff --git a/src/core/devices/nm-device.c b/src/core/devices/nm-device.c
index d080b540..62a9ff1e 100644
--- a/src/core/devices/nm-device.c
+++ b/src/core/devices/nm-device.c
@@ -11691,7 +11691,18 @@ _dev_ipac6_start(NMDevice *self)
     }
 
     if (nm_ndisc_get_node_type(priv->ipac6_data.ndisc) == NM_NDISC_NODE_TYPE_ROUTER) {
-        nm_device_sysctl_ip_conf_set(self, AF_INET6, "forwarding", "1");
+        gs_free char *sysctl_value = NULL;
+
+        sysctl_value = nm_device_sysctl_ip_conf_get(self, AF_INET6, "forwarding");
+        if (!nm_streq0(sysctl_value, "1")) {
+            if (sysctl_value && !g_hash_table_contains(priv->ip6_saved_properties, "forwarding")) {
+                g_hash_table_insert(priv->ip6_saved_properties,
+                                    "forwarding",
+                                    g_steal_pointer(&sysctl_value));
+            }
+            nm_device_sysctl_ip_conf_set(self, AF_INET6, "forwarding", "1");
+        }
+
         priv->needs_ip6_subnet = TRUE;
         g_signal_emit(self, signals[IP6_SUBNET_NEEDED], 0);
     }
@@ -11749,7 +11760,6 @@ _dev_sysctl_save_ip6_properties(NMDevice *self)
 {
     static const char *const ip6_properties_to_save[] = {
         "accept_ra",
-        "forwarding",
         "disable_ipv6",
         "hop_limit",
         "use_tempaddr",
@@ -12892,6 +12902,7 @@ check_and_reapply_connection(NMDevice            *self,
     NMConnection                  *con_old;
     NMConnection                  *con_new;
     GHashTableIter                 iter;
+    NMSettingsConnection          *sett_conn;
 
     if (priv->state < NM_DEVICE_STATE_PREPARE || priv->state > NM_DEVICE_STATE_ACTIVATED) {
         g_set_error_literal(error,
@@ -13064,6 +13075,14 @@ check_and_reapply_connection(NMDevice            *self,
     if (priv->state >= NM_DEVICE_STATE_ACTIVATED)
         nm_device_update_metered(self);
 
+    sett_conn = nm_device_get_settings_connection(self);
+    if (sett_conn) {
+        nm_settings_connection_autoconnect_blocked_reason_set(
+            sett_conn,
+            NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_USER_REQUEST,
+            FALSE);
+    }
+
     /* Notify dispatcher when re-applied */
     _LOGD(LOGD_DEVICE, "Notifying re-apply complete");
     nm_dispatcher_call_device(NM_DISPATCHER_ACTION_REAPPLY, self, NULL, NULL, NULL, NULL);
@@ -15649,7 +15668,6 @@ ip6_managed_setup(NMDevice *self)
     _dev_sysctl_set_disable_ipv6(self, FALSE);
     nm_device_sysctl_ip_conf_set(self, AF_INET6, "accept_ra", "0");
     nm_device_sysctl_ip_conf_set(self, AF_INET6, "use_tempaddr", "0");
-    nm_device_sysctl_ip_conf_set(self, AF_INET6, "forwarding", "0");
 }
 
 static void
diff --git a/src/core/devices/team/nm-device-team.c b/src/core/devices/team/nm-device-team.c
index 3e3fe52b..5c955986 100644
--- a/src/core/devices/team/nm-device-team.c
+++ b/src/core/devices/team/nm-device-team.c
@@ -226,24 +226,10 @@ update_connection(NMDevice *device, NMConnection *connection)
     NMDeviceTeam        *self   = NM_DEVICE_TEAM(device);
     NMSettingTeam       *s_team = _nm_connection_ensure_setting(connection, NM_TYPE_SETTING_TEAM);
     NMDeviceTeamPrivate *priv   = NM_DEVICE_TEAM_GET_PRIVATE(self);
-    struct teamdctl     *tdc    = priv->tdc;
-    GError              *error  = NULL;
 
     /* Read the configuration only if not already set */
-    if (!priv->config) {
-        if (ensure_teamd_connection(device, &error)) {
-            teamd_read_config(self);
-        } else {
-            _LOGD(LOGD_TEAM, "could not connect to teamd: %s", error->message);
-            g_clear_error(&error);
-        }
-    }
-
-    /* Restore previous tdc state */
-    if (priv->tdc && !tdc) {
-        teamdctl_disconnect(priv->tdc);
-        teamdctl_free(priv->tdc);
-        priv->tdc = NULL;
+    if (!priv->config && priv->tdc) {
+        teamd_read_config(self);
     }
 
     g_object_set(G_OBJECT(s_team), NM_SETTING_TEAM_CONFIG, _get_config(self), NULL);
diff --git a/src/core/devices/wifi/nm-device-wifi-p2p.c b/src/core/devices/wifi/nm-device-wifi-p2p.c
index 6c0a4d5e..424464c1 100644
--- a/src/core/devices/wifi/nm-device-wifi-p2p.c
+++ b/src/core/devices/wifi/nm-device-wifi-p2p.c
@@ -585,7 +585,7 @@ act_stage3_ip_config(NMDevice *device, int addr_family)
             nm_device_devip_set_state(device, AF_INET, NM_DEVICE_IP_STATE_READY, l3cd);
 
             /* This just disables the addressing indicator. */
-            method = NM_SETTING_IP4_CONFIG_METHOD_DISABLED;
+            method = NM_SETTING_IP4_CONFIG_METHOD_MANUAL;
         }
     }
 
@@ -643,7 +643,7 @@ get_auto_ip_config_method(NMDevice *device, int addr_family)
     if (addr_family == AF_INET && priv->group_iface
         && !nm_supplicant_interface_get_p2p_group_owner(priv->group_iface)
         && nm_supplicant_interface_get_p2p_assigned_addr(priv->group_iface, NULL, NULL))
-        return NM_SETTING_IP4_CONFIG_METHOD_DISABLED;
+        return NM_SETTING_IP4_CONFIG_METHOD_MANUAL;
 
     /* Override the AUTO method to mean shared if we are group owner. */
     if (priv->group_iface && nm_supplicant_interface_get_p2p_group_owner(priv->group_iface)) {
diff --git a/src/core/nm-firewall-utils.c b/src/core/nm-firewall-utils.c
index f231583a..03f1a9a5 100644
--- a/src/core/nm-firewall-utils.c
+++ b/src/core/nm-firewall-utils.c
@@ -889,12 +889,12 @@ nm_firewall_nft_stdio_mlag(gboolean           up,
          */
         _append(&strbuf,
                 "add set netdev %s macset-tagged {"
-                " typeof ether saddr . vlan id; flags timeout; "
+                " typeof ether saddr . vlan id; flags dynamic,timeout; "
                 "}",
                 table_name);
         _append(&strbuf,
                 "add set netdev %s macset-untagged {"
-                " typeof ether saddr; flags timeout;"
+                " typeof ether saddr; flags dynamic,timeout; "
                 "}",
                 table_name);
 
diff --git a/src/core/nm-l3-ipv6ll.c b/src/core/nm-l3-ipv6ll.c
index 2e2a6a0a..0133ebe6 100644
--- a/src/core/nm-l3-ipv6ll.c
+++ b/src/core/nm-l3-ipv6ll.c
@@ -391,7 +391,7 @@ _pladdr_find_ll(NML3IPv6LL *self, gboolean *out_cur_addr_failed)
 /*****************************************************************************/
 
 static void
-_lladdr_handle_changed(NML3IPv6LL *self)
+_lladdr_handle_changed(NML3IPv6LL *self, gboolean force_commit)
 {
     const NML3ConfigData *l3cd;
     gboolean              changed = FALSE;
@@ -420,7 +420,9 @@ _lladdr_handle_changed(NML3IPv6LL *self)
                                 NM_DNS_PRIORITY_DEFAULT_NORMAL,
                                 NM_L3_ACD_DEFEND_TYPE_ALWAYS,
                                 0,
-                                NM_L3CFG_CONFIG_FLAGS_NONE,
+                                /* Even if the address was removed from platform, it must
+                                 * be re-added, hence FORCE_ONCE. */
+                                NM_L3CFG_CONFIG_FLAGS_FORCE_ONCE,
                                 NM_L3_CONFIG_MERGE_FLAGS_NONE))
             changed = TRUE;
     } else {
@@ -434,7 +436,7 @@ _lladdr_handle_changed(NML3IPv6LL *self)
                                                               self->l3cfg_commit_handle,
                                                               "ipv6ll");
 
-    if (changed)
+    if (changed || force_commit)
         nm_l3cfg_commit_on_idle_schedule(self->l3cfg, NM_L3_CFG_COMMIT_TYPE_AUTO);
 
     if (!self->emit_changed_idle_source) {
@@ -515,6 +517,7 @@ _check(NML3IPv6LL *self)
     const NMPlatformIP6Address *pladdr;
     char                        sbuf[INET6_ADDRSTRLEN];
     gboolean                    cur_addr_failed;
+    gboolean                    restarted = FALSE;
     struct in6_addr             lladdr;
 
     pladdr = _pladdr_find_ll(self, &cur_addr_failed);
@@ -526,14 +529,14 @@ _check(NML3IPv6LL *self)
             if (_set_cur_lladdr_obj(self, NM_L3_IPV6LL_STATE_DAD_IN_PROGRESS, pladdr)) {
                 _LOGT("changed: waiting for address %s to complete DAD",
                       nm_inet6_ntop(&self->cur_lladdr, sbuf));
-                _lladdr_handle_changed(self);
+                _lladdr_handle_changed(self, FALSE);
             }
             return;
         }
 
         if (_set_cur_lladdr_obj(self, NM_L3_IPV6LL_STATE_READY, pladdr)) {
             _LOGT("changed: address %s is ready", nm_inet6_ntop(&self->cur_lladdr, sbuf));
-            _lladdr_handle_changed(self);
+            _lladdr_handle_changed(self, FALSE);
         }
         return;
     }
@@ -543,11 +546,17 @@ _check(NML3IPv6LL *self)
          * Prematurely abort DAD to generate a new address below. */
         nm_assert(
             NM_IN_SET(self->state, NM_L3_IPV6LL_STATE_DAD_IN_PROGRESS, NM_L3_IPV6LL_STATE_READY));
-        if (self->state == NM_L3_IPV6LL_STATE_DAD_IN_PROGRESS)
-            _LOGT("changed: address %s did not complete DAD",
-                  nm_inet6_ntop(&self->cur_lladdr, sbuf));
-        else {
+
+        if (cur_addr_failed) {
+            /* On DAD failure, we always try to regenerate a new address. */
+            _LOGT("changed: address %s failed", nm_inet6_ntop(&self->cur_lladdr, sbuf));
+        } else {
             _LOGT("changed: address %s is gone", nm_inet6_ntop(&self->cur_lladdr, sbuf));
+            /* When the address is removed, we always try to re-add it. */
+            nm_clear_g_source_inst(&self->wait_for_addr_source);
+            lladdr    = self->cur_lladdr;
+            restarted = TRUE;
+            goto commit;
         }
 
         /* reset the state here, so that we are sure that the following
@@ -569,18 +578,19 @@ _check(NML3IPv6LL *self)
         if (_set_cur_lladdr_bin(self, NM_L3_IPV6LL_STATE_DAD_FAILED, NULL)) {
             _LOGW("changed: no IPv6 link local address to retry after Duplicate Address Detection "
                   "failures (back off)");
-            _lladdr_handle_changed(self);
+            _lladdr_handle_changed(self, FALSE);
         }
         return;
     }
 
+commit:
     /* we give NML3Cfg 2 seconds to configure the address on the interface. We
      * thus very soon expect to see this address configured (and kernel started DAD).
      * If that does not happen within timeout, we assume that this address failed DAD. */
     self->wait_for_addr_source = nm_g_timeout_add_source(2000, _wait_for_addr_timeout_cb, self);
-    if (_set_cur_lladdr_bin(self, NM_L3_IPV6LL_STATE_DAD_IN_PROGRESS, &lladdr)) {
+    if (_set_cur_lladdr_bin(self, NM_L3_IPV6LL_STATE_DAD_IN_PROGRESS, &lladdr) || restarted) {
         _LOGT("changed: starting DAD for address %s", nm_inet6_ntop(&self->cur_lladdr, sbuf));
-        _lladdr_handle_changed(self);
+        _lladdr_handle_changed(self, restarted);
     }
     return;
 }
diff --git a/src/core/platform/nm-fake-platform.c b/src/core/platform/nm-fake-platform.c
index 86d6bc5f..0c366d82 100644
--- a/src/core/platform/nm-fake-platform.c
+++ b/src/core/platform/nm-fake-platform.c
@@ -667,6 +667,31 @@ link_supports_sriov(NMPlatform *platform, int ifindex)
 }
 
 static gboolean
+link_change(NMPlatform                   *platform,
+            int                           ifindex,
+            NMPortKind                    port_kind,
+            const NMPlatformLinkPortData *port_data)
+{
+    NMFakePlatformLink       *device  = link_get(platform, ifindex);
+    nm_auto_nmpobj NMPObject *obj_tmp = NULL;
+
+    switch (port_kind) {
+    case NM_PORT_KIND_BOND:
+        obj_tmp                               = nmp_object_clone(device->obj, FALSE);
+        obj_tmp->link.port_kind               = NM_PORT_KIND_BOND;
+        obj_tmp->link.port_data.bond.queue_id = port_data->bond.queue_id;
+        obj_tmp->link.port_data.bond.prio_has = port_data->bond.prio_has;
+        obj_tmp->link.port_data.bond.prio     = port_data->bond.prio;
+        link_set_obj(platform, device, obj_tmp);
+        return TRUE;
+    case NM_PORT_KIND_NONE:
+        return TRUE;
+    }
+
+    return nm_assert_unreachable_val(TRUE);
+}
+
+static gboolean
 link_enslave(NMPlatform *platform, int master, int slave)
 {
     NMFakePlatformLink *device        = link_get(platform, slave);
@@ -770,7 +795,7 @@ static gboolean
 infiniband_partition_delete(NMPlatform *platform, int parent, int p_key)
 {
     NMFakePlatformLink *parent_device;
-    gs_free char       *name = NULL;
+    char                name[IFNAMSIZ];
 
     parent_device = link_get(platform, parent);
     g_return_val_if_fail(parent_device != NULL, FALSE);
@@ -1325,6 +1350,7 @@ nm_fake_platform_class_init(NMFakePlatformClass *klass)
     platform_class->link_set_address = link_set_address;
     platform_class->link_set_mtu     = link_set_mtu;
 
+    platform_class->link_change       = link_change;
     platform_class->link_change_flags = link_change_flags;
 
     platform_class->link_get_driver_info = link_get_driver_info;
diff --git a/src/core/platform/tests/test-link.c b/src/core/platform/tests/test-link.c
index 9a1eaf89..33858671 100644
--- a/src/core/platform/tests/test-link.c
+++ b/src/core/platform/tests/test-link.c
@@ -112,7 +112,7 @@ software_add(NMLinkType link_type, const char *name)
         gboolean bond0_exists = !!nm_platform_link_get_by_ifname(NM_PLATFORM_GET, "bond0");
         int      r;
         const NMPlatformLnkBond nm_platform_lnk_bond_default = {
-            .mode = 3,
+            .mode = nmtst_rand_select(3, 1),
         };
 
         r = nm_platform_link_bond_add(NM_PLATFORM_GET, name, &nm_platform_lnk_bond_default, NULL);
@@ -263,6 +263,38 @@ test_slave(int master, int type, SignalData *master_changed)
     else
         g_assert(!nm_platform_link_is_up(NM_PLATFORM_GET, ifindex));
 
+    if (NM_IN_SET(link_type, NM_LINK_TYPE_BOND)) {
+        NMPlatformLinkBondPort   bond_port;
+        gboolean                 prio_has;
+        gboolean                 prio_supported;
+        const NMPlatformLink    *link;
+        const NMPlatformLnkBond *lnk;
+
+        link = nmtstp_link_get_typed(NM_PLATFORM_GET, 0, SLAVE_NAME, NM_LINK_TYPE_DUMMY);
+        g_assert(link);
+
+        lnk = nm_platform_link_get_lnk_bond(NM_PLATFORM_GET, master, NULL);
+        g_assert(lnk);
+
+        g_assert(NM_IN_SET(lnk->mode, 3, 1));
+        prio_supported = (lnk->mode == 1);
+        prio_has       = nmtst_get_rand_bool() && prio_supported;
+
+        bond_port = (NMPlatformLinkBondPort){
+            .queue_id = 5,
+            .prio_has = prio_has,
+            .prio     = prio_has ? 6 : 0,
+        };
+
+        g_assert(nm_platform_link_change(NM_PLATFORM_GET, ifindex, &bond_port));
+        accept_signals(link_changed, 1, 3);
+
+        link = nmtstp_link_get(NM_PLATFORM_GET, ifindex, SLAVE_NAME);
+        g_assert(link);
+        g_assert_cmpint(link->port_data.bond.queue_id, ==, 5);
+        g_assert(link->port_data.bond.prio_has || link->port_data.bond.prio == 0);
+    }
+
     test_link_changed_signal_arg1 = FALSE;
     test_link_changed_signal_arg2 = FALSE;
     g_signal_connect(NM_PLATFORM_GET,
@@ -3349,7 +3381,9 @@ test_netns_bind_to_path(gpointer fixture, gconstpointer test_data)
     nm_auto_pop_netns NMPNetns *netns_pop  = NULL;
     NMPlatform                 *platforms[3];
     NMPNetns                   *netns;
+    int                         errsv;
     int                         i;
+    int                         r;
 
     if (_test_netns_check_skip())
         return;
@@ -3379,7 +3413,15 @@ test_netns_bind_to_path(gpointer fixture, gconstpointer test_data)
     g_assert(nmp_netns_bind_to_path(netns, P_VAR_RUN_NETNS_BINDNAME, NULL));
 
     g_assert(g_file_test(P_VAR_RUN_NETNS_BINDNAME, G_FILE_TEST_EXISTS));
-    g_assert_cmpint(nmtstp_run_command("ip netns exec " P_NETNS_BINDNAME " true"), ==, 0);
+
+    r = nmtstp_run_command("ip netns exec " P_NETNS_BINDNAME " true");
+    if (r != 0) {
+        gs_free char *msg = g_strdup_printf("`ip netns exec` fails with code %d. Skip test", r);
+
+        g_test_skip(msg);
+        return;
+    }
+
     g_assert_cmpint(
         nmtstp_run_command("ip netns exec " P_NETNS_BINDNAME " ip link show dummy2b 1>/dev/null"),
         ==,
@@ -3392,7 +3434,12 @@ test_netns_bind_to_path(gpointer fixture, gconstpointer test_data)
                     !=,
                     0);
 
-    g_assert_cmpint(umount(P_VAR_RUN), ==, 0);
+    r = umount(P_VAR_RUN);
+    if (r != 0) {
+        errsv = errno;
+        g_assert_cmpint(errsv, ==, 0);
+        g_assert_cmpint(r, ==, 0);
+    }
 }
 
 /*****************************************************************************/
diff --git a/src/core/ppp/meson.build b/src/core/ppp/meson.build
index 9ee46113..2365ffa8 100644
--- a/src/core/ppp/meson.build
+++ b/src/core/ppp/meson.build
@@ -3,7 +3,10 @@
 nm_pppd_plugin = shared_module(
   'nm-pppd-plugin',
   name_prefix: '',
-  sources: 'nm-pppd-plugin.c',
+  sources: [
+    'nm-pppd-compat.c',
+    'nm-pppd-plugin.c',
+  ],
   include_directories: [
     src_inc,
     top_inc,
@@ -11,6 +14,7 @@ nm_pppd_plugin = shared_module(
   dependencies: [
     libnm_core_public_dep,
     glib_dep,
+    pppd_dep,
   ],
   link_with: [
     libnm_core_impl,
diff --git a/src/core/ppp/nm-ppp-manager.c b/src/core/ppp/nm-ppp-manager.c
index 14cd5731..fbc5d075 100644
--- a/src/core/ppp/nm-ppp-manager.c
+++ b/src/core/ppp/nm-ppp-manager.c
@@ -428,7 +428,7 @@ impl_ppp_manager_set_state(NMDBusObject                      *obj,
 
     if (ppp_state >= NM_PPP_STATUS_INTERN_DEAD) {
         /* we don't expect an intern state to be reported by the plugin. */
-        ppp_state = NM_PPP_STATUS_UNKNOWN;
+        ppp_state = NM_PPP_STATUS_INTERN_UNKNOWN;
     }
 
     g_signal_emit(self, signals[STATE_CHANGED], 0, (guint) ppp_state);
@@ -843,7 +843,7 @@ create_pppd_cmd_line(NMPPPManager   *self,
         const char *pppoe_service;
 
         nm_strv_ptrarray_add_string_dup(cmd, "plugin");
-        nm_strv_ptrarray_add_string_dup(cmd, "rp-pppoe.so");
+        nm_strv_ptrarray_add_string_dup(cmd, NM_PPPOE_PLUGIN_NAME);
 
         nm_strv_ptrarray_add_string_concat(cmd, "nic-", priv->parent_iface);
 
@@ -872,7 +872,7 @@ create_pppd_cmd_line(NMPPPManager   *self,
 
         } else if (!strcmp(protocol, NM_SETTING_ADSL_PROTOCOL_PPPOE)) {
             nm_strv_ptrarray_add_string_dup(cmd, "plugin");
-            nm_strv_ptrarray_add_string_dup(cmd, "rp-pppoe.so");
+            nm_strv_ptrarray_add_string_dup(cmd, NM_PPPOE_PLUGIN_NAME);
             nm_strv_ptrarray_add_string_dup(cmd, priv->parent_iface);
         }
 
diff --git a/src/core/ppp/nm-ppp-mgr.c b/src/core/ppp/nm-ppp-mgr.c
index bebffaf3..91d9a021 100644
--- a/src/core/ppp/nm-ppp-mgr.c
+++ b/src/core/ppp/nm-ppp-mgr.c
@@ -257,7 +257,7 @@ _ppp_signal_state_changed(NMPPPManager *ppp_manager, guint ppp_state_u, gpointer
     const char         *reason_msg;
 
     if ((guint) ppp_state != ppp_state_u)
-        ppp_state = NM_PPP_STATUS_UNKNOWN;
+        ppp_state = NM_PPP_STATUS_INTERN_UNKNOWN;
 
     switch (ppp_state) {
     case NM_PPP_STATUS_DISCONNECT:
diff --git a/src/core/ppp/nm-ppp-status.h b/src/core/ppp/nm-ppp-status.h
index df0ac10b..a2576e9c 100644
--- a/src/core/ppp/nm-ppp-status.h
+++ b/src/core/ppp/nm-ppp-status.h
@@ -8,24 +8,31 @@
 #define __NM_PPP_STATUS_H__
 
 typedef enum {
-    NM_PPP_STATUS_UNKNOWN,
-
-    NM_PPP_STATUS_DEAD,
-    NM_PPP_STATUS_INITIALIZE,
-    NM_PPP_STATUS_SERIALCONN,
-    NM_PPP_STATUS_DORMANT,
-    NM_PPP_STATUS_ESTABLISH,
-    NM_PPP_STATUS_AUTHENTICATE,
-    NM_PPP_STATUS_CALLBACK,
-    NM_PPP_STATUS_NETWORK,
-    NM_PPP_STATUS_RUNNING,
-    NM_PPP_STATUS_TERMINATE,
-    NM_PPP_STATUS_DISCONNECT,
-    NM_PPP_STATUS_HOLDOFF,
-    NM_PPP_STATUS_MASTER,
+
+    /* The numeric values correspond to the PHASE_{DEAD,} defines from <pppd/pppd.h>. */
+    NM_PPP_STATUS_DEAD         = 0,
+    NM_PPP_STATUS_INITIALIZE   = 1,
+    NM_PPP_STATUS_SERIALCONN   = 2,
+    NM_PPP_STATUS_DORMANT      = 3,
+    NM_PPP_STATUS_ESTABLISH    = 4,
+    NM_PPP_STATUS_AUTHENTICATE = 5,
+    NM_PPP_STATUS_CALLBACK     = 6,
+    NM_PPP_STATUS_NETWORK      = 7,
+    NM_PPP_STATUS_RUNNING      = 8,
+    NM_PPP_STATUS_TERMINATE    = 9,
+    NM_PPP_STATUS_DISCONNECT   = 10,
+    NM_PPP_STATUS_HOLDOFF      = 11,
+    NM_PPP_STATUS_MASTER       = 12,
 
     /* these states are internal and not announced by the pppd plugin. */
+    NM_PPP_STATUS_INTERN_UNKNOWN = 20,
     NM_PPP_STATUS_INTERN_DEAD,
 } NMPPPStatus;
 
+/*****************************************************************************/
+
+/* The plugin name "(rp-)pppoe.so" depends on the ppp version. */
+
+#define NM_PPPOE_PLUGIN_NAME (NM_PPP_VERSION_2_5_OR_NEWER ? "pppoe.so" : "rp-pppoe.so")
+
 #endif /* __NM_PPP_STATUS_H__ */
diff --git a/src/core/ppp/nm-pppd-compat.c b/src/core/ppp/nm-pppd-compat.c
new file mode 100644
index 00000000..f7940c55
--- /dev/null
+++ b/src/core/ppp/nm-pppd-compat.c
@@ -0,0 +1,308 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2023 Eivind Næss, eivnaes@yahoo.com
+ */
+
+/*****************************************************************************/
+
+/* PPP headers define some symbols as we do. We need to be careful to handle
+ * the conflict, and include stuff in a certain order. */
+
+#include <config.h>
+#define ___CONFIG_H__
+
+/*****************************************************************************/
+
+#if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))
+_Pragma("GCC diagnostic push") _Pragma("GCC diagnostic ignored \"-Wstrict-prototypes\"")
+#define _NM_PRAGMA_WARNING_REENABLE _Pragma("GCC diagnostic pop")
+#elif defined(__clang__)
+_Pragma("clang diagnostic push") _Pragma("clang diagnostic ignored \"-Wunknown-warning-option\"")
+    _Pragma("clang diagnostic ignored \"-Wstrict-prototypes\"")
+#define _NM_PRAGMA_WARNING_REENABLE _Pragma("clang diagnostic pop")
+#else
+#define _NM_PRAGMA_WARNING_REENABLE
+#endif
+
+/*****************************************************************************/
+
+#define INET6 1
+
+#include <pppd/pppd.h>
+
+#ifdef DATE
+/* Before ppp 2.5, pppd/patchlevel.h defined "DATE". Use that for detection. */
+#define PPP_VERSION_2_5_OR_NEWER 0
+#else
+#define PPP_VERSION_2_5_OR_NEWER 1
+#endif
+
+#include <pppd/eui64.h>
+#include <pppd/fsm.h>
+#include <pppd/ipcp.h>
+#include <pppd/ipv6cp.h>
+#include <pppd/upap.h>
+
+#if PPP_VERSION_2_5_OR_NEWER
+#include <pppd/chap.h>
+#else
+#include <pppd/chap-new.h>
+#include <pppd/chap_ms.h>
+#endif
+
+    char pppd_version[] = (
+#if PPP_VERSION_2_5_OR_NEWER
+        PPPD_VERSION
+#else
+            VERSION
+#endif
+    );
+
+#undef VERSION
+
+_NM_PRAGMA_WARNING_REENABLE;
+
+/*****************************************************************************/
+
+#include "libnm-glib-aux/nm-default-glib.h"
+
+#include "nm-pppd-compat.h"
+
+#include <net/if.h>
+#include <dlfcn.h>
+
+#include "nm-ppp-status.h"
+
+/*****************************************************************************/
+
+G_STATIC_ASSERT(PPP_VERSION_2_5_OR_NEWER == NM_PPP_VERSION_2_5_OR_NEWER);
+
+/*****************************************************************************/
+
+G_STATIC_ASSERT((gint64) NM_PPP_STATUS_DEAD == PHASE_DEAD);
+G_STATIC_ASSERT((gint64) NM_PPP_STATUS_INITIALIZE == PHASE_INITIALIZE);
+G_STATIC_ASSERT((gint64) NM_PPP_STATUS_SERIALCONN == PHASE_SERIALCONN);
+G_STATIC_ASSERT((gint64) NM_PPP_STATUS_DORMANT == PHASE_DORMANT);
+G_STATIC_ASSERT((gint64) NM_PPP_STATUS_ESTABLISH == PHASE_ESTABLISH);
+G_STATIC_ASSERT((gint64) NM_PPP_STATUS_AUTHENTICATE == PHASE_AUTHENTICATE);
+G_STATIC_ASSERT((gint64) NM_PPP_STATUS_CALLBACK == PHASE_CALLBACK);
+G_STATIC_ASSERT((gint64) NM_PPP_STATUS_NETWORK == PHASE_NETWORK);
+G_STATIC_ASSERT((gint64) NM_PPP_STATUS_RUNNING == PHASE_RUNNING);
+G_STATIC_ASSERT((gint64) NM_PPP_STATUS_TERMINATE == PHASE_TERMINATE);
+G_STATIC_ASSERT((gint64) NM_PPP_STATUS_DISCONNECT == PHASE_DISCONNECT);
+G_STATIC_ASSERT((gint64) NM_PPP_STATUS_HOLDOFF == PHASE_HOLDOFF);
+G_STATIC_ASSERT((gint64) NM_PPP_STATUS_MASTER == PHASE_MASTER);
+
+G_STATIC_ASSERT(NM_PPPD_COMPAT_MAXNAMELEN == MAXNAMELEN);
+G_STATIC_ASSERT(NM_PPPD_COMPAT_MAXSECRETLEN == MAXSECRETLEN);
+
+/*****************************************************************************/
+
+int
+nm_pppd_compat_get_ifunit(void)
+{
+    int i;
+
+#if PPP_VERSION_2_5_OR_NEWER
+    i = ppp_ifunit();
+#else
+    i = ifunit;
+#endif
+
+    return i;
+}
+
+const char *
+nm_pppd_compat_get_ifname(void)
+{
+    const char *s;
+
+#if PPP_VERSION_2_5_OR_NEWER
+    s = ppp_ifname();
+#else
+    s = ifname;
+#endif
+
+    nm_assert(s);
+    nm_assert(strlen(s) < IFNAMSIZ);
+    return s;
+}
+
+void
+nm_pppd_compat_set_ifname(const char *arg_ifname)
+{
+    nm_assert(arg_ifname);
+    nm_assert(strlen(arg_ifname) < IFNAMSIZ);
+
+#if PPP_VERSION_2_5_OR_NEWER
+    ppp_set_ifname(arg_ifname);
+#else
+    g_strlcpy(ifname, arg_ifname, IFNAMSIZ);
+#endif
+}
+
+const char *
+nm_pppd_compat_get_ipparam(void)
+{
+    const char *s;
+
+#if PPP_VERSION_2_5_OR_NEWER
+    s = ppp_ipparam();
+#else
+    s = ipparam;
+#endif
+
+    return s;
+}
+
+void
+nm_pppd_compat_get_ipcp_options(NMPppdCompatIPCPOptions *out_got, NMPppdCompatIPCPOptions *out_his)
+{
+    const ipcp_options *const got = &ipcp_gotoptions[0];
+    const ipcp_options *const his = &ipcp_hisoptions[0];
+
+    nm_assert(out_got);
+    nm_assert(out_his);
+
+    *out_got = (NMPppdCompatIPCPOptions){
+        .ouraddr  = got->ouraddr,
+        .hisaddr  = got->hisaddr,
+        .dnsaddr  = {got->dnsaddr[0], got->dnsaddr[1]},
+        .winsaddr = {got->winsaddr[0], got->winsaddr[1]},
+    };
+
+    *out_his = (NMPppdCompatIPCPOptions){
+        .ouraddr  = his->ouraddr,
+        .hisaddr  = his->hisaddr,
+        .dnsaddr  = {his->dnsaddr[0], his->dnsaddr[1]},
+        .winsaddr = {his->winsaddr[0], his->winsaddr[1]},
+    };
+}
+
+void
+nm_pppd_compat_get_ipv6cp_options(NMPppdCompatIPV6CPOptions *out_got,
+                                  NMPppdCompatIPV6CPOptions *out_his)
+{
+    const ipv6cp_options *const his = &ipv6cp_hisoptions[0];
+    const ipv6cp_options *const got = &ipv6cp_gotoptions[0];
+
+    G_STATIC_ASSERT(sizeof(guint64) == sizeof(eui64_t));
+
+    nm_assert(out_got);
+    nm_assert(out_his);
+
+    *out_got = (NMPppdCompatIPV6CPOptions){};
+    memcpy(&out_got->ourid, &got->ourid, sizeof(guint64));
+    memcpy(&out_got->hisid, &got->hisid, sizeof(guint64));
+
+    *out_his = (NMPppdCompatIPV6CPOptions){};
+    memcpy(&out_his->ourid, &his->ourid, sizeof(guint64));
+    memcpy(&out_his->hisid, &his->hisid, sizeof(guint64));
+}
+
+void
+nm_pppd_compat_set_chap_passwd_hook(int (*hook)(char *user, char *password))
+{
+    chap_passwd_hook = hook;
+}
+
+void
+nm_pppd_compat_set_chap_check_hook(int (*hook)(void))
+{
+    chap_check_hook = hook;
+}
+
+void
+nm_pppd_compat_set_pap_passwd_hook(int (*hook)(char *user, char *passwd))
+{
+    pap_passwd_hook = hook;
+}
+
+void
+nm_pppd_compat_set_pap_check_hook(int (*hook)(void))
+{
+    pap_check_hook = hook;
+}
+
+gboolean
+nm_pppd_compat_add_notify(NMPppdCompatNotifyT type, void (*func)(void *ctx, int arg), void *ctx)
+{
+    nm_assert(NM_IN_SET(type,
+                        NM_PPPD_COMPAT_NF_PID_CHANGE,
+                        NM_PPPD_COMPAT_NF_PHASE_CHANGE,
+                        NM_PPPD_COMPAT_NF_EXIT,
+                        NM_PPPD_COMPAT_NF_SIGNALED,
+                        NM_PPPD_COMPAT_NF_IP_UP,
+                        NM_PPPD_COMPAT_NF_IP_DOWN,
+                        NM_PPPD_COMPAT_NF_IPV6_UP,
+                        NM_PPPD_COMPAT_NF_IPV6_DOWN,
+                        NM_PPPD_COMPAT_NF_AUTH_UP,
+                        NM_PPPD_COMPAT_NF_LINK_DOWN,
+                        NM_PPPD_COMPAT_NF_FORK));
+    nm_assert(func);
+
+#if PPP_VERSION_2_5_OR_NEWER
+    {
+        G_STATIC_ASSERT((gint64) NM_PPPD_COMPAT_NF_PID_CHANGE == NF_PID_CHANGE);
+        G_STATIC_ASSERT((gint64) NM_PPPD_COMPAT_NF_PHASE_CHANGE == NF_PHASE_CHANGE);
+        G_STATIC_ASSERT((gint64) NM_PPPD_COMPAT_NF_EXIT == NF_EXIT);
+        G_STATIC_ASSERT((gint64) NM_PPPD_COMPAT_NF_SIGNALED == NF_SIGNALED);
+        G_STATIC_ASSERT((gint64) NM_PPPD_COMPAT_NF_IP_UP == NF_IP_UP);
+        G_STATIC_ASSERT((gint64) NM_PPPD_COMPAT_NF_IP_DOWN == NF_IP_DOWN);
+        G_STATIC_ASSERT((gint64) NM_PPPD_COMPAT_NF_IPV6_UP == NF_IPV6_UP);
+        G_STATIC_ASSERT((gint64) NM_PPPD_COMPAT_NF_IPV6_DOWN == NF_IPV6_DOWN);
+        G_STATIC_ASSERT((gint64) NM_PPPD_COMPAT_NF_AUTH_UP == NF_AUTH_UP);
+        G_STATIC_ASSERT((gint64) NM_PPPD_COMPAT_NF_LINK_DOWN == NF_LINK_DOWN);
+        G_STATIC_ASSERT((gint64) NM_PPPD_COMPAT_NF_FORK == NF_FORK);
+
+        ppp_add_notify((gint64) type, func, ctx);
+        return TRUE;
+    }
+#else
+    {
+        static struct notifier **list[] = {
+            [NM_PPPD_COMPAT_NF_PID_CHANGE]   = &pidchange,
+            [NM_PPPD_COMPAT_NF_PHASE_CHANGE] = &phasechange,
+            [NM_PPPD_COMPAT_NF_EXIT]         = &exitnotify,
+            [NM_PPPD_COMPAT_NF_SIGNALED]     = &sigreceived,
+            [NM_PPPD_COMPAT_NF_IP_UP]        = &ip_up_notifier,
+            [NM_PPPD_COMPAT_NF_IP_DOWN]      = &ip_down_notifier,
+            [NM_PPPD_COMPAT_NF_IPV6_UP]      = NULL /* ipv6_up_notifier */,
+            [NM_PPPD_COMPAT_NF_IPV6_DOWN]    = NULL /* ipv6_down_notifier */,
+            [NM_PPPD_COMPAT_NF_AUTH_UP]      = &auth_up_notifier,
+            [NM_PPPD_COMPAT_NF_LINK_DOWN]    = &link_down_notifier,
+            [NM_PPPD_COMPAT_NF_FORK]         = &fork_notifier,
+        };
+        struct notifier **notifier;
+
+        nm_assert(_NM_INT_NOT_NEGATIVE(type) && type < G_N_ELEMENTS(list));
+
+        if (NM_IN_SET(type, NM_PPPD_COMPAT_NF_IPV6_UP, NM_PPPD_COMPAT_NF_IPV6_DOWN)) {
+            static gsize load_once = 0;
+
+            /* pppd might be build without IPv6 support. Load the symbols dynamically. */
+            if (g_once_init_enter(&load_once)) {
+                void *handle;
+
+                handle = dlopen(NULL, RTLD_NOW | RTLD_GLOBAL);
+                if (handle) {
+                    list[NM_PPPD_COMPAT_NF_IPV6_UP]   = dlsym(handle, "ipv6_up_notifier");
+                    list[NM_PPPD_COMPAT_NF_IPV6_DOWN] = dlsym(handle, "ipv6_down_notifier");
+                    dlclose(handle);
+                }
+                g_once_init_leave(&load_once, 1);
+            }
+
+            notifier = list[type];
+        } else {
+            notifier = list[type];
+            nm_assert(notifier);
+        }
+
+        if (notifier)
+            add_notifier(notifier, func, ctx);
+
+        return !!notifier;
+    }
+#endif
+}
diff --git a/src/core/ppp/nm-pppd-compat.h b/src/core/ppp/nm-pppd-compat.h
new file mode 100644
index 00000000..11376e36
--- /dev/null
+++ b/src/core/ppp/nm-pppd-compat.h
@@ -0,0 +1,64 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2023 Eivind Næss, eivnaes@yahoo.com
+ */
+
+#ifndef __NM_PPPD_COMPAT_H__
+#define __NM_PPPD_COMPAT_H__
+
+#define NM_PPPD_COMPAT_MAXNAMELEN   256
+#define NM_PPPD_COMPAT_MAXSECRETLEN 256
+
+int nm_pppd_compat_get_ifunit(void);
+
+const char *nm_pppd_compat_get_ifname(void);
+void        nm_pppd_compat_set_ifname(const char *ifname);
+
+const char *nm_pppd_compat_get_ipparam(void);
+
+typedef struct {
+    /* has information from "ipcp_options" */
+    in_addr_t ouraddr;
+    in_addr_t hisaddr;
+    in_addr_t dnsaddr[2];
+    in_addr_t winsaddr[2];
+} NMPppdCompatIPCPOptions;
+
+void nm_pppd_compat_get_ipcp_options(NMPppdCompatIPCPOptions *out_got,
+                                     NMPppdCompatIPCPOptions *out_his);
+
+typedef struct {
+    /* has information from "ipv6cp_options" */
+    guint64 ourid;
+    guint64 hisid;
+} NMPppdCompatIPV6CPOptions;
+
+void nm_pppd_compat_get_ipv6cp_options(NMPppdCompatIPV6CPOptions *out_got,
+                                       NMPppdCompatIPV6CPOptions *out_his);
+
+void nm_pppd_compat_set_chap_passwd_hook(int (*hook)(char *user, char *password));
+
+void nm_pppd_compat_set_chap_check_hook(int (*hook)(void));
+
+void nm_pppd_compat_set_pap_passwd_hook(int (*hook)(char *user, char *passwd));
+
+void nm_pppd_compat_set_pap_check_hook(int (*hook)(void));
+
+typedef enum {
+    NM_PPPD_COMPAT_NF_PID_CHANGE,
+    NM_PPPD_COMPAT_NF_PHASE_CHANGE,
+    NM_PPPD_COMPAT_NF_EXIT,
+    NM_PPPD_COMPAT_NF_SIGNALED,
+    NM_PPPD_COMPAT_NF_IP_UP,
+    NM_PPPD_COMPAT_NF_IP_DOWN,
+    NM_PPPD_COMPAT_NF_IPV6_UP,
+    NM_PPPD_COMPAT_NF_IPV6_DOWN,
+    NM_PPPD_COMPAT_NF_AUTH_UP,
+    NM_PPPD_COMPAT_NF_LINK_DOWN,
+    NM_PPPD_COMPAT_NF_FORK,
+} NMPppdCompatNotifyT;
+
+gboolean
+nm_pppd_compat_add_notify(NMPppdCompatNotifyT type, void (*func)(void *ctx, int arg), void *ctx);
+
+#endif /* #ifdef __NM_PPPD_COMPAT_H__ */
diff --git a/src/core/ppp/nm-pppd-plugin.c b/src/core/ppp/nm-pppd-plugin.c
index e2e34d2a..93425f38 100644
--- a/src/core/ppp/nm-pppd-plugin.c
+++ b/src/core/ppp/nm-pppd-plugin.c
@@ -4,33 +4,23 @@
  * Copyright (C) 2008 Red Hat, Inc.
  */
 
-#include <config.h>
-#define ___CONFIG_H__
+#include "libnm-glib-aux/nm-default-glib.h"
+
+#include "nm-pppd-plugin.h"
 
-#include <pppd/pppd.h>
-#include <pppd/fsm.h>
-#include <pppd/ipcp.h>
 #include <sys/socket.h>
 #include <netinet/in.h>
 #include <net/if.h>
 #include <arpa/inet.h>
 #include <dlfcn.h>
+#include <glib.h>
 
-#define INET6
-#include <pppd/eui64.h>
-#include <pppd/ipv6cp.h>
-
-#include "libnm-glib-aux/nm-default-glib.h"
-
-#include "nm-dbus-interface.h"
-
-#include "nm-pppd-plugin.h"
+#include "nm-pppd-compat.h"
 #include "nm-ppp-status.h"
+#include "nm-dbus-interface.h"
 
 int plugin_init(void);
 
-char pppd_version[] = VERSION;
-
 static struct {
     GDBusConnection *dbus_connection;
     char            *ipparam;
@@ -39,73 +29,63 @@ static struct {
 static void
 nm_phasechange(int arg)
 {
-    NMPPPStatus ppp_status = NM_PPP_STATUS_UNKNOWN;
+    NMPPPStatus ppp_status;
     char       *ppp_phase;
 
     g_return_if_fail(G_IS_DBUS_CONNECTION(gl.dbus_connection));
 
+    ppp_status = arg;
+
     switch (arg) {
-    case PHASE_DEAD:
-        ppp_status = NM_PPP_STATUS_DEAD;
-        ppp_phase  = "dead";
+    case NM_PPP_STATUS_DEAD:
+        ppp_phase = "dead";
         break;
-    case PHASE_INITIALIZE:
-        ppp_status = NM_PPP_STATUS_INITIALIZE;
-        ppp_phase  = "initialize";
+    case NM_PPP_STATUS_INITIALIZE:
+        ppp_phase = "initialize";
         break;
-    case PHASE_SERIALCONN:
-        ppp_status = NM_PPP_STATUS_SERIALCONN;
-        ppp_phase  = "serial connection";
+    case NM_PPP_STATUS_SERIALCONN:
+        ppp_phase = "serial connection";
         break;
-    case PHASE_DORMANT:
-        ppp_status = NM_PPP_STATUS_DORMANT;
-        ppp_phase  = "dormant";
+    case NM_PPP_STATUS_DORMANT:
+        ppp_phase = "dormant";
         break;
-    case PHASE_ESTABLISH:
-        ppp_status = NM_PPP_STATUS_ESTABLISH;
-        ppp_phase  = "establish";
+    case NM_PPP_STATUS_ESTABLISH:
+        ppp_phase = "establish";
         break;
-    case PHASE_AUTHENTICATE:
-        ppp_status = NM_PPP_STATUS_AUTHENTICATE;
-        ppp_phase  = "authenticate";
+    case NM_PPP_STATUS_AUTHENTICATE:
+        ppp_phase = "authenticate";
         break;
-    case PHASE_CALLBACK:
-        ppp_status = NM_PPP_STATUS_CALLBACK;
-        ppp_phase  = "callback";
+    case NM_PPP_STATUS_CALLBACK:
+        ppp_phase = "callback";
         break;
-    case PHASE_NETWORK:
-        ppp_status = NM_PPP_STATUS_NETWORK;
-        ppp_phase  = "network";
+    case NM_PPP_STATUS_NETWORK:
+        ppp_phase = "network";
         break;
-    case PHASE_RUNNING:
-        ppp_status = NM_PPP_STATUS_RUNNING;
-        ppp_phase  = "running";
+    case NM_PPP_STATUS_RUNNING:
+        ppp_phase = "running";
         break;
-    case PHASE_TERMINATE:
-        ppp_status = NM_PPP_STATUS_TERMINATE;
-        ppp_phase  = "terminate";
+    case NM_PPP_STATUS_TERMINATE:
+        ppp_phase = "terminate";
         break;
-    case PHASE_DISCONNECT:
-        ppp_status = NM_PPP_STATUS_DISCONNECT;
-        ppp_phase  = "disconnect";
+    case NM_PPP_STATUS_DISCONNECT:
+        ppp_phase = "disconnect";
         break;
-    case PHASE_HOLDOFF:
-        ppp_status = NM_PPP_STATUS_HOLDOFF;
-        ppp_phase  = "holdoff";
+    case NM_PPP_STATUS_HOLDOFF:
+        ppp_phase = "holdoff";
         break;
-    case PHASE_MASTER:
-        ppp_status = NM_PPP_STATUS_MASTER;
-        ppp_phase  = "master";
+    case NM_PPP_STATUS_MASTER:
+        ppp_phase = "master";
         break;
 
     default:
-        ppp_phase = "unknown";
+        ppp_status = NM_PPP_STATUS_INTERN_UNKNOWN;
+        ppp_phase  = "unknown";
         break;
     }
 
     g_message("nm-ppp-plugin: status %d / phase '%s'", ppp_status, ppp_phase);
 
-    if (ppp_status != NM_PPP_STATUS_UNKNOWN) {
+    if (ppp_status != NM_PPP_STATUS_INTERN_UNKNOWN) {
         g_dbus_connection_call(gl.dbus_connection,
                                NM_DBUS_SERVICE,
                                gl.ipparam,
@@ -125,7 +105,7 @@ nm_phasechange(int arg)
         char                       new_name[IF_NAMESIZE];
         int                        ifindex;
 
-        ifindex = if_nametoindex(ifname);
+        ifindex = if_nametoindex(nm_pppd_compat_get_ifname());
 
         /* Make a sync call to ensure that when the call
          * terminates the interface already has its final
@@ -143,9 +123,12 @@ nm_phasechange(int arg)
                                           NULL);
 
         /* Update the name in pppd if NM changed it */
-        if (if_indextoname(ifindex, new_name) && !nm_streq0(ifname, new_name)) {
-            g_message("nm-ppp-plugin: interface name changed from '%s' to '%s'", ifname, new_name);
-            g_strlcpy(ifname, new_name, IF_NAMESIZE);
+        if (if_indextoname(ifindex, new_name)
+            && !nm_streq0(nm_pppd_compat_get_ifname(), new_name)) {
+            g_message("nm-ppp-plugin: interface name changed from '%s' to '%s'",
+                      nm_pppd_compat_get_ifname(),
+                      new_name);
+            nm_pppd_compat_set_ifname(new_name);
         }
     }
 }
@@ -154,7 +137,7 @@ static void
 nm_phasechange_hook(void *data, int arg)
 {
     /* We send the nofication in exitnotify instead */
-    if (arg == PHASE_DEAD)
+    if (arg == NM_PPP_STATUS_DEAD)
         return;
 
     nm_phasechange(arg);
@@ -163,18 +146,21 @@ nm_phasechange_hook(void *data, int arg)
 static void
 nm_ip_up(void *data, int arg)
 {
-    ipcp_options    opts      = ipcp_gotoptions[0];
-    ipcp_options    peer_opts = ipcp_hisoptions[0];
-    GVariantBuilder builder;
-    guint32         pppd_made_up_address = htonl(0x0a404040 + ifunit);
+    NMPppdCompatIPCPOptions opts;
+    NMPppdCompatIPCPOptions peer_opts;
+    GVariantBuilder         builder;
+    const in_addr_t         pppd_made_up_address =
+        htonl(0x0a404040u + ((guint) nm_pppd_compat_get_ifunit()));
 
     g_return_if_fail(G_IS_DBUS_CONNECTION(gl.dbus_connection));
 
     g_message("nm-ppp-plugin: ip-up event");
 
+    nm_pppd_compat_get_ipcp_options(&opts, &peer_opts);
+
     if (!opts.ouraddr) {
         g_warning("nm-ppp-plugin: didn't receive an internal IP from pppd!");
-        nm_phasechange(PHASE_DEAD);
+        nm_phasechange(NM_PPP_STATUS_DEAD);
         return;
     }
 
@@ -186,7 +172,7 @@ nm_ip_up(void *data, int arg)
     g_variant_builder_add(&builder,
                           "{sv}",
                           NM_PPP_IP4_CONFIG_INTERFACE,
-                          g_variant_new_string(ifname));
+                          g_variant_new_string(nm_pppd_compat_get_ifname()));
 
     g_variant_builder_add(&builder,
                           "{sv}",
@@ -263,28 +249,19 @@ nm_ip_up(void *data, int arg)
                            NULL);
 }
 
-static GVariant *
-eui64_to_variant(eui64_t eui)
-{
-    guint64 iid;
-
-    G_STATIC_ASSERT(sizeof(iid) == sizeof(eui));
-
-    memcpy(&iid, &eui, sizeof(eui));
-    return g_variant_new_uint64(iid);
-}
-
 static void
 nm_ip6_up(void *data, int arg)
 {
-    ipv6cp_options *ho = &ipv6cp_hisoptions[0];
-    ipv6cp_options *go = &ipv6cp_gotoptions[0];
-    GVariantBuilder builder;
+    NMPppdCompatIPV6CPOptions his;
+    NMPppdCompatIPV6CPOptions got;
+    GVariantBuilder           builder;
 
     g_return_if_fail(G_IS_DBUS_CONNECTION(gl.dbus_connection));
 
     g_message("nm-ppp-plugin: ip6-up event");
 
+    nm_pppd_compat_get_ipv6cp_options(&got, &his);
+
     g_variant_builder_init(&builder, G_VARIANT_TYPE_VARDICT);
     /* Keep sending the interface name to be backwards compatible
      * with older versions of NM during a package upgrade, where
@@ -292,12 +269,15 @@ nm_ip6_up(void *data, int arg)
     g_variant_builder_add(&builder,
                           "{sv}",
                           NM_PPP_IP6_CONFIG_INTERFACE,
-                          g_variant_new_string(ifname));
-    g_variant_builder_add(&builder, "{sv}", NM_PPP_IP6_CONFIG_OUR_IID, eui64_to_variant(go->ourid));
+                          g_variant_new_string(nm_pppd_compat_get_ifname()));
+    g_variant_builder_add(&builder,
+                          "{sv}",
+                          NM_PPP_IP6_CONFIG_OUR_IID,
+                          g_variant_new_uint64(got.ourid));
     g_variant_builder_add(&builder,
                           "{sv}",
                           NM_PPP_IP6_CONFIG_PEER_IID,
-                          eui64_to_variant(ho->hisid));
+                          g_variant_new_uint64(his.hisid));
 
     /* DNS is done via DHCPv6 or router advertisements */
 
@@ -368,8 +348,8 @@ get_credentials(char *username, char *password)
 
     g_variant_get(ret, "(&s&s)", &my_username, &my_password);
 
-    g_strlcpy(username, my_username, MAXNAMELEN);
-    g_strlcpy(password, my_password, MAXSECRETLEN);
+    g_strlcpy(username, my_username, NM_PPPD_COMPAT_MAXNAMELEN);
+    g_strlcpy(password, my_password, NM_PPPD_COMPAT_MAXSECRETLEN);
 
     return 1;
 }
@@ -382,7 +362,7 @@ nm_exit_notify(void *data, int arg)
     /* We wait until this point to notify dead phase to make sure that
      * the serial port has recovered already its original settings.
      */
-    nm_phasechange(PHASE_DEAD);
+    nm_phasechange(NM_PPP_STATUS_DEAD);
 
     g_message("nm-ppp-plugin: cleaning up");
 
@@ -390,27 +370,6 @@ nm_exit_notify(void *data, int arg)
     nm_clear_g_free(&gl.ipparam);
 }
 
-static void
-add_ip6_notifier(void)
-{
-    static struct notifier **notifier  = NULL;
-    static gsize             load_once = 0;
-
-    if (g_once_init_enter(&load_once)) {
-        void *handle = dlopen(NULL, RTLD_NOW | RTLD_GLOBAL);
-
-        if (handle) {
-            notifier = dlsym(handle, "ipv6_up_notifier");
-            dlclose(handle);
-        }
-        g_once_init_leave(&load_once, 1);
-    }
-    if (notifier)
-        add_notifier(notifier, nm_ip6_up, NULL);
-    else
-        g_message("nm-ppp-plugin: no IPV6CP notifier support; IPv6 not available");
-}
-
 int
 plugin_init(void)
 {
@@ -427,17 +386,16 @@ plugin_init(void)
         return -1;
     }
 
-    gl.ipparam = g_strdup(ipparam);
-
-    chap_passwd_hook = get_credentials;
-    chap_check_hook  = get_chap_check;
-    pap_passwd_hook  = get_credentials;
-    pap_check_hook   = get_pap_check;
+    gl.ipparam = g_strdup(nm_pppd_compat_get_ipparam());
 
-    add_notifier(&phasechange, nm_phasechange_hook, NULL);
-    add_notifier(&ip_up_notifier, nm_ip_up, NULL);
-    add_notifier(&exitnotify, nm_exit_notify, NULL);
-    add_ip6_notifier();
+    nm_pppd_compat_set_chap_passwd_hook(get_credentials);
+    nm_pppd_compat_set_chap_check_hook(get_chap_check);
+    nm_pppd_compat_set_pap_passwd_hook(get_credentials);
+    nm_pppd_compat_set_pap_check_hook(get_pap_check);
 
+    nm_pppd_compat_add_notify(NM_PPPD_COMPAT_NF_PHASE_CHANGE, nm_phasechange_hook, NULL);
+    nm_pppd_compat_add_notify(NM_PPPD_COMPAT_NF_IP_UP, nm_ip_up, NULL);
+    nm_pppd_compat_add_notify(NM_PPPD_COMPAT_NF_EXIT, nm_exit_notify, NULL);
+    nm_pppd_compat_add_notify(NM_PPPD_COMPAT_NF_IPV6_UP, nm_ip6_up, NULL);
     return 0;
 }
diff --git a/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c b/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c
index aa593331..6cfb5705 100644
--- a/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c
+++ b/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c
@@ -5377,6 +5377,7 @@ parse_infiniband_p_key(shvarFile *ifcfg, int *out_p_key, char **out_parent, GErr
     gs_free char *physdev = NULL;
     gs_free char *pkey_id = NULL;
     int           id;
+    int           fixup_id = 0;
 
     physdev = svGetValueStr_cp(ifcfg, "PHYSDEV");
     if (!physdev) {
@@ -5387,7 +5388,14 @@ parse_infiniband_p_key(shvarFile *ifcfg, int *out_p_key, char **out_parent, GErr
         return FALSE;
     }
 
-    pkey_id = svGetValueStr_cp(ifcfg, "PKEY_ID");
+    pkey_id = svGetValueStr_cp(ifcfg, "PKEY_ID_NM");
+    if (!pkey_id) {
+        /* Only check for "$PKEY_ID". That key is interpreted as having the
+         * full membership flag set ("fixup_id"). */
+        fixup_id = 0x8000;
+        pkey_id  = svGetValueStr_cp(ifcfg, "PKEY_ID");
+    }
+
     if (!pkey_id) {
         g_set_error(error,
                     NM_SETTINGS_ERROR,
@@ -5406,6 +5414,8 @@ parse_infiniband_p_key(shvarFile *ifcfg, int *out_p_key, char **out_parent, GErr
         return FALSE;
     }
 
+    id |= fixup_id;
+
     *out_p_key  = id;
     *out_parent = g_steal_pointer(&physdev);
     return TRUE;
@@ -5576,6 +5586,7 @@ make_bond_port_setting(shvarFile *ifcfg)
     gs_free char *value_to_free = NULL;
     const char   *value;
     guint         queue_id;
+    gint32        prio;
 
     g_return_val_if_fail(ifcfg != NULL, FALSE);
 
@@ -5584,11 +5595,23 @@ make_bond_port_setting(shvarFile *ifcfg)
         s_port = nm_setting_bond_port_new();
         queue_id =
             _nm_utils_ascii_str_to_uint64(value, 10, 0, G_MAXUINT16, NM_BOND_PORT_QUEUE_ID_DEF);
-        if (errno != 0) {
-            PARSE_WARNING("Invalid bond port queue_id value '%s'", value);
-            return s_port;
-        }
-        g_object_set(G_OBJECT(s_port), NM_SETTING_BOND_PORT_QUEUE_ID, queue_id, NULL);
+        if (errno != 0)
+            PARSE_WARNING("Invalid bond port queue_id value BOND_PORT_QUEUE_ID '%s'", value);
+        else
+            g_object_set(G_OBJECT(s_port), NM_SETTING_BOND_PORT_QUEUE_ID, queue_id, NULL);
+    }
+
+    nm_clear_g_free(&value_to_free);
+    value = svGetValue(ifcfg, "BOND_PORT_PRIO", &value_to_free);
+    if (value) {
+        if (!s_port)
+            s_port = nm_setting_bond_port_new();
+        prio =
+            _nm_utils_ascii_str_to_int64(value, 10, G_MININT32, G_MAXINT32, NM_BOND_PORT_PRIO_DEF);
+        if (errno != 0)
+            PARSE_WARNING("Invalid bond port prio value BOND_PORT_PRIO '%s'", value);
+        else
+            g_object_set(G_OBJECT(s_port), NM_SETTING_BOND_PORT_PRIO, prio, NULL);
     }
 
     return s_port;
diff --git a/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.c b/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.c
index 552310dd..207b8700 100644
--- a/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.c
+++ b/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.c
@@ -827,6 +827,7 @@ const NMSIfcfgKeyTypeInfo nms_ifcfg_well_known_keys[] = {
     _KEY_TYPE("BAND", NMS_IFCFG_KEY_TYPE_IS_PLAIN),
     _KEY_TYPE("BONDING_MASTER", NMS_IFCFG_KEY_TYPE_IS_PLAIN),
     _KEY_TYPE("BONDING_OPTS", NMS_IFCFG_KEY_TYPE_IS_PLAIN),
+    _KEY_TYPE("BOND_PORT_PRIO", NMS_IFCFG_KEY_TYPE_IS_PLAIN),
     _KEY_TYPE("BOND_PORT_QUEUE_ID", NMS_IFCFG_KEY_TYPE_IS_PLAIN),
     _KEY_TYPE("BOOTPROTO", NMS_IFCFG_KEY_TYPE_IS_PLAIN),
     _KEY_TYPE("BRIDGE", NMS_IFCFG_KEY_TYPE_IS_PLAIN),
@@ -1031,6 +1032,7 @@ const NMSIfcfgKeyTypeInfo nms_ifcfg_well_known_keys[] = {
     _KEY_TYPE("PHYSDEV", NMS_IFCFG_KEY_TYPE_IS_PLAIN),
     _KEY_TYPE("PKEY", NMS_IFCFG_KEY_TYPE_IS_PLAIN),
     _KEY_TYPE("PKEY_ID", NMS_IFCFG_KEY_TYPE_IS_PLAIN),
+    _KEY_TYPE("PKEY_ID_NM", NMS_IFCFG_KEY_TYPE_IS_PLAIN),
     _KEY_TYPE("PMF", NMS_IFCFG_KEY_TYPE_IS_PLAIN),
     _KEY_TYPE("PORTNAME", NMS_IFCFG_KEY_TYPE_IS_PLAIN),
     _KEY_TYPE("POWERSAVE", NMS_IFCFG_KEY_TYPE_IS_PLAIN),
diff --git a/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.h b/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.h
index 4fa9f18c..51b118e3 100644
--- a/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.h
+++ b/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.h
@@ -33,7 +33,7 @@ typedef struct {
     NMSIfcfgKeyTypeFlags key_flags;
 } NMSIfcfgKeyTypeInfo;
 
-extern const NMSIfcfgKeyTypeInfo nms_ifcfg_well_known_keys[261];
+extern const NMSIfcfgKeyTypeInfo nms_ifcfg_well_known_keys[263];
 
 const NMSIfcfgKeyTypeInfo *nms_ifcfg_well_known_key_find_info(const char *key, gssize *out_idx);
 
diff --git a/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c b/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c
index 41260020..97637063 100644
--- a/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c
+++ b/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c
@@ -1023,7 +1023,10 @@ write_wireless_setting(NMConnection *connection,
 }
 
 static gboolean
-write_infiniband_setting(NMConnection *connection, shvarFile *ifcfg, GError **error)
+write_infiniband_setting(NMConnection *connection,
+                         shvarFile    *ifcfg,
+                         char        **out_interface_name,
+                         GError      **error)
 {
     NMSettingInfiniband *s_infiniband;
     const char          *mac, *transport_mode, *parent;
@@ -1052,11 +1055,23 @@ write_infiniband_setting(NMConnection *connection, shvarFile *ifcfg, GError **er
     p_key = nm_setting_infiniband_get_p_key(s_infiniband);
     if (p_key != -1) {
         svSetValueStr(ifcfg, "PKEY", "yes");
+
         svSetValueInt64(ifcfg, "PKEY_ID", p_key);
 
+        if (!NM_FLAGS_HAS(p_key, 0x8000)) {
+            /* initscripts' ifup-ib used to always interpret the PKEY_ID with
+             * the full membership flag (0x8000) set. For compatibility, we do
+             * interpret PKEY_ID as having that flag set.
+             *
+             * However, now we want to persist a p-key which doesn't have the
+             * flag. Use a NetworkManager specific variable for that. This configuration
+             * is not supported by initscripts' ifup-ib.
+             */
+            svSetValueInt64(ifcfg, "PKEY_ID_NM", p_key);
+        }
+
         parent = nm_setting_infiniband_get_parent(s_infiniband);
-        if (parent)
-            svSetValueStr(ifcfg, "PHYSDEV", parent);
+        svSetValueStr(ifcfg, "PHYSDEV", parent);
     }
 
     svSetValueStr(ifcfg, "TYPE", TYPE_INFINIBAND);
@@ -1911,8 +1926,10 @@ write_bond_port_setting(NMConnection *connection, shvarFile *ifcfg)
     NMSettingBondPort *s_port;
 
     s_port = _nm_connection_get_setting(connection, NM_TYPE_SETTING_BOND_PORT);
-    if (s_port)
+    if (s_port) {
         svSetValueInt64(ifcfg, "BOND_PORT_QUEUE_ID", nm_setting_bond_port_get_queue_id(s_port));
+        svSetValueInt64(ifcfg, "BOND_PORT_PRIO", nm_setting_bond_port_get_prio(s_port));
+    }
 }
 
 static gboolean
@@ -2093,7 +2110,7 @@ write_dcb_setting(NMConnection *connection, shvarFile *ifcfg, GError **error)
 }
 
 static void
-write_connection_setting(NMSettingConnection *s_con, shvarFile *ifcfg)
+write_connection_setting(NMSettingConnection *s_con, shvarFile *ifcfg, const char *interface_name)
 {
     guint32                       n, i;
     nm_auto_free_gstring GString *str = NULL;
@@ -2110,7 +2127,9 @@ write_connection_setting(NMSettingConnection *s_con, shvarFile *ifcfg)
     svSetValueStr(ifcfg, "NAME", nm_setting_connection_get_id(s_con));
     svSetValueStr(ifcfg, "UUID", nm_setting_connection_get_uuid(s_con));
     svSetValueStr(ifcfg, "STABLE_ID", nm_setting_connection_get_stable_id(s_con));
-    svSetValueStr(ifcfg, "DEVICE", nm_setting_connection_get_interface_name(s_con));
+    svSetValueStr(ifcfg,
+                  "DEVICE",
+                  interface_name ?: nm_setting_connection_get_interface_name(s_con));
     svSetValueBoolean(ifcfg, "ONBOOT", nm_setting_connection_get_autoconnect(s_con));
 
     vint = nm_setting_connection_get_autoconnect_priority(s_con);
@@ -3308,6 +3327,7 @@ do_write_construct(NMConnection                   *connection,
     nm_auto_shvar_file_close shvarFile *route_content_svformat = NULL;
     nm_auto_free_gstring GString       *route_content          = NULL;
     nm_auto_free_gstring GString       *route6_content         = NULL;
+    gs_free char                       *interface_name         = NULL;
 
     nm_assert(NM_IS_CONNECTION(connection));
     nm_assert(_nm_connection_verify(connection, NULL) == NM_SETTING_VERIFY_SUCCESS);
@@ -3413,7 +3433,7 @@ do_write_construct(NMConnection                   *connection,
         if (!write_wireless_setting(connection, ifcfg, secrets, &no_8021x, error))
             return FALSE;
     } else if (!strcmp(type, NM_SETTING_INFINIBAND_SETTING_NAME)) {
-        if (!write_infiniband_setting(connection, ifcfg, error))
+        if (!write_infiniband_setting(connection, ifcfg, &interface_name, error))
             return FALSE;
     } else if (!strcmp(type, NM_SETTING_BOND_SETTING_NAME)) {
         if (!write_bond_setting(connection, ifcfg, &wired, error))
@@ -3518,7 +3538,7 @@ do_write_construct(NMConnection                   *connection,
 
     write_ip_routing_rules(connection, ifcfg, route_ignore);
 
-    write_connection_setting(s_con, ifcfg);
+    write_connection_setting(s_con, ifcfg, interface_name);
 
     NM_SET_OUT(out_ifcfg, g_steal_pointer(&ifcfg));
     NM_SET_OUT(out_blobs, g_steal_pointer(&blobs));
diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-infiniband b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-infiniband0
index 6c36f1de..6c36f1de 100644
--- a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-infiniband
+++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-infiniband0
diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-infiniband1 b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-infiniband1
new file mode 100644
index 00000000..dcb7758e
--- /dev/null
+++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-infiniband1
@@ -0,0 +1,12 @@
+TYPE=InfiniBand
+HWADDR=80:00:11:22:33:44:55:66:77:88:99:aa:bb:cc:dd:ee:ff:00:11:22
+CONNECTED_MODE=yes
+MTU=65520
+IPADDR=192.168.2.2
+NETMASK=255.255.255.0
+GATEWAY=192.168.2.1
+
+DEVICE=ib0.80c1
+PKEY=yes
+PKEY_ID=0x00c1
+PHYSDEV=ib0
diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-infiniband2 b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-infiniband2
new file mode 100644
index 00000000..2e6d9edf
--- /dev/null
+++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-infiniband2
@@ -0,0 +1,12 @@
+TYPE=InfiniBand
+HWADDR=80:00:11:22:33:44:55:66:77:88:99:aa:bb:cc:dd:ee:ff:00:11:22
+CONNECTED_MODE=yes
+MTU=65520
+IPADDR=192.168.2.2
+NETMASK=255.255.255.0
+GATEWAY=192.168.2.1
+
+DEVICE=ib0.00c1
+PKEY=yes
+PKEY_ID=0x00c1
+PHYSDEV=ib0
diff --git a/src/core/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c b/src/core/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c
index 40ff7c67..8c1e9d94 100644
--- a/src/core/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c
+++ b/src/core/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c
@@ -8352,6 +8352,7 @@ test_write_bond_port(void)
 
     s_bond_port = _nm_connection_new_setting(connection, NM_TYPE_SETTING_BOND_PORT);
     g_object_set(s_bond_port, NM_SETTING_BOND_PORT_QUEUE_ID, 1, NULL);
+    g_object_set(s_bond_port, NM_SETTING_BOND_PORT_PRIO, 10, NULL);
 
     nmtst_assert_connection_verifies(connection);
 
@@ -8363,8 +8364,9 @@ test_write_bond_port(void)
 }
 
 static void
-test_read_infiniband(void)
+test_read_infiniband(gconstpointer test_data)
 {
+    const guint                   TEST_IDX   = GPOINTER_TO_UINT(test_data);
     gs_unref_object NMConnection *connection = NULL;
     NMSettingInfiniband          *s_infiniband;
     char                         *unmanaged = NULL;
@@ -8373,11 +8375,15 @@ test_read_infiniband(void)
                                                          0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc,
                                                          0xdd, 0xee, 0xff, 0x00, 0x11, 0x22};
     const char *transport_mode;
+    const char *test_files[] = {
+        TEST_IFCFG_DIR "/ifcfg-test-infiniband0",
+        TEST_IFCFG_DIR "/ifcfg-test-infiniband1",
+        TEST_IFCFG_DIR "/ifcfg-test-infiniband2",
+    };
 
-    connection = _connection_from_file(TEST_IFCFG_DIR "/ifcfg-test-infiniband",
-                                       NULL,
-                                       TYPE_INFINIBAND,
-                                       &unmanaged);
+    g_assert(TEST_IDX < G_N_ELEMENTS(test_files));
+
+    connection = _connection_from_file(test_files[TEST_IDX], NULL, TYPE_INFINIBAND, &unmanaged);
     g_assert(!unmanaged);
 
     s_infiniband = nmtst_connection_assert_setting(connection, NM_TYPE_SETTING_INFINIBAND);
@@ -8389,6 +8395,25 @@ test_read_infiniband(void)
     transport_mode = nm_setting_infiniband_get_transport_mode(s_infiniband);
     g_assert(transport_mode);
     g_assert_cmpstr(transport_mode, ==, "connected");
+
+    nmtst_assert_connection_verifies_without_normalization(connection);
+
+    switch (TEST_IDX) {
+    case 0:
+        g_assert_cmpint(nm_setting_infiniband_get_p_key(s_infiniband), ==, -1);
+        g_assert_cmpstr(nm_setting_infiniband_get_parent(s_infiniband), ==, NULL);
+        g_assert_cmpstr(nm_connection_get_interface_name(connection), ==, "ib0");
+        break;
+    case 1:
+    case 2:
+        g_assert_cmpint(nm_setting_infiniband_get_p_key(s_infiniband), ==, 0x80c1);
+        g_assert_cmpstr(nm_setting_infiniband_get_parent(s_infiniband), ==, "ib0");
+        g_assert_cmpstr(nm_connection_get_interface_name(connection), ==, "ib0.80c1");
+        break;
+    default:
+        g_assert_not_reached();
+        break;
+    }
 }
 
 static void
@@ -8409,21 +8434,20 @@ test_read_ipoib(void)
     s_infiniband = nmtst_connection_assert_setting(connection, NM_TYPE_SETTING_INFINIBAND);
 
     pkey = nm_setting_infiniband_get_p_key(s_infiniband);
-    g_assert(pkey);
-    g_assert_cmpint(pkey, ==, 12);
+    g_assert_cmpint(pkey, ==, 0x800c);
 
     transport_mode = nm_setting_infiniband_get_transport_mode(s_infiniband);
-    g_assert(transport_mode);
     g_assert_cmpstr(transport_mode, ==, "connected");
 }
 
 static void
 test_write_infiniband(gconstpointer test_data)
 {
-    const int                     TEST_IDX   = GPOINTER_TO_INT(test_data);
-    nmtst_auto_unlinkfile char   *testfile   = NULL;
-    gs_unref_object NMConnection *connection = NULL;
-    gs_unref_object NMConnection *reread     = NULL;
+    const int                     TEST_IDX    = GPOINTER_TO_INT(test_data);
+    nmtst_auto_unlinkfile char   *testfile    = NULL;
+    gs_unref_object NMConnection *connection  = NULL;
+    gs_unref_object NMConnection *reread      = NULL;
+    gboolean                      reread_same = FALSE;
     NMSettingConnection          *s_con;
     NMSettingInfiniband          *s_infiniband;
     NMSettingIPConfig            *s_ip4;
@@ -8433,6 +8457,7 @@ test_write_infiniband(gconstpointer test_data)
     NMIPAddress *addr;
     GError      *error          = NULL;
     const char  *interface_name = NULL;
+    int          p_key;
 
     connection = nm_simple_connection_new();
 
@@ -8448,14 +8473,21 @@ test_write_infiniband(gconstpointer test_data)
                  NM_SETTING_INFINIBAND_SETTING_NAME,
                  NULL);
 
-    if (NM_IN_SET(TEST_IDX, 1, 3))
-        interface_name = "ib0.000c";
+    if (NM_IN_SET(TEST_IDX, 1, 2))
+        p_key = nmtst_get_rand_bool() ? 0x000c : 0x800c;
+    else
+        p_key = -1;
+
+    if (NM_IN_SET(TEST_IDX, 1, 3)) {
+        if (p_key >= 0x8000)
+            interface_name = "ib0.800c";
+    }
 
     g_object_set(s_con, NM_SETTING_CONNECTION_INTERFACE_NAME, interface_name, NULL);
 
     s_infiniband = _nm_connection_new_setting(connection, NM_TYPE_SETTING_INFINIBAND);
     g_object_set(s_infiniband, NM_SETTING_INFINIBAND_TRANSPORT_MODE, "connected", NULL);
-    if (NM_IN_SET(TEST_IDX, 1, 2)) {
+    if (p_key == -1) {
         g_object_set(s_infiniband,
                      NM_SETTING_INFINIBAND_MAC_ADDRESS,
                      mac,
@@ -8465,7 +8497,7 @@ test_write_infiniband(gconstpointer test_data)
     } else {
         g_object_set(s_infiniband,
                      NM_SETTING_INFINIBAND_P_KEY,
-                     12,
+                     p_key,
                      NM_SETTING_INFINIBAND_PARENT,
                      "ib0",
                      NULL);
@@ -8494,13 +8526,20 @@ test_write_infiniband(gconstpointer test_data)
 
     nmtst_assert_connection_verifies(connection);
 
-    _writer_new_connection(connection, TEST_SCRATCH_DIR, &testfile);
-
-    reread = _connection_from_file(testfile, NULL, TYPE_INFINIBAND, NULL);
-
-    nmtst_assert_connection_equals(connection, TRUE, reread, FALSE);
+    _writer_new_connection_reread(connection,
+                                  TEST_SCRATCH_DIR,
+                                  &testfile,
+                                  NO_EXPECTED,
+                                  &reread,
+                                  &reread_same);
+    _assert_reread_same(connection, reread);
+    g_assert(reread_same);
 
     g_assert_cmpstr(interface_name, ==, nm_connection_get_interface_name(reread));
+    g_assert_cmpint(nm_setting_infiniband_get_p_key(
+                        _nm_connection_get_setting(reread, NM_TYPE_SETTING_INFINIBAND)),
+                    ==,
+                    p_key);
 }
 
 static void
@@ -10672,7 +10711,9 @@ main(int argc, char **argv)
 
     g_test_add_func(TPATH "wifi/read/wep-no-keys", test_read_wifi_wep_no_keys);
     g_test_add_func(TPATH "wifi/read/wep-agent-keys", test_read_wifi_wep_agent_keys);
-    g_test_add_func(TPATH "infiniband/read", test_read_infiniband);
+    g_test_add_data_func(TPATH "infiniband/read/0", GUINT_TO_POINTER(0), test_read_infiniband);
+    g_test_add_data_func(TPATH "infiniband/read/1", GUINT_TO_POINTER(1), test_read_infiniband);
+    g_test_add_data_func(TPATH "infiniband/read/2", GUINT_TO_POINTER(2), test_read_infiniband);
     g_test_add_func(TPATH "ipoib/read", test_read_ipoib);
     g_test_add_func(TPATH "vlan/read", test_read_vlan_interface);
     g_test_add_func(TPATH "vlan/read-flags-1", test_read_vlan_flags_1);
diff --git a/src/libnm-base/nm-base.h b/src/libnm-base/nm-base.h
index 77d2ef0a..440fbc90 100644
--- a/src/libnm-base/nm-base.h
+++ b/src/libnm-base/nm-base.h
@@ -392,6 +392,7 @@ typedef struct {
 /****************************************************************************/
 
 #define NM_BOND_PORT_QUEUE_ID_DEF 0
+#define NM_BOND_PORT_PRIO_DEF     0
 
 /****************************************************************************/
 
diff --git a/src/libnm-client-impl/libnm.ver b/src/libnm-client-impl/libnm.ver
index 3c200fb3..3fbd701d 100644
--- a/src/libnm-client-impl/libnm.ver
+++ b/src/libnm-client-impl/libnm.ver
@@ -1879,6 +1879,11 @@ global:
 	nm_vpn_plugin_info_supports_multiple;
 } libnm_1_40_0;
 
+libnm_1_40_20_bondp {
+global:
+	nm_setting_bond_port_get_prio;
+} libnm_1_40_0;
+
 libnm_1_42_0 {
 global:
 	nm_client_get_version_info;
diff --git a/src/libnm-client-impl/tests/test-gir.py b/src/libnm-client-impl/tests/test-gir.py
index d91849b8..9b42431c 100755
--- a/src/libnm-client-impl/tests/test-gir.py
+++ b/src/libnm-client-impl/tests/test-gir.py
@@ -7,6 +7,7 @@
 from __future__ import print_function
 import xml.etree.ElementTree as ET
 import argparse
+import re
 import sys
 
 C_NS = "http://www.gtk.org/introspection/c/1.0"
@@ -60,17 +61,6 @@ def str_removesuffix(string, suffix):
             return string
 
 
-# Older Python doesn't have str.removeprefix()
-def str_removeprefix(string, prefix):
-    try:
-        return string.removeprefix(prefix)
-    except AttributeError:
-        if string.startswith(prefix):
-            return string[len(prefix) :]
-        else:
-            return string
-
-
 def syms_from_ver(verfile):
     c_syms = {}
     for line in open(verfile).readlines():
@@ -78,8 +68,10 @@ def syms_from_ver(verfile):
 
         if line.endswith("{"):
             line = str_removesuffix(line, " {")
-            line = str_removeprefix(line, "libnm_")
-            (major, minor, micro) = line.split("_")
+            m = re.search(r"^libnm_([0-9]+)_([0-9]+)_([0-9]+)$", line)
+            if not m:
+                continue
+            (major, minor, micro) = m.groups()
             if int(major) > 1 or int(minor) > 0:
                 if int(micro) > 0:
                     # Snap to next major version. Perhaps not
@@ -97,8 +89,10 @@ def syms_from_ver(verfile):
         ):
             c_syms[str_removesuffix(line, ";")] = version
 
-    # This one is... messy.
+    # These are exceptions and we cannot know the version for the symbol so we
+    # hardcode it.
     c_syms["nm_ethtool_optname_is_feature"] = "1.20"
+    c_syms["nm_setting_bond_port_get_prio"] = "1.44"
 
     return c_syms
 
diff --git a/src/libnm-core-impl/gen-metadata-nm-settings-libnm-core.xml.in b/src/libnm-core-impl/gen-metadata-nm-settings-libnm-core.xml.in
index a40b8695..4a6e70fe 100644
--- a/src/libnm-core-impl/gen-metadata-nm-settings-libnm-core.xml.in
+++ b/src/libnm-core-impl/gen-metadata-nm-settings-libnm-core.xml.in
@@ -582,6 +582,10 @@
     <setting name="bond-port"
              gtype="NMSettingBondPort"
              >
+        <property name="prio"
+                  dbus-type="i"
+                  gprop-type="gint"
+                  />
         <property name="queue-id"
                   dbus-type="u"
                   gprop-type="guint"
diff --git a/src/libnm-core-impl/nm-connection.c b/src/libnm-core-impl/nm-connection.c
index 3a9eda0e..b017a6e5 100644
--- a/src/libnm-core-impl/nm-connection.c
+++ b/src/libnm-core-impl/nm-connection.c
@@ -1332,18 +1332,41 @@ _normalize_ip_config(NMConnection *self, GHashTable *parameters)
 }
 
 static gboolean
-_normalize_infiniband_mtu(NMConnection *self)
+_normalize_infiniband(NMConnection *self)
 {
     NMSettingInfiniband *s_infini = nm_connection_get_setting_infiniband(self);
+    gboolean             changed  = FALSE;
+    const char          *interface_name;
+    int                  p_key;
 
-    if (!s_infini || nm_setting_infiniband_get_mtu(s_infini) <= NM_INFINIBAND_MAX_MTU
-        || !NM_IN_STRSET(nm_setting_infiniband_get_transport_mode(s_infini),
-                         "datagram",
-                         "connected"))
+    if (!s_infini)
         return FALSE;
 
-    g_object_set(s_infini, NM_SETTING_INFINIBAND_MTU, (guint) NM_INFINIBAND_MAX_MTU, NULL);
-    return TRUE;
+    if (nm_setting_infiniband_get_mtu(s_infini) > NM_INFINIBAND_MAX_MTU) {
+        if (NM_IN_STRSET(nm_setting_infiniband_get_transport_mode(s_infini),
+                         "datagram",
+                         "connected")) {
+            g_object_set(s_infini, NM_SETTING_INFINIBAND_MTU, (guint) NM_INFINIBAND_MAX_MTU, NULL);
+            changed = TRUE;
+        }
+    }
+
+    if ((p_key = nm_setting_infiniband_get_p_key(s_infini)) != -1
+        && (interface_name = nm_connection_get_interface_name(self))) {
+        const char *virtual_iface_name;
+
+        virtual_iface_name = nm_setting_infiniband_get_virtual_interface_name(s_infini);
+
+        if (!nm_streq0(interface_name, virtual_iface_name)) {
+            g_object_set(nm_connection_get_setting_connection(self),
+                         NM_SETTING_CONNECTION_INTERFACE_NAME,
+                         virtual_iface_name,
+                         NULL);
+            changed = TRUE;
+        }
+    }
+
+    return changed;
 }
 
 static gboolean
@@ -1986,7 +2009,7 @@ _connection_normalize(NMConnection *connection,
     was_modified |= _normalize_invalid_slave_port_settings(connection);
     was_modified |= _normalize_ip_config(connection, parameters);
     was_modified |= _normalize_ethernet_link_neg(connection);
-    was_modified |= _normalize_infiniband_mtu(connection);
+    was_modified |= _normalize_infiniband(connection);
     was_modified |= _normalize_bond_mode(connection);
     was_modified |= _normalize_bond_options(connection);
     was_modified |= _normalize_wireless_mac_address_randomization(connection);
diff --git a/src/libnm-core-impl/nm-setting-bond-port.c b/src/libnm-core-impl/nm-setting-bond-port.c
index 7ea82a76..16512a6a 100644
--- a/src/libnm-core-impl/nm-setting-bond-port.c
+++ b/src/libnm-core-impl/nm-setting-bond-port.c
@@ -22,9 +22,10 @@
 
 /*****************************************************************************/
 
-NM_GOBJECT_PROPERTIES_DEFINE(NMSettingBondPort, PROP_QUEUE_ID, );
+NM_GOBJECT_PROPERTIES_DEFINE(NMSettingBondPort, PROP_QUEUE_ID, PROP_PRIO, );
 
 typedef struct {
+    gint32  prio;
     guint32 queue_id;
 } NMSettingBondPortPrivate;
 
@@ -65,6 +66,22 @@ nm_setting_bond_port_get_queue_id(NMSettingBondPort *setting)
     return NM_SETTING_BOND_PORT_GET_PRIVATE(setting)->queue_id;
 }
 
+/**
+ * nm_setting_bond_port_get_prio:
+ * @setting: the #NMSettingBondPort
+ *
+ * Returns: the #NMSettingBondPort:prio property of the setting
+ *
+ * Since: 1.44, 1.42.8
+ **/
+gint32
+nm_setting_bond_port_get_prio(NMSettingBondPort *setting)
+{
+    g_return_val_if_fail(NM_IS_SETTING_BOND_PORT(setting), 0);
+
+    return NM_SETTING_BOND_PORT_GET_PRIVATE(setting)->prio;
+}
+
 /*****************************************************************************/
 
 static gboolean
@@ -148,7 +165,7 @@ nm_setting_bond_port_class_init(NMSettingBondPortClass *klass)
      **/
     /* ---ifcfg-rh---
      * property: queue-id
-     * variable: BONDING_OPTS: queue-id=
+     * variable: BOND_PORT_QUEUE_ID(+)
      * values: 0 - 65535
      * default: 0
      * description: Queue ID.
@@ -165,6 +182,35 @@ nm_setting_bond_port_class_init(NMSettingBondPortClass *klass)
                                               NMSettingBondPort,
                                               _priv.queue_id);
 
+    /**
+     * NMSettingBondPort:prio:
+     *
+     * The port priority for bond active port re-selection during failover. A
+     * higher number means a higher priority in selection. The primary port has
+     * the highest priority. This option is only compatible with active-backup,
+     * balance-tlb and balance-alb modes.
+     *
+     * Since: 1.44, 1.42.8
+     **/
+    /* ---ifcfg-rh---
+     * property: prio
+     * variable: BOND_PORT_PRIO(+)
+     * values: -2147483648 - 2147483647
+     * default: 0
+     * description: Port priority.
+     * ---end---
+     */
+    _nm_setting_property_define_direct_int32(properties_override,
+                                             obj_properties,
+                                             NM_SETTING_BOND_PORT_PRIO,
+                                             PROP_PRIO,
+                                             G_MININT32,
+                                             G_MAXINT32,
+                                             NM_BOND_PORT_PRIO_DEF,
+                                             NM_SETTING_PARAM_INFERRABLE,
+                                             NMSettingBondPort,
+                                             _priv.prio);
+
     g_object_class_install_properties(object_class, _PROPERTY_ENUMS_LAST, obj_properties);
 
     _nm_setting_class_commit(setting_class,
diff --git a/src/libnm-core-impl/nm-setting-infiniband.c b/src/libnm-core-impl/nm-setting-infiniband.c
index 410f1f06..a3bda692 100644
--- a/src/libnm-core-impl/nm-setting-infiniband.c
+++ b/src/libnm-core-impl/nm-setting-infiniband.c
@@ -10,6 +10,7 @@
 #include <stdlib.h>
 #include <linux/if_infiniband.h>
 
+#include "libnm-platform/nmp-base.h"
 #include "nm-utils.h"
 #include "nm-utils-private.h"
 #include "nm-setting-private.h"
@@ -144,6 +145,17 @@ nm_setting_infiniband_get_parent(NMSettingInfiniband *setting)
     return NM_SETTING_INFINIBAND_GET_PRIVATE(setting)->parent;
 }
 
+char *
+nm_setting_infiniband_create_virtual_interface_name(const char *parent, int p_key)
+{
+    char *s;
+
+    s = g_strdup_printf("%s.%04x", parent, (guint) p_key);
+    if (strlen(s) >= NMP_IFNAMSIZ)
+        s[NMP_IFNAMSIZ - 1] = '\0';
+    return s;
+}
+
 /**
  * nm_setting_infiniband_get_virtual_interface_name:
  * @setting: the #NMSettingInfiniband
@@ -172,7 +184,8 @@ nm_setting_infiniband_get_virtual_interface_name(NMSettingInfiniband *setting)
         priv->virtual_iface_name_p_key         = priv->p_key;
         priv->virtual_iface_name_parent_length = len;
         g_free(priv->virtual_iface_name);
-        priv->virtual_iface_name = g_strdup_printf("%s.%04x", priv->parent, priv->p_key);
+        priv->virtual_iface_name =
+            nm_setting_infiniband_create_virtual_interface_name(priv->parent, priv->p_key);
     }
 
     return priv->virtual_iface_name;
@@ -181,8 +194,8 @@ nm_setting_infiniband_get_virtual_interface_name(NMSettingInfiniband *setting)
 static gboolean
 verify(NMSetting *setting, NMConnection *connection, GError **error)
 {
-    NMSettingConnection        *s_con = NULL;
-    NMSettingInfinibandPrivate *priv  = NM_SETTING_INFINIBAND_GET_PRIVATE(setting);
+    NMSettingConnection        *s_con;
+    NMSettingInfinibandPrivate *priv = NM_SETTING_INFINIBAND_GET_PRIVATE(setting);
 
     if (priv->mac_address && !nm_utils_hwaddr_valid(priv->mac_address, INFINIBAND_ALEN)) {
         g_set_error_literal(error,
@@ -251,8 +264,10 @@ verify(NMSetting *setting, NMConnection *connection, GError **error)
         }
     }
 
-    if (connection)
-        s_con = nm_connection_get_setting_connection(connection);
+    /* *** errors above here should be always fatal, below NORMALIZABLE_ERROR *** */
+
+    s_con = connection ? nm_connection_get_setting_connection(connection) : NULL;
+
     if (s_con) {
         const char *interface_name = nm_setting_connection_get_interface_name(s_con);
 
@@ -287,13 +302,11 @@ verify(NMSetting *setting, NMConnection *connection, GError **error)
                                "%s.%s: ",
                                NM_SETTING_CONNECTION_SETTING_NAME,
                                NM_SETTING_CONNECTION_INTERFACE_NAME);
-                return FALSE;
+                return NM_SETTING_VERIFY_NORMALIZABLE_ERROR;
             }
         }
     }
 
-    /* *** errors above here should be always fatal, below NORMALIZABLE_ERROR *** */
-
     if (priv->mtu > NM_INFINIBAND_MAX_MTU) {
         /* Traditionally, MTU for "datagram" mode was limited to 2044
          * and for "connected" mode it was 65520.
@@ -448,18 +461,31 @@ nm_setting_infiniband_class_init(NMSettingInfinibandClass *klass)
     /**
      * NMSettingInfiniband:p-key:
      *
-     * The InfiniBand P_Key to use for this device. A value of -1 means to use
-     * the default P_Key (aka "the P_Key at index 0"). Otherwise, it is a 16-bit
-     * unsigned integer, whose high bit is set if it is a "full membership"
-     * P_Key.
+     * The InfiniBand p-key to use for this device. A value of -1 means to use
+     * the default p-key (aka "the p-key at index 0"). Otherwise, it is a
+     * 16-bit unsigned integer, whose high bit 0x8000 is set if it is a "full
+     * membership" p-key. The values 0 and 0x8000 are not allowed.
+     *
+     * With the p-key set, the interface name is always "$parent.$p_key".
+     * Setting "connection.interface-name" to another name is not supported.
+     *
+     * Note that kernel will internally always set the full membership bit,
+     * although the interface name does not reflect that. Usually the user
+     * would want to configure a full membership p-key with 0x8000 flag set.
      **/
     /* ---ifcfg-rh---
      * property: p-key
-     * variable: PKEY_ID (and PKEY=yes)
+     * variable: PKEY_ID or PKEY_ID_NM(*) (requires PKEY=yes)
      * default: PKEY=no
      * description: InfiniBand P_Key. The value can be a hex number prefixed with "0x"
      *   or a decimal number.
-     *   When PKEY_ID is specified, PHYSDEV and DEVICE also must be specified.
+     *   When PKEY_ID is specified, PHYSDEV must be specified.
+     *   Note that ifcfg-rh format will always automatically set the full membership
+     *   flag 0x8000 for the PKEY_ID variable. To express IDs without the full membership
+     *   flag, use PKEY_ID_NM. Note that kernel internally treats the interface as
+     *   having the full membership flag set, this mainly affects the interface name.
+     *   For the ifcfg file to be supported by initscripts' ifup-ib, the DEVICE=
+     *   must always be set. NetworkManager does not require that.
      * example: PKEY=yes PKEY_ID=2 PHYSDEV=mlx4_ib0 DEVICE=mlx4_ib0.8002
      * ---end---
      */
diff --git a/src/libnm-core-impl/tests/test-general.c b/src/libnm-core-impl/tests/test-general.c
index 8a98265a..87f7b042 100644
--- a/src/libnm-core-impl/tests/test-general.c
+++ b/src/libnm-core-impl/tests/test-general.c
@@ -6156,16 +6156,17 @@ test_connection_normalize_slave_type_2(void)
 }
 
 static void
-test_connection_normalize_infiniband_mtu(void)
+test_connection_normalize_infiniband(void)
 {
     gs_unref_object NMConnection *con = NULL;
     NMSettingInfiniband          *s_infini;
+    NMSettingConnection          *s_con;
     guint                         mtu_regular = nmtst_rand_select(2044, 2045, 65520);
 
-    con = nmtst_create_minimal_connection("test_connection_normalize_infiniband_mtu",
+    con = nmtst_create_minimal_connection("test_connection_normalize_infiniband",
                                           NULL,
                                           NM_SETTING_INFINIBAND_SETTING_NAME,
-                                          NULL);
+                                          &s_con);
 
     s_infini = nm_connection_get_setting_infiniband(con);
     g_object_set(s_infini, NM_SETTING_INFINIBAND_TRANSPORT_MODE, "connected", NULL);
@@ -6213,6 +6214,57 @@ test_connection_normalize_infiniband_mtu(void)
                                                          NM_CONNECTION_ERROR_INVALID_PROPERTY);
     nmtst_connection_normalize(con);
     g_assert_cmpint(65520, ==, nm_setting_infiniband_get_mtu(s_infini));
+
+    g_object_set(s_infini,
+                 NM_SETTING_INFINIBAND_PARENT,
+                 "foo",
+                 NM_SETTING_INFINIBAND_P_KEY,
+                 0x005c,
+                 NULL);
+    nmtst_assert_connection_verifies_without_normalization(con);
+
+    g_object_set(s_con, NM_SETTING_CONNECTION_INTERFACE_NAME, "foo.005c", NULL);
+    nmtst_assert_connection_verifies_without_normalization(con);
+
+    g_object_set(s_con, NM_SETTING_CONNECTION_INTERFACE_NAME, "foo", NULL);
+    nmtst_assert_connection_verifies_after_normalization(con,
+                                                         NM_CONNECTION_ERROR,
+                                                         NM_CONNECTION_ERROR_INVALID_PROPERTY);
+
+    nmtst_connection_normalize(con);
+    g_assert_cmpstr(nm_connection_get_interface_name(con), ==, "foo.005c");
+
+    g_object_set(s_infini,
+                 NM_SETTING_INFINIBAND_PARENT,
+                 "x234567890123",
+                 NM_SETTING_INFINIBAND_P_KEY,
+                 0x005c,
+                 NULL);
+    nmtst_assert_connection_verifies_after_normalization(con,
+                                                         NM_CONNECTION_ERROR,
+                                                         NM_CONNECTION_ERROR_INVALID_PROPERTY);
+
+    nmtst_connection_normalize(con);
+    g_assert_cmpstr(nm_connection_get_interface_name(con), ==, "x234567890123.0");
+
+#define iface_name(parent, p_key, expected)                                                        \
+    G_STMT_START                                                                                   \
+    {                                                                                              \
+        gs_free char *_s = nm_setting_infiniband_create_virtual_interface_name((parent), (p_key)); \
+                                                                                                   \
+        g_assert(nm_utils_ifname_valid_kernel(_s, NULL));                                          \
+        g_assert_cmpstr(_s, ==, (expected));                                                       \
+    }                                                                                              \
+    G_STMT_END
+
+    iface_name("foo", 15, "foo.000f");
+    iface_name("x23456789012345", 15, "x23456789012345");
+    iface_name("x2345678901234", 15, "x2345678901234.");
+    iface_name("x234567890123", 15, "x234567890123.0");
+    iface_name("x23456789012", 15, "x23456789012.00");
+    iface_name("x2345678901", 15, "x2345678901.000");
+    iface_name("x234567890", 15, "x234567890.000f");
+    iface_name("x23456789", 15, "x23456789.000f");
 }
 
 static void
@@ -11511,8 +11563,8 @@ main(int argc, char **argv)
                     test_connection_normalize_slave_type_1);
     g_test_add_func("/core/general/test_connection_normalize_slave_type_2",
                     test_connection_normalize_slave_type_2);
-    g_test_add_func("/core/general/test_connection_normalize_infiniband_mtu",
-                    test_connection_normalize_infiniband_mtu);
+    g_test_add_func("/core/general/test_connection_normalize_infiniband",
+                    test_connection_normalize_infiniband);
     g_test_add_func("/core/general/test_connection_normalize_gateway_never_default",
                     test_connection_normalize_gateway_never_default);
     g_test_add_func("/core/general/test_connection_normalize_may_fail",
diff --git a/src/libnm-core-intern/nm-core-internal.h b/src/libnm-core-intern/nm-core-internal.h
index 9e9d55aa..ce27e8e0 100644
--- a/src/libnm-core-intern/nm-core-internal.h
+++ b/src/libnm-core-intern/nm-core-internal.h
@@ -322,6 +322,8 @@ typedef gpointer (*NMUtilsCopyFunc)(gpointer);
 const char **
 _nm_ip_address_get_attribute_names(const NMIPAddress *addr, gboolean sorted, guint *out_length);
 
+char *nm_setting_infiniband_create_virtual_interface_name(const char *parent, int p_key);
+
 #define NM_SETTING_WIRED_S390_OPTION_MAX_LEN 200u
 
 void     _nm_setting_wired_clear_s390_options(NMSettingWired *setting);
diff --git a/src/libnm-core-public/nm-setting-bond-port.h b/src/libnm-core-public/nm-setting-bond-port.h
index 0b20e4a8..29b0beb0 100644
--- a/src/libnm-core-public/nm-setting-bond-port.h
+++ b/src/libnm-core-public/nm-setting-bond-port.h
@@ -29,6 +29,7 @@ G_BEGIN_DECLS
 #define NM_SETTING_BOND_PORT_SETTING_NAME "bond-port"
 
 #define NM_SETTING_BOND_PORT_QUEUE_ID "queue-id"
+#define NM_SETTING_BOND_PORT_PRIO     "prio"
 
 typedef struct _NMSettingBondPortClass NMSettingBondPortClass;
 
@@ -41,6 +42,9 @@ NMSetting *nm_setting_bond_port_new(void);
 NM_AVAILABLE_IN_1_34
 guint32 nm_setting_bond_port_get_queue_id(NMSettingBondPort *setting);
 
+NM_AVAILABLE_IN_1_42_8
+gint32 nm_setting_bond_port_get_prio(NMSettingBondPort *setting);
+
 G_END_DECLS
 
 #endif /* __NM_SETTING_BOND_PORT_H__ */
diff --git a/src/libnm-core-public/nm-version-macros.h b/src/libnm-core-public/nm-version-macros.h
index 80e7f251..40c89169 100644
--- a/src/libnm-core-public/nm-version-macros.h
+++ b/src/libnm-core-public/nm-version-macros.h
@@ -30,7 +30,7 @@
  * Evaluates to the micro version number of NetworkManager which this source
  * compiled against.
  */
-#define NM_MICRO_VERSION (6)
+#define NM_MICRO_VERSION (8)
 
 /**
  * NM_CHECK_VERSION:
@@ -73,6 +73,7 @@
 #define NM_VERSION_1_40   (NM_ENCODE_VERSION(1, 40, 0))
 #define NM_VERSION_1_42   (NM_ENCODE_VERSION(1, 42, 0))
 #define NM_VERSION_1_42_2 (NM_ENCODE_VERSION(1, 42, 2))
+#define NM_VERSION_1_42_8 (NM_ENCODE_VERSION(1, 42, 8))
 
 /* For releases, NM_API_VERSION is equal to NM_VERSION.
  *
diff --git a/src/libnm-core-public/nm-version-macros.h.in b/src/libnm-core-public/nm-version-macros.h.in
index 5cdba608..711b9e19 100644
--- a/src/libnm-core-public/nm-version-macros.h.in
+++ b/src/libnm-core-public/nm-version-macros.h.in
@@ -73,6 +73,7 @@
 #define NM_VERSION_1_40   (NM_ENCODE_VERSION(1, 40, 0))
 #define NM_VERSION_1_42   (NM_ENCODE_VERSION(1, 42, 0))
 #define NM_VERSION_1_42_2 (NM_ENCODE_VERSION(1, 42, 2))
+#define NM_VERSION_1_42_8 (NM_ENCODE_VERSION(1, 42, 8))
 
 /* For releases, NM_API_VERSION is equal to NM_VERSION.
  *
diff --git a/src/libnm-core-public/nm-version.h b/src/libnm-core-public/nm-version.h
index 249084ae..b963671c 100644
--- a/src/libnm-core-public/nm-version.h
+++ b/src/libnm-core-public/nm-version.h
@@ -361,6 +361,12 @@
 #define NM_AVAILABLE_IN_1_42_2
 #endif
 
+#if NM_VERSION_MAX_ALLOWED < NM_VERSION_1_42_8
+#define NM_AVAILABLE_IN_1_42_8 G_UNAVAILABLE(1, 42.8)
+#else
+#define NM_AVAILABLE_IN_1_42_8
+#endif
+
 /*
  * Synchronous API for calling D-Bus in libnm is deprecated. See
  * https://networkmanager.dev/docs/libnm/latest/usage.html#sync-api
diff --git a/src/libnm-glib-aux/nm-shared-utils.h b/src/libnm-glib-aux/nm-shared-utils.h
index 083ed137..d2e30504 100644
--- a/src/libnm-glib-aux/nm-shared-utils.h
+++ b/src/libnm-glib-aux/nm-shared-utils.h
@@ -93,6 +93,14 @@ G_STATIC_ASSERT(sizeof(int) == sizeof(gint32));
 
 /*****************************************************************************/
 
+typedef enum _nm_packed {
+    /* No type, empty value */
+    NM_PORT_KIND_NONE,
+    NM_PORT_KIND_BOND,
+} NMPortKind;
+
+/*****************************************************************************/
+
 typedef enum {
 
     /* No type, used as error value */
diff --git a/src/libnm-platform/nm-linux-platform.c b/src/libnm-platform/nm-linux-platform.c
index 049095b2..484087b9 100644
--- a/src/libnm-platform/nm-linux-platform.c
+++ b/src/libnm-platform/nm-linux-platform.c
@@ -177,6 +177,8 @@ G_STATIC_ASSERT(RTA_MAX == (__RTA_MAX - 1));
 
 /*****************************************************************************/
 
+#define IFLA_BOND_SLAVE_PRIO 9
+
 #define IFLA_BOND_PEER_NOTIF_DELAY 28
 
 #undef IFLA_BOND_MAX
@@ -3347,9 +3349,11 @@ _new_from_nl_link(NMPlatform            *platform,
 
     if (tb[IFLA_LINKINFO]) {
         static const struct nla_policy policy_link_info[] = {
-            [IFLA_INFO_KIND]   = {.type = NLA_STRING},
-            [IFLA_INFO_DATA]   = {.type = NLA_NESTED},
-            [IFLA_INFO_XSTATS] = {.type = NLA_NESTED},
+            [IFLA_INFO_KIND]       = {.type = NLA_STRING},
+            [IFLA_INFO_DATA]       = {.type = NLA_NESTED},
+            [IFLA_INFO_XSTATS]     = {.type = NLA_NESTED},
+            [IFLA_INFO_SLAVE_KIND] = {.type = NLA_STRING},
+            [IFLA_INFO_SLAVE_DATA] = {.type = NLA_NESTED},
         };
         struct nlattr *li[G_N_ELEMENTS(policy_link_info)];
 
@@ -3360,6 +3364,49 @@ _new_from_nl_link(NMPlatform            *platform,
             nl_info_kind = nla_get_string(li[IFLA_INFO_KIND]);
 
         nl_info_data = li[IFLA_INFO_DATA];
+
+        if (li[IFLA_INFO_SLAVE_KIND]) {
+            const char *s = nla_get_string(li[IFLA_INFO_SLAVE_KIND]);
+
+            if (nm_streq(s, "bond"))
+                obj->link.port_kind = NM_PORT_KIND_BOND;
+        }
+
+        if (li[IFLA_INFO_SLAVE_DATA]) {
+            static const struct nla_policy policy_bond_port[] = {
+                [IFLA_BOND_SLAVE_QUEUE_ID] = {.type = NLA_U16},
+                [IFLA_BOND_SLAVE_PRIO]     = {.type = NLA_S32},
+            };
+            struct nlattr *bp[G_N_ELEMENTS(policy_bond_port)];
+
+            switch (obj->link.port_kind) {
+            case NM_PORT_KIND_BOND:
+                if (nla_parse_nested_arr(bp, li[IFLA_INFO_SLAVE_DATA], policy_bond_port) < 0)
+                    return NULL;
+
+                if (bp[IFLA_BOND_SLAVE_QUEUE_ID])
+                    obj->link.port_data.bond.queue_id = nla_get_u16(bp[IFLA_BOND_SLAVE_QUEUE_ID]);
+
+                if (bp[IFLA_BOND_SLAVE_PRIO]) {
+                    obj->link.port_data.bond.prio     = nla_get_s32(bp[IFLA_BOND_SLAVE_PRIO]);
+                    obj->link.port_data.bond.prio_has = TRUE;
+                    if (!_nm_platform_kernel_support_detected(
+                            NM_PLATFORM_KERNEL_SUPPORT_TYPE_IFLA_BOND_SLAVE_PRIO)) {
+                        /* support for IFLA_BOND_SLAVE_PRIO was added in 0a2ff7cc8ad48a86939a91bd3457f38e59e741a1,
+                         * kernel 6.0, 2 October 2022.
+                         *
+                         * We can only detect support if the attribute is present. A missing attribute
+                         * is not conclusive. */
+                        _nm_platform_kernel_support_init(
+                            NM_PLATFORM_KERNEL_SUPPORT_TYPE_IFLA_BOND_SLAVE_PRIO,
+                            1);
+                    }
+                }
+                break;
+            case NM_PORT_KIND_NONE:
+                break;
+            }
+        }
     }
 
     if (tb[IFLA_STATS64]) {
@@ -8277,7 +8324,7 @@ out:
 }
 
 static int
-link_change(NMPlatform *platform, NMLinkType type, int ifindex, gconstpointer extra_data)
+link_change_extra(NMPlatform *platform, NMLinkType type, int ifindex, gconstpointer extra_data)
 {
     nm_auto_nlmsg struct nl_msg *nlmsg = NULL;
 
@@ -8355,6 +8402,51 @@ link_delete(NMPlatform *platform, int ifindex)
 }
 
 static gboolean
+link_change(NMPlatform                   *platform,
+            int                           ifindex,
+            NMPortKind                    port_kind,
+            const NMPlatformLinkPortData *port_data)
+{
+    nm_auto_nlmsg struct nl_msg *nlmsg = NULL;
+    struct nlattr               *nl_info;
+    struct nlattr               *nl_port_data;
+
+    nlmsg = _nl_msg_new_link(RTM_NEWLINK, 0, ifindex, NULL);
+    if (!nlmsg)
+        return FALSE;
+
+    switch (port_kind) {
+    case NM_PORT_KIND_BOND:
+
+        nm_assert(port_data);
+
+        if (!(nl_info = nla_nest_start(nlmsg, IFLA_LINKINFO)))
+            goto nla_put_failure;
+
+        nm_assert(nm_streq0("bond", nm_link_type_to_rtnl_type_string(NM_LINK_TYPE_BOND)));
+        NLA_PUT_STRING(nlmsg, IFLA_INFO_SLAVE_KIND, "bond");
+
+        if (!(nl_port_data = nla_nest_start(nlmsg, IFLA_INFO_SLAVE_DATA)))
+            goto nla_put_failure;
+
+        NLA_PUT_U16(nlmsg, IFLA_BOND_SLAVE_QUEUE_ID, port_data->bond.queue_id);
+
+        if (port_data->bond.prio_has)
+            NLA_PUT_S32(nlmsg, IFLA_BOND_SLAVE_PRIO, port_data->bond.prio);
+
+        nla_nest_end(nlmsg, nl_port_data);
+        nla_nest_end(nlmsg, nl_info);
+        break;
+    case NM_PORT_KIND_NONE:
+        break;
+    }
+
+    return do_change_link(platform, CHANGE_LINK_TYPE_UNSPEC, ifindex, nlmsg, NULL) == 0;
+nla_put_failure:
+    g_return_val_if_reached(FALSE);
+}
+
+static gboolean
 link_refresh(NMPlatform *platform, int ifindex)
 {
     do_request_link(platform, ifindex, NULL);
@@ -11121,9 +11213,11 @@ nm_linux_platform_class_init(NMLinuxPlatformClass *klass)
     platform_class->sysctl_set_async = sysctl_set_async;
     platform_class->sysctl_get       = sysctl_get;
 
-    platform_class->link_add    = link_add;
+    platform_class->link_add          = link_add;
+    platform_class->link_change_extra = link_change_extra;
+    platform_class->link_delete       = link_delete;
+
     platform_class->link_change = link_change;
-    platform_class->link_delete = link_delete;
 
     platform_class->link_refresh = link_refresh;
 
diff --git a/src/libnm-platform/nm-platform.c b/src/libnm-platform/nm-platform.c
index dc643ed2..1c495d60 100644
--- a/src/libnm-platform/nm-platform.c
+++ b/src/libnm-platform/nm-platform.c
@@ -62,6 +62,41 @@ G_STATIC_ASSERT(sizeof(((NMPlatformLink *) NULL)->l_perm_address.data) == _NM_UT
 G_STATIC_ASSERT(sizeof(((NMPlatformLink *) NULL)->l_broadcast.data) == _NM_UTILS_HWADDR_LEN_MAX);
 
 static const char *
+_nmp_link_port_data_to_string(NMPortKind                    port_kind,
+                              const NMPlatformLinkPortData *port_data,
+                              char                         *sbuf,
+                              gsize                         sbuf_len)
+{
+    const char *sbuf0 = sbuf;
+    char        s0[120];
+
+    nm_assert(port_data);
+
+    switch (port_kind) {
+    case NM_PORT_KIND_NONE:
+        nm_strbuf_append_c(&sbuf, &sbuf_len, '\0');
+        goto out;
+    case NM_PORT_KIND_BOND:
+        nm_strbuf_append(&sbuf,
+                         &sbuf_len,
+                         "port bond queue-id %u%s",
+                         port_data->bond.queue_id,
+                         port_data->bond.prio_has || port_data->bond.prio != 0
+                             ? nm_sprintf_buf(s0,
+                                              " prio%s %u",
+                                              port_data->bond.prio_has ? "" : "?",
+                                              port_data->bond.prio)
+                             : "");
+        goto out;
+    }
+
+    nm_strbuf_append(&sbuf, &sbuf_len, "invalid-port-type %d", (int) port_kind);
+
+out:
+    return sbuf0;
+}
+
+static const char *
 _nmp_link_address_to_string(const NMPLinkAddress *addr,
                             char                  buf[static(_NM_UTILS_HWADDR_LEN_MAX * 3)])
 {
@@ -1406,7 +1441,10 @@ nm_platform_link_add(NMPlatform            *self,
 }
 
 int
-nm_platform_link_change(NMPlatform *self, NMLinkType type, int ifindex, gconstpointer extra_data)
+nm_platform_link_change_extra(NMPlatform   *self,
+                              NMLinkType    type,
+                              int           ifindex,
+                              gconstpointer extra_data)
 {
     char        buf[512];
     const char *name = nm_platform_link_get_name(self, ifindex);
@@ -1447,7 +1485,7 @@ nm_platform_link_change(NMPlatform *self, NMLinkType type, int ifindex, gconstpo
                buf;
            }));
 
-    return klass->link_change(self, type, ifindex, extra_data);
+    return klass->link_change_extra(self, type, ifindex, extra_data);
 }
 
 /**
@@ -2107,6 +2145,43 @@ nm_platform_link_set_name(NMPlatform *self, int ifindex, const char *name)
     return klass->link_set_name(self, ifindex, name);
 }
 
+gboolean
+nm_platform_link_change(NMPlatform *self, int ifindex, NMPlatformLinkBondPort *bond_port)
+{
+    char sbuf_prio[100];
+
+    _CHECK_SELF(self, klass, FALSE);
+
+    g_return_val_if_fail(ifindex >= 0, FALSE);
+
+    if (_LOGD_ENABLED()) {
+        nm_auto_free_gstring GString *str = g_string_new("");
+
+        if (bond_port) {
+            nm_assert(bond_port->prio_has || bond_port->prio == 0);
+            g_string_append_printf(str,
+                                   "bond-port queue-id %d %s",
+                                   bond_port->queue_id,
+                                   bond_port->prio_has || bond_port->prio != 0
+                                       ? nm_sprintf_buf(sbuf_prio,
+                                                        "prio%s %" G_GINT32_FORMAT,
+                                                        !bond_port->prio_has ? "?" : "",
+                                                        bond_port->prio)
+                                       : "");
+        }
+
+        if (str->len > 0 && str->str[str->len - 1] == ' ')
+            g_string_truncate(str, str->len - 1);
+
+        _LOG3D("link: change: %s", str->str);
+    }
+
+    return klass->link_change(self,
+                              ifindex,
+                              bond_port ? NM_PORT_KIND_BOND : NM_PORT_KIND_NONE,
+                              (const NMPlatformLinkPortData *) bond_port);
+}
+
 /**
  * nm_platform_link_get_physical_port_id:
  * @self: platform instance
@@ -5893,6 +5968,7 @@ nm_platform_link_to_string(const NMPlatformLink *link, char *buf, gsize len)
     char       *s;
     gsize       l;
     char        str_addrmode[30];
+    char        str_port_data[200];
     char        str_address[_NM_UTILS_HWADDR_LEN_MAX * 3];
     char        str_perm_address[_NM_UTILS_HWADDR_LEN_MAX * 3];
     char        str_broadcast[_NM_UTILS_HWADDR_LEN_MAX * 3];
@@ -5936,6 +6012,11 @@ nm_platform_link_to_string(const NMPlatformLink *link, char *buf, gsize len)
     _nmp_link_address_to_string(&link->l_perm_address, str_perm_address);
     _nmp_link_address_to_string(&link->l_broadcast, str_broadcast);
 
+    _nmp_link_port_data_to_string(link->port_kind,
+                                  &link->port_data,
+                                  str_port_data,
+                                  sizeof(str_port_data));
+
     str_link_type = nm_link_type_to_string(link->type);
 
     g_snprintf(
@@ -5957,6 +6038,7 @@ nm_platform_link_to_string(const NMPlatformLink *link, char *buf, gsize len)
         "%s%s"    /* l_broadcast */
         "%s%s"    /* inet6_token */
         "%s%s"    /* driver */
+        "%s%s"    /* port_data */
         " rx:%" G_GUINT64_FORMAT ",%" G_GUINT64_FORMAT " tx:%" G_GUINT64_FORMAT
         ",%" G_GUINT64_FORMAT,
         link->ifindex,
@@ -5989,6 +6071,7 @@ nm_platform_link_to_string(const NMPlatformLink *link, char *buf, gsize len)
             : "",
         link->driver ? " driver " : "",
         link->driver ?: "",
+        NM_PRINT_FMT_QUOTED2(str_port_data[0] != '\0', " ", str_port_data, ""),
         link->rx_packets,
         link->rx_bytes,
         link->tx_packets,
@@ -7821,6 +7904,7 @@ nm_platform_link_hash_update(const NMPlatformLink *obj, NMHashState *h)
                         obj->arptype,
                         obj->inet6_addr_gen_mode_inv,
                         obj->inet6_token,
+                        obj->port_kind,
                         obj->rx_packets,
                         obj->rx_bytes,
                         obj->tx_packets,
@@ -7839,6 +7923,20 @@ nm_platform_link_hash_update(const NMPlatformLink *obj, NMHashState *h)
     nm_hash_update_mem(h,
                        obj->l_broadcast.data,
                        NM_MIN(obj->l_broadcast.len, sizeof(obj->l_broadcast.data)));
+
+    switch (obj->port_kind) {
+    case NM_PORT_KIND_NONE:
+        break;
+    case NM_PORT_KIND_BOND:
+        nm_platform_link_bond_port_hash_update(&obj->port_data.bond, h);
+        break;
+    }
+}
+
+void
+nm_platform_link_bond_port_hash_update(const NMPlatformLinkBondPort *obj, NMHashState *h)
+{
+    nm_hash_update_vals(h, obj->prio, obj->queue_id, NM_HASH_COMBINE_BOOLS(guint8, obj->prio_has));
 }
 
 int
@@ -7868,6 +7966,14 @@ nm_platform_link_cmp(const NMPlatformLink *a, const NMPlatformLink *b)
     if (a->l_broadcast.len)
         NM_CMP_FIELD_MEMCMP_LEN(a, b, l_broadcast.data, a->l_broadcast.len);
     NM_CMP_FIELD_MEMCMP(a, b, inet6_token);
+    NM_CMP_FIELD(a, b, port_kind);
+    switch (a->port_kind) {
+    case NM_PORT_KIND_NONE:
+        break;
+    case NM_PORT_KIND_BOND:
+        NM_CMP_RETURN(nm_platform_link_bond_port_cmp(&a->port_data.bond, &b->port_data.bond));
+        break;
+    }
     NM_CMP_FIELD(a, b, rx_packets);
     NM_CMP_FIELD(a, b, rx_bytes);
     NM_CMP_FIELD(a, b, tx_packets);
@@ -7948,6 +8054,17 @@ nm_platform_lnk_bond_hash_update(const NMPlatformLnkBond *obj, NMHashState *h)
 }
 
 int
+nm_platform_link_bond_port_cmp(const NMPlatformLinkBondPort *a, const NMPlatformLinkBondPort *b)
+{
+    NM_CMP_SELF(a, b);
+    NM_CMP_FIELD(a, b, queue_id);
+    NM_CMP_FIELD(a, b, prio);
+    NM_CMP_FIELD_BOOL(a, b, prio_has);
+
+    return 0;
+}
+
+int
 nm_platform_lnk_bond_cmp(const NMPlatformLnkBond *a, const NMPlatformLnkBond *b)
 {
     NM_CMP_SELF(a, b);
diff --git a/src/libnm-platform/nm-platform.h b/src/libnm-platform/nm-platform.h
index ca6cdff8..d442ad9b 100644
--- a/src/libnm-platform/nm-platform.h
+++ b/src/libnm-platform/nm-platform.h
@@ -154,6 +154,16 @@ struct _NMPlatformObjWithIfindex {
     __NMPlatformObjWithIfindex_COMMON;
 } _nm_alignas(NMPlatformObject);
 
+typedef struct {
+    gint32  prio;
+    guint16 queue_id;
+    bool    prio_has : 1;
+} NMPlatformLinkBondPort;
+
+typedef union {
+    NMPlatformLinkBondPort bond;
+} NMPlatformLinkPortData;
+
 struct _NMPlatformLink {
     __NMPlatformObjWithIfindex_COMMON;
     char       name[NMP_IFNAMSIZ];
@@ -204,6 +214,12 @@ struct _NMPlatformLink {
     guint64 tx_packets;
     guint64 tx_bytes;
 
+    /* an interface can only hold IFLA_INFO_SLAVE_DATA for one link type */
+    NMPlatformLinkPortData port_data;
+
+    /* IFLA_INFO_SLAVE_KIND */
+    NMPortKind port_kind;
+
     /* @connected is mostly identical to (@n_ifi_flags & IFF_UP). Except for bridge/bond masters,
      * where we coerce the link as disconnect if it has no slaves. */
     bool connected : 1;
@@ -990,6 +1006,8 @@ typedef enum {
      * were added at the same time. */
     NM_PLATFORM_KERNEL_SUPPORT_TYPE_FRA_IP_PROTO,
 
+    NM_PLATFORM_KERNEL_SUPPORT_TYPE_IFLA_BOND_SLAVE_PRIO,
+
     _NM_PLATFORM_KERNEL_SUPPORT_NUM,
 } NMPlatformKernelSupportType;
 
@@ -1086,9 +1104,14 @@ typedef struct {
                     guint32                mtu,
                     gconstpointer          extra_data,
                     const NMPlatformLink **out_link);
-
-    int (*link_change)(NMPlatform *self, NMLinkType type, int ifindex, gconstpointer extra_data);
-
+    int (*link_change_extra)(NMPlatform   *self,
+                             NMLinkType    type,
+                             int           ifindex,
+                             gconstpointer extra_data);
+    gboolean (*link_change)(NMPlatform                   *self,
+                            int                           ifindex,
+                            NMPortKind                    port_kind,
+                            const NMPlatformLinkPortData *port_data);
     gboolean (*link_delete)(NMPlatform *self, int ifindex);
     gboolean (*link_refresh)(NMPlatform *self, int ifindex);
     gboolean (*link_set_netns)(NMPlatform *self, int ifindex, int netns_fd);
@@ -1590,8 +1613,10 @@ int nm_platform_link_add(NMPlatform            *self,
                          gconstpointer          extra_data,
                          const NMPlatformLink **out_link);
 
-int
-nm_platform_link_change(NMPlatform *self, NMLinkType type, int ifindex, gconstpointer extra_data);
+int nm_platform_link_change_extra(NMPlatform   *self,
+                                  NMLinkType    type,
+                                  int           ifindex,
+                                  gconstpointer extra_data);
 
 static inline int
 nm_platform_link_veth_add(NMPlatform            *self,
@@ -1631,13 +1656,13 @@ nm_platform_link_bridge_add(NMPlatform                *self,
 static inline int
 nm_platform_link_bridge_change(NMPlatform *self, int ifindex, const NMPlatformLnkBridge *props)
 {
-    return nm_platform_link_change(self, NM_LINK_TYPE_BRIDGE, ifindex, props);
+    return nm_platform_link_change_extra(self, NM_LINK_TYPE_BRIDGE, ifindex, props);
 }
 
 static inline int
 nm_platform_link_bond_change(NMPlatform *self, int ifindex, const NMPlatformLnkBond *props)
 {
-    return nm_platform_link_change(self, NM_LINK_TYPE_BOND, ifindex, props);
+    return nm_platform_link_change_extra(self, NM_LINK_TYPE_BOND, ifindex, props);
 }
 
 static inline int
@@ -1914,6 +1939,8 @@ nm_platform_link_change_flags(NMPlatform *self, int ifindex, unsigned value, gbo
     return nm_platform_link_change_flags_full(self, ifindex, value, set ? value : 0u);
 }
 
+gboolean nm_platform_link_change(NMPlatform *self, int ifindex, NMPlatformLinkBondPort *bond_port);
+
 gboolean    nm_platform_link_get_udev_property(NMPlatform  *self,
                                                int          ifindex,
                                                const char  *name,
@@ -2391,6 +2418,9 @@ int nm_platform_tfilter_cmp(const NMPlatformTfilter *a, const NMPlatformTfilter
 int nm_platform_mptcp_addr_cmp(const NMPlatformMptcpAddr *a, const NMPlatformMptcpAddr *b);
 
 void nm_platform_link_hash_update(const NMPlatformLink *obj, NMHashState *h);
+void nm_platform_link_bond_port_hash_update(const NMPlatformLinkBondPort *obj, NMHashState *h);
+int  nm_platform_link_bond_port_cmp(const NMPlatformLinkBondPort *a,
+                                    const NMPlatformLinkBondPort *b);
 void nm_platform_ip4_route_hash_update(const NMPlatformIP4Route *obj,
                                        NMPlatformIPRouteCmpType  cmp_type,
                                        NMHashState              *h);
diff --git a/src/libnmc-setting/nm-meta-setting-desc.c b/src/libnmc-setting/nm-meta-setting-desc.c
index 0f67c9bd..e9185e34 100644
--- a/src/libnmc-setting/nm-meta-setting-desc.c
+++ b/src/libnmc-setting/nm-meta-setting-desc.c
@@ -5235,6 +5235,12 @@ static const NMMetaPropertyInfo *const property_infos_BOND_PORT[] = {
         .prompt =                       N_("Queue ID"),
         .property_type =                &_pt_gobject_int,
     ),
+    PROPERTY_INFO_WITH_DESC (NM_SETTING_BOND_PORT_PRIO,
+        .is_cli_option =                TRUE,
+        .property_alias =               "prio",
+        .prompt =                       N_("Port Priority"),
+        .property_type=                 &_pt_gobject_int,
+    ),
     NULL
 };
 
diff --git a/src/libnmc-setting/settings-docs.h b/src/libnmc-setting/settings-docs.h
index c10ece6a..0b2f8eca 100644
--- a/src/libnmc-setting/settings-docs.h
+++ b/src/libnmc-setting/settings-docs.h
@@ -153,7 +153,7 @@
 #define DESCRIBE_DOC_NM_SETTING_GSM_USERNAME N_("The username used to authenticate with the network, if required.  Many providers do not require a username, or accept any username.  But if a username is required, it is specified here.")
 #define DESCRIBE_DOC_NM_SETTING_INFINIBAND_MAC_ADDRESS N_("If specified, this connection will only apply to the IPoIB device whose permanent MAC address matches. This property does not change the MAC address of the device (i.e. MAC spoofing).")
 #define DESCRIBE_DOC_NM_SETTING_INFINIBAND_MTU N_("If non-zero, only transmit packets of the specified size or smaller, breaking larger packets up into multiple frames.")
-#define DESCRIBE_DOC_NM_SETTING_INFINIBAND_P_KEY N_("The InfiniBand P_Key to use for this device. A value of -1 means to use the default P_Key (aka \"the P_Key at index 0\"). Otherwise, it is a 16-bit unsigned integer, whose high bit is set if it is a \"full membership\" P_Key.")
+#define DESCRIBE_DOC_NM_SETTING_INFINIBAND_P_KEY N_("The InfiniBand p-key to use for this device. A value of -1 means to use the default p-key (aka \"the p-key at index 0\"). Otherwise, it is a 16-bit unsigned integer, whose high bit 0x8000 is set if it is a \"full membership\" p-key. The values 0 and 0x8000 are not allowed. With the p-key set, the interface name is always \"$parent.$p_key\". Setting \"connection.interface-name\" to another name is not supported. Note that kernel will internally always set the full membership bit, although the interface name does not reflect that. Usually the user would want to configure a full membership p-key with 0x8000 flag set.")
 #define DESCRIBE_DOC_NM_SETTING_INFINIBAND_PARENT N_("The interface name of the parent device of this device. Normally NULL, but if the \"p_key\" property is set, then you must specify the base device by setting either this property or \"mac-address\".")
 #define DESCRIBE_DOC_NM_SETTING_INFINIBAND_TRANSPORT_MODE N_("The IP-over-InfiniBand transport mode. Either \"datagram\" or \"connected\".")
 #define DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_ADDRESSES N_("A list of IPv4 addresses and their prefix length. Multiple addresses can be separated by comma. For example \"192.168.1.5/24, 10.1.0.5/24\". The addresses are listed in decreasing priority, meaning the first address will be the primary address.")
@@ -436,6 +436,7 @@
 #define DESCRIBE_DOC_NM_SETTING_WPAN_PAGE N_("IEEE 802.15.4 channel page. A positive integer or -1, meaning \"do not set, use whatever the device is already set to\".")
 #define DESCRIBE_DOC_NM_SETTING_WPAN_PAN_ID N_("IEEE 802.15.4 Personal Area Network (PAN) identifier.")
 #define DESCRIBE_DOC_NM_SETTING_WPAN_SHORT_ADDRESS N_("Short IEEE 802.15.4 address to be used within a restricted environment.")
+#define DESCRIBE_DOC_NM_SETTING_BOND_PORT_PRIO N_("The port priority for bond active port re-selection during failover. A higher number means a higher priority in selection. The primary port has the highest priority. This option is only compatible with active-backup, balance-tlb and balance-alb modes.")
 #define DESCRIBE_DOC_NM_SETTING_BOND_PORT_QUEUE_ID N_("The queue ID of this bond port. The maximum value of queue ID is the number of TX queues currently active in device.")
 #define DESCRIBE_DOC_NM_SETTING_HOSTNAME_FROM_DHCP N_("Whether the system hostname can be determined from DHCP on this connection. When set to NM_TERNARY_DEFAULT (-1), the value from global configuration is used. If the property doesn't have a value in the global configuration, NetworkManager assumes the value to be NM_TERNARY_TRUE (1).")
 #define DESCRIBE_DOC_NM_SETTING_HOSTNAME_FROM_DNS_LOOKUP N_("Whether the system hostname can be determined from reverse DNS lookup of addresses on this device. When set to NM_TERNARY_DEFAULT (-1), the value from global configuration is used. If the property doesn't have a value in the global configuration, NetworkManager assumes the value to be NM_TERNARY_TRUE (1).")
diff --git a/src/libnmc-setting/settings-docs.h.in b/src/libnmc-setting/settings-docs.h.in
index c10ece6a..0b2f8eca 100644
--- a/src/libnmc-setting/settings-docs.h.in
+++ b/src/libnmc-setting/settings-docs.h.in
@@ -153,7 +153,7 @@
 #define DESCRIBE_DOC_NM_SETTING_GSM_USERNAME N_("The username used to authenticate with the network, if required.  Many providers do not require a username, or accept any username.  But if a username is required, it is specified here.")
 #define DESCRIBE_DOC_NM_SETTING_INFINIBAND_MAC_ADDRESS N_("If specified, this connection will only apply to the IPoIB device whose permanent MAC address matches. This property does not change the MAC address of the device (i.e. MAC spoofing).")
 #define DESCRIBE_DOC_NM_SETTING_INFINIBAND_MTU N_("If non-zero, only transmit packets of the specified size or smaller, breaking larger packets up into multiple frames.")
-#define DESCRIBE_DOC_NM_SETTING_INFINIBAND_P_KEY N_("The InfiniBand P_Key to use for this device. A value of -1 means to use the default P_Key (aka \"the P_Key at index 0\"). Otherwise, it is a 16-bit unsigned integer, whose high bit is set if it is a \"full membership\" P_Key.")
+#define DESCRIBE_DOC_NM_SETTING_INFINIBAND_P_KEY N_("The InfiniBand p-key to use for this device. A value of -1 means to use the default p-key (aka \"the p-key at index 0\"). Otherwise, it is a 16-bit unsigned integer, whose high bit 0x8000 is set if it is a \"full membership\" p-key. The values 0 and 0x8000 are not allowed. With the p-key set, the interface name is always \"$parent.$p_key\". Setting \"connection.interface-name\" to another name is not supported. Note that kernel will internally always set the full membership bit, although the interface name does not reflect that. Usually the user would want to configure a full membership p-key with 0x8000 flag set.")
 #define DESCRIBE_DOC_NM_SETTING_INFINIBAND_PARENT N_("The interface name of the parent device of this device. Normally NULL, but if the \"p_key\" property is set, then you must specify the base device by setting either this property or \"mac-address\".")
 #define DESCRIBE_DOC_NM_SETTING_INFINIBAND_TRANSPORT_MODE N_("The IP-over-InfiniBand transport mode. Either \"datagram\" or \"connected\".")
 #define DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_ADDRESSES N_("A list of IPv4 addresses and their prefix length. Multiple addresses can be separated by comma. For example \"192.168.1.5/24, 10.1.0.5/24\". The addresses are listed in decreasing priority, meaning the first address will be the primary address.")
@@ -436,6 +436,7 @@
 #define DESCRIBE_DOC_NM_SETTING_WPAN_PAGE N_("IEEE 802.15.4 channel page. A positive integer or -1, meaning \"do not set, use whatever the device is already set to\".")
 #define DESCRIBE_DOC_NM_SETTING_WPAN_PAN_ID N_("IEEE 802.15.4 Personal Area Network (PAN) identifier.")
 #define DESCRIBE_DOC_NM_SETTING_WPAN_SHORT_ADDRESS N_("Short IEEE 802.15.4 address to be used within a restricted environment.")
+#define DESCRIBE_DOC_NM_SETTING_BOND_PORT_PRIO N_("The port priority for bond active port re-selection during failover. A higher number means a higher priority in selection. The primary port has the highest priority. This option is only compatible with active-backup, balance-tlb and balance-alb modes.")
 #define DESCRIBE_DOC_NM_SETTING_BOND_PORT_QUEUE_ID N_("The queue ID of this bond port. The maximum value of queue ID is the number of TX queues currently active in device.")
 #define DESCRIBE_DOC_NM_SETTING_HOSTNAME_FROM_DHCP N_("Whether the system hostname can be determined from DHCP on this connection. When set to NM_TERNARY_DEFAULT (-1), the value from global configuration is used. If the property doesn't have a value in the global configuration, NetworkManager assumes the value to be NM_TERNARY_TRUE (1).")
 #define DESCRIBE_DOC_NM_SETTING_HOSTNAME_FROM_DNS_LOOKUP N_("Whether the system hostname can be determined from reverse DNS lookup of addresses on this device. When set to NM_TERNARY_DEFAULT (-1), the value from global configuration is used. If the property doesn't have a value in the global configuration, NetworkManager assumes the value to be NM_TERNARY_TRUE (1).")
diff --git a/src/nm-cloud-setup/main.c b/src/nm-cloud-setup/main.c
index d6802634..630f26ef 100644
--- a/src/nm-cloud-setup/main.c
+++ b/src/nm-cloud-setup/main.c
@@ -16,6 +16,12 @@
 /*****************************************************************************/
 
 typedef struct {
+    GCancellable *cancellable;
+    gboolean      enabled;
+    gboolean      signal_received;
+} SigTermData;
+
+typedef struct {
     GMainLoop    *main_loop;
     GCancellable *cancellable;
     NMCSProvider *provider_result;
@@ -443,7 +449,7 @@ _nmc_mangle_connection(NMDevice                             *device,
 /*****************************************************************************/
 
 static gboolean
-_config_one(GCancellable                      *sigterm_cancellable,
+_config_one(SigTermData                       *sigterm_data,
             NMClient                          *nmc,
             const NMCSProviderGetConfigResult *result,
             guint                              idx)
@@ -463,7 +469,7 @@ _config_one(GCancellable                      *sigterm_cancellable,
 
     g_main_context_iteration(NULL, FALSE);
 
-    if (g_cancellable_is_cancelled(sigterm_cancellable))
+    if (g_cancellable_is_cancelled(sigterm_data->cancellable))
         return FALSE;
 
     device = nm_g_object_ref(_nmc_get_device_by_hwaddr(nmc, hwaddr));
@@ -497,7 +503,7 @@ try_again:
     g_clear_error(&error);
 
     applied_connection = nmcs_device_get_applied_connection(device,
-                                                            sigterm_cancellable,
+                                                            sigterm_data->cancellable,
                                                             &applied_version_id,
                                                             &error);
     if (!applied_connection) {
@@ -558,8 +564,12 @@ try_again:
     maybe_no_preserved_external_ip =
         (nmc_client_has_version_info_v(nmc) < NM_ENCODE_VERSION(1, 41, 6));
 
+    /* Once we start reconfiguring the system, we cannot abort in the middle. From now on,
+     * any SIGTERM gets ignored until we are done.  */
+    sigterm_data->enabled = FALSE;
+
     if (!nmcs_device_reapply(device,
-                             sigterm_cancellable,
+                             NULL,
                              applied_connection,
                              applied_version_id,
                              maybe_no_preserved_external_ip,
@@ -590,15 +600,13 @@ try_again:
 }
 
 static gboolean
-_config_all(GCancellable                      *sigterm_cancellable,
-            NMClient                          *nmc,
-            const NMCSProviderGetConfigResult *result)
+_config_all(SigTermData *sigterm_data, NMClient *nmc, const NMCSProviderGetConfigResult *result)
 {
     gboolean any_changes = FALSE;
     guint    i;
 
     for (i = 0; i < result->n_iface_datas; i++) {
-        if (_config_one(sigterm_cancellable, nmc, result, i))
+        if (_config_one(sigterm_data, nmc, result, i))
             any_changes = TRUE;
     }
 
@@ -610,13 +618,16 @@ _config_all(GCancellable                      *sigterm_cancellable,
 static gboolean
 sigterm_handler(gpointer user_data)
 {
-    GCancellable *sigterm_cancellable = user_data;
+    SigTermData *sigterm_data = user_data;
 
-    if (!g_cancellable_is_cancelled(sigterm_cancellable)) {
-        _LOGD("SIGTERM received");
-        g_cancellable_cancel(user_data);
-    } else
-        _LOGD("SIGTERM received (again)");
+    _LOGD("SIGTERM received (%s) (%s)",
+          sigterm_data->signal_received ? "first time" : "again",
+          sigterm_data->enabled ? "cancel operation" : "ignore");
+
+    sigterm_data->signal_received = TRUE;
+
+    if (sigterm_data->enabled)
+        g_cancellable_cancel(sigterm_data->cancellable);
     return G_SOURCE_CONTINUE;
 }
 
@@ -631,6 +642,7 @@ main(int argc, const char *const *argv)
     gs_unref_object NMClient                  *nmc                                   = NULL;
     nm_auto_free_nmcs_provider_get_config_result NMCSProviderGetConfigResult *result = NULL;
     gs_free_error GError                                                     *error  = NULL;
+    SigTermData                                                               sigterm_data;
 
     _nm_logging_enabled_init(g_getenv(NMCS_ENV_VARIABLE("NM_CLOUD_SETUP_LOG")));
 
@@ -643,7 +655,12 @@ main(int argc, const char *const *argv)
 
     sigterm_cancellable = g_cancellable_new();
 
-    sigterm_source = nm_g_unix_signal_add_source(SIGTERM, sigterm_handler, sigterm_cancellable);
+    sigterm_data = (SigTermData){
+        .cancellable     = sigterm_cancellable,
+        .enabled         = TRUE,
+        .signal_received = FALSE,
+    };
+    sigterm_source = nm_g_unix_signal_add_source(SIGTERM, sigterm_handler, &sigterm_data);
 
     provider = _provider_detect(sigterm_cancellable);
     if (!provider)
@@ -674,7 +691,7 @@ main(int argc, const char *const *argv)
     if (!result)
         goto done;
 
-    if (_config_all(sigterm_cancellable, nmc, result))
+    if (_config_all(&sigterm_data, nmc, result))
         _LOGI("some changes were applied for provider %s", nmcs_provider_get_name(provider));
     else
         _LOGD("no changes were applied for provider %s", nmcs_provider_get_name(provider));
diff --git a/src/nm-cloud-setup/nm-cloud-setup-utils.c b/src/nm-cloud-setup/nm-cloud-setup-utils.c
index cb8e9f55..fb4883b7 100644
--- a/src/nm-cloud-setup/nm-cloud-setup-utils.c
+++ b/src/nm-cloud-setup/nm-cloud-setup-utils.c
@@ -833,6 +833,8 @@ nmcs_device_reapply(NMDevice     *device,
     NMDeviceReapplyFlags reapply_flags = NM_DEVICE_REAPPLY_FLAGS_PRESERVE_EXTERNAL_IP;
 
 again:
+    g_clear_error(&data.error);
+
     nm_device_reapply_async(device,
                             connection,
                             version_id,
diff --git a/src/nm-cloud-setup/nmcs-provider-aliyun.c b/src/nm-cloud-setup/nmcs-provider-aliyun.c
index 86ecd76b..7f51f431 100644
--- a/src/nm-cloud-setup/nmcs-provider-aliyun.c
+++ b/src/nm-cloud-setup/nmcs-provider-aliyun.c
@@ -31,11 +31,12 @@ again:
          * This is mainly for testing, it's not usually supposed to be configured.
          * Consider this private API! */
         base = g_getenv(NMCS_ENV_VARIABLE("NM_CLOUD_SETUP_ALIYUN_HOST"));
+        base = nmcs_utils_uri_complete_interned(base) ?: ("" NM_ALIYUN_HOST);
 
         if (!g_atomic_pointer_compare_and_exchange(&base_cached, NULL, base))
             goto again;
     }
-    base = nmcs_utils_uri_complete_interned(base) ?: ("" NM_ALIYUN_BASE);
+
     return base;
 }
 
diff --git a/src/nmcli/gen-metadata-nm-settings-nmcli.xml.in b/src/nmcli/gen-metadata-nm-settings-nmcli.xml.in
index a3409afb..e0fa54ef 100644
--- a/src/nmcli/gen-metadata-nm-settings-nmcli.xml.in
+++ b/src/nmcli/gen-metadata-nm-settings-nmcli.xml.in
@@ -271,6 +271,9 @@
         <property name="queue-id"
                   alias="queue-id"
                   description="The queue ID of this bond port. The maximum value of queue ID is the number of TX queues currently active in device." />
+        <property name="prio"
+                  alias="prio"
+                  description="The port priority for bond active port re-selection during failover. A higher number means a higher priority in selection. The primary port has the highest priority. This option is only compatible with active-backup, balance-tlb and balance-alb modes." />
     </setting>
     <setting name="bridge" >
         <property name="mac-address"
@@ -611,7 +614,7 @@
                   description="The IP-over-InfiniBand transport mode. Either &quot;datagram&quot; or &quot;connected&quot;." />
         <property name="p-key"
                   alias="p-key"
-                  description="The InfiniBand P_Key to use for this device. A value of -1 means to use the default P_Key (aka &quot;the P_Key at index 0&quot;). Otherwise, it is a 16-bit unsigned integer, whose high bit is set if it is a &quot;full membership&quot; P_Key." />
+                  description="The InfiniBand p-key to use for this device. A value of -1 means to use the default p-key (aka &quot;the p-key at index 0&quot;). Otherwise, it is a 16-bit unsigned integer, whose high bit 0x8000 is set if it is a &quot;full membership&quot; p-key. The values 0 and 0x8000 are not allowed. With the p-key set, the interface name is always &quot;$parent.$p_key&quot;. Setting &quot;connection.interface-name&quot; to another name is not supported. Note that kernel will internally always set the full membership bit, although the interface name does not reflect that. Usually the user would want to configure a full membership p-key with 0x8000 flag set." />
         <property name="parent"
                   alias="parent"
                   description="The interface name of the parent device of this device. Normally NULL, but if the &quot;p_key&quot; property is set, then you must specify the base device by setting either this property or &quot;mac-address&quot;." />