about summary refs log tree commit diff
path: root/src/libnm-platform/nm-platform.h
diff options
context:
space:
mode:
authorMichael Biebl <biebl@debian.org>2023-03-09 20:01:53 +0100
committerMichael Biebl <biebl@debian.org>2023-03-09 20:01:53 +0100
commitcffb548b401e2cd3fad166aa92e0f634bbb1d4d6 (patch)
tree6d803d8f7d0d92a1c5e5705974fff8975472af95 /src/libnm-platform/nm-platform.h
parent647d6b95d5475e0a1bf3d6f299d1cb8c9041a7a4 (diff)
parent12e1ee797bbf0b10d1fd5afd4b947484769f2b9f (diff)
Merge tag 'debian/1.42.4-1' into debian/bullseye-backports
network-manager Debian release 1.42.4-1
Diffstat (limited to 'src/libnm-platform/nm-platform.h')
-rw-r--r--src/libnm-platform/nm-platform.h634
1 files changed, 242 insertions, 392 deletions
diff --git a/src/libnm-platform/nm-platform.h b/src/libnm-platform/nm-platform.h
index 30d0b506..1cd9c6c6 100644
--- a/src/libnm-platform/nm-platform.h
+++ b/src/libnm-platform/nm-platform.h
@@ -8,9 +8,10 @@
 
 #include "libnm-platform/nmp-base.h"
 #include "libnm-base/nm-base.h"
+#include "nmp-plobj.h"
 
 #define NM_TYPE_PLATFORM (nm_platform_get_type())
-#define NM_PLATFORM(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_PLATFORM, NMPlatform))
+#define NM_PLATFORM(obj) (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_PLATFORM, NMPlatform))
 #define NM_PLATFORM_CLASS(klass) \
     (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_PLATFORM, NMPlatformClass))
 #define NM_IS_PLATFORM(obj)         (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_PLATFORM))
@@ -22,16 +23,11 @@
 
 /*****************************************************************************/
 
+#define NM_PLATFORM_CACHE_TC      "cache-tc"
+#define NM_PLATFORM_LOG_WITH_PTR  "log-with-ptr"
+#define NM_PLATFORM_MULTI_IDX     "multi-idx"
 #define NM_PLATFORM_NETNS_SUPPORT "netns-support"
 #define NM_PLATFORM_USE_UDEV      "use-udev"
-#define NM_PLATFORM_LOG_WITH_PTR  "log-with-ptr"
-#define NM_PLATFORM_CACHE_TC      "cache-tc"
-
-/*****************************************************************************/
-
-/* IFNAMSIZ is both defined in <linux/if.h> and <net/if.h>. In the past, these
- * headers conflicted, so we cannot simply include either of them in a header-file.*/
-#define NMP_IFNAMSIZ 16
 
 /*****************************************************************************/
 
@@ -41,24 +37,8 @@ struct udev_device;
 
 typedef gboolean (*NMPObjectPredicateFunc)(const NMPObject *obj, gpointer user_data);
 
-/* workaround for older libnl version, that does not define these flags. */
-#ifndef IFA_F_MANAGETEMPADDR
-#define IFA_F_MANAGETEMPADDR 0x100
-#endif
-#ifndef IFA_F_NOPREFIXROUTE
-#define IFA_F_NOPREFIXROUTE 0x200
-#endif
-
 #define NM_RT_SCOPE_LINK 253 /* RT_SCOPE_LINK */
 
-/* Define of the IN6_ADDR_GEN_MODE_* values to workaround old kernel headers
- * that don't define it. */
-#define NM_IN6_ADDR_GEN_MODE_UNKNOWN        255 /* no corresponding value.  */
-#define NM_IN6_ADDR_GEN_MODE_EUI64          0   /* IN6_ADDR_GEN_MODE_EUI64 */
-#define NM_IN6_ADDR_GEN_MODE_NONE           1   /* IN6_ADDR_GEN_MODE_NONE */
-#define NM_IN6_ADDR_GEN_MODE_STABLE_PRIVACY 2   /* IN6_ADDR_GEN_MODE_STABLE_PRIVACY */
-#define NM_IN6_ADDR_GEN_MODE_RANDOM         3   /* IN6_ADDR_GEN_MODE_RANDOM */
-
 #define NM_IFF_MULTI_QUEUE 0x0100 /* IFF_MULTI_QUEUE */
 
 #define NM_MPTCP_PM_ADDR_FLAG_SIGNAL   ((guint32) (1 << 0))
@@ -101,14 +81,6 @@ typedef enum {
 } NMPNlmFlags;
 
 typedef enum {
-    NM_PLATFORM_IP_ADDRESS_CMP_TYPE_ID,
-
-    NM_PLATFORM_IP_ADDRESS_CMP_TYPE_SEMANTICALLY,
-
-    NM_PLATFORM_IP_ADDRESS_CMP_TYPE_FULL,
-} NMPlatformIPAddressCmpType;
-
-typedef enum {
     /* compare fields which kernel considers as similar routes.
      * It is a looser comparisong then NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID
      * and means that `ip route add` would fail to add two routes
@@ -131,6 +103,11 @@ typedef enum {
      */
     NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID,
 
+    /* IPv4 route can have multiple hops. This is the ID, by which multiple
+     * routes are merged according to the next hop. This is basically NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID
+     * which ignores the next hops. */
+    NM_PLATFORM_IP_ROUTE_CMP_TYPE_ECMP_ID,
+
     /* compare all fields as they make sense for kernel. For example,
      * a route destination 192.168.1.5/24 is not accepted by kernel and
      * we treat it identical to 192.168.1.0/24. Semantically these
@@ -171,50 +148,11 @@ G_STATIC_ASSERT(_nm_alignof(NMPLinkAddress) == 1);
 gconstpointer nmp_link_address_get(const NMPLinkAddress *addr, size_t *length);
 GBytes       *nmp_link_address_get_as_bytes(const NMPLinkAddress *addr);
 
-typedef enum {
-
-    /* match-flags are strictly inclusive. That means,
-     * by default nothing is matched, but if you enable a particular
-     * flag, a candidate that matches passes the check.
-     *
-     * In other words: adding more flags can only extend the result
-     * set of matching objects.
-     *
-     * Also, the flags form partitions. Like, an address can be either of
-     * ADDRTYPE_NORMAL or ADDRTYPE_LINKLOCAL, but never both. Same for
-     * the ADDRSTATE match types.
-     */
-    NM_PLATFORM_MATCH_WITH_NONE = 0,
-
-    NM_PLATFORM_MATCH_WITH_ADDRTYPE_NORMAL    = (1LL << 0),
-    NM_PLATFORM_MATCH_WITH_ADDRTYPE_LINKLOCAL = (1LL << 1),
-    NM_PLATFORM_MATCH_WITH_ADDRTYPE__ANY =
-        NM_PLATFORM_MATCH_WITH_ADDRTYPE_NORMAL | NM_PLATFORM_MATCH_WITH_ADDRTYPE_LINKLOCAL,
-
-    NM_PLATFORM_MATCH_WITH_ADDRSTATE_NORMAL     = (1LL << 2),
-    NM_PLATFORM_MATCH_WITH_ADDRSTATE_TENTATIVE  = (1LL << 3),
-    NM_PLATFORM_MATCH_WITH_ADDRSTATE_DADFAILED  = (1LL << 4),
-    NM_PLATFORM_MATCH_WITH_ADDRSTATE_DEPRECATED = (1LL << 5),
-    NM_PLATFORM_MATCH_WITH_ADDRSTATE__ANY =
-        NM_PLATFORM_MATCH_WITH_ADDRSTATE_NORMAL | NM_PLATFORM_MATCH_WITH_ADDRSTATE_TENTATIVE
-        | NM_PLATFORM_MATCH_WITH_ADDRSTATE_DADFAILED | NM_PLATFORM_MATCH_WITH_ADDRSTATE_DEPRECATED,
-} NMPlatformMatchFlags;
-
 #define NM_PLATFORM_LINK_OTHER_NETNS (-1)
 
-struct _NMPlatformObject {
-    /* the object type has no fields of its own, it is only used to having
-     * a special pointer type that can be used to indicate "any" type. */
-    char _dummy_don_t_use_me;
-};
-
-#define __NMPlatformObjWithIfindex_COMMON \
-    int ifindex;                          \
-    ;
-
 struct _NMPlatformObjWithIfindex {
     __NMPlatformObjWithIfindex_COMMON;
-};
+} _nm_alignas(NMPlatformObject);
 
 struct _NMPlatformLink {
     __NMPlatformObjWithIfindex_COMMON;
@@ -271,7 +209,7 @@ struct _NMPlatformLink {
     bool connected : 1;
 
     bool initialized : 1;
-};
+} _nm_alignas(NMPlatformObject);
 
 typedef enum {
     NM_PLATFORM_SIGNAL_ID_NONE,
@@ -295,129 +233,6 @@ typedef enum {
     NM_PLATFORM_SIGNAL_REMOVED,
 } NMPlatformSignalChangeType;
 
-#define NM_PLATFORM_IP_ADDRESS_CAST(address) \
-    NM_CONSTCAST(NMPlatformIPAddress,        \
-                 (address),                  \
-                 NMPlatformIPXAddress,       \
-                 NMPlatformIP4Address,       \
-                 NMPlatformIP6Address)
-
-#define __NMPlatformIPAddress_COMMON                                                         \
-    __NMPlatformObjWithIfindex_COMMON;                                                       \
-    NMIPConfigSource addr_source;                                                            \
-                                                                                             \
-    /* Timestamp in seconds in the reference system of nm_utils_get_monotonic_timestamp_*().
-     *
-     * The rules are:
-     * 1 @lifetime==0: @timestamp and @preferred is irrelevant (but mostly set to 0 too). Such addresses
-     *   are permanent. This rule is so that unset addresses (calloc) are permanent by default.
-     * 2 @lifetime==@preferred==NM_PLATFORM_LIFETIME_PERMANENT: @timestamp is irrelevant (but mostly
-     *   set to 0). Such addresses are permanent.
-     * 3 Non permanent addresses should (almost) always have @timestamp > 0. 0 is not a valid timestamp
-     *   and never returned by nm_utils_get_monotonic_timestamp_sec(). In this case @valid/@preferred
-     *   is anchored at @timestamp.
-     * 4 Non permanent addresses with @timestamp == 0 are implicitly anchored at *now*, thus the time
-     *   moves as time goes by. This is usually not useful, except e.g. nm_platform_ip[46]_address_add().
-     *
-     * Non permanent addresses from DHCP/RA might have the @timestamp set to the moment of when the
-     * lease was received. Addresses from kernel might have the @timestamp based on the last modification
-     * time of the addresses. But don't rely on this behaviour, the @timestamp is only defined for anchoring
-     * @lifetime and @preferred.
-     */ \
-    guint32 timestamp;                                                                       \
-    guint32 lifetime;  /* seconds since timestamp */                                         \
-    guint32 preferred; /* seconds since timestamp */                                         \
-                                                                                             \
-    /* ifa_flags in 'struct ifaddrmsg' from <linux/if_addr.h>, extended to 32 bit by
-     * IFA_FLAGS attribute. */         \
-    guint32 n_ifa_flags;                                                                     \
-                                                                                             \
-    bool use_ip4_broadcast_address : 1;                                                      \
-                                                                                             \
-    /* Meta flags not honored by NMPlatform (netlink code). Instead, they can be
-     * used by the upper layers which use NMPlatformIPRoute to track addresses that
-     * should be configured. */             \
-    bool a_force_commit : 1;                                                                 \
-                                                                                             \
-    /* nm_platform_ip_address_sync() likes to add IFA_F_NOPREFIXROUTE flag for all
-     * addresses, regardless of a_ifi_flags property. By setting this boolean, that
-     * automatism can be suppressed, and the noprefixroute flag does not get added
-     * automatically. */           \
-    bool a_no_auto_noprefixroute : 1;                                                        \
-                                                                                             \
-    /* Don't have a bitfield as last field in __NMPlatformIPAddress_COMMON. It would then
-     * be unclear how the following fields get merged. We could also use a zero bitfield,
-     * but instead we just have there the uint8 field. */    \
-    guint8 plen;                                                                             \
-    ;
-
-/**
- * NMPlatformIPAddress:
- *
- * Common parts of NMPlatformIP4Address and NMPlatformIP6Address.
- **/
-typedef struct {
-    __NMPlatformIPAddress_COMMON;
-    _nm_alignas(NMIPAddr) guint8 address_ptr[];
-} NMPlatformIPAddress;
-
-/**
- * NMPlatformIP4Address:
- * @timestamp: timestamp as returned by nm_utils_get_monotonic_timestamp_sec()
- **/
-struct _NMPlatformIP4Address {
-    __NMPlatformIPAddress_COMMON;
-
-    /* The local address IFA_LOCAL. */
-    _nm_alignas(NMIPAddr) in_addr_t address;
-
-    /* The IFA_ADDRESS PTP peer address. This field is rather important, because
-     * it constitutes the identifier for the IPv4 address (e.g. you can add two
-     * addresses that only differ by their peer's network-part.
-     *
-     * Beware that for most cases, NetworkManager doesn't want to set an explicit
-     * peer-address. However, that corresponds to setting the peer address to @address
-     * itself. Leaving peer-address unset/zero, means explicitly setting the peer
-     * address to 0.0.0.0, which you probably don't want.
-     * */
-    in_addr_t peer_address; /* PTP peer address */
-
-    /* IFA_BROADCAST.
-     *
-     * This parameter is ignored unless use_ip4_broadcast_address is TRUE.
-     * See nm_platform_ip4_broadcast_address_from_addr(). */
-    in_addr_t broadcast_address;
-
-    char label[NMP_IFNAMSIZ];
-
-    /* Whether the address is ready to be configured. By default, an address is, but this
-     * flag may indicate that the address is just for tracking purpose only, but the ACD
-     * state is not yet ready for the address to be configured. */
-    bool a_acd_not_ready : 1;
-};
-
-/**
- * NMPlatformIP6Address:
- * @timestamp: timestamp as returned by nm_utils_get_monotonic_timestamp_sec()
- **/
-struct _NMPlatformIP6Address {
-    __NMPlatformIPAddress_COMMON;
-    _nm_alignas(NMIPAddr) struct in6_addr address;
-    struct in6_addr peer_address;
-};
-
-typedef union {
-    NMPlatformIPAddress  ax;
-    NMPlatformIP4Address a4;
-    NMPlatformIP6Address a6;
-} NMPlatformIPXAddress;
-
-#undef __NMPlatformIPAddress_COMMON
-
-#define NM_PLATFORM_IP4_ADDRESS_INIT(...) (&((const NMPlatformIP4Address){__VA_ARGS__}))
-
-#define NM_PLATFORM_IP6_ADDRESS_INIT(...) (&((const NMPlatformIP6Address){__VA_ARGS__}))
-
 /* Default value for adding an IPv4 route. This is also what iproute2 does.
  * Note that contrary to IPv6, you can add routes with metric 0 and it is even
  * the default.
@@ -545,16 +360,16 @@ typedef union {
      */                                                                          \
     guint8 type_coerced;                                                                  \
                                                                                           \
-    /* Don't have a bitfield as last field in __NMPlatformIPAddress_COMMON. It would then
+    /* Don't have a bitfield as last field in __NMPlatformIPRoute_COMMON. It would then
      * be unclear how the following fields get merged. We could also use a zero bitfield,
-     * but instead we just have there the uint8 field. */ \
+     * but instead we just have there the uint8 field. */   \
     guint8 plen;                                                                          \
     ;
 
 typedef struct {
     __NMPlatformIPRoute_COMMON;
     _nm_alignas(NMIPAddr) guint8 network_ptr[];
-} NMPlatformIPRoute;
+} _nm_alignas(NMPlatformObject) NMPlatformIPRoute;
 
 #define NM_PLATFORM_IP_ROUTE_CAST(route) \
     NM_CONSTCAST(NMPlatformIPRoute,      \
@@ -567,9 +382,27 @@ typedef struct {
 
 struct _NMPlatformIP4Route {
     __NMPlatformIPRoute_COMMON;
+
     in_addr_t network;
 
-    /* RTA_GATEWAY. The gateway is part of the primary key for a route */
+    /* If n_nexthops is zero, the the address has no next hops. That applies
+     *    to certain route types like blackhole.
+     * If n_nexthops is 1, then the fields "ifindex", "gateway" and "weight"
+     *   are the first next-hop. There are no further nexthops.
+     * If n_nexthops is greater than 1, the first next hop is in the fields
+     *   "ifindex", "gateway", "weight", and the (n_nexthops-1) hops are in
+     *   NMPObjectIP4Route.extra_nexthops field (outside the NMPlatformIP4Route
+     *   struct).
+     *
+     * For convenience, if ifindex > 0 and n_nexthops == 0, we assume that n_nexthops
+     * is in fact 1. If ifindex is <= 0, n_nexthops must be zero.
+     * See nm_platform_ip4_route_get_n_nexthops(). */
+    guint n_nexthops;
+
+    /* RTA_GATEWAY. The gateway is part of the primary key for a route.
+     * If n_nexthops is zero, this value is undefined (should be zero).
+     * If n_nexthops is greater or equal to one, this is the gateway of
+     * the first hop. */
     in_addr_t gateway;
 
     /* RTA_PREFSRC (called "src" by iproute2).
@@ -578,6 +411,20 @@ struct _NMPlatformIP4Route {
      * pref_src must match, unless set to 0.0.0.0 to match any. */
     in_addr_t pref_src;
 
+    /* This is the weight of for the first next-hop, in case of n_nexthops > 1.
+     *
+     * If n_nexthops is zero, this value is undefined (should be zero).
+     * If n_nexthops is 1, this also doesn't matter, but it's usually set to
+     * zero.
+     * If n_nexthops is greater or equal to one, this is the weight of
+     * the first hop.
+     *
+     * Note that upper layers (nm_utils_ip_route_attribute_to_platform()) use this flag to indicate
+     * whether this is a multihop route. Single-hop, non-ECMP routes will have a weight of zero.
+     *
+     * The valid range for weight in kernel is 1-256. */
+    guint16 weight;
+
     /* rtm_tos (iproute2: tos)
      *
      * For IPv4, tos is part of the weak-id (like metric).
@@ -597,7 +444,7 @@ struct _NMPlatformIP4Route {
      * For IPv6 routes, the scope is ignored and kernel always assumes global scope.
      * Hence, this field is only in NMPlatformIP4Route. */
     guint8 scope_inv;
-};
+} _nm_alignas(NMPlatformObject);
 
 struct _NMPlatformIP6Route {
     __NMPlatformIPRoute_COMMON;
@@ -630,7 +477,7 @@ struct _NMPlatformIP6Route {
      * The type is guint8 to keep the struct size small. But the values are compatible with
      * the NMIcmpv6RouterPref enum. */
     guint8 rt_pref;
-};
+} _nm_alignas(NMPlatformObject);
 
 typedef union {
     NMPlatformIPRoute  rx;
@@ -684,7 +531,7 @@ typedef struct {
     guint8             ip_proto;                   /* FRA_IP_PROTO */
 
     bool uid_range_has : 1; /* has(FRA_UID_RANGE) */
-} NMPlatformRoutingRule;
+} _nm_alignas(NMPlatformObject) NMPlatformRoutingRule;
 
 #define NM_PLATFORM_FQ_CODEL_MEMORY_LIMIT_UNSET (~((guint32) 0))
 
@@ -752,7 +599,7 @@ typedef struct {
         NMPlatformQdiscSfq     sfq;
         NMPlatformQdiscTbf     tbf;
     };
-} NMPlatformQdisc;
+} _nm_alignas(NMPlatformObject) NMPlatformQdisc;
 
 typedef struct {
     char sdata[32];
@@ -792,28 +639,7 @@ typedef struct {
     guint32          parent;
     guint32          info;
     NMPlatformAction action;
-} NMPlatformTfilter;
-
-typedef struct {
-    bool          is_ip4;
-    NMPObjectType obj_type;
-    gint8         addr_family;
-    guint8        sizeof_address;
-    int (*address_cmp)(const NMPlatformIPXAddress *a,
-                       const NMPlatformIPXAddress *b,
-                       NMPlatformIPAddressCmpType  cmp_type);
-    const char *(*address_to_string)(const NMPlatformIPXAddress *address, char *buf, gsize len);
-} NMPlatformVTableAddress;
-
-typedef union {
-    struct {
-        NMPlatformVTableAddress v6;
-        NMPlatformVTableAddress v4;
-    };
-    NMPlatformVTableAddress vx[2];
-} _NMPlatformVTableAddressUnion;
-
-extern const _NMPlatformVTableAddressUnion nm_platform_vtable_address;
+} _nm_alignas(NMPlatformObject) NMPlatformTfilter;
 
 typedef struct {
     bool          is_ip4;
@@ -843,6 +669,25 @@ typedef struct {
 } NMPlatformVFVlan;
 
 typedef struct {
+    int       ifindex;
+    in_addr_t gateway;
+    /* The valid range for weight is 1-256. Single hop routes in kernel
+     * don't have a weight, we assign them weight zero (to indicate the
+     * weight is missing).
+     *
+     * Upper layers (nm_utils_ip_route_attribute_to_platform()) care about
+     * the distinction of unset weight (no-ECMP). They express no-ECMP as
+     * zero.
+     */
+    guint16 weight;
+
+    /* FIXME: each next hop in kernel also has a rtnh_flags (for example to
+     * set RTNH_F_ONLINK). As the next hop is part of the identifier of an
+     * IPv4 route, so is their flags. We must also track the flag, otherwise
+     * two routes that look different for kernel, get merged by platform cache. */
+} NMPlatformIP4RtNextHop;
+
+typedef struct {
     guint             num_vlans;
     guint32           index;
     guint32           min_tx_rate;
@@ -887,7 +732,7 @@ typedef struct {
     bool        mcast_snooping : 1;
     bool        stp_state : 1;
     bool        vlan_stats_enabled : 1;
-} NMPlatformLnkBridge;
+} _nm_alignas(NMPlatformObject) NMPlatformLnkBridge;
 
 extern const NMPlatformLnkBridge nm_platform_lnk_bridge_default;
 
@@ -929,7 +774,7 @@ typedef struct {
     bool        tlb_dynamic_lb_has : 1;
     bool        updelay_has : 1;
     bool        use_carrier : 1;
-} NMPlatformLnkBond;
+} _nm_alignas(NMPlatformObject) NMPlatformLnkBond;
 
 typedef struct {
     int       parent_ifindex;
@@ -943,12 +788,12 @@ typedef struct {
     guint8    tos;
     bool      path_mtu_discovery : 1;
     bool      is_tap : 1;
-} NMPlatformLnkGre;
+} _nm_alignas(NMPlatformObject) NMPlatformLnkGre;
 
 typedef struct {
     int         p_key;
     const char *mode;
-} NMPlatformLnkInfiniband;
+} _nm_alignas(NMPlatformObject) NMPlatformLnkInfiniband;
 
 typedef struct {
     struct in6_addr local;
@@ -968,7 +813,7 @@ typedef struct {
     guint16 output_flags;
     bool    is_tap : 1;
     bool    is_gre : 1;
-} NMPlatformLnkIp6Tnl;
+} _nm_alignas(NMPlatformObject) NMPlatformLnkIp6Tnl;
 
 typedef struct {
     int       parent_ifindex;
@@ -977,9 +822,28 @@ typedef struct {
     guint8    ttl;
     guint8    tos;
     bool      path_mtu_discovery : 1;
-} NMPlatformLnkIpIp;
+} _nm_alignas(NMPlatformObject) NMPlatformLnkIpIp;
+
+typedef struct {
+    int       parent_ifindex;
+    in_addr_t local;
+    in_addr_t remote;
+    guint32   ikey;
+    guint32   okey;
+    guint32   fwmark;
+} _nm_alignas(NMPlatformObject) NMPlatformLnkVti;
+
+typedef struct {
+    int             parent_ifindex;
+    struct in6_addr local;
+    struct in6_addr remote;
+    guint32         ikey;
+    guint32         okey;
+    guint32         fwmark;
+} _nm_alignas(NMPlatformObject) NMPlatformLnkVti6;
 
 typedef struct {
+    int     parent_ifindex;
     guint64 sci; /* host byte order */
     guint64 cipher_suite;
     guint32 window;
@@ -992,13 +856,13 @@ typedef struct {
     bool    es : 1;
     bool    scb : 1;
     bool    replay_protect : 1;
-} NMPlatformLnkMacsec;
+} _nm_alignas(NMPlatformObject) NMPlatformLnkMacsec;
 
 typedef struct {
     guint mode;
     bool  no_promisc : 1;
     bool  tap : 1;
-} NMPlatformLnkMacvlan;
+} _nm_alignas(NMPlatformObject) NMPlatformLnkMacvlan;
 
 typedef struct {
     int       parent_ifindex;
@@ -1009,7 +873,7 @@ typedef struct {
     guint8    tos;
     guint8    proto;
     bool      path_mtu_discovery : 1;
-} NMPlatformLnkSit;
+} _nm_alignas(NMPlatformObject) NMPlatformLnkSit;
 
 typedef struct {
     guint32 owner;
@@ -1024,17 +888,17 @@ typedef struct {
     bool vnet_hdr : 1;
     bool multi_queue : 1;
     bool persist : 1;
-} NMPlatformLnkTun;
+} _nm_alignas(NMPlatformObject) NMPlatformLnkTun;
 
 typedef struct {
-    /* rtnl_link_vlan_get_id(), IFLA_VLAN_ID */
     guint16      id;
+    guint16      protocol;
     _NMVlanFlags flags;
-} NMPlatformLnkVlan;
+} _nm_alignas(NMPlatformObject) NMPlatformLnkVlan;
 
 typedef struct {
     guint32 table;
-} NMPlatformLnkVrf;
+} _nm_alignas(NMPlatformObject) NMPlatformLnkVrf;
 
 typedef struct {
     struct in6_addr group6;
@@ -1055,7 +919,7 @@ typedef struct {
     bool            rsc : 1;
     bool            l2miss : 1;
     bool            l3miss : 1;
-} NMPlatformLnkVxlan;
+} _nm_alignas(NMPlatformObject) NMPlatformLnkVxlan;
 
 #define NMP_WIREGUARD_PUBLIC_KEY_LEN    32
 #define NMP_WIREGUARD_SYMMETRIC_KEY_LEN 32
@@ -1065,7 +929,7 @@ typedef struct {
     guint16 listen_port;
     guint8  private_key[NMP_WIREGUARD_PUBLIC_KEY_LEN];
     guint8  public_key[NMP_WIREGUARD_PUBLIC_KEY_LEN];
-} NMPlatformLnkWireGuard;
+} _nm_alignas(NMPlatformObject) NMPlatformLnkWireGuard;
 
 typedef enum {
     NM_PLATFORM_WIREGUARD_CHANGE_FLAG_NONE            = 0,
@@ -1370,10 +1234,7 @@ typedef struct {
                                    struct in6_addr address,
                                    guint8          plen);
 
-    int (*ip_route_add)(NMPlatform              *self,
-                        NMPNlmFlags              flags,
-                        int                      addr_family,
-                        const NMPlatformIPRoute *route);
+    int (*ip_route_add)(NMPlatform *self, NMPNlmFlags flags, NMPObject *obj_stack);
     int (*ip_route_get)(NMPlatform   *self,
                         int           addr_family,
                         gconstpointer address,
@@ -1426,29 +1287,6 @@ GType nm_platform_get_type(void);
 
 /*****************************************************************************/
 
-static inline in_addr_t
-nm_platform_ip4_broadcast_address_create(in_addr_t address, guint8 plen)
-{
-    return address | ~_nm_utils_ip4_prefix_to_netmask(plen);
-}
-
-static inline in_addr_t
-nm_platform_ip4_broadcast_address_from_addr(const NMPlatformIP4Address *addr)
-{
-    nm_assert(addr);
-
-    if (addr->use_ip4_broadcast_address)
-        return addr->broadcast_address;
-
-    /* the set broadcast-address gets ignored, and we determine a default brd base
-     * on the peer IFA_ADDRESS. */
-    if (addr->peer_address != 0u && addr->plen < 31 /* RFC3021 */)
-        return nm_platform_ip4_broadcast_address_create(addr->peer_address, addr->plen);
-    return 0u;
-}
-
-/*****************************************************************************/
-
 /**
  * nm_platform_route_table_coerce:
  * @table: the route table, in its original value as received
@@ -1589,9 +1427,9 @@ nm_platform_ip4_address_get_scope(in_addr_t addr)
     /* For IPv4 addresses, we can set any scope we want (for any address).
      * However, there are scopes that make sense based on the address,
      * so choose those. */
-    return nm_utils_ip4_address_is_loopback(addr)     ? (254 /* RT_SCOPE_HOST */)
-           : nm_utils_ip4_address_is_link_local(addr) ? (253 /* RT_SCOPE_LINK */)
-                                                      : (0 /* RT_SCOPE_UNIVERSE */);
+    return nm_ip4_addr_is_loopback(addr)     ? (254 /* RT_SCOPE_HOST */)
+           : nm_ip4_addr_is_link_local(addr) ? (253 /* RT_SCOPE_LINK */)
+                                             : (0 /* RT_SCOPE_UNIVERSE */);
 }
 
 static inline guint8
@@ -1612,7 +1450,7 @@ nm_platform_ip_address_get_scope(int addr_family, gconstpointer addr)
     /* Note that this function returns the scope as we configure
      * it in kernel (for IPv4) or as kernel chooses it (for IPv6).
      *
-     * That means, rfc1918 private addresses nm_utils_ip_is_site_local() are
+     * That means, rfc1918 private addresses nm_ip_addr_is_site_local() are
      * considered RT_SCOPE_UNIVERSE.
      *
      * Also, the deprecated IN6_IS_ADDR_SITELOCAL() addresses (fec0::/10)
@@ -1851,28 +1689,13 @@ nm_platform_link_sit_add(NMPlatform             *self,
 }
 
 static inline int
-nm_platform_link_vlan_add(NMPlatform            *self,
-                          const char            *name,
-                          int                    parent,
-                          int                    vlanid,
-                          guint32                vlanflags,
-                          const NMPlatformLink **out_link)
+nm_platform_link_vlan_add(NMPlatform              *self,
+                          const char              *name,
+                          int                      parent,
+                          const NMPlatformLnkVlan *props,
+                          const NMPlatformLink   **out_link)
 {
-    g_return_val_if_fail(parent >= 0, -NME_BUG);
-    g_return_val_if_fail(vlanid >= 0, -NME_BUG);
-
-    return nm_platform_link_add(self,
-                                NM_LINK_TYPE_VLAN,
-                                name,
-                                parent,
-                                NULL,
-                                0,
-                                0,
-                                &((NMPlatformLnkVlan){
-                                    .id    = vlanid,
-                                    .flags = vlanflags,
-                                }),
-                                out_link);
+    return nm_platform_link_add(self, NM_LINK_TYPE_VLAN, name, parent, NULL, 0, 0, props, out_link);
 }
 
 static inline int
@@ -1885,6 +1708,24 @@ nm_platform_link_vrf_add(NMPlatform             *self,
 }
 
 static inline int
+nm_platform_link_vti_add(NMPlatform             *self,
+                         const char             *name,
+                         const NMPlatformLnkVti *props,
+                         const NMPlatformLink  **out_link)
+{
+    return nm_platform_link_add(self, NM_LINK_TYPE_VTI, name, 0, NULL, 0, 0, props, out_link);
+}
+
+static inline int
+nm_platform_link_vti6_add(NMPlatform              *self,
+                          const char              *name,
+                          const NMPlatformLnkVti6 *props,
+                          const NMPlatformLink   **out_link)
+{
+    return nm_platform_link_add(self, NM_LINK_TYPE_VTI6, name, 0, NULL, 0, 0, props, out_link);
+}
+
+static inline int
 nm_platform_link_vxlan_add(NMPlatform               *self,
                            const char               *name,
                            const NMPlatformLnkVxlan *props,
@@ -2173,6 +2014,10 @@ const NMPlatformLnkVlan *
 nm_platform_link_get_lnk_vlan(NMPlatform *self, int ifindex, const NMPlatformLink **out_link);
 const NMPlatformLnkVrf *
 nm_platform_link_get_lnk_vrf(NMPlatform *self, int ifindex, const NMPlatformLink **out_link);
+const NMPlatformLnkVti *
+nm_platform_link_get_lnk_vti(NMPlatform *self, int ifindex, const NMPlatformLink **out_link);
+const NMPlatformLnkVti6 *
+nm_platform_link_get_lnk_vti6(NMPlatform *self, int ifindex, const NMPlatformLink **out_link);
 const NMPlatformLnkVxlan *
 nm_platform_link_get_lnk_vxlan(NMPlatform *self, int ifindex, const NMPlatformLink **out_link);
 const NMPlatformLnkWireGuard *
@@ -2238,9 +2083,6 @@ guint16  nm_platform_wpan_get_short_addr(NMPlatform *self, int ifindex);
 gboolean nm_platform_wpan_set_short_addr(NMPlatform *self, int ifindex, guint16 short_addr);
 gboolean nm_platform_wpan_set_channel(NMPlatform *self, int ifindex, guint8 page, guint8 channel);
 
-void nm_platform_ip4_address_set_addr(NMPlatformIP4Address *addr, in_addr_t address, guint8 plen);
-const struct in6_addr *nm_platform_ip6_address_get_peer(const NMPlatformIP6Address *addr);
-
 const NMPObject *nm_platform_ip_address_get(NMPlatform                                 *self,
                                             int                                         addr_family,
                                             int                                         ifindex,
@@ -2252,6 +2094,9 @@ const NMPlatformIP4Address *nm_platform_ip4_address_get(NMPlatform *self,
                                                         guint8      plen,
                                                         in_addr_t   peer_address);
 
+const NMPlatformIP6Address *
+nm_platform_ip6_address_get(NMPlatform *self, int ifindex, const struct in6_addr *address);
+
 int      nm_platform_link_sit_add(NMPlatform             *self,
                                   const char             *name,
                                   const NMPlatformLnkSit *props,
@@ -2274,9 +2119,6 @@ int nm_platform_link_wireguard_change(NMPlatform                               *
                                       guint                                     peers_len,
                                       NMPlatformWireGuardChangeFlags            change_flags);
 
-const NMPlatformIP6Address *
-nm_platform_ip6_address_get(NMPlatform *self, int ifindex, const struct in6_addr *address);
-
 gboolean nm_platform_object_delete(NMPlatform *self, const NMPObject *route);
 
 gboolean nm_platform_ip4_address_add(NMPlatform *self,
@@ -2328,11 +2170,17 @@ nm_platform_ip_address_delete(NMPlatform                                       *
     }
 }
 
-gboolean nm_platform_ip_address_sync(NMPlatform *self,
-                                     int         addr_family,
-                                     int         ifindex,
-                                     GPtrArray  *known_addresses,
-                                     GPtrArray  *addresses_prune);
+typedef enum {
+    NMP_IP_ADDRESS_SYNC_FLAGS_NONE               = 0,
+    NMP_IP_ADDRESS_SYNC_FLAGS_WITH_NOPREFIXROUTE = (1 << 0),
+} NMPIPAddressSyncFlags;
+
+gboolean nm_platform_ip_address_sync(NMPlatform           *self,
+                                     int                   addr_family,
+                                     int                   ifindex,
+                                     GPtrArray            *known_addresses,
+                                     GPtrArray            *addresses_prune,
+                                     NMPIPAddressSyncFlags flags);
 
 GPtrArray *
 nm_platform_ip_address_get_prune_list(NMPlatform            *self,
@@ -2343,17 +2191,6 @@ nm_platform_ip_address_get_prune_list(NMPlatform            *self,
 
 gboolean nm_platform_ip_address_flush(NMPlatform *self, int addr_family, int ifindex);
 
-static inline gpointer
-nm_platform_ip_address_get_peer_address(int addr_family, const NMPlatformIPAddress *addr)
-{
-    nm_assert_addr_family(addr_family);
-    nm_assert(addr);
-
-    if (NM_IS_IPv4(addr_family))
-        return &((NMPlatformIP4Address *) addr)->peer_address;
-    return &((NMPlatformIP6Address *) addr)->peer_address;
-}
-
 void nm_platform_ip_route_normalize(int addr_family, NMPlatformIPRoute *route);
 
 static inline guint32
@@ -2365,6 +2202,23 @@ nm_platform_ip4_route_get_effective_metric(const NMPlatformIP4Route *r)
                          : r->metric;
 }
 
+static inline guint
+nm_platform_ip4_route_get_n_nexthops(const NMPlatformIP4Route *r)
+{
+    /* The first hop of the "n_nexthops" is in NMPlatformIP4Route
+     * itself. Thus, if the caller only sets ifindex and leaves
+     * n_nexthops at zero, the number of next hops is still 1
+     * (for convenience of the user who wants to initialize a
+     * single hop route). */
+    if (r->n_nexthops >= 1) {
+        nm_assert(r->ifindex > 0);
+        return r->n_nexthops;
+    }
+    if (r->ifindex > 0)
+        return 1;
+    return 0;
+}
+
 static inline guint32
 nm_platform_ip6_route_get_effective_metric(const NMPlatformIP6Route *r)
 {
@@ -2398,7 +2252,10 @@ nm_platform_ip_route_get_gateway(int addr_family, const NMPlatformIPRoute *route
 }
 
 int nm_platform_ip_route_add(NMPlatform *self, NMPNlmFlags flags, const NMPObject *route);
-int nm_platform_ip4_route_add(NMPlatform *self, NMPNlmFlags flags, const NMPlatformIP4Route *route);
+int nm_platform_ip4_route_add(NMPlatform                   *self,
+                              NMPNlmFlags                   flags,
+                              const NMPlatformIP4Route     *route,
+                              const NMPlatformIP4RtNextHop *extra_nexthops);
 int nm_platform_ip6_route_add(NMPlatform *self, NMPNlmFlags flags, const NMPlatformIP6Route *route);
 
 GPtrArray *nm_platform_ip_route_get_prune_list(NMPlatform            *self,
@@ -2449,14 +2306,23 @@ const char *nm_platform_lnk_sit_to_string(const NMPlatformLnkSit *lnk, char *buf
 const char *nm_platform_lnk_tun_to_string(const NMPlatformLnkTun *lnk, char *buf, gsize len);
 const char *nm_platform_lnk_vlan_to_string(const NMPlatformLnkVlan *lnk, char *buf, gsize len);
 const char *nm_platform_lnk_vrf_to_string(const NMPlatformLnkVrf *lnk, char *buf, gsize len);
+const char *nm_platform_lnk_vti_to_string(const NMPlatformLnkVti *lnk, char *buf, gsize len);
+const char *nm_platform_lnk_vti6_to_string(const NMPlatformLnkVti6 *lnk, char *buf, gsize len);
 const char *nm_platform_lnk_vxlan_to_string(const NMPlatformLnkVxlan *lnk, char *buf, gsize len);
 const char *
 nm_platform_lnk_wireguard_to_string(const NMPlatformLnkWireGuard *lnk, char *buf, gsize len);
-const char *
-nm_platform_ip4_address_to_string(const NMPlatformIP4Address *address, char *buf, gsize len);
-const char *
-nm_platform_ip6_address_to_string(const NMPlatformIP6Address *address, char *buf, gsize len);
-const char *nm_platform_ip4_route_to_string(const NMPlatformIP4Route *route, char *buf, gsize len);
+
+const char *nm_platform_ip4_route_to_string_full(const NMPlatformIP4Route     *route,
+                                                 const NMPlatformIP4RtNextHop *extra_nexthops,
+                                                 char                         *buf,
+                                                 gsize                         len);
+
+static inline const char *
+nm_platform_ip4_route_to_string(const NMPlatformIP4Route *route, char *buf, gsize len)
+{
+    return nm_platform_ip4_route_to_string_full(route, NULL, buf, len);
+}
+
 const char *nm_platform_ip6_route_to_string(const NMPlatformIP6Route *route, char *buf, gsize len);
 const char *
 nm_platform_routing_rule_to_string(const NMPlatformRoutingRule *routing_rule, char *buf, gsize len);
@@ -2492,79 +2358,52 @@ int nm_platform_lnk_sit_cmp(const NMPlatformLnkSit *a, const NMPlatformLnkSit *b
 int nm_platform_lnk_tun_cmp(const NMPlatformLnkTun *a, const NMPlatformLnkTun *b);
 int nm_platform_lnk_vlan_cmp(const NMPlatformLnkVlan *a, const NMPlatformLnkVlan *b);
 int nm_platform_lnk_vrf_cmp(const NMPlatformLnkVrf *a, const NMPlatformLnkVrf *b);
+int nm_platform_lnk_vti_cmp(const NMPlatformLnkVti *a, const NMPlatformLnkVti *b);
+int nm_platform_lnk_vti6_cmp(const NMPlatformLnkVti6 *a, const NMPlatformLnkVti6 *b);
 int nm_platform_lnk_vxlan_cmp(const NMPlatformLnkVxlan *a, const NMPlatformLnkVxlan *b);
 int nm_platform_lnk_wireguard_cmp(const NMPlatformLnkWireGuard *a, const NMPlatformLnkWireGuard *b);
-int nm_platform_ip4_address_cmp(const NMPlatformIP4Address *a,
-                                const NMPlatformIP4Address *b,
-                                NMPlatformIPAddressCmpType  cmp_type);
-int nm_platform_ip6_address_cmp(const NMPlatformIP6Address *a,
-                                const NMPlatformIP6Address *b,
-                                NMPlatformIPAddressCmpType  cmp_type);
-
-static inline int
-nm_platform_ip4_address_cmp_full(const NMPlatformIP4Address *a, const NMPlatformIP4Address *b)
-{
-    return nm_platform_ip4_address_cmp(a, b, NM_PLATFORM_IP_ADDRESS_CMP_TYPE_FULL);
-}
-
-static inline int
-nm_platform_ip6_address_cmp_full(const NMPlatformIP6Address *a, const NMPlatformIP6Address *b)
-{
-    return nm_platform_ip6_address_cmp(a, b, NM_PLATFORM_IP_ADDRESS_CMP_TYPE_FULL);
-}
-
-int nm_platform_ip4_address_pretty_sort_cmp(const NMPlatformIP4Address *a1,
-                                            const NMPlatformIP4Address *a2);
-
-int nm_platform_ip6_address_pretty_sort_cmp(const NMPlatformIP6Address *a1,
-                                            const NMPlatformIP6Address *a2,
-                                            gboolean                    prefer_temp);
 
 GHashTable *nm_platform_ip4_address_addr_to_hash(NMPlatform *self, int ifindex);
 
 int nm_platform_ip4_route_cmp(const NMPlatformIP4Route *a,
                               const NMPlatformIP4Route *b,
                               NMPlatformIPRouteCmpType  cmp_type);
+int nm_platform_ip4_rt_nexthop_cmp(const NMPlatformIP4RtNextHop *a,
+                                   const NMPlatformIP4RtNextHop *b,
+                                   gboolean                      for_id);
 int nm_platform_ip6_route_cmp(const NMPlatformIP6Route *a,
                               const NMPlatformIP6Route *b,
                               NMPlatformIPRouteCmpType  cmp_type);
 
-static inline int
-nm_platform_ip4_route_cmp_full(const NMPlatformIP4Route *a, const NMPlatformIP4Route *b)
-{
-    return nm_platform_ip4_route_cmp(a, b, NM_PLATFORM_IP_ROUTE_CMP_TYPE_FULL);
-}
-
-static inline int
-nm_platform_ip6_route_cmp_full(const NMPlatformIP6Route *a, const NMPlatformIP6Route *b)
-{
-    return nm_platform_ip6_route_cmp(a, b, NM_PLATFORM_IP_ROUTE_CMP_TYPE_FULL);
-}
-
 int nm_platform_routing_rule_cmp(const NMPlatformRoutingRule *a,
                                  const NMPlatformRoutingRule *b,
                                  NMPlatformRoutingRuleCmpType cmp_type);
 
-static inline int
-nm_platform_routing_rule_cmp_full(const NMPlatformRoutingRule *a, const NMPlatformRoutingRule *b)
-{
-    return nm_platform_routing_rule_cmp(a, b, NM_PLATFORM_ROUTING_RULE_CMP_TYPE_FULL);
-}
+int
+nm_platform_qdisc_cmp(const NMPlatformQdisc *a, const NMPlatformQdisc *b, gboolean compare_handle);
 
-int nm_platform_qdisc_cmp(const NMPlatformQdisc *a, const NMPlatformQdisc *b);
-int nm_platform_qdisc_cmp_full(const NMPlatformQdisc *a,
-                               const NMPlatformQdisc *b,
-                               gboolean               compare_handle);
 int nm_platform_tfilter_cmp(const NMPlatformTfilter *a, const NMPlatformTfilter *b);
 
 int nm_platform_mptcp_addr_cmp(const NMPlatformMptcpAddr *a, const NMPlatformMptcpAddr *b);
 
 void nm_platform_link_hash_update(const NMPlatformLink *obj, NMHashState *h);
-void nm_platform_ip4_address_hash_update(const NMPlatformIP4Address *obj, NMHashState *h);
-void nm_platform_ip6_address_hash_update(const NMPlatformIP6Address *obj, NMHashState *h);
 void nm_platform_ip4_route_hash_update(const NMPlatformIP4Route *obj,
                                        NMPlatformIPRouteCmpType  cmp_type,
                                        NMHashState              *h);
+
+static inline guint
+nm_platform_ip4_route_hash(const NMPlatformIP4Route *obj, NMPlatformIPRouteCmpType cmp_type)
+{
+    NMHashState h;
+
+    nm_hash_init(&h, 1118769853u);
+    nm_platform_ip4_route_hash_update(obj, cmp_type, &h);
+    return nm_hash_complete(&h);
+}
+
+void nm_platform_ip4_rt_nexthop_hash_update(const NMPlatformIP4RtNextHop *obj,
+                                            gboolean                      for_id,
+                                            NMHashState                  *h);
 void nm_platform_ip6_route_hash_update(const NMPlatformIP6Route *obj,
                                        NMPlatformIPRouteCmpType  cmp_type,
                                        NMHashState              *h);
@@ -2583,6 +2422,8 @@ void nm_platform_lnk_sit_hash_update(const NMPlatformLnkSit *obj, NMHashState *h
 void nm_platform_lnk_tun_hash_update(const NMPlatformLnkTun *obj, NMHashState *h);
 void nm_platform_lnk_vlan_hash_update(const NMPlatformLnkVlan *obj, NMHashState *h);
 void nm_platform_lnk_vrf_hash_update(const NMPlatformLnkVrf *obj, NMHashState *h);
+void nm_platform_lnk_vti_hash_update(const NMPlatformLnkVti *obj, NMHashState *h);
+void nm_platform_lnk_vti6_hash_update(const NMPlatformLnkVti6 *obj, NMHashState *h);
 void nm_platform_lnk_vxlan_hash_update(const NMPlatformLnkVxlan *obj, NMHashState *h);
 void nm_platform_lnk_wireguard_hash_update(const NMPlatformLnkWireGuard *obj, NMHashState *h);
 
@@ -2596,13 +2437,6 @@ gboolean nm_platform_mptcp_addr_index_addr_equal(gconstpointer data_a, gconstpoi
 
 #define NM_PLATFORM_LINK_FLAGS2STR_MAX_LEN ((gsize) 162)
 
-const char *nm_platform_link_flags2str(unsigned flags, char *buf, gsize len);
-const char *nm_platform_link_inet6_addrgenmode2str(guint8 mode, char *buf, gsize len);
-const char *nm_platform_addr_flags2str(unsigned flags, char *buf, gsize len);
-const char *nm_platform_route_scope2str(int scope, char *buf, gsize len);
-
-int nm_platform_ip_address_cmp_expiry(const NMPlatformIPAddress *a, const NMPlatformIPAddress *b);
-
 gboolean nm_platform_ethtool_set_wake_on_lan(NMPlatform              *self,
                                              int                      ifindex,
                                              _NMSettingWiredWakeOnLan wol,
@@ -2653,21 +2487,6 @@ struct _NMDedupMultiIndex *nm_platform_get_multi_idx(NMPlatform *self);
 
 /*****************************************************************************/
 
-NMPlatformIP4Route *nm_platform_ip4_address_generate_device_route(const NMPlatformIP4Address *addr,
-                                                                  int                 ifindex,
-                                                                  guint32             route_table,
-                                                                  guint32             route_metric,
-                                                                  gboolean            force_commit,
-                                                                  NMPlatformIP4Route *dst);
-
-/*****************************************************************************/
-
-gboolean nm_platform_ip_address_match(int                        addr_family,
-                                      const NMPlatformIPAddress *addr,
-                                      NMPlatformMatchFlags       match_flag);
-
-/*****************************************************************************/
-
 guint16 nm_platform_genl_get_family_id(NMPlatform *self, NMPGenlFamilyType family_type);
 
 int
@@ -2675,4 +2494,35 @@ nm_platform_mptcp_addr_update(NMPlatform *self, NMOptionBool add, const NMPlatfo
 
 GPtrArray *nm_platform_mptcp_addrs_dump(NMPlatform *self);
 
+gboolean nm_platform_ip6_dadfailed_check(NMPlatform *self, int ifindex, const struct in6_addr *ip6);
+void     nm_platform_ip6_dadfailed_set(NMPlatform            *self,
+                                       int                    ifindex,
+                                       const struct in6_addr *ip6,
+                                       gboolean               failed);
+
+/*****************************************************************************/
+
+static inline NMPlatformIP4Address *
+nm_platform_ip4_address_init_loopback_addr1(NMPlatformIP4Address *a)
+{
+    *a = ((NMPlatformIP4Address){
+        .address      = NM_IPV4LO_ADDR1,
+        .peer_address = NM_IPV4LO_ADDR1,
+        .ifindex      = NM_LOOPBACK_IFINDEX,
+        .plen         = NM_IPV4LO_PREFIXLEN,
+    });
+    return a;
+}
+
+static inline NMPlatformIP6Address *
+nm_platform_ip6_address_init_loopback(NMPlatformIP6Address *a)
+{
+    *a = ((NMPlatformIP6Address){
+        .address = IN6ADDR_LOOPBACK_INIT,
+        .ifindex = NM_LOOPBACK_IFINDEX,
+        .plen    = 128,
+    });
+    return a;
+}
+
 #endif /* __NETWORKMANAGER_PLATFORM_H__ */