about summary refs log tree commit diff
path: root/src/libnm-glib-aux
diff options
context:
space:
mode:
Diffstat (limited to 'src/libnm-glib-aux')
-rw-r--r--src/libnm-glib-aux/nm-glib.h10
-rw-r--r--src/libnm-glib-aux/nm-json-aux.h5
-rw-r--r--src/libnm-glib-aux/nm-logging-fwd.h117
-rw-r--r--src/libnm-glib-aux/nm-macros-internal.h39
-rw-r--r--src/libnm-glib-aux/nm-secret-utils.c33
-rw-r--r--src/libnm-glib-aux/nm-secret-utils.h4
-rw-r--r--src/libnm-glib-aux/nm-shared-utils.c99
-rw-r--r--src/libnm-glib-aux/nm-shared-utils.h123
-rw-r--r--src/libnm-glib-aux/nm-test-utils.h14
-rw-r--r--src/libnm-glib-aux/nm-time-utils.h6
-rw-r--r--src/libnm-glib-aux/nm-uuid.c70
-rw-r--r--src/libnm-glib-aux/nm-value-type.h66
-rw-r--r--src/libnm-glib-aux/tests/test-shared-general.c3
13 files changed, 370 insertions, 219 deletions
diff --git a/src/libnm-glib-aux/nm-glib.h b/src/libnm-glib-aux/nm-glib.h
index 4ab9cbbd..f3be3b32 100644
--- a/src/libnm-glib-aux/nm-glib.h
+++ b/src/libnm-glib-aux/nm-glib.h
@@ -717,4 +717,14 @@ _nm_deprecated("Don't use this API") void _nm_forbidden_glib_api_n(gconstpointer
 
 /*****************************************************************************/
 
+/* g_alloca0() evaluates the "size" argument multiple times. That seems an error
+ * prone API (as it's not function-like).
+ *
+ * We could fix it by using an expression statement. But it doesn't seem
+ * worth it, so hide it to prevent its use. */
+#undef g_alloca0
+#undef g_newa0
+
+/*****************************************************************************/
+
 #endif /* __NM_GLIB_H__ */
diff --git a/src/libnm-glib-aux/nm-json-aux.h b/src/libnm-glib-aux/nm-json-aux.h
index 275ed770..3054b1f9 100644
--- a/src/libnm-glib-aux/nm-json-aux.h
+++ b/src/libnm-glib-aux/nm-json-aux.h
@@ -391,6 +391,7 @@ nm_value_type_to_json(NMValueType value_type, GString *gstr, gconstpointer p_fie
         nm_json_gstr_append_string(gstr, *((const char *const *) p_field));
         return;
     case NM_VALUE_TYPE_BYTES:
+    case NM_VALUE_TYPE_STRV:
     case NM_VALUE_TYPE_NONE:
     case NM_VALUE_TYPE_UNSPEC:
         break;
@@ -428,12 +429,12 @@ nm_value_type_from_json(const NMJsonVt  *vt,
         return (nm_jansson_json_as_string(vt, elem, out_val) > 0);
 
     case NM_VALUE_TYPE_BYTES:
+    case NM_VALUE_TYPE_STRV:
     case NM_VALUE_TYPE_NONE:
     case NM_VALUE_TYPE_UNSPEC:
         break;
     }
-    nm_assert_not_reached();
-    return FALSE;
+    return nm_assert_unreachable_val(FALSE);
 }
 
 #endif /* NM_VALUE_TYPE_DEFINE_FUNCTIONS */
diff --git a/src/libnm-glib-aux/nm-logging-fwd.h b/src/libnm-glib-aux/nm-logging-fwd.h
index 3e999c4a..0e715c50 100644
--- a/src/libnm-glib-aux/nm-logging-fwd.h
+++ b/src/libnm-glib-aux/nm-logging-fwd.h
@@ -9,54 +9,54 @@
 
 /* Log domains */
 
-typedef enum { /*< skip >*/
-               LOGD_NONE       = 0LL,
-               LOGD_PLATFORM   = (1LL << 0), /* Platform services */
-               LOGD_RFKILL     = (1LL << 1),
-               LOGD_ETHER      = (1LL << 2),
-               LOGD_WIFI       = (1LL << 3),
-               LOGD_BT         = (1LL << 4),
-               LOGD_MB         = (1LL << 5), /* mobile broadband */
-               LOGD_DHCP4      = (1LL << 6),
-               LOGD_DHCP6      = (1LL << 7),
-               LOGD_PPP        = (1LL << 8),
-               LOGD_WIFI_SCAN  = (1LL << 9),
-               LOGD_IP4        = (1LL << 10),
-               LOGD_IP6        = (1LL << 11),
-               LOGD_AUTOIP4    = (1LL << 12),
-               LOGD_DNS        = (1LL << 13),
-               LOGD_VPN        = (1LL << 14),
-               LOGD_SHARING    = (1LL << 15), /* Connection sharing/dnsmasq */
-               LOGD_SUPPLICANT = (1LL << 16), /* Wi-Fi and 802.1x */
-               LOGD_AGENTS     = (1LL << 17), /* Secret agents */
-               LOGD_SETTINGS   = (1LL << 18), /* Settings */
-               LOGD_SUSPEND    = (1LL << 19), /* Suspend/Resume */
-               LOGD_CORE       = (1LL << 20), /* Core daemon and policy stuff */
-               LOGD_DEVICE     = (1LL << 21), /* Device state and activation */
-               LOGD_OLPC       = (1LL << 22),
-               LOGD_INFINIBAND = (1LL << 23),
-               LOGD_FIREWALL   = (1LL << 24),
-               LOGD_ADSL       = (1LL << 25),
-               LOGD_BOND       = (1LL << 26),
-               LOGD_VLAN       = (1LL << 27),
-               LOGD_BRIDGE     = (1LL << 28),
-               LOGD_DBUS_PROPS = (1LL << 29),
-               LOGD_TEAM       = (1LL << 30),
-               LOGD_CONCHECK   = (1LL << 31),
-               LOGD_DCB        = (1LL << 32), /* Data Center Bridging */
-               LOGD_DISPATCH   = (1LL << 33),
-               LOGD_AUDIT      = (1LL << 34),
-               LOGD_SYSTEMD    = (1LL << 35),
-               LOGD_VPN_PLUGIN = (1LL << 36),
-               LOGD_PROXY      = (1LL << 37),
-
-               __LOGD_MAX,
-               LOGD_ALL     = (((__LOGD_MAX - 1LL) << 1) - 1LL),
-               LOGD_DEFAULT = LOGD_ALL & ~(LOGD_DBUS_PROPS | LOGD_WIFI_SCAN | LOGD_VPN_PLUGIN | 0),
-
-               /* aliases: */
-               LOGD_DHCP = LOGD_DHCP4 | LOGD_DHCP6,
-               LOGD_IP   = LOGD_IP4 | LOGD_IP6,
+typedef enum {
+    LOGD_NONE       = 0LL,
+    LOGD_PLATFORM   = (1LL << 0), /* Platform services */
+    LOGD_RFKILL     = (1LL << 1),
+    LOGD_ETHER      = (1LL << 2),
+    LOGD_WIFI       = (1LL << 3),
+    LOGD_BT         = (1LL << 4),
+    LOGD_MB         = (1LL << 5), /* mobile broadband */
+    LOGD_DHCP4      = (1LL << 6),
+    LOGD_DHCP6      = (1LL << 7),
+    LOGD_PPP        = (1LL << 8),
+    LOGD_WIFI_SCAN  = (1LL << 9),
+    LOGD_IP4        = (1LL << 10),
+    LOGD_IP6        = (1LL << 11),
+    LOGD_AUTOIP4    = (1LL << 12),
+    LOGD_DNS        = (1LL << 13),
+    LOGD_VPN        = (1LL << 14),
+    LOGD_SHARING    = (1LL << 15), /* Connection sharing/dnsmasq */
+    LOGD_SUPPLICANT = (1LL << 16), /* Wi-Fi and 802.1x */
+    LOGD_AGENTS     = (1LL << 17), /* Secret agents */
+    LOGD_SETTINGS   = (1LL << 18), /* Settings */
+    LOGD_SUSPEND    = (1LL << 19), /* Suspend/Resume */
+    LOGD_CORE       = (1LL << 20), /* Core daemon and policy stuff */
+    LOGD_DEVICE     = (1LL << 21), /* Device state and activation */
+    LOGD_OLPC       = (1LL << 22),
+    LOGD_INFINIBAND = (1LL << 23),
+    LOGD_FIREWALL   = (1LL << 24),
+    LOGD_ADSL       = (1LL << 25),
+    LOGD_BOND       = (1LL << 26),
+    LOGD_VLAN       = (1LL << 27),
+    LOGD_BRIDGE     = (1LL << 28),
+    LOGD_DBUS_PROPS = (1LL << 29),
+    LOGD_TEAM       = (1LL << 30),
+    LOGD_CONCHECK   = (1LL << 31),
+    LOGD_DCB        = (1LL << 32), /* Data Center Bridging */
+    LOGD_DISPATCH   = (1LL << 33),
+    LOGD_AUDIT      = (1LL << 34),
+    LOGD_SYSTEMD    = (1LL << 35),
+    LOGD_VPN_PLUGIN = (1LL << 36),
+    LOGD_PROXY      = (1LL << 37),
+
+    __LOGD_MAX,
+    LOGD_ALL     = (((__LOGD_MAX - 1LL) << 1) - 1LL),
+    LOGD_DEFAULT = LOGD_ALL & ~(LOGD_DBUS_PROPS | LOGD_WIFI_SCAN | LOGD_VPN_PLUGIN | 0),
+
+    /* aliases: */
+    LOGD_DHCP = LOGD_DHCP4 | LOGD_DHCP6,
+    LOGD_IP   = LOGD_IP4 | LOGD_IP6,
 
 #define LOGD_DHCPX(is_ipv4) ((is_ipv4) ? LOGD_DHCP4 : LOGD_DHCP6)
 #define LOGD_IPX(is_ipv4)   ((is_ipv4) ? LOGD_IP4 : LOGD_IP6)
@@ -64,19 +64,20 @@ typedef enum { /*< skip >*/
 } NMLogDomain;
 
 /* Log levels */
-typedef enum { /*< skip >*/
-               LOGL_TRACE,
-               LOGL_DEBUG,
-               LOGL_INFO,
-               LOGL_WARN,
-               LOGL_ERR,
+typedef enum {
 
-               _LOGL_N_REAL, /* the number of actual logging levels */
+    LOGL_TRACE,
+    LOGL_DEBUG,
+    LOGL_INFO,
+    LOGL_WARN,
+    LOGL_ERR,
 
-               _LOGL_OFF = _LOGL_N_REAL, /* special logging level that is always disabled. */
-               _LOGL_KEEP, /* special logging level to indicate that the logging level should not be changed. */
+    _LOGL_N_REAL, /* the number of actual logging levels */
 
-               _LOGL_N, /* the number of logging levels including "OFF" */
+    _LOGL_OFF = _LOGL_N_REAL, /* special logging level that is always disabled. */
+    _LOGL_KEEP, /* special logging level to indicate that the logging level should not be changed. */
+
+    _LOGL_N, /* the number of logging levels including "OFF" */
 } NMLogLevel;
 
 gboolean _nm_log_enabled_impl(gboolean mt_require_locking, NMLogLevel level, NMLogDomain domain);
diff --git a/src/libnm-glib-aux/nm-macros-internal.h b/src/libnm-glib-aux/nm-macros-internal.h
index 14c16020..7cc8ac97 100644
--- a/src/libnm-glib-aux/nm-macros-internal.h
+++ b/src/libnm-glib-aux/nm-macros-internal.h
@@ -184,22 +184,31 @@ _nm_auto_freev(gpointer ptr)
  * same name for the same warning. */
 
 #if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))
+#define NM_PRAGMA_DIAGNOSTICS_PUSH _Pragma("GCC diagnostic push")
 #define NM_PRAGMA_WARNING_DISABLE(warning) \
-    _Pragma("GCC diagnostic push") _Pragma(_NM_PRAGMA_WARNING_DO(warning))
+    NM_PRAGMA_DIAGNOSTICS_PUSH _Pragma(_NM_PRAGMA_WARNING_DO(warning))
+#define NM_PRAGMA_WARNING_REENABLE _Pragma("GCC diagnostic pop")
 #elif defined(__clang__)
-#define NM_PRAGMA_WARNING_DISABLE(warning)                                                      \
-    _Pragma("clang diagnostic push") _Pragma(_NM_PRAGMA_WARNING_DO("-Wunknown-warning-option")) \
+#define NM_PRAGMA_DIAGNOSTICS_PUSH _Pragma("clang diagnostic push")
+#define NM_PRAGMA_WARNING_DISABLE(warning)                                                \
+    NM_PRAGMA_DIAGNOSTICS_PUSH _Pragma(_NM_PRAGMA_WARNING_DO("-Wunknown-warning-option")) \
         _Pragma(_NM_PRAGMA_WARNING_DO(warning))
+#define NM_PRAGMA_WARNING_REENABLE _Pragma("clang diagnostic pop")
 #else
+#define NM_PRAGMA_DIAGNOSTICS_PUSH
 #define NM_PRAGMA_WARNING_DISABLE(warning)
+#define NM_PRAGMA_WARNING_REENABLE
 #endif
 
-#if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))
-#define NM_PRAGMA_WARNING_REENABLE _Pragma("GCC diagnostic pop")
-#elif defined(__clang__)
-#define NM_PRAGMA_WARNING_REENABLE _Pragma("clang diagnostic pop")
+/*****************************************************************************/
+
+/* Seems gcc-12 has a tendency for false-positive -Wdangling-pointer warnings with
+ * g_error()'s `for(;;);`. See https://bugzilla.redhat.com/show_bug.cgi?id=2056613 .
+ * Work around, but only for the affected gcc 12.0.1. */
+#if defined(__GNUC__) && __GNUC__ == 12 && __GNUC_MINOR__ == 0 && __GNUC_PATCHLEVEL__ <= 1
+#define NM_PRAGMA_WARNING_DISABLE_DANGLING_POINTER NM_PRAGMA_WARNING_DISABLE("-Wdangling-pointer")
 #else
-#define NM_PRAGMA_WARNING_REENABLE
+#define NM_PRAGMA_WARNING_DISABLE_DANGLING_POINTER NM_PRAGMA_DIAGNOSTICS_PUSH
 #endif
 
 /*****************************************************************************/
@@ -611,10 +620,16 @@ nm_str_realloc(char *str)
 /* invokes _notify() for all arguments (of type _PropertyEnums). Note, that if
  * there are more than one prop arguments, this will involve a freeze/thaw
  * of GObject property notifications. */
-#define nm_gobject_notify_together_full(suffix, obj, ...)          \
-    _nm_gobject_notify_together_impl##suffix(obj,                  \
-                                             NM_NARG(__VA_ARGS__), \
-                                             (const _PropertyEnums##suffix[]){__VA_ARGS__})
+#define nm_gobject_notify_together_full(suffix, obj, ...)                            \
+    G_STMT_START                                                                     \
+    {                                                                                \
+        const _PropertyEnums##suffix _props[] = {__VA_ARGS__};                       \
+                                                                                     \
+        G_STATIC_ASSERT(G_N_ELEMENTS(_props) == NM_NARG(__VA_ARGS__));               \
+                                                                                     \
+        _nm_gobject_notify_together_impl##suffix(obj, G_N_ELEMENTS(_props), _props); \
+    }                                                                                \
+    G_STMT_END
 
 #define nm_gobject_notify_together(obj, ...) nm_gobject_notify_together_full(, obj, __VA_ARGS__)
 
diff --git a/src/libnm-glib-aux/nm-secret-utils.c b/src/libnm-glib-aux/nm-secret-utils.c
index c764b6e5..983b04ca 100644
--- a/src/libnm-glib-aux/nm-secret-utils.c
+++ b/src/libnm-glib-aux/nm-secret-utils.c
@@ -10,6 +10,8 @@
 
 #include <malloc.h>
 
+#include "nm-io-utils.h"
+
 /*****************************************************************************/
 
 void
@@ -176,3 +178,34 @@ nm_utils_memeqzero_secret(gconstpointer data, gsize length)
     }
     return 1 & ((acc - 1) >> 8);
 }
+
+/*****************************************************************************/
+
+gboolean
+nm_utils_read_crypto_file(const char *filename, NMSecretPtr *out_contents, GError **error)
+{
+    nm_assert(out_contents);
+    nm_assert(out_contents->len == 0);
+    nm_assert(!out_contents->str);
+
+    return nm_utils_file_get_contents(-1,
+                                      filename,
+                                      100 * 1024 * 1024,
+                                      NM_UTILS_FILE_GET_CONTENTS_FLAG_SECRET,
+                                      &out_contents->str,
+                                      &out_contents->len,
+                                      NULL,
+                                      error);
+}
+
+GBytes *
+nm_utils_read_crypto_file_to_bytes(const char *filename, GError **error)
+{
+    nm_auto_clear_secret_ptr NMSecretPtr contents = {0};
+
+    g_return_val_if_fail(filename, NULL);
+
+    if (!nm_utils_read_crypto_file(filename, &contents, error))
+        return NULL;
+    return nm_secret_copy_to_gbytes(contents.bin, contents.len);
+}
diff --git a/src/libnm-glib-aux/nm-secret-utils.h b/src/libnm-glib-aux/nm-secret-utils.h
index 513dbca5..c175bc8f 100644
--- a/src/libnm-glib-aux/nm-secret-utils.h
+++ b/src/libnm-glib-aux/nm-secret-utils.h
@@ -286,4 +286,8 @@ nm_secret_mem_try_realloc_take(gpointer m_old, gboolean do_bzero_mem, gsize cur_
 
 /*****************************************************************************/
 
+gboolean nm_utils_read_crypto_file(const char *filename, NMSecretPtr *out_contents, GError **error);
+
+GBytes *nm_utils_read_crypto_file_to_bytes(const char *filename, GError **error);
+
 #endif /* __NM_SECRET_UTILS_H__ */
diff --git a/src/libnm-glib-aux/nm-shared-utils.c b/src/libnm-glib-aux/nm-shared-utils.c
index 563b7cc2..ad99a6b9 100644
--- a/src/libnm-glib-aux/nm-shared-utils.c
+++ b/src/libnm-glib-aux/nm-shared-utils.c
@@ -870,7 +870,12 @@ nm_utils_to_string_buffer_init(char **buf, gsize *len)
 {
     if (!*buf) {
         *buf = _nm_utils_to_string_buffer;
-        *len = sizeof(_nm_utils_to_string_buffer);
+        *len = NM_UTILS_TO_STRING_BUFFER_SIZE;
+
+        /* We no longer want to support callers to omit the buffer
+         * and fallback to the global buffer. Callers should be fixed
+         * to always provide a valid buffer. */
+        g_return_if_reached();
     }
 }
 
@@ -945,20 +950,6 @@ nm_utils_flags2str(const NMUtilsFlags2StrDesc *descs,
 
 /*****************************************************************************/
 
-/**
- * _nm_utils_ip4_prefix_to_netmask:
- * @prefix: a CIDR prefix
- *
- * Returns: the netmask represented by the prefix, in network byte order
- **/
-guint32
-_nm_utils_ip4_prefix_to_netmask(guint32 prefix)
-{
-    return prefix < 32 ? ~htonl(0xFFFFFFFFu >> prefix) : 0xFFFFFFFFu;
-}
-
-/*****************************************************************************/
-
 guint32
 _nm_utils_ip4_get_default_prefix0(in_addr_t ip)
 {
@@ -1130,6 +1121,7 @@ nm_utils_parse_inaddr_bin_full(int         addr_family,
 
 #if NM_MORE_ASSERTS > 10
     if (addr_family == AF_INET) {
+        NM_PRAGMA_WARNING_DISABLE_DANGLING_POINTER
         gs_free_error GError *error = NULL;
         in_addr_t             a;
 
@@ -1145,6 +1137,7 @@ nm_utils_parse_inaddr_bin_full(int         addr_family,
                     error->message);
         }
         nm_assert(addrbin.addr4 == a);
+        NM_PRAGMA_WARNING_REENABLE
     }
 #endif
 
@@ -2958,8 +2951,8 @@ _str_buf_append_c_escape_octal(NMStrBuf *strbuf, char ch)
  *
  * Returns: the unescaped buffer of length @out_len. If @str is %NULL, this returns %NULL
  *   and sets @out_len to 0. Otherwise, a non-%NULL binary buffer is returned with
- *   @out_len bytes. Note that the binary buffer is guaranteed to be NUL terminated. That
- *   is @result[@out_len] is NUL.
+ *   @out_len bytes. Note that the binary buffer is guaranteed to be NUL terminated
+ *   (@result[@out_len] is NUL).
  *   Note that the result is binary, and may have embedded NUL characters and non-UTF-8.
  *   If the function can avoid cloning the input string, it will return a pointer inside
  *   the input @str. For example, if there is no backslash, no cloning is necessary. In that
@@ -3121,11 +3114,14 @@ nm_utils_buf_utf8safe_unescape(const char             *str,
  * invalid UTF-8 sequences, and other (depending on @flags).
  *
  * Returns: the escaped input buffer, as valid UTF-8. If no escaping
- *   is necessary, it returns the input @buf. Otherwise, an allocated
- *   string @to_free is returned which must be freed by the caller
- *   with g_free. The escaping can be reverted by g_strcompress().
- *   There are cases where this function can return NULL:
- *   - if @buflen is 0
+ *   is necessary and @buflen is negative, it returns the input @buf
+ *   that can be interpreted as NUL terminated UTF-8 string.
+ *   Otherwise, an allocated string @to_free is returned which must be freed
+ *   by the caller with g_free().
+ *   The escaping can be reverted by nm_utils_buf_utf8safe_unescape()
+ *   (or, if in the absence of NUL characters, with g_strcompress()).
+ *   There are cases where this function returns %NULL:
+ *   - if @buflen is 0.
  *   - if @buflen is negative and @buf is NULL.
  **/
 const char *
@@ -4689,14 +4685,20 @@ _nm_utils_invoke_on_idle_start(gboolean                    use_timeout,
     }
 
     if (use_timeout) {
+        /* We use G_PRIORITY_DEFAULT_IDLE both for the with/without timeout
+         * case. The reason is not strong, but it seems right that the caller
+         * requests a lower priority than G_PRIORITY_DEFAULT. That is unlike
+         * what g_timeout_add() would do. */
         source = nm_g_timeout_source_new(timeout_msec,
-                                         G_PRIORITY_DEFAULT,
+                                         G_PRIORITY_DEFAULT_IDLE,
                                          _nm_utils_invoke_on_idle_cb_idle,
                                          data,
                                          NULL);
     } else {
-        source =
-            nm_g_idle_source_new(G_PRIORITY_DEFAULT, _nm_utils_invoke_on_idle_cb_idle, data, NULL);
+        source = nm_g_idle_source_new(G_PRIORITY_DEFAULT_IDLE,
+                                      _nm_utils_invoke_on_idle_cb_idle,
+                                      data,
+                                      NULL);
     }
 
     /* use the current thread default context. */
@@ -4840,6 +4842,27 @@ nm_utils_bin2hexstr_full(gconstpointer addr,
     return out0;
 }
 
+char *
+_nm_utils_bin2hexstr(gconstpointer src, gsize len, int final_len)
+{
+    char *result;
+    gsize buflen = (len * 2) + 1;
+
+    nm_assert(src);
+    nm_assert(len > 0 && (buflen - 1) / 2 == len);
+    nm_assert(final_len < 0 || (gsize) final_len < buflen);
+
+    result = g_malloc(buflen);
+
+    nm_utils_bin2hexstr_full(src, len, '\0', FALSE, result);
+
+    /* Cut converted key off at the correct length for this cipher type */
+    if (final_len >= 0 && (gsize) final_len < buflen)
+        result[final_len] = '\0';
+
+    return result;
+}
+
 guint8 *
 nm_utils_hexstr2bin_full(const char *hexstr,
                          gboolean    allow_0x_prefix,
@@ -6259,7 +6282,7 @@ _nm_utils_ssid_to_string_gbytes(GBytes *ssid)
 /*****************************************************************************/
 
 gconstpointer
-nm_utils_ipx_address_clear_host_address(int family, gpointer dst, gconstpointer src, guint8 plen)
+nm_utils_ipx_address_clear_host_address(int family, gpointer dst, gconstpointer src, guint32 plen)
 {
     g_return_val_if_fail(dst, NULL);
 
@@ -6283,21 +6306,10 @@ nm_utils_ipx_address_clear_host_address(int family, gpointer dst, gconstpointer
     return dst;
 }
 
-/* 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.
- */
-in_addr_t
-nm_utils_ip4_address_clear_host_address(in_addr_t addr, guint8 plen)
-{
-    return addr & _nm_utils_ip4_prefix_to_netmask(plen);
-}
-
 /* nm_utils_ip6_address_clear_host_address:
  * @dst: destination output buffer, will contain the network part of the @src address
- * @src: source ip6 address
+ * @src: source ip6 address. If NULL, this does an in-place update of @dst.
+ *   Also, @src and @dst are allowed to be the same pointers.
  * @plen: prefix length of network
  *
  * Note: this function is self assignment safe, to update @src inplace, set both
@@ -6306,7 +6318,7 @@ nm_utils_ip4_address_clear_host_address(in_addr_t addr, guint8 plen)
 const struct in6_addr *
 nm_utils_ip6_address_clear_host_address(struct in6_addr       *dst,
                                         const struct in6_addr *src,
-                                        guint8                 plen)
+                                        guint32                plen)
 {
     g_return_val_if_fail(plen <= 128, NULL);
     g_return_val_if_fail(dst, NULL);
@@ -6335,14 +6347,15 @@ nm_utils_ip6_address_clear_host_address(struct in6_addr       *dst,
 int
 nm_utils_ip6_address_same_prefix_cmp(const struct in6_addr *addr_a,
                                      const struct in6_addr *addr_b,
-                                     guint8                 plen)
+                                     guint32                plen)
 {
     int    nbytes;
     guint8 va, vb, m;
 
-    if (plen >= 128)
+    if (plen >= 128) {
+        nm_assert(plen == 128);
         NM_CMP_DIRECT_MEMCMP(addr_a, addr_b, sizeof(struct in6_addr));
-    else {
+    } else {
         nbytes = plen / 8;
         if (nbytes)
             NM_CMP_DIRECT_MEMCMP(addr_a, addr_b, nbytes);
diff --git a/src/libnm-glib-aux/nm-shared-utils.h b/src/libnm-glib-aux/nm-shared-utils.h
index a953140b..941312bd 100644
--- a/src/libnm-glib-aux/nm-shared-utils.h
+++ b/src/libnm-glib-aux/nm-shared-utils.h
@@ -389,15 +389,43 @@ gboolean nm_utils_get_ipv6_interface_identifier(NMLinkType          link_type,
 
 /*****************************************************************************/
 
+/**
+ * _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, guint8 plen);
-in_addr_t              nm_utils_ip4_address_clear_host_address(in_addr_t addr, guint8 plen);
+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,
-                                                               guint8                 plen);
+                                                               guint32                plen);
 
 static inline int
-nm_utils_ip4_address_same_prefix_cmp(in_addr_t addr_a, in_addr_t addr_b, guint8 plen)
+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)));
@@ -406,7 +434,21 @@ nm_utils_ip4_address_same_prefix_cmp(in_addr_t addr_a, in_addr_t addr_b, guint8
 
 int nm_utils_ip6_address_same_prefix_cmp(const struct in6_addr *addr_a,
                                          const struct in6_addr *addr_b,
-                                         guint8                 plen);
+                                         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,
@@ -414,8 +456,6 @@ nm_utils_ip_address_same_prefix_cmp(int           addr_family,
                                     gconstpointer addr_b,
                                     guint8        plen)
 {
-    nm_assert_addr_family(addr_family);
-
     NM_CMP_SELF(addr_a, addr_b);
 
     if (NM_IS_IPv4(addr_family)) {
@@ -428,20 +468,6 @@ nm_utils_ip_address_same_prefix_cmp(int           addr_family,
 }
 
 static inline gboolean
-nm_utils_ip4_address_same_prefix(in_addr_t addr_a, in_addr_t addr_b, guint8 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 gboolean
 nm_utils_ip_address_same_prefix(int           addr_family,
                                 gconstpointer addr_a,
                                 gconstpointer addr_b,
@@ -458,6 +484,10 @@ nm_utils_ip_address_same_prefix(int           addr_family,
 
 /*****************************************************************************/
 
+gboolean nm_utils_ip_is_site_local(int addr_family, const void *address);
+
+/*****************************************************************************/
+
 #define NM_IPV4LL_NETWORK ((in_addr_t) (htonl(0xA9FE0000lu)))
 #define NM_IPV4LL_NETMASK ((in_addr_t) (htonl(0xFFFF0000lu)))
 
@@ -974,24 +1004,6 @@ nm_utils_escaped_tokens_options_escape_val(const char *val, char **out_to_free)
 
 /*****************************************************************************/
 
-guint32 _nm_utils_ip4_prefix_to_netmask(guint32 prefix);
-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, guint8 plen);
-in_addr_t              nm_utils_ip4_address_clear_host_address(in_addr_t addr, guint8 plen);
-const struct in6_addr *nm_utils_ip6_address_clear_host_address(struct in6_addr       *dst,
-                                                               const struct in6_addr *src,
-                                                               guint8                 plen);
-int                    nm_utils_ip6_address_same_prefix_cmp(const struct in6_addr *addr_a,
-                                                            const struct in6_addr *addr_b,
-                                                            guint8                 plen);
-
-gboolean nm_utils_ip_is_site_local(int addr_family, const void *address);
-
-/*****************************************************************************/
-
 gboolean nm_utils_parse_inaddr_bin_full(int         addr_family,
                                         gboolean    accept_legacy,
                                         const char *text,
@@ -1047,7 +1059,9 @@ int _nm_utils_ascii_str_to_bool(const char *str, int default_value);
 
 /*****************************************************************************/
 
-extern _nm_thread_local char _nm_utils_to_string_buffer[2096];
+#define NM_UTILS_TO_STRING_BUFFER_SIZE 2096
+
+extern _nm_thread_local char _nm_utils_to_string_buffer[NM_UTILS_TO_STRING_BUFFER_SIZE];
 
 void     nm_utils_to_string_buffer_init(char **buf, gsize *len);
 gboolean nm_utils_to_string_buffer_init_null(gconstpointer obj, char **buf, gsize *len);
@@ -2710,6 +2724,8 @@ char *nm_utils_bin2hexstr_full(gconstpointer addr,
                                gboolean      upper_case,
                                char         *out);
 
+char *_nm_utils_bin2hexstr(gconstpointer src, gsize len, int final_len);
+
 #define nm_utils_bin2hexstr_a(addr, length, delimiter, upper_case, str_to_free)               \
     ({                                                                                        \
         gconstpointer _addr        = (addr);                                                  \
@@ -3141,6 +3157,33 @@ nm_strvarray_remove_first(GArray *strv, const char *needle)
     return TRUE;
 }
 
+static inline int
+nm_strvarray_cmp(const GArray *a, const 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);
+}
+
+#define nm_strvarray_equal(a, b) (nm_strvarray_cmp((a), (b)) == 0)
+
+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);
+}
+#define nm_strvarray_cmp_strv(strv, ss, ss_len) \
+    _nm_strvarray_cmp_strv((strv), NM_CAST_STRV_CC(ss), (ss_len))
+
+#define nm_strvarray_equal_strv(strv, ss, ss_len) \
+    (nm_strvarray_cmp_strv((strv), (ss), (ss_len)) == 0)
+
 /*****************************************************************************/
 
 struct _NMVariantAttributeSpec {
diff --git a/src/libnm-glib-aux/nm-test-utils.h b/src/libnm-glib-aux/nm-test-utils.h
index caf43d5f..2dfe9e32 100644
--- a/src/libnm-glib-aux/nm-test-utils.h
+++ b/src/libnm-glib-aux/nm-test-utils.h
@@ -1714,8 +1714,11 @@ __nmtst_spawn_sync(const char *working_directory,
                            standard_err,
                            &exit_status,
                            &error);
-    if (!success)
+    if (!success) {
+        NM_PRAGMA_WARNING_DISABLE_DANGLING_POINTER
         g_error("nmtst_spawn_sync(%s): %s", ((char **) argv->pdata)[0], error->message);
+        NM_PRAGMA_WARNING_REENABLE
+    }
     g_assert(!error);
 
     g_assert(!standard_out || *standard_out);
@@ -1844,7 +1847,8 @@ _nmtst_assert_resolve_relative_path_equals(const char *f1,
 
     /* Fixme: later we might need to coalesce repeated '/', "./", and "../".
      * For now, it's good enough. */
-    if (g_strcmp0(p1, p2) != 0)
+    if (g_strcmp0(p1, p2) != 0) {
+        NM_PRAGMA_WARNING_DISABLE_DANGLING_POINTER
         g_error("%s:%d : filenames don't match \"%s\" vs. \"%s\" // \"%s\" - \"%s\"",
                 file,
                 line,
@@ -1852,6 +1856,8 @@ _nmtst_assert_resolve_relative_path_equals(const char *f1,
                 f2,
                 p1,
                 p2);
+        NM_PRAGMA_WARNING_REENABLE
+    }
 }
 #define nmtst_assert_resolve_relative_path_equals(f1, f2) \
     _nmtst_assert_resolve_relative_path_equals(f1, f2, __FILE__, __LINE__);
@@ -2404,9 +2410,11 @@ _nmtst_assert_connection_has_settings(NMConnection *connection,
     settings = nm_connection_get_settings(connection, &len);
     for (i = 0; i < len; i++) {
         if (!g_hash_table_remove(names, nm_setting_get_name(settings[i])) && has_at_most) {
+            NM_PRAGMA_WARNING_DISABLE_DANGLING_POINTER
             g_error(
                 "nmtst_assert_connection_has_settings(): has setting \"%s\" which is not expected",
                 nm_setting_get_name(settings[i]));
+            NM_PRAGMA_WARNING_REENABLE
         }
     }
     if (g_hash_table_size(names) > 0 && has_at_least) {
@@ -2419,11 +2427,13 @@ _nmtst_assert_connection_has_settings(NMConnection *connection,
             settings_names[i] = nm_setting_get_name(settings[i]);
         has_str = g_strjoinv(" ", (char **) settings_names);
 
+        NM_PRAGMA_WARNING_DISABLE_DANGLING_POINTER
         g_error("nmtst_assert_connection_has_settings(): the setting lacks %u expected settings "
                 "(expected: [%s] vs. has: [%s])",
                 g_hash_table_size(names),
                 expected_str,
                 has_str);
+        NM_PRAGMA_WARNING_REENABLE
     }
 }
 #define nmtst_assert_connection_has_settings(connection, ...) \
diff --git a/src/libnm-glib-aux/nm-time-utils.h b/src/libnm-glib-aux/nm-time-utils.h
index 3c3e935f..461d6845 100644
--- a/src/libnm-glib-aux/nm-time-utils.h
+++ b/src/libnm-glib-aux/nm-time-utils.h
@@ -41,6 +41,12 @@ nm_utils_get_monotonic_timestamp_msec_cached(gint64 *cache_now)
     return (*cache_now) ?: (*cache_now = nm_utils_get_monotonic_timestamp_msec());
 }
 
+static inline gint32
+nm_utils_get_monotonic_timestamp_sec_cached(gint32 *cache_now)
+{
+    return (*cache_now) ?: (*cache_now = nm_utils_get_monotonic_timestamp_sec());
+}
+
 gint64 nm_utils_clock_gettime_nsec(clockid_t clockid);
 gint64 nm_utils_clock_gettime_msec(clockid_t clockid);
 
diff --git a/src/libnm-glib-aux/nm-uuid.c b/src/libnm-glib-aux/nm-uuid.c
index 19b09667..ff0649d0 100644
--- a/src/libnm-glib-aux/nm-uuid.c
+++ b/src/libnm-glib-aux/nm-uuid.c
@@ -316,8 +316,20 @@ nm_uuid_generate_from_string(NMUuid       *uuid,
                              NMUuidType    uuid_type,
                              const NMUuid *type_args)
 {
-    g_return_val_if_fail(uuid, FALSE);
-    g_return_val_if_fail(slen == 0 || s, FALSE);
+    nm_auto_free_checksum GChecksum *sum = NULL;
+    union {
+        guint8 sha1[NM_UTILS_CHECKSUM_LENGTH_SHA1];
+        guint8 md5[NM_UTILS_CHECKSUM_LENGTH_MD5];
+        NMUuid uuid;
+    } digest;
+    gsize         digest_len;
+    GChecksumType checksum_type;
+
+    G_STATIC_ASSERT_EXPR(sizeof(digest.md5) >= sizeof(digest.uuid));
+    G_STATIC_ASSERT_EXPR(sizeof(digest.sha1) >= sizeof(digest.uuid));
+
+    g_return_val_if_fail(uuid, NULL);
+    g_return_val_if_fail(slen <= 0 || s, NULL);
 
     if (slen < 0)
         slen = s ? strlen(s) : 0;
@@ -325,43 +337,41 @@ nm_uuid_generate_from_string(NMUuid       *uuid,
     switch (uuid_type) {
     case NM_UUID_TYPE_LEGACY:
         nm_assert(!type_args);
-        nm_crypto_md5_hash(NULL, 0, (guint8 *) s, slen, (guint8 *) uuid, sizeof(*uuid));
+        type_args     = NULL;
+        checksum_type = G_CHECKSUM_MD5;
         break;
     case NM_UUID_TYPE_VERSION3:
+        if (!type_args)
+            type_args = &nm_uuid_ns_zero;
+        checksum_type = G_CHECKSUM_MD5;
+        break;
     case NM_UUID_TYPE_VERSION5:
-    {
         if (!type_args)
             type_args = &nm_uuid_ns_zero;
+        checksum_type = G_CHECKSUM_SHA1;
+        break;
+    default:
+        g_return_val_if_reached(NULL);
+    }
 
-        if (uuid_type == NM_UUID_TYPE_VERSION3) {
-            nm_crypto_md5_hash((guint8 *) s,
-                               slen,
-                               (guint8 *) type_args,
-                               sizeof(*type_args),
-                               (guint8 *) uuid,
-                               sizeof(*uuid));
-        } else {
-            nm_auto_free_checksum GChecksum *sum = NULL;
-            union {
-                guint8 sha1[NM_UTILS_CHECKSUM_LENGTH_SHA1];
-                NMUuid uuid;
-            } digest;
-
-            sum = g_checksum_new(G_CHECKSUM_SHA1);
-            g_checksum_update(sum, (guchar *) type_args, sizeof(*type_args));
-            g_checksum_update(sum, (guchar *) s, slen);
-            nm_utils_checksum_get_digest(sum, digest.sha1);
-
-            G_STATIC_ASSERT_EXPR(sizeof(digest.sha1) > sizeof(digest.uuid));
-            *uuid = digest.uuid;
-        }
+    sum = g_checksum_new(checksum_type);
+    if (type_args)
+        g_checksum_update(sum, (guchar *) type_args, sizeof(*type_args));
+    g_checksum_update(sum, (guchar *) s, slen);
+
+    digest_len = sizeof(digest);
+    g_checksum_get_digest(sum, (guint8 *) &digest, &digest_len);
+
+    nm_assert(digest_len >= sizeof(digest.uuid));
+    nm_assert(digest_len
+              == ((checksum_type == G_CHECKSUM_MD5 ? NM_UTILS_CHECKSUM_LENGTH_MD5
+                                                   : NM_UTILS_CHECKSUM_LENGTH_SHA1)));
+
+    *uuid = digest.uuid;
 
+    if (uuid_type != NM_UUID_TYPE_LEGACY) {
         uuid->uuid[6] = (uuid->uuid[6] & 0x0F) | (uuid_type << 4);
         uuid->uuid[8] = (uuid->uuid[8] & 0x3F) | 0x80;
-        break;
-    }
-    default:
-        g_return_val_if_reached(NULL);
     }
 
     return uuid;
diff --git a/src/libnm-glib-aux/nm-value-type.h b/src/libnm-glib-aux/nm-value-type.h
index d774e120..771021da 100644
--- a/src/libnm-glib-aux/nm-value-type.h
+++ b/src/libnm-glib-aux/nm-value-type.h
@@ -9,25 +9,26 @@
 typedef enum _nm_packed {
     NM_VALUE_TYPE_NONE   = 0,
     NM_VALUE_TYPE_UNSPEC = 1,
-    NM_VALUE_TYPE_BOOL   = 2,
-    NM_VALUE_TYPE_INT32  = 3,
-    NM_VALUE_TYPE_INT    = 4,
-    NM_VALUE_TYPE_INT64  = 5,
-    NM_VALUE_TYPE_UINT32 = 6,
-    NM_VALUE_TYPE_UINT   = 7,
-    NM_VALUE_TYPE_UINT64 = 8,
+
+    NM_VALUE_TYPE_BOOL,
+    NM_VALUE_TYPE_INT32,
+    NM_VALUE_TYPE_INT,
+    NM_VALUE_TYPE_INT64,
+    NM_VALUE_TYPE_UINT32,
+    NM_VALUE_TYPE_UINT,
+    NM_VALUE_TYPE_UINT64,
 
     /* Flags are for G_TYPE_FLAGS. That is, internally they are tracked
      * as a guint, they have a g_param_spec_flags() property and they are
      * serialized on D-Bus as "u". */
-    NM_VALUE_TYPE_FLAGS = 9,
+    NM_VALUE_TYPE_FLAGS,
 
     /* G_TYPE_ENUM */
-    NM_VALUE_TYPE_ENUM = 10,
-
-    NM_VALUE_TYPE_STRING = 11,
+    NM_VALUE_TYPE_ENUM,
 
-    NM_VALUE_TYPE_BYTES = 12,
+    NM_VALUE_TYPE_STRING,
+    NM_VALUE_TYPE_BYTES,
+    NM_VALUE_TYPE_STRV,
 } NMValueType;
 
 /*****************************************************************************/
@@ -112,14 +113,17 @@ nm_value_type_cmp(NMValueType value_type, gconstpointer p_a, gconstpointer p_b)
         return 0;
     case NM_VALUE_TYPE_STRING:
         return nm_strcmp0(*((const char *const *) p_a), *((const char *const *) p_b));
+
     case NM_VALUE_TYPE_BYTES:
-        return nm_g_bytes_equal0(*((const GBytes *const *) p_a), *((const GBytes *const *) p_b));
+    case NM_VALUE_TYPE_STRV:
+        /* These types have implementation define memory representations. */
+        break;
+
     case NM_VALUE_TYPE_NONE:
     case NM_VALUE_TYPE_UNSPEC:
         break;
     }
-    nm_assert_not_reached();
-    return 0;
+    return nm_assert_unreachable_val(0);
 }
 
 static inline gboolean
@@ -163,14 +167,12 @@ nm_value_type_copy(NMValueType value_type, gpointer dst, gconstpointer src)
             *((char **) dst) = g_strdup(*((const char *const *) src));
         }
         return;
+
     case NM_VALUE_TYPE_BYTES:
-        /* self assignment safe! */
-        if (*((GBytes **) dst) != *((const GBytes *const *) src)) {
-            _nm_unused gs_unref_bytes GBytes *old = *((GBytes **) dst);
+    case NM_VALUE_TYPE_STRV:
+        /* These types have implementation define memory representations. */
+        break;
 
-            *((GBytes **) dst) = g_bytes_ref(*((GBytes *const *) src));
-        }
-        return;
     case NM_VALUE_TYPE_NONE:
     case NM_VALUE_TYPE_UNSPEC:
         break;
@@ -212,14 +214,18 @@ nm_value_type_get_from_variant(NMValueType value_type,
         return;
 
     case NM_VALUE_TYPE_BYTES:
+    case NM_VALUE_TYPE_STRV:
+        /* These types have implementation define memory representations. */
+        break;
+
     case NM_VALUE_TYPE_INT:
     case NM_VALUE_TYPE_UINT:
     case NM_VALUE_TYPE_ENUM:
     case NM_VALUE_TYPE_FLAGS:
         /* These types don't have a defined variant type, because it's not
          * clear how many bits we would need or how to handle the type. */
+        break;
 
-        /* fall-through */
     case NM_VALUE_TYPE_NONE:
     case NM_VALUE_TYPE_UNSPEC:
         break;
@@ -230,8 +236,7 @@ nm_value_type_get_from_variant(NMValueType value_type,
 static inline GVariant *
 nm_value_type_to_variant(NMValueType value_type, gconstpointer src)
 {
-    const char   *v_string;
-    const GBytes *v_bytes;
+    const char *v_string;
 
     switch (value_type) {
     case NM_VALUE_TYPE_BOOL:
@@ -247,9 +252,11 @@ nm_value_type_to_variant(NMValueType value_type, gconstpointer src)
     case NM_VALUE_TYPE_STRING:
         v_string = *((const char *const *) src);
         return v_string ? g_variant_new_string(v_string) : NULL;
+
     case NM_VALUE_TYPE_BYTES:
-        v_bytes = *((const GBytes *const *) src);
-        return v_bytes ? nm_g_bytes_to_variant_ay(v_bytes) : NULL;
+    case NM_VALUE_TYPE_STRV:
+        /* These types have implementation define memory representations. */
+        break;
 
     case NM_VALUE_TYPE_INT:
     case NM_VALUE_TYPE_UINT:
@@ -257,14 +264,13 @@ nm_value_type_to_variant(NMValueType value_type, gconstpointer src)
     case NM_VALUE_TYPE_FLAGS:
         /* These types don't have a defined variant type, because it's not
          * clear how many bits we would need or how to handle the type. */
+        break;
 
-        /* fall-through */
     case NM_VALUE_TYPE_NONE:
     case NM_VALUE_TYPE_UNSPEC:
         break;
     }
-    nm_assert_not_reached();
-    return NULL;
+    return nm_assert_unreachable_val(NULL);
 }
 
 static inline const GVariantType *
@@ -285,6 +291,8 @@ nm_value_type_get_variant_type(NMValueType value_type)
         return G_VARIANT_TYPE_STRING;
     case NM_VALUE_TYPE_BYTES:
         return G_VARIANT_TYPE_BYTESTRING;
+    case NM_VALUE_TYPE_STRV:
+        return G_VARIANT_TYPE_STRING_ARRAY;
 
     case NM_VALUE_TYPE_INT:
     case NM_VALUE_TYPE_UINT:
diff --git a/src/libnm-glib-aux/tests/test-shared-general.c b/src/libnm-glib-aux/tests/test-shared-general.c
index ecba02e0..46689947 100644
--- a/src/libnm-glib-aux/tests/test-shared-general.c
+++ b/src/libnm-glib-aux/tests/test-shared-general.c
@@ -283,9 +283,6 @@ test_nm_utils_ip4_prefix_to_netmask(void)
     g_assert_cmpint(_nm_utils_ip4_prefix_to_netmask(32),
                     ==,
                     nmtst_inet4_from_string("255.255.255.255"));
-    g_assert_cmpint(_nm_utils_ip4_prefix_to_netmask(33),
-                    ==,
-                    nmtst_inet4_from_string("255.255.255.255"));
 }
 
 /*****************************************************************************/