summary refs log tree commit diff
path: root/src/core/NetworkManagerUtils.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/NetworkManagerUtils.c')
-rw-r--r--src/core/NetworkManagerUtils.c358
1 files changed, 243 insertions, 115 deletions
diff --git a/src/core/NetworkManagerUtils.c b/src/core/NetworkManagerUtils.c
index 0da8e0a9..c50dbada 100644
--- a/src/core/NetworkManagerUtils.c
+++ b/src/core/NetworkManagerUtils.c
@@ -28,6 +28,7 @@
 
 #include "libnm-platform/nm-platform.h"
 #include "libnm-platform/nm-linux-platform.h"
+#include "libnm-platform/nm-platform-utils.h"
 #include "nm-auth-utils.h"
 #include "libnm-systemd-shared/nm-sd-utils-shared.h"
 
@@ -42,9 +43,9 @@
 const char *
 nm_utils_get_shared_wifi_permission(NMConnection *connection)
 {
-    NMSettingWireless *        s_wifi;
+    NMSettingWireless         *s_wifi;
     NMSettingWirelessSecurity *s_wsec;
-    const char *               method;
+    const char                *method;
 
     method = nm_utils_get_ip_config_method(connection, AF_INET);
     if (!nm_streq(method, NM_SETTING_IP4_CONFIG_METHOD_SHARED))
@@ -66,8 +67,8 @@ nm_utils_get_shared_wifi_permission(NMConnection *connection)
 
 static char *
 get_new_connection_name(NMConnection *const *existing_connections,
-                        const char *         preferred,
-                        const char *         fallback_prefix)
+                        const char          *preferred,
+                        const char          *fallback_prefix)
 {
     gs_free const char **existing_names = NULL;
     guint                i, existing_len = 0;
@@ -79,7 +80,7 @@ get_new_connection_name(NMConnection *const *existing_connections,
         existing_names = g_new(const char *, existing_len);
         for (i = 0; i < existing_len; i++) {
             NMConnection *candidate;
-            const char *  id;
+            const char   *id;
 
             candidate = existing_connections[i];
             nm_assert(NM_IS_CONNECTION(candidate));
@@ -121,9 +122,9 @@ get_new_connection_name(NMConnection *const *existing_connections,
 }
 
 static char *
-get_new_connection_ifname(NMPlatform *         platform,
+get_new_connection_ifname(NMPlatform          *platform,
                           NMConnection *const *existing_connections,
-                          const char *         prefix)
+                          const char          *prefix)
 {
     guint i, j;
 
@@ -153,8 +154,8 @@ const char *
 nm_utils_get_ip_config_method(NMConnection *connection, int addr_family)
 {
     NMSettingConnection *s_con;
-    NMSettingIPConfig *  s_ip;
-    const char *         method;
+    NMSettingIPConfig   *s_ip;
+    const char          *method;
 
     s_con = nm_connection_get_setting_connection(connection);
 
@@ -184,9 +185,9 @@ nm_utils_get_ip_config_method(NMConnection *connection, int addr_family)
 gboolean
 nm_utils_connection_has_default_route(NMConnection *connection,
                                       int           addr_family,
-                                      gboolean *    out_is_never_default)
+                                      gboolean     *out_is_never_default)
 {
-    const char *       method;
+    const char        *method;
     NMSettingIPConfig *s_ip;
     gboolean           is_never_default  = FALSE;
     gboolean           has_default_route = FALSE;
@@ -228,23 +229,38 @@ out:
 /*****************************************************************************/
 
 void
-_nm_utils_complete_generic_with_params(NMPlatform *         platform,
-                                       NMConnection *       connection,
-                                       const char *         ctype,
+nm_utils_ppp_ip_methods_enabled(NMConnection *connection,
+                                gboolean     *out_ip4_enabled,
+                                gboolean     *out_ip6_enabled)
+{
+    NM_SET_OUT(out_ip4_enabled,
+               nm_streq0(nm_utils_get_ip_config_method(connection, AF_INET),
+                         NM_SETTING_IP4_CONFIG_METHOD_AUTO));
+    NM_SET_OUT(out_ip6_enabled,
+               nm_streq0(nm_utils_get_ip_config_method(connection, AF_INET6),
+                         NM_SETTING_IP6_CONFIG_METHOD_AUTO));
+}
+
+/*****************************************************************************/
+
+void
+_nm_utils_complete_generic_with_params(NMPlatform          *platform,
+                                       NMConnection        *connection,
+                                       const char          *ctype,
                                        NMConnection *const *existing_connections,
-                                       const char *         preferred_id,
-                                       const char *         fallback_id_prefix,
-                                       const char *         ifname_prefix,
-                                       const char *         ifname,
+                                       const char          *preferred_id,
+                                       const char          *fallback_id_prefix,
+                                       const char          *ifname_prefix,
+                                       const char          *ifname,
                                        ...)
 {
-    NMSettingConnection *s_con;
-    char *               id;
-    char *               generated_ifname;
+    NMSettingConnection           *s_con;
+    char                          *id;
+    char                          *generated_ifname;
     gs_unref_hashtable GHashTable *parameters = NULL;
     va_list                        ap;
-    const char *                   p_val;
-    const char *                   p_key;
+    const char                    *p_val;
+    const char                    *p_key;
 
     g_assert(fallback_id_prefix);
     g_return_if_fail(ifname_prefix == NULL || ifname == NULL);
@@ -324,8 +340,8 @@ remove_from_hash(GHashTable *s_hash, GHashTable *p_hash, const char *s_name, con
 static gboolean
 check_ip6_method(NMConnection *orig, NMConnection *candidate, GHashTable *settings)
 {
-    GHashTable *       props;
-    const char *       orig_ip6_method, *candidate_ip6_method;
+    GHashTable        *props;
+    const char        *orig_ip6_method, *candidate_ip6_method;
     NMSettingIPConfig *candidate_ip6;
     gboolean           allow = FALSE;
 
@@ -415,16 +431,16 @@ route_ptr_compare(const void *a, const void *b, gpointer metric)
 static gboolean
 check_ip_routes(NMConnection *orig,
                 NMConnection *candidate,
-                GHashTable *  settings,
+                GHashTable   *settings,
                 gint64        default_metric,
                 gboolean      v4)
 {
     gs_free NMIPRoute **routes1 = NULL;
-    NMIPRoute **        routes2;
-    NMSettingIPConfig * s_ip1, *s_ip2;
+    NMIPRoute         **routes2;
+    NMSettingIPConfig  *s_ip1, *s_ip2;
     gint64              m;
-    const char *        s_name;
-    GHashTable *        props;
+    const char         *s_name;
+    GHashTable         *props;
     guint               i, i1, i2, num1, num2;
     const guint8        PLEN = v4 ? 32 : 128;
 
@@ -493,11 +509,11 @@ check_ip_routes(NMConnection *orig,
 static gboolean
 check_ip4_method(NMConnection *orig,
                  NMConnection *candidate,
-                 GHashTable *  settings,
+                 GHashTable   *settings,
                  gboolean      device_has_carrier)
 {
-    GHashTable *       props;
-    const char *       orig_ip4_method, *candidate_ip4_method;
+    GHashTable        *props;
+    const char        *orig_ip4_method, *candidate_ip4_method;
     NMSettingIPConfig *candidate_ip4;
 
     props = check_property_in_hash(settings,
@@ -531,8 +547,8 @@ check_ip4_method(NMConnection *orig,
 static gboolean
 check_connection_interface_name(NMConnection *orig, NMConnection *candidate, GHashTable *settings)
 {
-    GHashTable *         props;
-    const char *         orig_ifname, *cand_ifname;
+    GHashTable          *props;
+    const char          *orig_ifname, *cand_ifname;
     NMSettingConnection *s_con_orig, *s_con_cand;
 
     props = check_property_in_hash(settings,
@@ -560,8 +576,8 @@ check_connection_interface_name(NMConnection *orig, NMConnection *candidate, GHa
 static gboolean
 check_connection_mac_address(NMConnection *orig, NMConnection *candidate, GHashTable *settings)
 {
-    GHashTable *    props;
-    const char *    orig_mac = NULL, *cand_mac = NULL;
+    GHashTable     *props;
+    const char     *orig_mac = NULL, *cand_mac = NULL;
     NMSettingWired *s_wired_orig, *s_wired_cand;
 
     props = check_property_in_hash(settings,
@@ -592,10 +608,10 @@ check_connection_mac_address(NMConnection *orig, NMConnection *candidate, GHashT
 static gboolean
 check_connection_infiniband_mac_address(NMConnection *orig,
                                         NMConnection *candidate,
-                                        GHashTable *  settings)
+                                        GHashTable   *settings)
 {
-    GHashTable *         props;
-    const char *         orig_mac = NULL, *cand_mac = NULL;
+    GHashTable          *props;
+    const char          *orig_mac = NULL, *cand_mac = NULL;
     NMSettingInfiniband *s_infiniband_orig, *s_infiniband_cand;
 
     props = check_property_in_hash(settings,
@@ -626,10 +642,10 @@ check_connection_infiniband_mac_address(NMConnection *orig,
 static gboolean
 check_connection_cloned_mac_address(NMConnection *orig,
                                     NMConnection *candidate,
-                                    GHashTable *  settings)
+                                    GHashTable   *settings)
 {
-    GHashTable *    props;
-    const char *    orig_mac = NULL, *cand_mac = NULL;
+    GHashTable     *props;
+    const char     *orig_mac = NULL, *cand_mac = NULL;
     NMSettingWired *s_wired_orig, *s_wired_cand;
 
     props = check_property_in_hash(settings,
@@ -666,7 +682,7 @@ check_connection_cloned_mac_address(NMConnection *orig,
 static gboolean
 check_connection_s390_props(NMConnection *orig, NMConnection *candidate, GHashTable *settings)
 {
-    GHashTable *    props1, *props2, *props3;
+    GHashTable     *props1, *props2, *props3;
     NMSettingWired *s_wired_orig, *s_wired_cand;
 
     props1 = check_property_in_hash(settings,
@@ -688,7 +704,7 @@ check_connection_s390_props(NMConnection *orig, NMConnection *candidate, GHashTa
     s_wired_cand = nm_connection_get_setting_wired(candidate);
     if (!s_wired_orig && s_wired_cand) {
         const char *const *subchans    = nm_setting_wired_get_s390_subchannels(s_wired_cand);
-        const char *       nettype     = nm_setting_wired_get_s390_nettype(s_wired_cand);
+        const char        *nettype     = nm_setting_wired_get_s390_nettype(s_wired_cand);
         guint32            num_options = nm_setting_wired_get_num_s390_options(s_wired_cand);
 
         if ((!subchans || !*subchans) && !nettype && num_options == 0) {
@@ -713,7 +729,7 @@ check_connection_s390_props(NMConnection *orig, NMConnection *candidate, GHashTa
 static NMConnection *
 check_possible_match(NMConnection *orig,
                      NMConnection *candidate,
-                     GHashTable *  settings,
+                     GHashTable   *settings,
                      gboolean      device_has_carrier,
                      gint64        default_v4_metric,
                      gint64        default_v6_metric)
@@ -779,8 +795,8 @@ check_possible_match(NMConnection *orig,
  * matches well enough.
  */
 NMConnection *
-nm_utils_match_connection(NMConnection *const *  connections,
-                          NMConnection *         original,
+nm_utils_match_connection(NMConnection *const   *connections,
+                          NMConnection          *original,
                           gboolean               indicated,
                           gboolean               device_has_carrier,
                           gint64                 default_v4_metric,
@@ -795,7 +811,7 @@ nm_utils_match_connection(NMConnection *const *  connections,
 
     for (; *connections; connections++) {
         NMConnection *candidate = *connections;
-        GHashTable *  diffs     = NULL;
+        GHashTable   *diffs     = NULL;
 
         nm_assert(NM_IS_CONNECTION(candidate));
 
@@ -835,7 +851,7 @@ nm_utils_match_connection(NMConnection *const *  connections,
             }
 
             if (!best_match && nm_logging_enabled(LOGL_DEBUG, LOGD_CORE)) {
-                GString *      diff_string;
+                GString       *diff_string;
                 GHashTableIter s_iter, p_iter;
                 gpointer       setting_name, setting;
                 gpointer       property_name, value;
@@ -878,9 +894,9 @@ nm_utils_match_connection(NMConnection *const *  connections,
 
 int
 nm_match_spec_device_by_pllink(const NMPlatformLink *pllink,
-                               const char *          match_device_type,
-                               const char *          match_dhcp_plugin,
-                               const GSList *        specs,
+                               const char           *match_device_type,
+                               const char           *match_dhcp_plugin,
+                               const GSList         *specs,
                                int                   no_match_value)
 {
     NMMatchSpecMatchType m;
@@ -971,7 +987,7 @@ nm_ip_routing_rule_to_platform(const NMIPRoutingRule *rule, NMPlatformRoutingRul
 struct _NMShutdownWaitObjHandle {
     CList    lst;
     gpointer watched_obj;
-    char *   msg_reason;
+    char    *msg_reason;
     bool     free_msg_reason : 1;
     bool     is_cancellable : 1;
 };
@@ -1044,7 +1060,7 @@ _shutdown_waitobj_cb(gpointer user_data, GObject *where_the_object_was)
 NMShutdownWaitObjHandle *
 nm_shutdown_wait_obj_register_full(gpointer           watched_obj,
                                    NMShutdownWaitType wait_type,
-                                   char *             msg_reason,
+                                   char              *msg_reason,
                                    gboolean           free_msg_reason)
 {
     NMShutdownWaitObjHandle *handle;
@@ -1164,8 +1180,8 @@ nm_utils_qdiscs_from_tc_setting(NMPlatform *platform, NMSettingTCConfig *s_tc, i
     qdiscs  = g_ptr_array_new_full(nqdiscs, (GDestroyNotify) nmp_object_unref);
 
     for (i = 0; i < nqdiscs; i++) {
-        NMTCQdisc *      s_qdisc = nm_setting_tc_config_get_qdisc(s_tc, i);
-        NMPObject *      q       = nmp_object_new(NMP_OBJECT_TYPE_QDISC, NULL);
+        NMTCQdisc       *s_qdisc = nm_setting_tc_config_get_qdisc(s_tc, i);
+        NMPObject       *q       = nmp_object_new(NMP_OBJECT_TYPE_QDISC, NULL);
         NMPlatformQdisc *qdisc   = NMP_OBJECT_CAST_QDISC(q);
 
         qdisc->ifindex = ip_ifindex;
@@ -1238,9 +1254,9 @@ nm_utils_tfilters_from_tc_setting(NMPlatform *platform, NMSettingTCConfig *s_tc,
     tfilters  = g_ptr_array_new_full(ntfilters, (GDestroyNotify) nmp_object_unref);
 
     for (i = 0; i < ntfilters; i++) {
-        NMTCTfilter *      s_tfilter = nm_setting_tc_config_get_tfilter(s_tc, i);
-        NMTCAction *       action;
-        NMPObject *        t       = nmp_object_new(NMP_OBJECT_TYPE_TFILTER, NULL);
+        NMTCTfilter       *s_tfilter = nm_setting_tc_config_get_tfilter(s_tc, i);
+        NMTCAction        *action;
+        NMPObject         *t       = nmp_object_new(NMP_OBJECT_TYPE_TFILTER, NULL);
         NMPlatformTfilter *tfilter = NMP_OBJECT_CAST_TFILTER(t);
 
         tfilter->ifindex     = ip_ifindex;
@@ -1296,11 +1312,12 @@ nm_utils_tfilters_from_tc_setting(NMPlatform *platform, NMSettingTCConfig *s_tc,
 
 void
 nm_utils_ip_route_attribute_to_platform(int                addr_family,
-                                        NMIPRoute *        s_route,
+                                        NMIPRoute         *s_route,
                                         NMPlatformIPRoute *r,
-                                        guint32            route_table)
+                                        gint64             route_table)
 {
-    GVariant *          variant;
+    const int           IS_IPv4 = NM_IS_IPv4(addr_family);
+    GVariant           *variant;
     guint32             table;
     NMIPAddr            addr;
     NMPlatformIP4Route *r4 = (NMPlatformIP4Route *) r;
@@ -1308,8 +1325,10 @@ nm_utils_ip_route_attribute_to_platform(int                addr_family,
     gboolean            onlink;
 
     nm_assert(s_route);
-    nm_assert_addr_family(addr_family);
+    nm_assert(addr_family == nm_ip_route_get_family(s_route));
     nm_assert(r);
+    nm_assert(route_table >= -1);
+    nm_assert(route_table <= (gint64) G_MAXUINT32);
 
 #define GET_ATTR(name, dst, variant_type, type, dflt)                                  \
     G_STMT_START                                                                       \
@@ -1328,7 +1347,8 @@ nm_utils_ip_route_attribute_to_platform(int                addr_family,
         int type;
 
         type = nm_net_aux_rtnl_rtntype_a2n(g_variant_get_string(variant, NULL));
-        nm_assert(NM_IN_SET(type, RTN_UNICAST, RTN_LOCAL));
+        nm_assert(
+            NM_IN_SET(type, RTN_UNICAST, RTN_LOCAL, RTN_BLACKHOLE, RTN_UNREACHABLE, RTN_PROHIBIT));
 
         r->type_coerced = nm_platform_route_type_coerce(type);
     } else
@@ -1336,12 +1356,18 @@ nm_utils_ip_route_attribute_to_platform(int                addr_family,
 
     GET_ATTR(NM_IP_ROUTE_ATTRIBUTE_TABLE, table, UINT32, uint32, 0);
 
-    if (!table && r->type_coerced == nm_platform_route_type_coerce(RTN_LOCAL))
+    if (table != 0)
+        r->table_coerced = nm_platform_route_table_coerce(table);
+    else if (r->type_coerced == nm_platform_route_type_coerce(RTN_LOCAL))
         r->table_coerced = nm_platform_route_table_coerce(RT_TABLE_LOCAL);
+    else if (route_table == 0)
+        r->table_coerced = nm_platform_route_table_coerce(RT_TABLE_MAIN);
+    else if (route_table > 0)
+        r->table_coerced = nm_platform_route_table_coerce(route_table);
     else
-        r->table_coerced = nm_platform_route_table_coerce(table ?: (route_table ?: RT_TABLE_MAIN));
+        r->table_any = TRUE;
 
-    if (NM_IS_IPv4(addr_family)) {
+    if (IS_IPv4) {
         guint8 scope;
 
         GET_ATTR(NM_IP_ROUTE_ATTRIBUTE_TOS, r4->tos, BYTE, byte, 0);
@@ -1367,15 +1393,14 @@ nm_utils_ip_route_attribute_to_platform(int                addr_family,
     if ((variant = nm_ip_route_get_attribute(s_route, NM_IP_ROUTE_ATTRIBUTE_SRC))
         && g_variant_is_of_type(variant, G_VARIANT_TYPE_STRING)) {
         if (inet_pton(addr_family, g_variant_get_string(variant, NULL), &addr) == 1) {
-            if (NM_IS_IPv4(addr_family))
+            if (IS_IPv4)
                 r4->pref_src = addr.addr4;
             else
                 r6->pref_src = addr.addr6;
         }
     }
 
-    if (!NM_IS_IPv4(addr_family)
-        && (variant = nm_ip_route_get_attribute(s_route, NM_IP_ROUTE_ATTRIBUTE_FROM))
+    if (!IS_IPv4 && (variant = nm_ip_route_get_attribute(s_route, NM_IP_ROUTE_ATTRIBUTE_FROM))
         && g_variant_is_of_type(variant, G_VARIANT_TYPE_STRING)) {
         int prefix;
 
@@ -1395,39 +1420,20 @@ nm_utils_ip_route_attribute_to_platform(int                addr_family,
 
 /*****************************************************************************/
 
-static int
-_addresses_sort_cmp_4(gconstpointer a, gconstpointer b, gpointer user_data)
-{
-    return nm_platform_ip4_address_pretty_sort_cmp(
-        NMP_OBJECT_CAST_IP4_ADDRESS(*((const NMPObject **) a)),
-        NMP_OBJECT_CAST_IP4_ADDRESS(*((const NMPObject **) b)));
-}
-
-static int
-_addresses_sort_cmp_6(gconstpointer a, gconstpointer b, gpointer user_data)
-{
-    return nm_platform_ip6_address_pretty_sort_cmp(
-        NMP_OBJECT_CAST_IP6_ADDRESS(*((const NMPObject **) a)),
-        NMP_OBJECT_CAST_IP6_ADDRESS(*((const NMPObject **) b)),
-        (((NMSettingIP6ConfigPrivacy) GPOINTER_TO_INT(user_data))
-         == NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_TEMP_ADDR));
-}
-
 void
 nm_utils_ip_addresses_to_dbus(int                          addr_family,
                               const NMDedupMultiHeadEntry *head_entry,
-                              const NMPObject *            best_default_route,
-                              NMSettingIP6ConfigPrivacy    ipv6_privacy,
-                              GVariant **                  out_address_data,
-                              GVariant **                  out_addresses)
+                              const NMPObject             *best_default_route,
+                              GVariant                   **out_address_data,
+                              GVariant                   **out_addresses)
 {
-    const int       IS_IPv4 = NM_IS_IPv4(addr_family);
-    GVariantBuilder builder_data;
-    GVariantBuilder builder_legacy;
-    char            addr_str[NM_UTILS_INET_ADDRSTRLEN];
-    gs_free const NMPObject **addresses = NULL;
-    guint                     naddr;
-    guint                     i;
+    const int        IS_IPv4 = NM_IS_IPv4(addr_family);
+    GVariantBuilder  builder_data;
+    GVariantBuilder  builder_legacy;
+    char             addr_str[NM_UTILS_INET_ADDRSTRLEN];
+    NMDedupMultiIter iter;
+    const NMPObject *obj;
+    guint            i;
 
     nm_assert_addr_family(addr_family);
 
@@ -1443,19 +1449,11 @@ nm_utils_ip_addresses_to_dbus(int                          addr_family,
     if (!head_entry)
         goto out;
 
-    addresses =
-        (const NMPObject **) nm_dedup_multi_objs_to_array_head(head_entry, NULL, NULL, &naddr);
-
-    nm_assert(addresses && naddr);
-
-    g_qsort_with_data(addresses,
-                      naddr,
-                      sizeof(addresses[0]),
-                      IS_IPv4 ? _addresses_sort_cmp_4 : _addresses_sort_cmp_6,
-                      GINT_TO_POINTER(ipv6_privacy));
-
-    for (i = 0; i < naddr; i++) {
-        const NMPlatformIPXAddress *address = NMP_OBJECT_CAST_IPX_ADDRESS(addresses[i]);
+    i = 0;
+    nm_dedup_multi_iter_init(&iter, head_entry);
+    while (
+        nm_platform_dedup_multi_iter_next_obj(&iter, &obj, NMP_OBJECT_TYPE_IP_ADDRESS(IS_IPv4))) {
+        const NMPlatformIPXAddress *address = NMP_OBJECT_CAST_IPX_ADDRESS(obj);
 
         if (out_address_data) {
             GVariantBuilder addr_builder;
@@ -1527,6 +1525,8 @@ nm_utils_ip_addresses_to_dbus(int                          addr_family,
                             : &in6addr_any));
             }
         }
+
+        i++;
     }
 
 out:
@@ -1537,8 +1537,8 @@ out:
 void
 nm_utils_ip_routes_to_dbus(int                          addr_family,
                            const NMDedupMultiHeadEntry *head_entry,
-                           GVariant **                  out_route_data,
-                           GVariant **                  out_routes)
+                           GVariant                   **out_route_data,
+                           GVariant                   **out_routes)
 {
     const int        IS_IPv4 = NM_IS_IPv4(addr_family);
     NMDedupMultiIter iter;
@@ -1654,6 +1654,134 @@ nm_utils_ip_routes_to_dbus(int                          addr_family,
 
 /*****************************************************************************/
 
+NMSetting *
+nm_utils_platform_capture_ip_setting(NMPlatform *platform,
+                                     int         addr_family,
+                                     int         ifindex,
+                                     gboolean    maybe_ipv6_disabled)
+{
+    const int                          IS_IPv4 = NM_IS_IPv4(addr_family);
+    gs_unref_object NMSettingIPConfig *s_ip    = NULL;
+    NMPLookup                          lookup;
+    NMDedupMultiIter                   iter;
+    const NMPObject                   *obj;
+    const char                        *method = NULL;
+    char                               sbuf[NM_UTILS_INET_ADDRSTRLEN];
+    const NMPlatformIPXRoute          *best_default_route = NULL;
+
+    s_ip =
+        NM_SETTING_IP_CONFIG(IS_IPv4 ? nm_setting_ip4_config_new() : nm_setting_ip6_config_new());
+
+    if (ifindex <= 0 || !nm_platform_link_get(platform, ifindex)) {
+        g_object_set(s_ip,
+                     NM_SETTING_IP_CONFIG_METHOD,
+                     IS_IPv4 ? NM_SETTING_IP4_CONFIG_METHOD_DISABLED
+                             : NM_SETTING_IP6_CONFIG_METHOD_IGNORE,
+                     NULL);
+        return NM_SETTING(g_steal_pointer(&s_ip));
+    }
+
+    nmp_lookup_init_object(&lookup, NMP_OBJECT_TYPE_IP_ADDRESS(IS_IPv4), ifindex);
+    nm_platform_iter_obj_for_each (&iter, platform, &lookup, &obj) {
+        const NMPlatformIPXAddress           *address = NMP_OBJECT_CAST_IPX_ADDRESS(obj);
+        nm_auto_unref_ip_address NMIPAddress *s_addr  = NULL;
+
+        if (!IS_IPv4) {
+            /* Ignore link-local address. */
+            if (IN6_IS_ADDR_LINKLOCAL(address->ax.address_ptr)) {
+                if (!method)
+                    method = NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL;
+                continue;
+            }
+        }
+
+        /* Detect dynamic address */
+        if (address->ax.lifetime != NM_PLATFORM_LIFETIME_PERMANENT) {
+            method =
+                IS_IPv4 ? NM_SETTING_IP4_CONFIG_METHOD_AUTO : NM_SETTING_IP6_CONFIG_METHOD_AUTO;
+            continue;
+        }
+
+        /* Static address found. */
+        if (IS_IPv4) {
+            if (!method)
+                method = NM_SETTING_IP4_CONFIG_METHOD_MANUAL;
+        } else {
+            if (NM_IN_STRSET(method, NULL, NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL))
+                method = NM_SETTING_IP6_CONFIG_METHOD_MANUAL;
+        }
+
+        s_addr =
+            nm_ip_address_new_binary(addr_family, address->ax.address_ptr, address->ax.plen, NULL);
+
+        if (IS_IPv4) {
+            if (address->a4.label[0]) {
+                nm_ip_address_set_attribute(s_addr,
+                                            NM_IP_ADDRESS_ATTRIBUTE_LABEL,
+                                            g_variant_new_string(address->a4.label));
+            }
+        }
+
+        nm_setting_ip_config_add_address(s_ip, s_addr);
+    }
+
+    if (!method) {
+        /* Use 'disabled/ignore' if the method wasn't previously set */
+        if (IS_IPv4)
+            method = NM_SETTING_IP4_CONFIG_METHOD_DISABLED;
+        else
+            method = maybe_ipv6_disabled ? NM_SETTING_IP6_CONFIG_METHOD_DISABLED
+                                         : NM_SETTING_IP6_CONFIG_METHOD_IGNORE;
+    }
+    g_object_set(s_ip, NM_SETTING_IP_CONFIG_METHOD, method, NULL);
+
+    nmp_lookup_init_object(&lookup, NMP_OBJECT_TYPE_IP_ROUTE(IS_IPv4), ifindex);
+    nm_platform_iter_obj_for_each (&iter, platform, &lookup, &obj) {
+        const NMPlatformIPXRoute         *route   = NMP_OBJECT_CAST_IPX_ROUTE(obj);
+        nm_auto_unref_ip_route NMIPRoute *s_route = NULL;
+
+        if (!IS_IPv4) {
+            /* Ignore link-local route. */
+            if (IN6_IS_ADDR_LINKLOCAL(route->rx.network_ptr))
+                continue;
+        }
+
+        if (NM_PLATFORM_IP_ROUTE_IS_DEFAULT(route)) {
+            if (!best_default_route)
+                best_default_route = route;
+            continue;
+        }
+
+        if (route->rx.rt_source
+            != nmp_utils_ip_config_source_round_trip_rtprot(NM_IP_CONFIG_SOURCE_USER)) {
+            /* Ignore routes provided by external sources */
+            continue;
+        }
+
+        s_route = nm_ip_route_new_binary(addr_family,
+                                         route->rx.network_ptr,
+                                         route->rx.plen,
+                                         nm_platform_ip_route_get_gateway(addr_family, &route->rx),
+                                         route->rx.metric,
+                                         NULL);
+        nm_setting_ip_config_add_route(s_ip, s_route);
+    }
+
+    if (best_default_route && nm_setting_ip_config_get_num_addresses(s_ip) > 0) {
+        g_object_set(s_ip,
+                     NM_SETTING_IP_CONFIG_GATEWAY,
+                     nm_utils_inet_ntop(
+                         addr_family,
+                         nm_platform_ip_route_get_gateway(addr_family, &best_default_route->rx),
+                         sbuf),
+                     NULL);
+    }
+
+    return NM_SETTING(g_steal_pointer(&s_ip));
+}
+
+/*****************************************************************************/
+
 /* Singleton NMPlatform subclass instance and cached class object */
 NM_DEFINE_SINGLETON_INSTANCE(NMPlatform);