about summary refs log tree commit diff
path: root/src/core
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/core
parent150fe9eef8dd22307ee16687509acde616663982 (diff)
New upstream version 1.42.8 upstream/1.42.8
Diffstat (limited to 'src/core')
-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
23 files changed, 812 insertions, 246 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);