summary refs log tree commit diff
path: root/src/libnm-glib-aux/nm-shared-utils.h
diff options
context:
space:
mode:
authorMichael Biebl <biebl@debian.org>2023-02-10 11:50:34 +0100
committerMichael Biebl <biebl@debian.org>2023-02-10 11:50:34 +0100
commit1372848511cb896b80b51ed1a3e9606bd9816631 (patch)
tree674792b9385bdef935988894b45f06b2af39f88c /src/libnm-glib-aux/nm-shared-utils.h
parent40ec077ea305994c1fc2130add6787ca0c73e2c6 (diff)
New upstream version 1.42.0 upstream/1.42.0
Diffstat (limited to 'src/libnm-glib-aux/nm-shared-utils.h')
-rw-r--r--src/libnm-glib-aux/nm-shared-utils.h692
1 files changed, 231 insertions, 461 deletions
diff --git a/src/libnm-glib-aux/nm-shared-utils.h b/src/libnm-glib-aux/nm-shared-utils.h
index 53cf7f3e..083ed137 100644
--- a/src/libnm-glib-aux/nm-shared-utils.h
+++ b/src/libnm-glib-aux/nm-shared-utils.h
@@ -139,6 +139,8 @@ typedef enum {
     NM_LINK_TYPE_VETH,
     NM_LINK_TYPE_VLAN,
     NM_LINK_TYPE_VRF,
+    NM_LINK_TYPE_VTI,
+    NM_LINK_TYPE_VTI6,
     NM_LINK_TYPE_VXLAN,
     NM_LINK_TYPE_WIREGUARD,
 #define _NM_LINK_TYPE_SW_LAST NM_LINK_TYPE_WIREGUARD
@@ -198,6 +200,13 @@ typedef struct {
 
 #define NM_ETHER_ADDR_INIT(...) ((NMEtherAddr) _NM_ETHER_ADDR_INIT(__VA_ARGS__))
 
+struct _NMIPAddr;
+extern const struct _NMIPAddr nm_ip_addr_zero;
+
+/* Let's reuse nm_ip_addr_zero also for nm_ether_addr_zero. It's a union that
+ * also contains a NMEtherAddr field. */
+#define nm_ether_addr_zero (*((const NMEtherAddr *) ((gconstpointer) &nm_ip_addr_zero)))
+
 static inline int
 nm_ether_addr_cmp(const NMEtherAddr *a, const NMEtherAddr *b)
 {
@@ -212,126 +221,10 @@ nm_ether_addr_equal(const NMEtherAddr *a, const NMEtherAddr *b)
     return nm_ether_addr_cmp(a, b) == 0;
 }
 
-/*****************************************************************************/
-
-typedef struct {
-    union {
-        guint8          addr_ptr[sizeof(struct in6_addr)];
-        in_addr_t       addr4;
-        struct in_addr  addr4_struct;
-        struct in6_addr addr6;
-
-        /* NMIPAddr is really a union for IP addresses.
-         * However, as ethernet addresses fit in here nicely, use
-         * it also for an ethernet MAC address. */
-        guint8      ether_addr_octet[6 /*ETH_ALEN*/];
-        NMEtherAddr ether_addr;
-
-        guint8 array[sizeof(struct in6_addr)];
-    };
-} NMIPAddr;
-
-#define NM_IP_ADDR_INIT \
-    {                   \
-        .array = { 0 }  \
-    }
-
-extern const NMIPAddr nm_ip_addr_zero;
-
-#define nm_ether_addr_zero (nm_ip_addr_zero.ether_addr)
-
-static inline int
-nm_ip_addr_cmp(int addr_family, gconstpointer a, gconstpointer b)
-{
-    /* Note that @a and @b are not required to be full NMIPAddr unions.
-     * Depending on @addr_family, they can also be only in_addr_t or
-     * struct in6_addr. */
-    NM_CMP_SELF(a, b);
-    NM_CMP_DIRECT_MEMCMP(a, b, nm_utils_addr_family_to_size(addr_family));
-    return 0;
-}
-
-int nm_ip_addr_cmp_for_sort(gconstpointer a, gconstpointer b, gpointer user_data);
-
 static inline gboolean
-nm_ip_addr_equal(int addr_family, gconstpointer a, gconstpointer b)
+nm_ether_addr_is_zero(const NMEtherAddr *a)
 {
-    return nm_ip_addr_cmp(addr_family, a, b) == 0;
-}
-
-static inline gboolean
-nm_ip_addr_is_null(int addr_family, gconstpointer addr)
-{
-    nm_assert(addr);
-
-    if (NM_IS_IPv4(addr_family)) {
-        in_addr_t t;
-
-        /* also for in_addr_t type (AF_INET), we accept that the pointer might
-         * be unaligned. */
-        memcpy(&t, addr, sizeof(t));
-        return t == 0;
-    }
-
-    return IN6_IS_ADDR_UNSPECIFIED((const struct in6_addr *) addr);
-}
-
-static inline void
-nm_ip_addr_set(int addr_family, gpointer dst, gconstpointer src)
-{
-    nm_assert(dst);
-    nm_assert(src);
-
-    /* this MUST use memcpy() to support unaligned src/dst pointers. */
-    memcpy(dst, src, nm_utils_addr_family_to_size(addr_family));
-
-    /* Note that @dst is not necessarily a NMIPAddr, it could also be just
-     * an in_addr_t/struct in6_addr. We thus can only set the bytes that
-     * we know are present based on the address family.
-     *
-     * Using this function to initialize an NMIPAddr union (for IPv4) leaves
-     * uninitalized bytes. Avoid that by using nm_ip_addr_init() instead. */
-}
-
-static inline NMIPAddr
-nm_ip_addr_init(int addr_family, gconstpointer src)
-{
-    NMIPAddr a;
-
-    nm_assert_addr_family(addr_family);
-    nm_assert(src);
-
-    G_STATIC_ASSERT_EXPR(sizeof(NMIPAddr) == sizeof(struct in6_addr));
-
-    /* this MUST use memcpy() to support unaligned src/dst pointers. */
-
-    if (NM_IS_IPv4(addr_family)) {
-        memcpy(&a, src, sizeof(in_addr_t));
-
-        /* ensure all bytes of the union are initialized. If only to make
-         * valgrind happy. */
-        memset(&a.array[sizeof(in_addr_t)], 0, sizeof(a) - sizeof(in_addr_t));
-    } else
-        memcpy(&a, src, sizeof(struct in6_addr));
-
-    return a;
-}
-
-gboolean nm_ip_addr_set_from_untrusted(int           addr_family,
-                                       gpointer      dst,
-                                       gconstpointer src,
-                                       gsize         src_len,
-                                       int          *out_addr_family);
-
-gboolean
-nm_ip_addr_set_from_variant(int addr_family, gpointer dst, GVariant *variant, int *out_addr_family);
-
-static inline gconstpointer
-nm_ip_addr_from_packed_array(int addr_family, gconstpointer ipaddr_arr, gsize idx)
-{
-    return NM_IS_IPv4(addr_family)
-               ? ((gconstpointer) & (((const struct in_addr *) ipaddr_arr)[idx]))
-               : ((gconstpointer) & (((const struct in6_addr *) ipaddr_arr)[idx]));
+    return nm_memeq(a, &nm_ether_addr_zero, sizeof(NMEtherAddr));
 }
 
 /*****************************************************************************/
@@ -343,6 +236,7 @@ nm_utils_ether_addr_cmp(const struct ether_addr *a1, const struct ether_addr *a2
 {
     nm_assert(a1);
     nm_assert(a2);
+
     return memcmp(a1, a2, 6 /*ETH_ALEN*/);
 }
 
@@ -377,11 +271,36 @@ typedef struct _NMUtilsIPv6IfaceId {
         }                           \
     }
 
-void nm_utils_ipv6_addr_set_interface_identifier(struct in6_addr          *addr,
-                                                 const NMUtilsIPv6IfaceId *iid);
+/**
+ * nm_utils_ipv6_addr_set_interface_identifier:
+ * @addr: output token encoded as %in6_addr
+ * @iid: %NMUtilsIPv6IfaceId interface identifier
+ *
+ * Converts the %NMUtilsIPv6IfaceId to an %in6_addr (suitable for use
+ * with Linux platform). This only copies the lower 8 bytes, ignoring
+ * the /64 network prefix which is expected to be all-zero for a valid
+ * token.
+ */
+static inline void
+nm_utils_ipv6_addr_set_interface_identifier(struct in6_addr *addr, const NMUtilsIPv6IfaceId *iid)
+{
+    memcpy(addr->s6_addr + 8, &iid->id_u8, 8);
+}
 
-void nm_utils_ipv6_interface_identifier_get_from_addr(NMUtilsIPv6IfaceId    *iid,
-                                                      const struct in6_addr *addr);
+/**
+ * nm_utils_ipv6_interface_identifier_get_from_addr:
+ * @iid: output %NMUtilsIPv6IfaceId interface identifier set from the token
+ * @addr: token encoded as %in6_addr
+ *
+ * Converts the %in6_addr encoded token (as used by Linux platform) to
+ * the interface identifier.
+ */
+static inline void
+nm_utils_ipv6_interface_identifier_get_from_addr(NMUtilsIPv6IfaceId    *iid,
+                                                 const struct in6_addr *addr)
+{
+    memcpy(iid, addr->s6_addr + 8, 8);
+}
 
 gboolean nm_utils_ipv6_interface_identifier_get_from_token(NMUtilsIPv6IfaceId *iid,
                                                            const char         *token);
@@ -397,219 +316,6 @@ gboolean nm_utils_get_ipv6_interface_identifier(NMLinkType          link_type,
 
 /*****************************************************************************/
 
-static inline guint32
-_nm_utils_ip4_netmask_to_prefix(in_addr_t subnetmask)
-{
-    G_STATIC_ASSERT_EXPR(__SIZEOF_INT__ == 4);
-    G_STATIC_ASSERT_EXPR(sizeof(int) == 4);
-    G_STATIC_ASSERT_EXPR(sizeof(guint) == 4);
-    G_STATIC_ASSERT_EXPR(sizeof(subnetmask) == 4);
-
-    return ((subnetmask != 0u) ? (guint32) (32 - __builtin_ctz(ntohl(subnetmask))) : 0u);
-}
-
-/**
- * _nm_utils_ip4_prefix_to_netmask:
- * @prefix: a CIDR prefix
- *
- * Returns: the netmask represented by the prefix, in network byte order
- **/
-static inline in_addr_t
-_nm_utils_ip4_prefix_to_netmask(guint32 prefix)
-{
-    nm_assert(prefix <= 32);
-    return prefix < 32 ? ~htonl(0xFFFFFFFFu >> prefix) : 0xFFFFFFFFu;
-}
-
-guint32 _nm_utils_ip4_get_default_prefix0(in_addr_t ip);
-guint32 _nm_utils_ip4_get_default_prefix(in_addr_t ip);
-
-gconstpointer
-nm_utils_ipx_address_clear_host_address(int family, gpointer dst, gconstpointer src, guint32 plen);
-
-/* nm_utils_ip4_address_clear_host_address:
- * @addr: source ip6 address
- * @plen: prefix length of network
- *
- * returns: the input address, with the host address set to 0.
- */
-static inline in_addr_t
-nm_utils_ip4_address_clear_host_address(in_addr_t addr, guint32 plen)
-{
-    return addr & _nm_utils_ip4_prefix_to_netmask(plen);
-}
-
-const struct in6_addr *nm_utils_ip6_address_clear_host_address(struct in6_addr       *dst,
-                                                               const struct in6_addr *src,
-                                                               guint32                plen);
-
-static inline int
-nm_utils_ip4_address_same_prefix_cmp(in_addr_t addr_a, in_addr_t addr_b, guint32 plen)
-{
-    NM_CMP_DIRECT(htonl(nm_utils_ip4_address_clear_host_address(addr_a, plen)),
-                  htonl(nm_utils_ip4_address_clear_host_address(addr_b, plen)));
-    return 0;
-}
-
-int nm_utils_ip6_address_same_prefix_cmp(const struct in6_addr *addr_a,
-                                         const struct in6_addr *addr_b,
-                                         guint32                plen);
-
-static inline gboolean
-nm_utils_ip4_address_same_prefix(in_addr_t addr_a, in_addr_t addr_b, guint32 plen)
-{
-    return nm_utils_ip4_address_same_prefix_cmp(addr_a, addr_b, plen) == 0;
-}
-
-static inline gboolean
-nm_utils_ip6_address_same_prefix(const struct in6_addr *addr_a,
-                                 const struct in6_addr *addr_b,
-                                 guint8                 plen)
-{
-    return nm_utils_ip6_address_same_prefix_cmp(addr_a, addr_b, plen) == 0;
-}
-
-static inline int
-nm_utils_ip_address_same_prefix_cmp(int           addr_family,
-                                    gconstpointer addr_a,
-                                    gconstpointer addr_b,
-                                    guint8        plen)
-{
-    NM_CMP_SELF(addr_a, addr_b);
-
-    if (NM_IS_IPv4(addr_family)) {
-        return nm_utils_ip4_address_same_prefix_cmp(*((const in_addr_t *) addr_a),
-                                                    *((const in_addr_t *) addr_b),
-                                                    plen);
-    }
-
-    return nm_utils_ip6_address_same_prefix_cmp(addr_a, addr_b, plen);
-}
-
-static inline gboolean
-nm_utils_ip_address_same_prefix(int           addr_family,
-                                gconstpointer addr_a,
-                                gconstpointer addr_b,
-                                guint8        plen)
-{
-    return nm_utils_ip_address_same_prefix_cmp(addr_family, addr_a, addr_b, plen) == 0;
-}
-
-#define NM_CMP_DIRECT_IN4ADDR_SAME_PREFIX(a, b, plen) \
-    NM_CMP_RETURN(nm_utils_ip4_address_same_prefix_cmp((a), (b), (plen)))
-
-#define NM_CMP_DIRECT_IN6ADDR_SAME_PREFIX(a, b, plen) \
-    NM_CMP_RETURN(nm_utils_ip6_address_same_prefix_cmp((a), (b), (plen)))
-
-/*****************************************************************************/
-
-gboolean nm_utils_ip_is_site_local(int addr_family, const void *address);
-gboolean nm_utils_ip6_is_ula(const struct in6_addr *address);
-
-/*****************************************************************************/
-
-#define NM_IPV4LL_NETWORK ((in_addr_t) htonl(0xA9FE0000lu))
-#define NM_IPV4LL_NETMASK ((in_addr_t) htonl(0xFFFF0000lu))
-
-static inline gboolean
-nm_utils_ip4_address_is_loopback(in_addr_t addr)
-{
-    /* There is also IN_LOOPBACK() in <linux/in.h>, but there the
-     * argument is in host order not `in_addr_t`. */
-    return (addr & htonl(0xFF000000u)) == htonl(0x7F000000u);
-}
-
-static inline gboolean
-nm_utils_ip4_address_is_link_local(in_addr_t addr)
-{
-    return (addr & NM_IPV4LL_NETMASK) == NM_IPV4LL_NETWORK;
-}
-
-static inline gboolean
-nm_utils_ip4_address_is_zeronet(in_addr_t network)
-{
-    /* Same as ipv4_is_zeronet() from kernel's include/linux/in.h. */
-    return (network & htonl(0xFF000000u)) == htonl(0x00000000u);
-}
-
-/*****************************************************************************/
-
-#define NM_UTILS_INET_ADDRSTRLEN INET6_ADDRSTRLEN
-
-/* Forward declare function so we don't have to drag in <arpa/inet.h>. */
-const char *inet_ntop(int af, const void *src, char *dst, socklen_t size);
-
-static inline const char *
-nm_utils_inet_ntop(int addr_family, gconstpointer addr, char *dst)
-{
-    const char *s;
-
-    nm_assert_addr_family(addr_family);
-    nm_assert(addr);
-    nm_assert(dst);
-
-    s = inet_ntop(addr_family,
-                  addr,
-                  dst,
-                  addr_family == AF_INET6 ? INET6_ADDRSTRLEN : INET_ADDRSTRLEN);
-    nm_assert(s);
-    return s;
-}
-
-static inline const char *
-_nm_utils_inet4_ntop(in_addr_t addr, char dst[static INET_ADDRSTRLEN])
-{
-    return nm_utils_inet_ntop(AF_INET, &addr, dst);
-}
-
-static inline const char *
-_nm_utils_inet6_ntop(const struct in6_addr *addr, char dst[static INET6_ADDRSTRLEN])
-{
-    return nm_utils_inet_ntop(AF_INET6, addr, dst);
-}
-
-static inline char *
-nm_utils_inet_ntop_dup(int addr_family, gconstpointer addr)
-{
-    char buf[NM_UTILS_INET_ADDRSTRLEN];
-
-    return g_strdup(nm_utils_inet_ntop(addr_family, addr, buf));
-}
-
-static inline char *
-nm_utils_inet4_ntop_dup(in_addr_t addr)
-{
-    return nm_utils_inet_ntop_dup(AF_INET, &addr);
-}
-
-static inline char *
-nm_utils_inet6_ntop_dup(const struct in6_addr *addr)
-{
-    return nm_utils_inet_ntop_dup(AF_INET6, addr);
-}
-
-/*****************************************************************************/
-
-gboolean nm_utils_ipaddr_is_valid(int addr_family, const char *str_addr);
-
-gboolean nm_utils_ipaddr_is_normalized(int addr_family, const char *str_addr);
-
-/*****************************************************************************/
-
-/* this enum is compatible with ICMPV6_ROUTER_PREF_* (from <linux/icmpv6.h>,
- * the values for netlink attribute RTA_PREF) and "enum ndp_route_preference"
- * from <ndp.h>. */
-typedef enum _nm_packed {
-    NM_ICMPV6_ROUTER_PREF_MEDIUM  = 0x0, /* ICMPV6_ROUTER_PREF_MEDIUM */
-    NM_ICMPV6_ROUTER_PREF_LOW     = 0x3, /* ICMPV6_ROUTER_PREF_LOW */
-    NM_ICMPV6_ROUTER_PREF_HIGH    = 0x1, /* ICMPV6_ROUTER_PREF_HIGH */
-    NM_ICMPV6_ROUTER_PREF_INVALID = 0x2, /* ICMPV6_ROUTER_PREF_INVALID */
-} NMIcmpv6RouterPref;
-
-const char *nm_icmpv6_router_pref_to_string(NMIcmpv6RouterPref pref, char *buf, gsize len);
-
-/*****************************************************************************/
-
 gboolean nm_utils_memeqzero(gconstpointer data, gsize length);
 
 /*****************************************************************************/
@@ -1044,31 +750,6 @@ nm_utils_escaped_tokens_options_escape_val(const char *val, char **out_to_free)
 
 /*****************************************************************************/
 
-gboolean nm_utils_parse_inaddr_bin_full(int         addr_family,
-                                        gboolean    accept_legacy,
-                                        const char *text,
-                                        int        *out_addr_family,
-                                        gpointer    out_addr);
-static inline gboolean
-nm_utils_parse_inaddr_bin(int         addr_family,
-                          const char *text,
-                          int        *out_addr_family,
-                          gpointer    out_addr)
-{
-    return nm_utils_parse_inaddr_bin_full(addr_family, FALSE, text, out_addr_family, out_addr);
-}
-
-gboolean nm_utils_parse_inaddr(int addr_family, const char *text, char **out_addr);
-
-gboolean nm_utils_parse_inaddr_prefix_bin(int         addr_family,
-                                          const char *text,
-                                          int        *out_addr_family,
-                                          gpointer    out_addr,
-                                          int        *out_prefix);
-
-gboolean
-nm_utils_parse_inaddr_prefix(int addr_family, const char *text, char **out_addr, int *out_prefix);
-
 gboolean nm_utils_parse_next_line(const char **inout_ptr,
                                   gsize       *inout_len,
                                   const char **out_line,
@@ -1335,6 +1016,7 @@ typedef enum {
      * message from the device that returned the *highest* error code,
      * in the hope that this message makes the most sense for the caller.
      * */
+    NM_UTILS_ERROR_CONNECTION_AVAILABLE_STRICTLY_UNMANAGED_DEVICE,
     NM_UTILS_ERROR_CONNECTION_AVAILABLE_INCOMPATIBLE,
     NM_UTILS_ERROR_CONNECTION_AVAILABLE_UNMANAGED_DEVICE,
     NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
@@ -1662,6 +1344,10 @@ nm_g_variant_new_au(const guint32 *data, gsize len)
     return g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32, data, len, sizeof(guint32));
 }
 
+struct _NMIPAddr;
+
+extern const struct _NMIPAddr nm_ip_addr_zero;
+
 static inline GVariant *
 nm_g_variant_new_ay_inaddr(int addr_family, gconstpointer addr)
 {
@@ -2210,6 +1896,8 @@ char *nm_utils_g_slist_strlist_join(const GSList *a, const char *separator);
 static inline gpointer
 nm_g_array_data(const GArray *arr)
 {
+    /* You may want to use nm_g_array_first_p() instead, which can assert
+     * for the expected type. */
     return arr ? arr->data : NULL;
 }
 
@@ -2226,43 +1914,67 @@ nm_g_array_unref(GArray *arr)
         g_array_unref(arr);
 }
 
-#define nm_g_array_first(arr, Type)                                \
-    ({                                                             \
-        GArray *const _arr = (arr);                                \
-                                                                   \
-        nm_assert(_arr);                                           \
-        nm_assert(sizeof(Type) == g_array_get_element_size(_arr)); \
-        nm_assert(_arr->len > 0);                                  \
-                                                                   \
-        &g_array_index(arr, Type, 0);                              \
-    })
-
-#define nm_g_array_last(arr, Type)                                 \
-    ({                                                             \
-        GArray *const _arr = (arr);                                \
-                                                                   \
-        nm_assert(_arr);                                           \
-        nm_assert(sizeof(Type) == g_array_get_element_size(_arr)); \
-        nm_assert(_arr->len > 0);                                  \
-                                                                   \
-        &g_array_index(arr, Type, _arr->len - 1u);                 \
-    })
-
 /* Similar to g_array_index(). The differences are
  * - this does nm_assert() checks that the arguments are valid.
- * - returns a pointer to the element. */
-#define nm_g_array_index_p(arr, Type, idx)                            \
-    ({                                                                \
-        GArray *const _arr_55 = (arr);                                \
-        const guint   _idx_55 = (idx);                                \
-                                                                      \
-        nm_assert(_arr_55);                                           \
-        nm_assert(sizeof(Type) == g_array_get_element_size(_arr_55)); \
-        nm_assert(_idx_55 < _arr_55->len);                            \
-                                                                      \
-        &g_array_index(_arr_55, Type, _idx_55);                       \
+ * - returns a pointer to the element.
+ * - it asserts that @idx is <= arr->len. That is, it allows
+ *   to get a pointer after the data, of course, you are not
+ *   allowed to dereference in that case.
+ * - in particular, unlike nm_g_array_index(), you are allowed to call this
+ *   with "arr" NULL (for index zero) or with "arr->data" NULL
+ *   (for index zero). In that case, NULL is returned.
+ *
+ * When accessing index zero, then this returns NULL if-and-only-if
+ * "arr" is NULL or "arr->data" is NULL. In all other cases, this
+ * returns the pointer &((Type*) arr->data)[idx]. Note that the pointer
+ * may not be followed, if "idx" is equal to "arr->len". */
+#define nm_g_array_index_p(arr, Type, idx)                                                       \
+    ({                                                                                           \
+        const GArray *const _arr_55 = (arr);                                                     \
+        const guint         _idx_55 = (idx);                                                     \
+                                                                                                 \
+        nm_assert(_arr_55 || _idx_55 == 0);                                                      \
+        nm_assert(_idx_55 <= (_arr_55 ? _arr_55->len : 0u));                                     \
+        nm_assert(!_arr_55 || sizeof(Type) == g_array_get_element_size((GArray *) _arr_55));     \
+                                                                                                 \
+        ((_arr_55 && _arr_55->data) ? &(((Type *) ((gpointer) _arr_55->data))[_idx_55]) : NULL); \
     })
 
+/* Very similar to g_array_index().
+ * - nm_assert() that arguments are valid.
+ * - returns an lvalue to the element.
+ * - similar to nm_g_array_index_p(), but dereferences the pointer.
+ * - one difference to nm_g_array_index_p() is that it @idx MUST be
+ *   smaller than arr->len (unlike nm_g_array_index_p() which allows
+ *   access one element past the buffer. */
+#define nm_g_array_index(arr, Type, idx)                                         \
+    (*({                                                                         \
+        const GArray *const _arr_55 = (arr);                                     \
+        const guint         _idx_55 = (idx);                                     \
+                                                                                 \
+        nm_assert(_arr_55);                                                      \
+        nm_assert(sizeof(Type) == g_array_get_element_size((GArray *) _arr_55)); \
+        nm_assert(_idx_55 < _arr_55->len);                                       \
+                                                                                 \
+        &g_array_index((GArray *) _arr_55, Type, _idx_55);                       \
+    }))
+
+#define nm_g_array_first(arr, Type) nm_g_array_index(arr, Type, 0)
+
+#define nm_g_array_first_p(arr, Type) nm_g_array_index_p(arr, Type, 0)
+
+/* Same as g_array_index(arr, Type, arr->len-1). */
+#define nm_g_array_last(arr, Type)                                            \
+    (*({                                                                      \
+        const GArray *const _arr = (arr);                                     \
+                                                                              \
+        nm_assert(_arr);                                                      \
+        nm_assert(sizeof(Type) == g_array_get_element_size((GArray *) _arr)); \
+        nm_assert(_arr->len > 0);                                             \
+                                                                              \
+        &g_array_index((GArray *) arr, Type, _arr->len - 1u);                 \
+    }))
+
 #define nm_g_array_append_new(arr, Type)                           \
     ({                                                             \
         GArray *const _arr = (arr);                                \
@@ -2279,6 +1991,20 @@ nm_g_array_unref(GArray *arr)
         &g_array_index(arr, Type, _len);                           \
     })
 
+#define nm_g_array_append_simple(arr, val)                               \
+    G_STMT_START                                                         \
+    {                                                                    \
+        /* Similar to `g_array_append_val()`, but `g_array_append_val()`
+         * only works with lvalues. That makes sense if the value is a larger
+         * struct and you anyway have a pointer to it. It doesn't make sense
+         * if you have a list of int and want to append a number literal.
+         *
+         * nm_g_array_append_simple() is different. It depends on typeof(val)
+         * to be compatible. */ \
+        (*nm_g_array_append_new((arr), typeof(val))) = (val);            \
+    }                                                                    \
+    G_STMT_END
+
 /*****************************************************************************/
 
 static inline GPtrArray *
@@ -2342,40 +2068,29 @@ nm_g_ptr_array_pdata(const GPtrArray *arr)
     return arr ? arr->pdata : NULL;
 }
 
-GPtrArray *_nm_g_ptr_array_copy(GPtrArray     *array,
-                                GCopyFunc      func,
-                                gpointer       user_data,
-                                GDestroyNotify element_free_func);
-
 /**
- * nm_g_ptr_array_copy:
+ * nm_g_ptr_array_new_clone:
  * @array: the #GPtrArray to clone.
  * @func: the copy function.
  * @user_data: the user data for the copy function
- * @element_free_func: the free function of the elements. @array MUST have
- *   the same element_free_func. This argument is only used on older
- *   glib, that doesn't support g_ptr_array_copy().
+ * @element_free_func: the free function of the elements. This function
+ *   must agree with the owner-ship semantics of @func.
  *
  * This is a replacement for g_ptr_array_copy(), which is not available
  * before glib 2.62. Since GPtrArray does not allow to access the internal
  * element_free_func, we cannot add a compatibility implementation of g_ptr_array_copy()
- * and the user must provide a suitable destroy function.
+ * as the caller must provide the correct element_free_func.
  *
- * Note that the @element_free_func MUST correspond to free function set in @array.
+ * So this is not the same as g_ptr_array_copy() (hence the different name) because
+ * g_ptr_array_copy() uses the free func of the source array, which we cannot access.
+ * With g_ptr_array_copy() the copy func must agree with the array's free func.
+ * Here, it must agree with the provided @element_free_func. This allows for example
+ * to do a shallow-copy without cloning the elements (which you cannot do with g_ptr_array_copy()).
  */
-#if GLIB_CHECK_VERSION(2, 62, 0)
-#define nm_g_ptr_array_copy(array, func, user_data, element_free_func)            \
-    ({                                                                            \
-        _nm_unused GDestroyNotify const _element_free_func = (element_free_func); \
-                                                                                  \
-        G_GNUC_BEGIN_IGNORE_DEPRECATIONS;                                         \
-        g_ptr_array_copy((array), (func), (user_data));                           \
-        G_GNUC_END_IGNORE_DEPRECATIONS;                                           \
-    })
-#else
-#define nm_g_ptr_array_copy(array, func, user_data, element_free_func) \
-    _nm_g_ptr_array_copy((array), (func), (user_data), (element_free_func))
-#endif
+GPtrArray *nm_g_ptr_array_new_clone(GPtrArray     *array,
+                                    GCopyFunc      func,
+                                    gpointer       user_data,
+                                    GDestroyNotify element_free_func);
 
 /*****************************************************************************/
 
@@ -2443,42 +2158,95 @@ gboolean nm_utils_ptrarray_is_sorted(gconstpointer   *list,
                                      GCompareDataFunc cmpfcn,
                                      gpointer         user_data);
 
-gssize nm_utils_ptrarray_find_binary_search(gconstpointer   *list,
-                                            gsize            len,
-                                            gconstpointer    needle,
-                                            GCompareDataFunc cmpfcn,
-                                            gpointer         user_data);
-
-gssize nm_utils_ptrarray_find_binary_search_range(gconstpointer   *list,
-                                                  gsize            len,
-                                                  gconstpointer    needle,
-                                                  GCompareDataFunc cmpfcn,
-                                                  gpointer         user_data,
-                                                  gssize          *out_idx_first,
-                                                  gssize          *out_idx_last);
-
-#define nm_strv_find_binary_search(strv, len, needle)                 \
-    ({                                                                \
-        const char *const *const _strv   = NM_CAST_STRV_CC(strv);     \
-        const gsize              _len    = (len);                     \
-        const char *const        _needle = (needle);                  \
-                                                                      \
-        nm_assert(_len == 0 || _strv);                                \
-        nm_assert(_needle);                                           \
-                                                                      \
-        nm_utils_ptrarray_find_binary_search((gconstpointer *) _strv, \
-                                             _len,                    \
-                                             _needle,                 \
-                                             nm_strcmp_with_data,     \
-                                             NULL);                   \
+gssize nm_ptrarray_find_bsearch(gconstpointer   *list,
+                                gsize            len,
+                                gconstpointer    needle,
+                                GCompareDataFunc cmpfcn,
+                                gpointer         user_data);
+
+gssize nm_ptrarray_find_bsearch_range(gconstpointer   *list,
+                                      gsize            len,
+                                      gconstpointer    needle,
+                                      GCompareDataFunc cmpfcn,
+                                      gpointer         user_data,
+                                      gssize          *out_idx_first,
+                                      gssize          *out_idx_last);
+
+#define nm_strv_find_binary_search(strv, len, needle)             \
+    ({                                                            \
+        const char *const *const _strv   = NM_CAST_STRV_CC(strv); \
+        const gsize              _len    = (len);                 \
+        const char *const        _needle = (needle);              \
+                                                                  \
+        nm_assert(_len == 0 || _strv);                            \
+        nm_assert(_needle);                                       \
+                                                                  \
+        nm_ptrarray_find_bsearch((gconstpointer *) _strv,         \
+                                 _len,                            \
+                                 _needle,                         \
+                                 nm_strcmp_with_data,             \
+                                 NULL);                           \
     })
 
-gssize nm_utils_array_find_binary_search(gconstpointer    list,
-                                         gsize            elem_size,
-                                         gsize            len,
-                                         gconstpointer    needle,
-                                         GCompareDataFunc cmpfcn,
-                                         gpointer         user_data);
+/*****************************************************************************/
+
+#ifdef NM_WANT_NM_ARRAY_FIND_BSEARCH_INLINE
+/**
+ * nm_array_find_bsearch_inline:
+ *
+ * An inlined version of nm_array_find_bsearch(). See there.
+ * Define NM_WANT_NM_ARRAY_FIND_BSEARCH_INLINE to get it.
+ */
+_nm_always_inline static inline gssize
+nm_array_find_bsearch_inline(gconstpointer    list,
+                             gsize            len,
+                             gsize            elem_size,
+                             gconstpointer    needle,
+                             GCompareDataFunc cmpfcn,
+                             gpointer         user_data)
+{
+    gssize imax;
+    gssize imid;
+    gssize imin;
+    int    cmp;
+
+    nm_assert(list || len == 0);
+    nm_assert(cmpfcn);
+    nm_assert(elem_size > 0);
+
+    imin = 0;
+    if (len == 0)
+        return ~imin;
+
+    imax = len - 1;
+
+    while (imin <= imax) {
+        imid = imin + (imax - imin) / 2;
+
+        cmp = cmpfcn(&((const char *) list)[elem_size * imid], needle, user_data);
+        if (cmp == 0)
+            return imid;
+
+        if (cmp < 0)
+            imin = imid + 1;
+        else
+            imax = imid - 1;
+    }
+
+    /* return the inverse of @imin. This is a negative number, but
+     * also is ~imin the position where the value should be inserted. */
+    return ~imin;
+}
+#endif
+
+gssize nm_array_find_bsearch(gconstpointer    list,
+                             gsize            len,
+                             gsize            elem_size,
+                             gconstpointer    needle,
+                             GCompareDataFunc cmpfcn,
+                             gpointer         user_data);
+
+/*****************************************************************************/
 
 gssize nm_utils_ptrarray_find_first(gconstpointer *list, gssize len, gconstpointer needle);
 
@@ -2731,7 +2499,7 @@ nm_strv_ptrarray_clone(const GPtrArray *src, gboolean null_if_empty)
 {
     if (!src || (null_if_empty && src->len == 0))
         return NULL;
-    return nm_g_ptr_array_copy((GPtrArray *) src, nm_copy_func_g_strdup, NULL, g_free);
+    return nm_g_ptr_array_new_clone((GPtrArray *) src, nm_copy_func_g_strdup, NULL, g_free);
 }
 
 static inline void
@@ -2894,6 +2662,9 @@ nm_ether_addr_to_string(const NMEtherAddr *ether_addr, char sbuf[static(sizeof(N
 #define nm_ether_addr_to_string_a(ether_addr) \
     nm_ether_addr_to_string((ether_addr), g_alloca(sizeof(NMEtherAddr) * 3))
 
+#define nm_ether_addr_to_string_dup(ether_addr) \
+    ((char *) nm_ether_addr_to_string((ether_addr), g_malloc(sizeof(NMEtherAddr) * 3)))
+
 NMEtherAddr *nm_ether_addr_from_string(NMEtherAddr *addr, const char *str);
 
 guint8 *nm_utils_hexstr2bin_full(const char *hexstr,
@@ -3211,7 +2982,7 @@ nm_strvarray_add(GArray *array, const char *str)
 static inline const char *
 nm_strvarray_get_idx(GArray *array, guint idx)
 {
-    return *nm_g_array_index_p(array, const char *, idx);
+    return nm_g_array_index(array, const char *, idx);
 }
 
 static inline const char *const *
@@ -3310,12 +3081,12 @@ nm_strvarray_remove_first(GArray *strv, const char *needle)
 static inline int
 nm_strvarray_cmp(const GArray *a, const GArray *b)
 {
+    nm_assert(!a || sizeof(const char *const *) == g_array_get_element_size((GArray *) a));
+    nm_assert(!b || sizeof(const char *const *) == g_array_get_element_size((GArray *) b));
+
     NM_CMP_SELF(a, b);
 
-    return nm_strv_cmp_n((const char *const *) a->data,
-                         a->len,
-                         (const char *const *) b->data,
-                         b->len);
+    return nm_strv_cmp_n(nm_g_array_data(a), a->len, nm_g_array_data(b), b->len);
 }
 
 #define nm_strvarray_equal(a, b) (nm_strvarray_cmp((a), (b)) == 0)
@@ -3323,10 +3094,9 @@ nm_strvarray_cmp(const GArray *a, const GArray *b)
 static inline int
 _nm_strvarray_cmp_strv(const GArray *strv, const char *const *ss, gsize ss_len)
 {
-    return nm_strv_cmp_n(strv ? (const char *const *) strv->data : NULL,
-                         strv ? ((gssize) strv->len) : -1,
-                         ss,
-                         ss_len);
+    nm_assert(!strv || sizeof(const char *const *) == g_array_get_element_size((GArray *) strv));
+
+    return nm_strv_cmp_n(nm_g_array_data(strv), strv ? ((gssize) strv->len) : -1, ss, ss_len);
 }
 #define nm_strvarray_cmp_strv(strv, ss, ss_len) \
     _nm_strvarray_cmp_strv((strv), NM_CAST_STRV_CC(ss), (ss_len))