summary refs log tree commit diff
path: root/clients/common
diff options
context:
space:
mode:
authorMichael Biebl <biebl@debian.org>2019-03-26 23:25:23 +0100
committerMichael Biebl <biebl@debian.org>2019-03-26 23:25:23 +0100
commit9a6dcbf895f9da01768e64b73cec88c16157d91e (patch)
treea359958930d731e9f1b59344642e10754419fe84 /clients/common
parent964ae8cc391520440cf5aa13e2b9cc34850ea6c2 (diff)
New upstream version 1.16.0 upstream/1.16.0
Diffstat (limited to 'clients/common')
-rw-r--r--clients/common/meson.build18
-rw-r--r--clients/common/nm-client-utils.c3
-rw-r--r--clients/common/nm-client-utils.h7
-rw-r--r--clients/common/nm-meta-setting-desc.c695
-rw-r--r--clients/common/nm-meta-setting-desc.h5
-rw-r--r--clients/common/nm-polkit-listener.c3
-rw-r--r--clients/common/nm-polkit-listener.h4
-rw-r--r--clients/common/nm-secret-agent-simple.c1155
-rw-r--r--clients/common/nm-secret-agent-simple.h42
-rw-r--r--clients/common/nm-vpn-helpers.c569
-rw-r--r--clients/common/nm-vpn-helpers.h3
-rw-r--r--clients/common/settings-docs.h27
-rw-r--r--clients/common/settings-docs.h.in27
-rw-r--r--clients/common/tests/meson.build4
-rw-r--r--clients/common/tests/test-general.c96
-rw-r--r--clients/common/tests/wg-test0.conf18
-rw-r--r--clients/common/tests/wg-test1.conf3
-rw-r--r--clients/common/tests/wg-test2.conf8
-rw-r--r--clients/common/tests/wg-test3.conf3
19 files changed, 1978 insertions, 712 deletions
diff --git a/clients/common/meson.build b/clients/common/meson.build
index 30fd2cfa..b4b6bcac 100644
--- a/clients/common/meson.build
+++ b/clients/common/meson.build
@@ -4,7 +4,7 @@ nm_polkit_listener = files('nm-polkit-listener.c')
 
 deps = [
   libnm_dep,
-  nm_core_dep
+  nm_core_dep,
 ]
 
 cflags = clients_cflags + [
@@ -13,18 +13,18 @@ cflags = clients_cflags + [
 
 libnmc_base = static_library(
   'nmc-base',
-  sources: shared_files_clients_common + files(
+  sources: files(
     'nm-client-utils.c',
     'nm-secret-agent-simple.c',
     'nm-vpn-helpers.c',
   ),
   dependencies: deps,
-  c_args: cflags
+  c_args: cflags,
 )
 
 libnmc_base_dep = declare_dependency(
   include_directories: common_inc,
-  link_with: libnmc_base
+  link_with: libnmc_base,
 )
 
 settings_docs = 'settings-docs.h'
@@ -34,7 +34,7 @@ if enable_introspection
     settings_docs,
     input: nm_property_docs,
     output: settings_docs,
-    command: [xsltproc, '--output', '@OUTPUT@', join_paths(meson.current_source_dir(), 'settings-docs.xsl'), '@INPUT@']
+    command: [xsltproc, '--output', '@OUTPUT@', join_paths(meson.current_source_dir(), 'settings-docs.xsl'), '@INPUT@'],
   )
 
   test(
@@ -46,7 +46,7 @@ else
   settings_docs_source = configure_file(
     input: settings_docs + '.in',
     output: settings_docs,
-    configuration: configuration_data()
+    configuration: configuration_data(),
   )
 endif
 
@@ -54,17 +54,17 @@ libnmc = static_library(
   'nmc',
   sources: files(
     'nm-meta-setting-access.c',
-    'nm-meta-setting-desc.c'
+    'nm-meta-setting-desc.c',
   ) + shared_nm_meta_setting_c + shared_nm_ethtool_utils_c + [settings_docs_source],
   dependencies: deps,
   c_args: cflags,
   link_with: libnmc_base,
-  link_depends: settings_docs_source
+  link_depends: settings_docs_source,
 )
 
 libnmc_dep = declare_dependency(
   include_directories: common_inc,
-  link_with: libnmc
+  link_with: libnmc,
 )
 
 if (enable_introspection or enable_nmtui) and enable_tests
diff --git a/clients/common/nm-client-utils.c b/clients/common/nm-client-utils.c
index 8c7553d8..1241131a 100644
--- a/clients/common/nm-client-utils.c
+++ b/clients/common/nm-client-utils.c
@@ -348,10 +348,11 @@ NM_UTILS_LOOKUP_STR_DEFINE (nmc_device_reason_to_string, NMDeviceStateReason,
 	NM_UTILS_LOOKUP_ITEM (NM_DEVICE_STATE_REASON_NEW_ACTIVATION,                 N_("New connection activation was enqueued")),
 	NM_UTILS_LOOKUP_ITEM (NM_DEVICE_STATE_REASON_PARENT_CHANGED,                 N_("The device's parent changed")),
 	NM_UTILS_LOOKUP_ITEM (NM_DEVICE_STATE_REASON_PARENT_MANAGED_CHANGED,         N_("The device parent's management changed")),
-	NM_UTILS_LOOKUP_ITEM (NM_DEVICE_STATE_REASON_OVSDB_FAILED,                   N_("OpenVSwitch database connection failed")),
+	NM_UTILS_LOOKUP_ITEM (NM_DEVICE_STATE_REASON_OVSDB_FAILED,                   N_("Open vSwitch database connection failed")),
 	NM_UTILS_LOOKUP_ITEM (NM_DEVICE_STATE_REASON_IP_ADDRESS_DUPLICATE,           N_("A duplicate IP address was detected")),
 	NM_UTILS_LOOKUP_ITEM (NM_DEVICE_STATE_REASON_IP_METHOD_UNSUPPORTED,          N_("The selected IP method is not supported")),
 	NM_UTILS_LOOKUP_ITEM (NM_DEVICE_STATE_REASON_SRIOV_CONFIGURATION_FAILED,     N_("Failed to configure SR-IOV parameters")),
+	NM_UTILS_LOOKUP_ITEM (NM_DEVICE_STATE_REASON_PEER_NOT_FOUND,                 N_("The Wi-Fi P2P peer could not be found")),
 )
 
 NM_UTILS_LOOKUP_STR_DEFINE (nm_active_connection_state_reason_to_string, NMActiveConnectionStateReason,
diff --git a/clients/common/nm-client-utils.h b/clients/common/nm-client-utils.h
index 9d818873..a5bc05fa 100644
--- a/clients/common/nm-client-utils.h
+++ b/clients/common/nm-client-utils.h
@@ -24,6 +24,13 @@
 #include "nm-active-connection.h"
 #include "nm-device.h"
 
+
+#define nm_auto_unref_ip_address nm_auto (_nm_ip_address_unref)
+NM_AUTO_DEFINE_FCN0 (NMIPAddress *, _nm_ip_address_unref, nm_ip_address_unref)
+
+#define nm_auto_unref_wgpeer nm_auto (_nm_auto_unref_wgpeer)
+NM_AUTO_DEFINE_FCN0 (NMWireGuardPeer *, _nm_auto_unref_wgpeer, nm_wireguard_peer_unref)
+
 const NMObject **nmc_objects_sort_by_path (const NMObject *const*objs, gssize len);
 
 const char *nmc_string_is_valid (const char *input, const char **allowed, GError **error);
diff --git a/clients/common/nm-meta-setting-desc.c b/clients/common/nm-meta-setting-desc.c
index d13ee4ce..6e35228a 100644
--- a/clients/common/nm-meta-setting-desc.c
+++ b/clients/common/nm-meta-setting-desc.c
@@ -117,7 +117,6 @@ _parse_ip_route (int family,
 {
 	const int MAX_PREFIX = (family == AF_INET) ? 32 : 128;
 	const char *next_hop = NULL;
-	const char *canon_dest;
 	int prefix;
 	NMIPRoute *route = NULL;
 	GError *local = NULL;
@@ -229,18 +228,6 @@ _parse_ip_route (int family,
 		return NULL;
 	}
 
-	/* We don't accept default routes as NetworkManager handles it
-	 * itself. But we have to check this after @route has normalized the
-	 * dest string.
-	 */
-	canon_dest = nm_ip_route_get_dest (route);
-	if (!strcmp (canon_dest, "0.0.0.0") || !strcmp (canon_dest, "::")) {
-		g_set_error_literal (error, NM_UTILS_ERROR, NM_UTILS_ERROR_INVALID_ARGUMENT,
-		                     _("default route cannot be added (NetworkManager handles it by itself)"));
-		g_clear_pointer (&route, nm_ip_route_unref);
-		return NULL;
-	}
-
 	if (attrs) {
 		GHashTableIter iter;
 		char *name;
@@ -285,13 +272,14 @@ _dump_team_link_watcher (NMTeamLinkWatcher *watcher)
 	DUMP_WATCHER_INT (w_dump, watcher, "init-wait", init_wait);
 	DUMP_WATCHER_INT (w_dump, watcher, "interval", interval);
 	DUMP_WATCHER_INT (w_dump, watcher, "missed-max", missed_max);
-#undef DUMP_WATCHER_INT
 	g_string_append_printf (w_dump, " target-host=%s",
 	                        nm_team_link_watcher_get_target_host (watcher));
 
 	if (nm_streq (name, NM_TEAM_LINK_WATCHER_NSNA_PING))
 		return g_string_free (w_dump, FALSE);
 
+	DUMP_WATCHER_INT (w_dump, watcher, "vlanid", vlanid);
+#undef DUMP_WATCHER_INT
 	g_string_append_printf (w_dump, " source-host=%s",
 	                        nm_team_link_watcher_get_source_host (watcher));
 	flags = nm_team_link_watcher_get_flags (watcher);
@@ -313,7 +301,7 @@ _parse_team_link_watcher (const char *str,
 	gs_free char *str_clean = NULL;
 	guint i;
 	gs_free const char *name = NULL;
-	int val1 = 0, val2 = 0, val3 = 3;
+	int val1 = 0, val2 = 0, val3 = 3, val4 = -1;
 	gs_free const char *target_host = NULL;
 	gs_free const char *source_host = NULL;
 	NMTeamLinkWatcherArpPingFlags flags = 0;
@@ -358,6 +346,8 @@ _parse_team_link_watcher (const char *str,
 			val2 = _nm_utils_ascii_str_to_int64 (pair[1], 10, 0, G_MAXINT32, -1);
 		else if (nm_streq (pair[0], "missed-max"))
 			val3 = _nm_utils_ascii_str_to_int64 (pair[1], 10, 0, G_MAXINT32, -1);
+		else if (nm_streq (pair[0], "vlanid"))
+			val4 = _nm_utils_ascii_str_to_int64 (pair[1], 10, -1, 4094, -2);
 		else if (nm_streq (pair[0], "target-host"))
 			target_host = g_strdup (pair[1]);
 		else if (nm_streq (pair[0], "source-host"))
@@ -382,6 +372,11 @@ _parse_team_link_watcher (const char *str,
 			             "value is not a valid number [0, MAXINT]");
 			return NULL;
 		}
+		if (val4 < -1) {
+			g_set_error (error, 1, 0, "'%s' is not valid: %s", watcherv[i],
+			             "value is not a valid number [-1, 4094]");
+			return NULL;
+		}
 	}
 
 	if (nm_streq0 (name, NM_TEAM_LINK_WATCHER_ETHTOOL))
@@ -389,7 +384,7 @@ _parse_team_link_watcher (const char *str,
 	else if (nm_streq0 (name, NM_TEAM_LINK_WATCHER_NSNA_PING))
 		return nm_team_link_watcher_new_nsna_ping (val1, val2, val3, target_host, error);
 	else if (nm_streq0 (name, NM_TEAM_LINK_WATCHER_ARP_PING))
-		return nm_team_link_watcher_new_arp_ping (val1, val2, val3, target_host, source_host, flags, error);
+		return nm_team_link_watcher_new_arp_ping2 (val1, val2, val3, val4, target_host, source_host, flags, error);
 
 	if (!name)
 		g_set_error (error, 1, 0, "link watcher name missing");
@@ -1518,18 +1513,16 @@ bytes_to_string (GBytes *bytes)
 {
 	const guint8 *data;
 	gsize len;
-	GString *cert = NULL;
-	int i;
 
 	if (!bytes)
 		return NULL;
-	data = g_bytes_get_data (bytes, &len);
 
-	cert = g_string_new (NULL);
-	for (i = 0; i < len; i++)
-		g_string_append_printf (cert, "%02X", data[i]);
-
-	return g_string_free (cert, FALSE);
+	data = g_bytes_get_data (bytes, &len);
+	return nm_utils_bin2hexstr_full (data,
+	                                 len,
+	                                 '\0',
+	                                 TRUE,
+	                                 NULL);
 }
 
 static char *
@@ -1948,26 +1941,36 @@ nmc_util_is_domain (const char *domain)
 }
 
 static gboolean
-nmc_property_set_bytes (NMSetting *setting, const char *prop, const char *value, GError **error)
+_set_fcn_gobject_bytes (ARGS_SET_FCN)
 {
-	gs_free char *val_strip = NULL;
+	gs_free char *val_strip_free = NULL;
 	gs_free const char **strv = NULL;
+	const char *val_strip;
 	const char **iter;
-	GBytes *bytes;
-	GByteArray *array = NULL;
-	gboolean success = TRUE;
+	gs_unref_bytes GBytes *bytes = NULL;
+	GByteArray *array;
 
 	nm_assert (!error || !*error);
 
-	val_strip = g_strstrip (g_strdup (value));
+	val_strip = nm_strstrip_avoid_copy (value, &val_strip_free);
 
 	/* First try hex string in the format of AAbbCCDd */
 	bytes = nm_utils_hexstr2bin (val_strip);
 	if (bytes)
 		goto done;
 
+	if (   !property_info->property_typ_data
+	    || !property_info->property_typ_data->subtype.gobject_bytes.legacy_format) {
+		if (value && value[0]) {
+			g_set_error_literal (error, 1, 0, _("not a valid hex-string"));
+			return FALSE;
+		}
+		/* accept the empty word to reset the property to %NULL. */
+		goto done;
+	}
+
 	/* Otherwise, consider the following format: AA b 0xCc D */
-	strv = nm_utils_strsplit_set (val_strip, " \t", FALSE);
+	strv = nm_utils_strsplit_set (value, " \t", FALSE);
 	array = g_byte_array_sized_new (NM_PTRARRAY_LEN (strv));
 	for (iter = strv; iter && *iter; iter++) {
 		int v;
@@ -1977,8 +1980,7 @@ nmc_property_set_bytes (NMSetting *setting, const char *prop, const char *value,
 		if (v == -1) {
 			g_set_error (error, 1, 0, _("'%s' is not a valid hex character"), *iter);
 			g_byte_array_free (array, TRUE);
-			success = FALSE;
-			goto done;
+			return FALSE;
 		}
 		v8 = v;
 		g_byte_array_append (array, &v8, 1);
@@ -1986,13 +1988,8 @@ nmc_property_set_bytes (NMSetting *setting, const char *prop, const char *value,
 	bytes = g_byte_array_free_to_bytes (array);
 
 done:
-	if (success)
-		g_object_set (setting, prop, bytes, NULL);
-
-	if (bytes)
-		g_bytes_unref (bytes);
-
-	return success;
+	g_object_set (setting, property_info->property_name, bytes, NULL);
+	return TRUE;
 }
 
 /*****************************************************************************/
@@ -2108,14 +2105,16 @@ _get_fcn_802_1x_phase2_client_cert (ARGS_GET_FCN)
 }
 
 static gconstpointer
-_get_fcn_802_1x_password_raw (ARGS_GET_FCN)
+_get_fcn_gobject_bytes (ARGS_GET_FCN)
 {
-	NMSetting8021x *s_8021X = NM_SETTING_802_1X (setting);
+	gs_unref_bytes GBytes *bytes = NULL;
 	char *str;
 
 	RETURN_UNSUPPORTED_GET_TYPE ();
 
-	str = bytes_to_string (nm_setting_802_1x_get_password_raw (s_8021X));
+	g_object_get (setting, property_info->property_name, &bytes, NULL);
+
+	str = bytes_to_string (bytes);
 	NM_SET_OUT (out_is_default, !str || !str[0]);
 	RETURN_STR_TO_FREE (str);
 }
@@ -2333,12 +2332,6 @@ DEFINE_SETTER_PRIV_KEY (_set_fcn_802_1x_phase2_private_key,
                         nm_setting_802_1x_get_phase2_private_key_password,
                         nm_setting_802_1x_set_phase2_private_key)
 
-static gboolean
-_set_fcn_802_1x_password_raw (ARGS_SET_FCN)
-{
-	return nmc_property_set_bytes (setting, property_info->property_name, value, error);
-}
-
 static gconstpointer
 _get_fcn_bond_options (ARGS_GET_FCN)
 {
@@ -4847,16 +4840,6 @@ _gobject_enum_pre_set_notify_fcn_wireless_security_wep_key_type (const NMMetaPro
 	}
 }
 
-static const char *
-_validate_fcn_wireless_security_psk (const char *value, char **out_to_free, GError **error)
-{
-	if (!nm_utils_wpa_psk_valid (value)) {
-		g_set_error (error, 1, 0, _("'%s' is not a valid PSK"), value);
-		return NULL;
-	}
-	return value;
-}
-
 /*****************************************************************************/
 
 static gconstpointer
@@ -5071,6 +5054,11 @@ static const NMMetaPropertyType _pt_gobject_mtu = {
 	.set_fcn =                      _set_fcn_gobject_mtu,
 };
 
+static const NMMetaPropertyType _pt_gobject_bytes = {
+	.get_fcn =                     _get_fcn_gobject_bytes,
+	.set_fcn =                     _set_fcn_gobject_bytes,
+};
+
 static const NMMetaPropertyType _pt_gobject_mac = {
 	.get_fcn =                      _get_fcn_gobject,
 	.set_fcn =                      _set_fcn_gobject_mac,
@@ -5121,10 +5109,8 @@ static const NMMetaPropertyType _pt_ethtool = {
 #define PROPERTY_INFO_WITH_DESC(name, ...) \
 	PROPERTY_INFO (name, DESCRIBE_DOC_##name, ##__VA_ARGS__)
 
-#define VALUES_STATIC(...)  (((const char *[]) { __VA_ARGS__, NULL }))
-
-#define ENUM_VALUE_INFOS(...)  (((const NMUtilsEnumValueInfo []) { __VA_ARGS__, { 0 } }))
-#define INT_VALUE_INFOS(...)  (((const NMMetaUtilsIntValueInfo []) { __VA_ARGS__, { 0 } }))
+#define ENUM_VALUE_INFOS(...)  (((const NMUtilsEnumValueInfo    []) { __VA_ARGS__ { .nick = NULL, }, }))
+#define INT_VALUE_INFOS(...)   (((const NMMetaUtilsIntValueInfo []) { __VA_ARGS__ { .nick = NULL, }, }))
 
 #define GET_FCN_WITH_DEFAULT(type, func) \
 	/* macro that returns @func as const (gboolean(*)(NMSetting*)) type, but checks
@@ -5166,12 +5152,25 @@ static const NMMetaPropertyType _pt_ethtool = {
 		.property_typ_data = DEFINE_PROPERTY_TYP_DATA_SUBTYPE (gobject_int, \
 			.value_infos =              INT_VALUE_INFOS ( \
 				{ \
-					.value = -1, \
+					.value.i64 = -1, \
 					.nick = "unset", \
-				} \
+				}, \
 			), \
 		),
 
+#define _CURRENT_NM_META_SETTING_TYPE NM_META_SETTING_TYPE_6LOWPAN
+static const NMMetaPropertyInfo *const property_infos_6LOWPAN[] = {
+	PROPERTY_INFO_WITH_DESC (NM_SETTING_6LOWPAN_PARENT,
+		.is_cli_option =                TRUE,
+		.property_alias =               "dev",
+		.inf_flags =                    NM_META_PROPERTY_INF_FLAG_REQD,
+		.prompt =                       N_("IEEE 802.15.4 (WPAN) parent device or connection UUID"),
+		.property_type =                &_pt_gobject_string,
+	),
+	NULL
+};
+
+#undef  _CURRENT_NM_META_SETTING_TYPE
 #define _CURRENT_NM_META_SETTING_TYPE NM_META_SETTING_TYPE_802_1X
 static const NMMetaPropertyInfo *const property_infos_802_1X[] = {
 	PROPERTY_INFO_WITH_DESC (NM_SETTING_802_1X_EAP,
@@ -5181,7 +5180,7 @@ static const NMMetaPropertyInfo *const property_infos_802_1X[] = {
 			.remove_fcn =               _remove_fcn_802_1x_eap,
 		),
 		.property_typ_data = DEFINE_PROPERTY_TYP_DATA (
-			.values_static =            VALUES_STATIC ("leap", "md5", "tls", "peap", "ttls", "sim", "fast", "pwd"),
+			.values_static =            NM_MAKE_STRV ("leap", "md5", "tls", "peap", "ttls", "sim", "fast", "pwd"),
 		),
 	),
 	PROPERTY_INFO_WITH_DESC (NM_SETTING_802_1X_IDENTITY,
@@ -5248,19 +5247,19 @@ static const NMMetaPropertyInfo *const property_infos_802_1X[] = {
 	PROPERTY_INFO_WITH_DESC (NM_SETTING_802_1X_PHASE1_PEAPVER,
 		.property_type =                &_pt_gobject_string,
 		.property_typ_data = DEFINE_PROPERTY_TYP_DATA (
-			.values_static =            VALUES_STATIC ("0", "1"),
+			.values_static =            NM_MAKE_STRV ("0", "1"),
 		),
 	),
 	PROPERTY_INFO_WITH_DESC (NM_SETTING_802_1X_PHASE1_PEAPLABEL,
 		.property_type =                &_pt_gobject_string,
 		.property_typ_data = DEFINE_PROPERTY_TYP_DATA (
-			.values_static =            VALUES_STATIC ("0", "1"),
+			.values_static =            NM_MAKE_STRV ("0", "1"),
 		),
 	),
 	PROPERTY_INFO_WITH_DESC (NM_SETTING_802_1X_PHASE1_FAST_PROVISIONING,
 		.property_type =                &_pt_gobject_string,
 		.property_typ_data = DEFINE_PROPERTY_TYP_DATA (
-			.values_static =            VALUES_STATIC ("0", "1", "2", "3"),
+			.values_static =            NM_MAKE_STRV ("0", "1", "2", "3"),
 		),
 	),
 	PROPERTY_INFO_WITH_DESC (NM_SETTING_802_1X_PHASE1_AUTH_FLAGS,
@@ -5275,13 +5274,13 @@ static const NMMetaPropertyInfo *const property_infos_802_1X[] = {
 	PROPERTY_INFO_WITH_DESC (NM_SETTING_802_1X_PHASE2_AUTH,
 		.property_type =                &_pt_gobject_string,
 		.property_typ_data = DEFINE_PROPERTY_TYP_DATA (
-			.values_static =            VALUES_STATIC ("pap", "chap", "mschap", "mschapv2", "gtc", "otp", "md5", "tls"),
+			.values_static =            NM_MAKE_STRV ("pap", "chap", "mschap", "mschapv2", "gtc", "otp", "md5", "tls"),
 		),
 	),
 	PROPERTY_INFO_WITH_DESC (NM_SETTING_802_1X_PHASE2_AUTHEAP,
 		.property_type =                &_pt_gobject_string,
 		.property_typ_data = DEFINE_PROPERTY_TYP_DATA (
-			.values_static =            VALUES_STATIC ("md5", "mschapv2", "otp", "gtc", "tls"),
+			.values_static =            NM_MAKE_STRV ("md5", "mschapv2", "otp", "gtc", "tls"),
 		),
 	),
 	PROPERTY_INFO_WITH_DESC (NM_SETTING_802_1X_PHASE2_CA_CERT,
@@ -5355,9 +5354,9 @@ static const NMMetaPropertyInfo *const property_infos_802_1X[] = {
 		       "(with optional 0x/0X prefix, and optional leading 0).\n\n"
 		       "Examples: ab0455a6ea3a74C2\n"
 		       "          ab 4 55 0xa6 ea 3a 74 C2\n"),
-		.property_type = DEFINE_PROPERTY_TYPE (
-			.get_fcn =                  _get_fcn_802_1x_password_raw,
-			.set_fcn =                  _set_fcn_802_1x_password_raw,
+		.property_type =                 &_pt_gobject_bytes,
+		.property_typ_data = DEFINE_PROPERTY_TYP_DATA_SUBTYPE (gobject_bytes,
+			.legacy_format =            TRUE,
 		),
 	),
 	PROPERTY_INFO_WITH_DESC (NM_SETTING_802_1X_PASSWORD_RAW_FLAGS,
@@ -5443,9 +5442,9 @@ static const NMMetaPropertyInfo *const property_infos_ADSL[] = {
 		.def_hint =                     NM_META_TEXT_PROMPT_ADSL_PROTO_CHOICES,
 		.property_type =                &_pt_gobject_string,
 		.property_typ_data = DEFINE_PROPERTY_TYP_DATA (
-			.values_static =            VALUES_STATIC (NM_SETTING_ADSL_PROTOCOL_PPPOA,
-			                                           NM_SETTING_ADSL_PROTOCOL_PPPOE,
-			                                           NM_SETTING_ADSL_PROTOCOL_IPOATM),
+			.values_static =            NM_MAKE_STRV (NM_SETTING_ADSL_PROTOCOL_PPPOA,
+			                                          NM_SETTING_ADSL_PROTOCOL_PPPOE,
+			                                          NM_SETTING_ADSL_PROTOCOL_IPOATM),
 		),
 	),
 	PROPERTY_INFO_WITH_DESC (NM_SETTING_ADSL_ENCAPSULATION,
@@ -5455,8 +5454,8 @@ static const NMMetaPropertyInfo *const property_infos_ADSL[] = {
 		.def_hint =                     NM_META_TEXT_PROMPT_ADSL_ENCAP_CHOICES,
 		.property_type =                &_pt_gobject_string,
 		.property_typ_data = DEFINE_PROPERTY_TYP_DATA (
-			.values_static =            VALUES_STATIC (NM_SETTING_ADSL_ENCAPSULATION_VCMUX,
-			                                           NM_SETTING_ADSL_ENCAPSULATION_LLC),
+			.values_static =            NM_MAKE_STRV (NM_SETTING_ADSL_ENCAPSULATION_VCMUX,
+			                                          NM_SETTING_ADSL_ENCAPSULATION_LLC),
 		),
 	),
 	PROPERTY_INFO_WITH_DESC (NM_SETTING_ADSL_VPI,
@@ -5484,9 +5483,9 @@ static const NMMetaPropertyInfo *const property_infos_BLUETOOTH[] = {
 		.def_hint =                     NM_META_TEXT_PROMPT_BT_TYPE_CHOICES,
 		.property_type =                &_pt_gobject_string,
 		.property_typ_data = DEFINE_PROPERTY_TYP_DATA (
-			.values_static =            VALUES_STATIC (NM_SETTING_BLUETOOTH_TYPE_DUN,
-			                                           NM_SETTING_BLUETOOTH_TYPE_PANU,
-			                                           NM_SETTING_BLUETOOTH_TYPE_NAP),
+			.values_static =            NM_MAKE_STRV (NM_SETTING_BLUETOOTH_TYPE_DUN,
+			                                          NM_SETTING_BLUETOOTH_TYPE_PANU,
+			                                          NM_SETTING_BLUETOOTH_TYPE_NAP),
 		),
 	),
 	NULL
@@ -5680,13 +5679,13 @@ static const NMMetaPropertyInfo *const property_infos_CONNECTION[] = {
 		.property_typ_data = DEFINE_PROPERTY_TYP_DATA_SUBTYPE (gobject_int,
 			.value_infos =              INT_VALUE_INFOS (
 				{
-					.value = -1,
+					.value.i64 = -1,
 					.nick = "default",
 				},
 				{
-					.value = 0,
+					.value.i64 = 0,
 					.nick = "forever",
-				}
+				},
 			),
 		),
 	),
@@ -5739,11 +5738,11 @@ static const NMMetaPropertyInfo *const property_infos_CONNECTION[] = {
 		.inf_flags =                    NM_META_PROPERTY_INF_FLAG_DONT_ASK,
 		.property_type =                &_pt_gobject_string,
 		.property_typ_data = DEFINE_PROPERTY_TYP_DATA (
-			.values_static =            VALUES_STATIC (NM_SETTING_BOND_SETTING_NAME,
-			                                           NM_SETTING_BRIDGE_SETTING_NAME,
-			                                           NM_SETTING_OVS_BRIDGE_SETTING_NAME,
-			                                           NM_SETTING_OVS_PORT_SETTING_NAME,
-			                                           NM_SETTING_TEAM_SETTING_NAME),
+			.values_static =            NM_MAKE_STRV (NM_SETTING_BOND_SETTING_NAME,
+			                                          NM_SETTING_BRIDGE_SETTING_NAME,
+			                                          NM_SETTING_OVS_BRIDGE_SETTING_NAME,
+			                                          NM_SETTING_OVS_PORT_SETTING_NAME,
+			                                          NM_SETTING_TEAM_SETTING_NAME),
 		),
 	),
 	PROPERTY_INFO_WITH_DESC (NM_SETTING_CONNECTION_AUTOCONNECT_SLAVES,
@@ -5778,7 +5777,7 @@ static const NMMetaPropertyInfo *const property_infos_CONNECTION[] = {
 			.set_fcn =                  _set_fcn_connection_metered,
 		),
 		.property_typ_data = DEFINE_PROPERTY_TYP_DATA (
-			.values_static =            VALUES_STATIC ("yes", "no", "unknown"),
+			.values_static =            NM_MAKE_STRV ("yes", "no", "unknown"),
 		),
 	),
 	PROPERTY_INFO_WITH_DESC (NM_SETTING_CONNECTION_LLDP,
@@ -5790,7 +5789,7 @@ static const NMMetaPropertyInfo *const property_infos_CONNECTION[] = {
 					{
 						.value = NM_SETTING_CONNECTION_LLDP_ENABLE_RX,
 						.nick = "enable",
-					}
+					},
 				),
 			),
 			.typ_flags =                  NM_META_PROPERTY_TYP_FLAG_ENUM_GET_PARSABLE_TEXT
@@ -5831,7 +5830,7 @@ static const NMMetaPropertyInfo *const property_infos_DCB[] = {
 	PROPERTY_INFO_WITH_DESC (NM_SETTING_DCB_APP_FCOE_MODE,
 		.property_type =                &_pt_gobject_string,
 		.property_typ_data = DEFINE_PROPERTY_TYP_DATA (
-			.values_static =            VALUES_STATIC (NM_SETTING_DCB_FCOE_MODE_FABRIC,
+			.values_static =            NM_MAKE_STRV (NM_SETTING_DCB_FCOE_MODE_FABRIC,
 			                                           NM_SETTING_DCB_FCOE_MODE_VN2VN),
 		),
 	),
@@ -6061,7 +6060,7 @@ static const NMMetaPropertyInfo *const property_infos_INFINIBAND[] = {
 		.def_hint =                     NM_META_TEXT_PROMPT_IB_MODE_CHOICES,
 		.property_type =                &_pt_gobject_string,
 		.property_typ_data = DEFINE_PROPERTY_TYP_DATA (
-			.values_static =            VALUES_STATIC ("datagram", "connected"),
+			.values_static =            NM_MAKE_STRV ("datagram", "connected"),
 		),
 	),
 	PROPERTY_INFO_WITH_DESC (NM_SETTING_INFINIBAND_P_KEY,
@@ -6173,15 +6172,15 @@ static const NMMetaPropertyInfo *const property_infos_IP4_CONFIG[] = {
 	PROPERTY_INFO (NM_SETTING_IP_CONFIG_ROUTE_TABLE, DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_ROUTE_TABLE,
 		.property_type =                &_pt_gobject_int,
 		.property_typ_data = DEFINE_PROPERTY_TYP_DATA_SUBTYPE (gobject_int,
-			.value_infos = INT_VALUE_INFOS (
+			.value_infos =              INT_VALUE_INFOS (
 				{
-					.value = 0,
+					.value.i64 = 0,
 					.nick = "unspec",
 				},
 				{
-					.value = 254,
+					.value.i64 = 254,
 					.nick = "main",
-				}
+				},
 			),
 		),
 	),
@@ -6197,15 +6196,15 @@ static const NMMetaPropertyInfo *const property_infos_IP4_CONFIG[] = {
 	PROPERTY_INFO (NM_SETTING_IP_CONFIG_DHCP_TIMEOUT, DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_DHCP_TIMEOUT,
 		.property_type =                &_pt_gobject_int,
 		.property_typ_data = DEFINE_PROPERTY_TYP_DATA_SUBTYPE (gobject_int,
-			.value_infos =          INT_VALUE_INFOS (
+			.value_infos =              INT_VALUE_INFOS (
 				{
-					.value = 0,
+					.value.i64 = 0,
 					.nick = "default",
 				},
 				{
-					.value = G_MAXINT32,
+					.value.i64 = G_MAXINT32,
 					.nick = "infinity",
-				}
+				},
 			),
 		),
 	),
@@ -6229,13 +6228,13 @@ static const NMMetaPropertyInfo *const property_infos_IP4_CONFIG[] = {
 		.property_typ_data = DEFINE_PROPERTY_TYP_DATA_SUBTYPE (gobject_int,
 			.value_infos =              INT_VALUE_INFOS (
 				{
-					.value = -1,
+					.value.i64 = -1,
 					.nick = "default",
 				},
 				{
-					.value = 0,
+					.value.i64 = 0,
 					.nick = "off",
-				}
+				},
 			),
 		),
 	),
@@ -6336,15 +6335,15 @@ static const NMMetaPropertyInfo *const property_infos_IP6_CONFIG[] = {
 	PROPERTY_INFO (NM_SETTING_IP_CONFIG_ROUTE_TABLE, DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_ROUTE_TABLE,
 		.property_type =                &_pt_gobject_int,
 		.property_typ_data = DEFINE_PROPERTY_TYP_DATA_SUBTYPE (gobject_int,
-			.value_infos =          INT_VALUE_INFOS (
+			.value_infos =              INT_VALUE_INFOS (
 				{
-					.value = 0,
+					.value.i64 = 0,
 					.nick = "unspec",
 				},
 				{
-					.value = 254,
+					.value.i64 = 254,
 					.nick = "main",
-				}
+				},
 			),
 		),
 	),
@@ -6371,7 +6370,7 @@ static const NMMetaPropertyInfo *const property_infos_IP6_CONFIG[] = {
 				{
 					.value = NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_TEMP_ADDR,
 					.nick = "enabled, prefer temporary IP",
-				}
+				},
 			),
 		),
 	),
@@ -6621,47 +6620,12 @@ static const NMMetaPropertyInfo *const property_infos_OLPC_MESH[] = {
 };
 
 #undef  _CURRENT_NM_META_SETTING_TYPE
-#define _CURRENT_NM_META_SETTING_TYPE NM_META_SETTING_TYPE_PPPOE
-static const NMMetaPropertyInfo *const property_infos_PPPOE[] = {
-	PROPERTY_INFO_WITH_DESC (NM_SETTING_PPPOE_PARENT,
-		.is_cli_option =                TRUE,
-		.property_alias =               "parent",
-		.prompt =                       N_("PPPoE parent device"),
-		.property_type =                &_pt_gobject_string,
-	),
-	PROPERTY_INFO_WITH_DESC (NM_SETTING_PPPOE_SERVICE,
-		.is_cli_option =                TRUE,
-		.property_alias =               "service",
-		.prompt =                       N_("Service [none]"),
-		.property_type =                &_pt_gobject_string,
-	),
-	PROPERTY_INFO_WITH_DESC (NM_SETTING_PPPOE_USERNAME,
-		.is_cli_option =                TRUE,
-		.property_alias =               "username",
-		.inf_flags =                    NM_META_PROPERTY_INF_FLAG_REQD,
-		.prompt =                       N_("PPPoE username"),
-		.property_type =                &_pt_gobject_string,
-	),
-	PROPERTY_INFO_WITH_DESC (NM_SETTING_PPPOE_PASSWORD,
-		.is_cli_option =                TRUE,
-		.property_alias =               "password",
-		.prompt =                       N_("Password [none]"),
-		.is_secret =                    TRUE,
-		.property_type =                &_pt_gobject_string,
-	),
-	PROPERTY_INFO_WITH_DESC (NM_SETTING_PPPOE_PASSWORD_FLAGS,
-		.property_type =                &_pt_gobject_secret_flags,
-	),
-	NULL
-};
-
-#undef  _CURRENT_NM_META_SETTING_TYPE
 #define _CURRENT_NM_META_SETTING_TYPE NM_META_SETTING_TYPE_OVS_BRIDGE
 static const NMMetaPropertyInfo *const property_infos_OVS_BRIDGE[] = {
 	PROPERTY_INFO_WITH_DESC (NM_SETTING_OVS_BRIDGE_FAIL_MODE,
 		.property_type =                &_pt_gobject_string,
 		.property_typ_data = DEFINE_PROPERTY_TYP_DATA (
-			.values_static =        VALUES_STATIC ("secure", "standalone"),
+			.values_static =            NM_MAKE_STRV ("secure", "standalone"),
 		),
 	),
 	PROPERTY_INFO_WITH_DESC (NM_SETTING_OVS_BRIDGE_MCAST_SNOOPING_ENABLE,
@@ -6682,7 +6646,7 @@ static const NMMetaPropertyInfo *const property_infos_OVS_INTERFACE[] = {
 	PROPERTY_INFO_WITH_DESC (NM_SETTING_OVS_INTERFACE_TYPE,
 		.property_type =                &_pt_gobject_string,
 		.property_typ_data = DEFINE_PROPERTY_TYP_DATA (
-			.values_static =        VALUES_STATIC ("internal", "patch"),
+			.values_static =            NM_MAKE_STRV ("internal", "patch"),
 		),
 	),
 	NULL
@@ -6703,7 +6667,7 @@ static const NMMetaPropertyInfo *const property_infos_OVS_PORT[] = {
 	PROPERTY_INFO_WITH_DESC (NM_SETTING_OVS_PORT_VLAN_MODE,
 		.property_type =                &_pt_gobject_string,
 		.property_typ_data = DEFINE_PROPERTY_TYP_DATA (
-			.values_static =        VALUES_STATIC ("access", "native-tagged", "native-untagged", "trunk"),
+			.values_static =            NM_MAKE_STRV ("access", "native-tagged", "native-untagged", "trunk"),
 		),
 	),
 	PROPERTY_INFO_WITH_DESC (NM_SETTING_OVS_PORT_TAG,
@@ -6712,13 +6676,13 @@ static const NMMetaPropertyInfo *const property_infos_OVS_PORT[] = {
 	PROPERTY_INFO_WITH_DESC (NM_SETTING_OVS_PORT_LACP,
 		.property_type =                &_pt_gobject_string,
 		.property_typ_data = DEFINE_PROPERTY_TYP_DATA (
-			.values_static =        VALUES_STATIC ("active", "off", "passive"),
+			.values_static =            NM_MAKE_STRV ("active", "off", "passive"),
 		),
 	),
 	PROPERTY_INFO_WITH_DESC (NM_SETTING_OVS_PORT_BOND_MODE,
 		.property_type =                &_pt_gobject_string,
 		.property_typ_data = DEFINE_PROPERTY_TYP_DATA (
-			.values_static =        VALUES_STATIC ("active-backup", "balance-slb", "balance-tcp"),
+			.values_static =            NM_MAKE_STRV ("active-backup", "balance-slb", "balance-tcp"),
 		),
 	),
 	PROPERTY_INFO_WITH_DESC (NM_SETTING_OVS_PORT_BOND_UPDELAY,
@@ -6794,6 +6758,41 @@ static const NMMetaPropertyInfo *const property_infos_PPP[] = {
 };
 
 #undef  _CURRENT_NM_META_SETTING_TYPE
+#define _CURRENT_NM_META_SETTING_TYPE NM_META_SETTING_TYPE_PPPOE
+static const NMMetaPropertyInfo *const property_infos_PPPOE[] = {
+	PROPERTY_INFO_WITH_DESC (NM_SETTING_PPPOE_PARENT,
+		.is_cli_option =                TRUE,
+		.property_alias =               "parent",
+		.prompt =                       N_("PPPoE parent device"),
+		.property_type =                &_pt_gobject_string,
+	),
+	PROPERTY_INFO_WITH_DESC (NM_SETTING_PPPOE_SERVICE,
+		.is_cli_option =                TRUE,
+		.property_alias =               "service",
+		.prompt =                       N_("Service [none]"),
+		.property_type =                &_pt_gobject_string,
+	),
+	PROPERTY_INFO_WITH_DESC (NM_SETTING_PPPOE_USERNAME,
+		.is_cli_option =                TRUE,
+		.property_alias =               "username",
+		.inf_flags =                    NM_META_PROPERTY_INF_FLAG_REQD,
+		.prompt =                       N_("PPPoE username"),
+		.property_type =                &_pt_gobject_string,
+	),
+	PROPERTY_INFO_WITH_DESC (NM_SETTING_PPPOE_PASSWORD,
+		.is_cli_option =                TRUE,
+		.property_alias =               "password",
+		.prompt =                       N_("Password [none]"),
+		.is_secret =                    TRUE,
+		.property_type =                &_pt_gobject_string,
+	),
+	PROPERTY_INFO_WITH_DESC (NM_SETTING_PPPOE_PASSWORD_FLAGS,
+		.property_type =                &_pt_gobject_secret_flags,
+	),
+	NULL
+};
+
+#undef  _CURRENT_NM_META_SETTING_TYPE
 #define _CURRENT_NM_META_SETTING_TYPE NM_META_SETTING_TYPE_PROXY
 static const NMMetaPropertyInfo *const property_infos_PROXY[] = {
 	PROPERTY_INFO_WITH_DESC (NM_SETTING_PROXY_METHOD,
@@ -6835,6 +6834,78 @@ static const NMMetaPropertyInfo *const property_infos_PROXY[] = {
 };
 
 #undef  _CURRENT_NM_META_SETTING_TYPE
+#define _CURRENT_NM_META_SETTING_TYPE NM_META_SETTING_TYPE_SERIAL
+static const NMMetaPropertyInfo *const property_infos_SERIAL[] = {
+	PROPERTY_INFO_WITH_DESC (NM_SETTING_SERIAL_BAUD,
+		.property_type =                &_pt_gobject_int,
+	),
+	PROPERTY_INFO_WITH_DESC (NM_SETTING_SERIAL_BITS,
+		.property_type =                &_pt_gobject_int,
+	),
+	PROPERTY_INFO_WITH_DESC (NM_SETTING_SERIAL_PARITY,
+		.property_type =                &_pt_gobject_enum,
+		.property_typ_data = DEFINE_PROPERTY_TYP_DATA (
+			PROPERTY_TYP_DATA_SUBTYPE (gobject_enum,
+				.value_infos =          ENUM_VALUE_INFOS (
+					{
+						.value = NM_SETTING_SERIAL_PARITY_EVEN,
+						.nick = "E",
+					},
+					{
+						.value = NM_SETTING_SERIAL_PARITY_EVEN,
+						.nick = "e",
+					},
+					{
+						.value = NM_SETTING_SERIAL_PARITY_ODD,
+						.nick = "O",
+					},
+					{
+						.value = NM_SETTING_SERIAL_PARITY_ODD,
+						.nick = "o",
+					},
+					{
+						.value = NM_SETTING_SERIAL_PARITY_NONE,
+						.nick = "N",
+					},
+					{
+						.value = NM_SETTING_SERIAL_PARITY_NONE,
+						.nick = "n",
+					},
+				),
+			),
+			.typ_flags =                  NM_META_PROPERTY_TYP_FLAG_ENUM_GET_PARSABLE_TEXT
+			                            | NM_META_PROPERTY_TYP_FLAG_ENUM_GET_PRETTY_TEXT,
+		),
+	),
+	PROPERTY_INFO_WITH_DESC (NM_SETTING_SERIAL_STOPBITS,
+		.property_type =                &_pt_gobject_int,
+	),
+	PROPERTY_INFO_WITH_DESC (NM_SETTING_SERIAL_SEND_DELAY,
+		.property_type =                &_pt_gobject_int,
+	),
+	NULL
+};
+
+#undef  _CURRENT_NM_META_SETTING_TYPE
+#define _CURRENT_NM_META_SETTING_TYPE NM_META_SETTING_TYPE_SRIOV
+static const NMMetaPropertyInfo *const property_infos_SRIOV[] = {
+	PROPERTY_INFO_WITH_DESC (NM_SETTING_SRIOV_TOTAL_VFS,
+		.property_type =                &_pt_gobject_int,
+	),
+	PROPERTY_INFO_WITH_DESC (NM_SETTING_SRIOV_VFS,
+		.property_type = DEFINE_PROPERTY_TYPE (
+			.get_fcn =                  _get_fcn_sriov_vfs,
+			.set_fcn =                  _set_fcn_sriov_vfs,
+			.remove_fcn =               _remove_fcn_sriov_vfs,
+		),
+	),
+	PROPERTY_INFO_WITH_DESC (NM_SETTING_SRIOV_AUTOPROBE_DRIVERS,
+		.property_type =                &_pt_gobject_enum,
+	),
+	NULL
+};
+
+#undef  _CURRENT_NM_META_SETTING_TYPE
 #define _CURRENT_NM_META_SETTING_TYPE NM_META_SETTING_TYPE_TC_CONFIG
 static const NMMetaPropertyInfo *const property_infos_TC_CONFIG[] = {
 	PROPERTY_INFO (NM_SETTING_TC_CONFIG_QDISCS, DESCRIBE_DOC_NM_SETTING_TC_CONFIG_QDISCS,
@@ -6870,64 +6941,64 @@ static const NMMetaPropertyInfo *const property_infos_TEAM[] = {
 	PROPERTY_INFO_WITH_DESC (NM_SETTING_TEAM_NOTIFY_PEERS_COUNT,
 		.property_type =                &_pt_gobject_int,
 		.property_typ_data = DEFINE_PROPERTY_TYP_DATA_SUBTYPE (gobject_int,
-		        .value_infos =          INT_VALUE_INFOS (
+			.value_infos =              INT_VALUE_INFOS (
 				{
-					.value = 0,
+					.value.i64 = 0,
 					.nick = "disabled",
-				}
+				},
 			),
 		),
 	),
 	PROPERTY_INFO_WITH_DESC (NM_SETTING_TEAM_NOTIFY_PEERS_INTERVAL,
 		.property_type =                &_pt_gobject_int,
 		.property_typ_data = DEFINE_PROPERTY_TYP_DATA_SUBTYPE (gobject_int,
-		        .value_infos =          INT_VALUE_INFOS (
+			.value_infos =              INT_VALUE_INFOS (
 				{
-					.value = 0,
+					.value.i64 = 0,
 					.nick = "default",
-				}
+				},
 			),
 		),
 	),
 	PROPERTY_INFO_WITH_DESC (NM_SETTING_TEAM_MCAST_REJOIN_COUNT,
 		.property_type =                &_pt_gobject_int,
 		.property_typ_data = DEFINE_PROPERTY_TYP_DATA_SUBTYPE (gobject_int,
-		        .value_infos =          INT_VALUE_INFOS (
+			.value_infos =              INT_VALUE_INFOS (
 				{
-					.value = 0,
+					.value.i64 = 0,
 					.nick = "disabled",
-				}
+				},
 			),
 		),
 	),
 	PROPERTY_INFO_WITH_DESC (NM_SETTING_TEAM_MCAST_REJOIN_INTERVAL,
 		.property_type =                &_pt_gobject_int,
 		.property_typ_data = DEFINE_PROPERTY_TYP_DATA_SUBTYPE (gobject_int,
-		        .value_infos =          INT_VALUE_INFOS (
+			.value_infos =              INT_VALUE_INFOS (
 				{
-					.value = 0,
+					.value.i64 = 0,
 					.nick = "default",
-				}
+				},
 			),
 		),
 	),
 	PROPERTY_INFO_WITH_DESC (NM_SETTING_TEAM_RUNNER,
 		.property_type =                &_pt_gobject_string,
 		.property_typ_data = DEFINE_PROPERTY_TYP_DATA (
-			.values_static =            VALUES_STATIC (NM_SETTING_TEAM_RUNNER_BROADCAST,
-			                                           NM_SETTING_TEAM_RUNNER_ROUNDROBIN,
-			                                           NM_SETTING_TEAM_RUNNER_RANDOM,
-			                                           NM_SETTING_TEAM_RUNNER_ACTIVEBACKUP,
-			                                           NM_SETTING_TEAM_RUNNER_LOADBALANCE,
-			                                           NM_SETTING_TEAM_RUNNER_LACP),
+			.values_static =            NM_MAKE_STRV (NM_SETTING_TEAM_RUNNER_BROADCAST,
+			                                          NM_SETTING_TEAM_RUNNER_ROUNDROBIN,
+			                                          NM_SETTING_TEAM_RUNNER_RANDOM,
+			                                          NM_SETTING_TEAM_RUNNER_ACTIVEBACKUP,
+			                                          NM_SETTING_TEAM_RUNNER_LOADBALANCE,
+			                                          NM_SETTING_TEAM_RUNNER_LACP),
 		),
 	),
 	PROPERTY_INFO_WITH_DESC (NM_SETTING_TEAM_RUNNER_HWADDR_POLICY,
 		.property_type =                &_pt_gobject_string,
 		.property_typ_data = DEFINE_PROPERTY_TYP_DATA (
-			.values_static =            VALUES_STATIC (NM_SETTING_TEAM_RUNNER_HWADDR_POLICY_SAME_ALL,
-			                                           NM_SETTING_TEAM_RUNNER_HWADDR_POLICY_BY_ACTIVE,
-			                                           NM_SETTING_TEAM_RUNNER_HWADDR_POLICY_ONLY_ACTIVE),
+			.values_static =            NM_MAKE_STRV (NM_SETTING_TEAM_RUNNER_HWADDR_POLICY_SAME_ALL,
+			                                          NM_SETTING_TEAM_RUNNER_HWADDR_POLICY_BY_ACTIVE,
+			                                          NM_SETTING_TEAM_RUNNER_HWADDR_POLICY_ONLY_ACTIVE),
 		),
 	),
 	PROPERTY_INFO_WITH_DESC (NM_SETTING_TEAM_RUNNER_TX_HASH,
@@ -6940,17 +7011,17 @@ static const NMMetaPropertyInfo *const property_infos_TEAM[] = {
 	PROPERTY_INFO_WITH_DESC (NM_SETTING_TEAM_RUNNER_TX_BALANCER,
 		.property_type =                &_pt_gobject_string,
 		.property_typ_data = DEFINE_PROPERTY_TYP_DATA (
-			.values_static =            VALUES_STATIC ("basic"),
+			.values_static =            NM_MAKE_STRV ("basic"),
 		),
 	),
 	PROPERTY_INFO_WITH_DESC (NM_SETTING_TEAM_RUNNER_TX_BALANCER_INTERVAL,
 		.property_type =                &_pt_gobject_int,
 		.property_typ_data = DEFINE_PROPERTY_TYP_DATA_SUBTYPE (gobject_int,
-		        .value_infos =          INT_VALUE_INFOS (
+			.value_infos =              INT_VALUE_INFOS (
 				{
-					.value = NM_SETTING_TEAM_RUNNER_TX_BALANCER_INTERVAL_DEFAULT,
+					.value.i64 = NM_SETTING_TEAM_RUNNER_TX_BALANCER_INTERVAL_DEFAULT,
 					.nick = "default",
-				}
+				},
 			),
 		),
 	),
@@ -6963,33 +7034,33 @@ static const NMMetaPropertyInfo *const property_infos_TEAM[] = {
 	PROPERTY_INFO_WITH_DESC (NM_SETTING_TEAM_RUNNER_SYS_PRIO,
 		.property_type =                &_pt_gobject_int,
 		.property_typ_data = DEFINE_PROPERTY_TYP_DATA_SUBTYPE (gobject_int,
-		        .value_infos =          INT_VALUE_INFOS (
+			.value_infos =              INT_VALUE_INFOS (
 				{
-					.value = NM_SETTING_TEAM_RUNNER_SYS_PRIO_DEFAULT,
+					.value.i64 = NM_SETTING_TEAM_RUNNER_SYS_PRIO_DEFAULT,
 					.nick = "default",
-				}
+				},
 			),
 		),
 	),
 	PROPERTY_INFO_WITH_DESC (NM_SETTING_TEAM_RUNNER_MIN_PORTS,
 		.property_type =                &_pt_gobject_int,
 		.property_typ_data = DEFINE_PROPERTY_TYP_DATA_SUBTYPE (gobject_int,
-		        .value_infos =          INT_VALUE_INFOS (
+			.value_infos =              INT_VALUE_INFOS (
 				{
-					.value = 0,
+					.value.i64 = 0,
 					.nick = "default",
-				}
+				},
 			),
 		),
 	),
 	PROPERTY_INFO_WITH_DESC (NM_SETTING_TEAM_RUNNER_AGG_SELECT_POLICY,
 		.property_type =                &_pt_gobject_string,
 		.property_typ_data = DEFINE_PROPERTY_TYP_DATA (
-			.values_static =            VALUES_STATIC (NM_SETTING_TEAM_RUNNER_AGG_SELECT_POLICY_LACP_PRIO,
-			                                           NM_SETTING_TEAM_RUNNER_AGG_SELECT_POLICY_LACP_PRIO_STABLE,
-			                                           NM_SETTING_TEAM_RUNNER_AGG_SELECT_POLICY_BANDWIDTH,
-			                                           NM_SETTING_TEAM_RUNNER_AGG_SELECT_POLICY_COUNT,
-			                                           NM_SETTING_TEAM_RUNNER_AGG_SELECT_POLICY_PORT_CONFIG),
+			.values_static =            NM_MAKE_STRV (NM_SETTING_TEAM_RUNNER_AGG_SELECT_POLICY_LACP_PRIO,
+			                                          NM_SETTING_TEAM_RUNNER_AGG_SELECT_POLICY_LACP_PRIO_STABLE,
+			                                          NM_SETTING_TEAM_RUNNER_AGG_SELECT_POLICY_BANDWIDTH,
+			                                          NM_SETTING_TEAM_RUNNER_AGG_SELECT_POLICY_COUNT,
+			                                          NM_SETTING_TEAM_RUNNER_AGG_SELECT_POLICY_PORT_CONFIG),
 		),
 	),
 	PROPERTY_INFO_WITH_DESC (NM_SETTING_TEAM_LINK_WATCHERS,
@@ -7019,22 +7090,22 @@ static const NMMetaPropertyInfo *const property_infos_TEAM_PORT[] = {
 	PROPERTY_INFO_WITH_DESC (NM_SETTING_TEAM_PORT_QUEUE_ID,
 		.property_type =                &_pt_gobject_int,
 		.property_typ_data = DEFINE_PROPERTY_TYP_DATA_SUBTYPE (gobject_int,
-			.value_infos =          INT_VALUE_INFOS (
+			.value_infos =              INT_VALUE_INFOS (
 				{
-					.value = NM_SETTING_TEAM_PORT_QUEUE_ID_DEFAULT,
+					.value.i64 = NM_SETTING_TEAM_PORT_QUEUE_ID_DEFAULT,
 					.nick = "default",
-				}
+				},
 			),
 		),
 	),
 	PROPERTY_INFO_WITH_DESC (NM_SETTING_TEAM_PORT_PRIO,
 		.property_type =                &_pt_gobject_int,
 		.property_typ_data = DEFINE_PROPERTY_TYP_DATA_SUBTYPE (gobject_int,
-			.value_infos =          INT_VALUE_INFOS (
+			.value_infos =              INT_VALUE_INFOS (
 				{
-					.value = 0,
+					.value.i64 = 0,
 					.nick = "default",
-				}
+				},
 			),
 		),
 	),
@@ -7044,22 +7115,22 @@ static const NMMetaPropertyInfo *const property_infos_TEAM_PORT[] = {
 	PROPERTY_INFO_WITH_DESC (NM_SETTING_TEAM_PORT_LACP_PRIO,
 		.property_type =                &_pt_gobject_int,
 		.property_typ_data = DEFINE_PROPERTY_TYP_DATA_SUBTYPE (gobject_int,
-			.value_infos =          INT_VALUE_INFOS (
+			.value_infos =              INT_VALUE_INFOS (
 				{
-					.value = NM_SETTING_TEAM_PORT_LACP_PRIO_DEFAULT,
+					.value.i64 = NM_SETTING_TEAM_PORT_LACP_PRIO_DEFAULT,
 					.nick = "default",
-				}
+				},
 			),
 		),
 	),
 	PROPERTY_INFO_WITH_DESC (NM_SETTING_TEAM_PORT_LACP_KEY,
 		.property_type =                &_pt_gobject_int,
 		.property_typ_data = DEFINE_PROPERTY_TYP_DATA_SUBTYPE (gobject_int,
-			.value_infos =          INT_VALUE_INFOS (
+			.value_infos =              INT_VALUE_INFOS (
 				{
-					.value = 0,
+					.value.i64 = 0,
 					.nick = "default",
-				}
+				},
 			),
 		),
 	),
@@ -7075,78 +7146,6 @@ static const NMMetaPropertyInfo *const property_infos_TEAM_PORT[] = {
 };
 
 #undef  _CURRENT_NM_META_SETTING_TYPE
-#define _CURRENT_NM_META_SETTING_TYPE NM_META_SETTING_TYPE_SERIAL
-static const NMMetaPropertyInfo *const property_infos_SERIAL[] = {
-	PROPERTY_INFO_WITH_DESC (NM_SETTING_SERIAL_BAUD,
-		.property_type =                &_pt_gobject_int,
-	),
-	PROPERTY_INFO_WITH_DESC (NM_SETTING_SERIAL_BITS,
-		.property_type =                &_pt_gobject_int,
-	),
-	PROPERTY_INFO_WITH_DESC (NM_SETTING_SERIAL_PARITY,
-		.property_type =                &_pt_gobject_enum,
-		.property_typ_data = DEFINE_PROPERTY_TYP_DATA (
-			PROPERTY_TYP_DATA_SUBTYPE (gobject_enum,
-				.value_infos =          ENUM_VALUE_INFOS (
-					{
-						.value = NM_SETTING_SERIAL_PARITY_EVEN,
-						.nick = "E",
-					},
-					{
-						.value = NM_SETTING_SERIAL_PARITY_EVEN,
-						.nick = "e",
-					},
-					{
-						.value = NM_SETTING_SERIAL_PARITY_ODD,
-						.nick = "O",
-					},
-					{
-						.value = NM_SETTING_SERIAL_PARITY_ODD,
-						.nick = "o",
-					},
-					{
-						.value = NM_SETTING_SERIAL_PARITY_NONE,
-						.nick = "N",
-					},
-					{
-						.value = NM_SETTING_SERIAL_PARITY_NONE,
-						.nick = "n",
-					}
-				),
-			),
-			.typ_flags =                  NM_META_PROPERTY_TYP_FLAG_ENUM_GET_PARSABLE_TEXT
-			                            | NM_META_PROPERTY_TYP_FLAG_ENUM_GET_PRETTY_TEXT,
-		),
-	),
-	PROPERTY_INFO_WITH_DESC (NM_SETTING_SERIAL_STOPBITS,
-		.property_type =                &_pt_gobject_int,
-	),
-	PROPERTY_INFO_WITH_DESC (NM_SETTING_SERIAL_SEND_DELAY,
-		.property_type =                &_pt_gobject_int,
-	),
-	NULL
-};
-
-#undef  _CURRENT_NM_META_SETTING_TYPE
-#define _CURRENT_NM_META_SETTING_TYPE NM_META_SETTING_TYPE_SRIOV
-static const NMMetaPropertyInfo *const property_infos_SRIOV[] = {
-	PROPERTY_INFO_WITH_DESC (NM_SETTING_SRIOV_TOTAL_VFS,
-		.property_type =                &_pt_gobject_int,
-	),
-	PROPERTY_INFO_WITH_DESC (NM_SETTING_SRIOV_VFS,
-		.property_type = DEFINE_PROPERTY_TYPE (
-			.get_fcn =                  _get_fcn_sriov_vfs,
-			.set_fcn =                  _set_fcn_sriov_vfs,
-			.remove_fcn =               _remove_fcn_sriov_vfs,
-		),
-	),
-	PROPERTY_INFO_WITH_DESC (NM_SETTING_SRIOV_AUTOPROBE_DRIVERS,
-		.property_type =                &_pt_gobject_enum,
-	),
-	NULL
-};
-
-#undef  _CURRENT_NM_META_SETTING_TYPE
 #define _CURRENT_NM_META_SETTING_TYPE NM_META_SETTING_TYPE_TUN
 static const NMMetaPropertyInfo *const property_infos_TUN[] = {
 	PROPERTY_INFO_WITH_DESC (NM_SETTING_TUN_MODE,
@@ -7366,6 +7365,30 @@ static const NMMetaPropertyInfo *const property_infos_VXLAN[] = {
 };
 
 #undef  _CURRENT_NM_META_SETTING_TYPE
+#define _CURRENT_NM_META_SETTING_TYPE NM_META_SETTING_TYPE_WIFI_P2P
+static const NMMetaPropertyInfo *const property_infos_WIFI_P2P[] = {
+	PROPERTY_INFO_WITH_DESC (NM_SETTING_WIFI_P2P_PEER,
+		.is_cli_option =                TRUE,
+		.property_alias =               "peer",
+		.inf_flags =                    NM_META_PROPERTY_INF_FLAG_REQD,
+		.prompt =                       N_("Peer"),
+		.property_type =                &_pt_gobject_mac,
+	),
+	PROPERTY_INFO_WITH_DESC (NM_SETTING_WIFI_P2P_WPS_METHOD,
+		.property_type =                &_pt_gobject_enum,
+		.property_typ_data = DEFINE_PROPERTY_TYP_DATA (
+			PROPERTY_TYP_DATA_SUBTYPE (gobject_enum,
+				.get_gtype =            nm_setting_wireless_security_wps_method_get_type,
+			),
+		),
+	),
+	PROPERTY_INFO_WITH_DESC (NM_SETTING_WIFI_P2P_WFD_IES,
+		.property_type =                &_pt_gobject_bytes,
+	),
+	NULL
+};
+
+#undef  _CURRENT_NM_META_SETTING_TYPE
 #define _CURRENT_NM_META_SETTING_TYPE NM_META_SETTING_TYPE_WIMAX
 static const NMMetaPropertyInfo *const property_infos_WIMAX[] = {
 	PROPERTY_INFO_WITH_DESC (NM_SETTING_WIMAX_MAC_ADDRESS,
@@ -7400,7 +7423,7 @@ static const NMMetaPropertyInfo *const property_infos_WIRED[] = {
 	PROPERTY_INFO_WITH_DESC (NM_SETTING_WIRED_DUPLEX,
 		.property_type =                &_pt_gobject_string,
 		.property_typ_data = DEFINE_PROPERTY_TYP_DATA (
-			.values_static =            VALUES_STATIC ("half", "full"),
+			.values_static =            NM_MAKE_STRV ("half", "full"),
 		),
 	),
 	PROPERTY_INFO_WITH_DESC (NM_SETTING_WIRED_AUTO_NEGOTIATE,
@@ -7452,7 +7475,7 @@ static const NMMetaPropertyInfo *const property_infos_WIRED[] = {
 	PROPERTY_INFO_WITH_DESC (NM_SETTING_WIRED_S390_NETTYPE,
 		.property_type =                &_pt_gobject_string,
 		.property_typ_data = DEFINE_PROPERTY_TYP_DATA (
-			.values_static =            VALUES_STATIC ("qeth", "lcs", "ctc"),
+			.values_static =            NM_MAKE_STRV ("qeth", "lcs", "ctc"),
 		),
 	),
 	PROPERTY_INFO_WITH_DESC (NM_SETTING_WIRED_S390_OPTIONS,
@@ -7481,7 +7504,7 @@ static const NMMetaPropertyInfo *const property_infos_WIRED[] = {
 					{
 						.value = NM_SETTING_WIRED_WAKE_ON_LAN_NONE,
 						.nick = "disabled",
-					}
+					},
 				),
 			),
 			.typ_flags =                  NM_META_PROPERTY_TYP_FLAG_ENUM_GET_PARSABLE_TEXT
@@ -7495,6 +7518,34 @@ static const NMMetaPropertyInfo *const property_infos_WIRED[] = {
 };
 
 #undef  _CURRENT_NM_META_SETTING_TYPE
+#define _CURRENT_NM_META_SETTING_TYPE NM_META_SETTING_TYPE_WIREGUARD
+static const NMMetaPropertyInfo *const property_infos_WIREGUARD[] = {
+	PROPERTY_INFO_WITH_DESC (NM_SETTING_WIREGUARD_PRIVATE_KEY,
+		.is_secret =                    TRUE,
+		.property_type =                &_pt_gobject_string,
+	),
+	PROPERTY_INFO_WITH_DESC (NM_SETTING_WIREGUARD_PRIVATE_KEY_FLAGS,
+		.property_type =                &_pt_gobject_secret_flags,
+	),
+	PROPERTY_INFO_WITH_DESC (NM_SETTING_WIREGUARD_LISTEN_PORT,
+		.property_type =                &_pt_gobject_int,
+	),
+	PROPERTY_INFO_WITH_DESC (NM_SETTING_WIREGUARD_FWMARK,
+		.property_type =                &_pt_gobject_int,
+		.property_typ_data = DEFINE_PROPERTY_TYP_DATA_SUBTYPE (gobject_int, \
+			.base =                     16,
+		),
+	),
+	PROPERTY_INFO_WITH_DESC (NM_SETTING_WIREGUARD_PEER_ROUTES,
+		.property_type =                &_pt_gobject_bool,
+	),
+	PROPERTY_INFO_WITH_DESC (NM_SETTING_WIREGUARD_MTU,
+		.property_type =                &_pt_gobject_mtu,
+	),
+	NULL
+};
+
+#undef  _CURRENT_NM_META_SETTING_TYPE
 #define _CURRENT_NM_META_SETTING_TYPE NM_META_SETTING_TYPE_WIRELESS
 static const NMMetaPropertyInfo *const property_infos_WIRELESS[] = {
 	PROPERTY_INFO_WITH_DESC (NM_SETTING_WIRELESS_SSID,
@@ -7514,15 +7565,15 @@ static const NMMetaPropertyInfo *const property_infos_WIRELESS[] = {
 		.def_hint =                     NM_META_TEXT_PROMPT_WIFI_MODE_CHOICES,
 		.property_type =                &_pt_gobject_string,
 		.property_typ_data = DEFINE_PROPERTY_TYP_DATA (
-			.values_static =            VALUES_STATIC (NM_SETTING_WIRELESS_MODE_INFRA,
-			                                           NM_SETTING_WIRELESS_MODE_ADHOC,
-			                                           NM_SETTING_WIRELESS_MODE_AP),
+			.values_static =            NM_MAKE_STRV (NM_SETTING_WIRELESS_MODE_INFRA,
+			                                          NM_SETTING_WIRELESS_MODE_ADHOC,
+			                                          NM_SETTING_WIRELESS_MODE_AP),
 		),
 	),
 	PROPERTY_INFO_WITH_DESC (NM_SETTING_WIRELESS_BAND,
 		.property_type =                &_pt_gobject_string,
 		.property_typ_data = DEFINE_PROPERTY_TYP_DATA (
-			.values_static =            VALUES_STATIC ("a", "bg"),
+			.values_static =            NM_MAKE_STRV ("a", "bg"),
 		),
 	),
 	PROPERTY_INFO_WITH_DESC (NM_SETTING_WIRELESS_CHANNEL,
@@ -7610,7 +7661,7 @@ static const NMMetaPropertyInfo *const property_infos_WIRELESS[] = {
 					{
 						.value = NM_SETTING_WIRELESS_WAKE_ON_WLAN_NONE,
 						.nick = "disabled",
-					}
+					},
 				),
 			),
 		),
@@ -7624,7 +7675,7 @@ static const NMMetaPropertyInfo *const property_infos_WIRELESS_SECURITY[] = {
 	PROPERTY_INFO_WITH_DESC (NM_SETTING_WIRELESS_SECURITY_KEY_MGMT,
 		.property_type =                &_pt_gobject_string,
 		.property_typ_data = DEFINE_PROPERTY_TYP_DATA (
-			.values_static =            VALUES_STATIC ("none", "ieee8021x", "wpa-none", "wpa-psk", "wpa-eap"),
+			.values_static =            NM_MAKE_STRV ("none", "ieee8021x", "wpa-none", "wpa-psk", "wpa-eap", "sae"),
 		),
 	),
 	PROPERTY_INFO_WITH_DESC (NM_SETTING_WIRELESS_SECURITY_WEP_TX_KEYIDX,
@@ -7633,7 +7684,7 @@ static const NMMetaPropertyInfo *const property_infos_WIRELESS_SECURITY[] = {
 	PROPERTY_INFO_WITH_DESC (NM_SETTING_WIRELESS_SECURITY_AUTH_ALG,
 		.property_type =                &_pt_gobject_string,
 		.property_typ_data = DEFINE_PROPERTY_TYP_DATA (
-			.values_static =            VALUES_STATIC ("open", "shared", "leap"),
+			.values_static =            NM_MAKE_STRV ("open", "shared", "leap"),
 		),
 	),
 	PROPERTY_INFO_WITH_DESC (NM_SETTING_WIRELESS_SECURITY_PROTO,
@@ -7724,9 +7775,6 @@ static const NMMetaPropertyInfo *const property_infos_WIRELESS_SECURITY[] = {
 	PROPERTY_INFO_WITH_DESC (NM_SETTING_WIRELESS_SECURITY_PSK,
 		.is_secret =                    TRUE,
 		.property_type =                &_pt_gobject_string,
-		.property_typ_data = DEFINE_PROPERTY_TYP_DATA_SUBTYPE (gobject_string,
-			.validate_fcn =             _validate_fcn_wireless_security_psk,
-		),
 	),
 	PROPERTY_INFO_WITH_DESC (NM_SETTING_WIRELESS_SECURITY_PSK_FLAGS,
 		.property_type =                &_pt_gobject_secret_flags,
@@ -7775,12 +7823,12 @@ static const NMMetaPropertyInfo *const property_infos_WPAN[] = {
 		.prompt =                       N_("Short address (<0x0000-0xffff>)"),
 		.property_type =                &_pt_gobject_int,
 		.property_typ_data = DEFINE_PROPERTY_TYP_DATA_SUBTYPE (gobject_int, \
-			.base =			16,
-			.value_infos =          INT_VALUE_INFOS (
+			.base =                     16,
+			.value_infos =              INT_VALUE_INFOS (
 				{
-					.value = G_MAXUINT16,
+					.value.i64 = G_MAXUINT16,
 					.nick = "unset",
-				}
+				},
 			),
 		),
 	),
@@ -7791,27 +7839,42 @@ static const NMMetaPropertyInfo *const property_infos_WPAN[] = {
 		.prompt =                       N_("PAN Identifier (<0x0000-0xffff>)"),
 		.property_type =                &_pt_gobject_int,
 		.property_typ_data = DEFINE_PROPERTY_TYP_DATA_SUBTYPE (gobject_int, \
-			.base =			16,
-			.value_infos =          INT_VALUE_INFOS (
+			.base =                     16,
+			.value_infos =              INT_VALUE_INFOS (
 				{
-					.value = G_MAXUINT16,
+					.value.i64 = G_MAXUINT16,
 					.nick = "unset",
-				}
+				},
 			),
 		),
 	),
-	NULL
-};
-
-#undef  _CURRENT_NM_META_SETTING_TYPE
-#define _CURRENT_NM_META_SETTING_TYPE NM_META_SETTING_TYPE_6LOWPAN
-static const NMMetaPropertyInfo *const property_infos_6LOWPAN[] = {
-	PROPERTY_INFO_WITH_DESC (NM_SETTING_6LOWPAN_PARENT,
+	PROPERTY_INFO_WITH_DESC (NM_SETTING_WPAN_PAGE,
 		.is_cli_option =                TRUE,
-		.property_alias =               "dev",
-		.inf_flags =                    NM_META_PROPERTY_INF_FLAG_REQD,
-		.prompt =                       N_("IEEE 802.15.4 (WPAN) parent device or connection UUID"),
-		.property_type =                &_pt_gobject_string,
+		.property_alias =               "page",
+		.prompt =                       N_("Page (<default|0-31>)"),
+		.property_type =                &_pt_gobject_int,
+		.property_typ_data = DEFINE_PROPERTY_TYP_DATA_SUBTYPE (gobject_int, \
+			.value_infos =              INT_VALUE_INFOS (
+				{
+					.value.i64 = NM_SETTING_WPAN_PAGE_DEFAULT,
+					.nick = "default",
+				},
+			),
+		),
+	),
+	PROPERTY_INFO_WITH_DESC (NM_SETTING_WPAN_CHANNEL,
+		.is_cli_option =                TRUE,
+		.property_alias =               "channel",
+		.prompt =                       N_("Channel (<default|0-26>)"),
+		.property_type =                &_pt_gobject_int,
+		.property_typ_data = DEFINE_PROPERTY_TYP_DATA_SUBTYPE (gobject_int, \
+			.value_infos =              INT_VALUE_INFOS (
+				{
+					.value.i64 = NM_SETTING_WPAN_CHANNEL_DEFAULT,
+					.nick = "default",
+				},
+			),
+		),
 	),
 	NULL
 };
@@ -7844,9 +7907,9 @@ static void
 _setting_init_fcn_gsm (ARGS_SETTING_INIT_FCN)
 {
 	if (init_type == NM_META_ACCESSOR_SETTING_INIT_TYPE_CLI) {
-		/* Initialize 'number' so that 'gsm' is valid */
+		/* Initialize 'apn' so that 'gsm' is valid */
 		g_object_set (NM_SETTING_GSM (setting),
-		              NM_SETTING_GSM_NUMBER, "*99#",
+		              NM_SETTING_GSM_APN, "internet",
 		              NULL);
 	}
 }
@@ -7937,6 +8000,7 @@ _setting_init_fcn_wireless (ARGS_SETTING_INIT_FCN)
 
 /*****************************************************************************/
 
+#define SETTING_PRETTY_NAME_6LOWPAN             N_("6LOWPAN settings")
 #define SETTING_PRETTY_NAME_802_1X              N_("802-1x settings")
 #define SETTING_PRETTY_NAME_ADSL                N_("ADSL connection")
 #define SETTING_PRETTY_NAME_BLUETOOTH           N_("bluetooth connection")
@@ -7958,10 +8022,10 @@ _setting_init_fcn_wireless (ARGS_SETTING_INIT_FCN)
 #define SETTING_PRETTY_NAME_MACVLAN             N_("macvlan connection")
 #define SETTING_PRETTY_NAME_MATCH               N_("Match")
 #define SETTING_PRETTY_NAME_OLPC_MESH           N_("OLPC Mesh connection")
-#define SETTING_PRETTY_NAME_OVS_BRIDGE          N_("OpenVSwitch bridge settings")
-#define SETTING_PRETTY_NAME_OVS_INTERFACE       N_("OpenVSwitch interface settings")
-#define SETTING_PRETTY_NAME_OVS_PATCH           N_("OpenVSwitch patch interface settings")
-#define SETTING_PRETTY_NAME_OVS_PORT            N_("OpenVSwitch port settings")
+#define SETTING_PRETTY_NAME_OVS_BRIDGE          N_("Open vSwitch bridge settings")
+#define SETTING_PRETTY_NAME_OVS_INTERFACE       N_("Open vSwitch interface settings")
+#define SETTING_PRETTY_NAME_OVS_PATCH           N_("Open vSwitch patch interface settings")
+#define SETTING_PRETTY_NAME_OVS_PORT            N_("Open vSwitch port settings")
 #define SETTING_PRETTY_NAME_PPP                 N_("PPP settings")
 #define SETTING_PRETTY_NAME_PPPOE               N_("PPPoE")
 #define SETTING_PRETTY_NAME_PROXY               N_("Proxy")
@@ -7975,12 +8039,13 @@ _setting_init_fcn_wireless (ARGS_SETTING_INIT_FCN)
 #define SETTING_PRETTY_NAME_VLAN                N_("VLAN connection")
 #define SETTING_PRETTY_NAME_VPN                 N_("VPN connection")
 #define SETTING_PRETTY_NAME_VXLAN               N_("VXLAN connection")
+#define SETTING_PRETTY_NAME_WIFI_P2P            N_("Wi-Fi P2P connection")
 #define SETTING_PRETTY_NAME_WIMAX               N_("WiMAX connection")
 #define SETTING_PRETTY_NAME_WIRED               N_("Wired Ethernet")
+#define SETTING_PRETTY_NAME_WIREGUARD           N_("WireGuard VPN settings")
 #define SETTING_PRETTY_NAME_WIRELESS            N_("Wi-Fi connection")
 #define SETTING_PRETTY_NAME_WIRELESS_SECURITY   N_("Wi-Fi security settings")
 #define SETTING_PRETTY_NAME_WPAN                N_("WPAN settings")
-#define SETTING_PRETTY_NAME_6LOWPAN             N_("6LOWPAN settings")
 
 #define NM_META_SETTING_VALID_PARTS(...) \
 	((const NMMetaSettingValidPartItem *const[]) { __VA_ARGS__  NULL })
@@ -8218,6 +8283,12 @@ const NMMetaSettingInfoEditor nm_meta_setting_infos_editor[] = {
 			NM_META_SETTING_VALID_PART_ITEM (ETHTOOL,               FALSE),
 		),
 	),
+	SETTING_INFO (WIFI_P2P,
+		.valid_parts = NM_META_SETTING_VALID_PARTS (
+			NM_META_SETTING_VALID_PART_ITEM (CONNECTION,            TRUE),
+			NM_META_SETTING_VALID_PART_ITEM (WIFI_P2P,              TRUE),
+		),
+	),
 	SETTING_INFO (WIMAX,
 		.valid_parts = NM_META_SETTING_VALID_PARTS (
 			NM_META_SETTING_VALID_PART_ITEM (CONNECTION,            TRUE),
@@ -8235,6 +8306,12 @@ const NMMetaSettingInfoEditor nm_meta_setting_infos_editor[] = {
 			NM_META_SETTING_VALID_PART_ITEM (ETHTOOL,               FALSE),
 		),
 	),
+	SETTING_INFO (WIREGUARD,
+		.valid_parts = NM_META_SETTING_VALID_PARTS (
+			NM_META_SETTING_VALID_PART_ITEM (CONNECTION,            TRUE),
+			NM_META_SETTING_VALID_PART_ITEM (WIREGUARD,             TRUE),
+		),
+	),
 	SETTING_INFO (WIRELESS,
 		.alias =                            "wifi",
 		.valid_parts = NM_META_SETTING_VALID_PARTS (
diff --git a/clients/common/nm-meta-setting-desc.h b/clients/common/nm-meta-setting-desc.h
index 163dcf2e..e3f9d230 100644
--- a/clients/common/nm-meta-setting-desc.h
+++ b/clients/common/nm-meta-setting-desc.h
@@ -269,6 +269,9 @@ struct _NMMetaPropertyTypData {
 			const char *(*validate_fcn) (const char *value, char **out_to_free, GError **error);
 		} gobject_string;
 		struct {
+			bool legacy_format:1;
+		} gobject_bytes;
+		struct {
 			guint32 (*get_fcn) (NMSetting *setting);
 		} mtu;
 		struct {
@@ -412,7 +415,7 @@ typedef enum {
 
 /* the settings-meta data is supposed to be independent of an actual client
  * implementation. Hence, there is a need for hooks to the meta-data.
- * The meta-data handlers may call back to the enviroment with certain
+ * The meta-data handlers may call back to the environment with certain
  * actions. */
 struct _NMMetaEnvironment {
 
diff --git a/clients/common/nm-polkit-listener.c b/clients/common/nm-polkit-listener.c
index a7bce6ad..7367459d 100644
--- a/clients/common/nm-polkit-listener.c
+++ b/clients/common/nm-polkit-listener.c
@@ -35,7 +35,6 @@
 
 #include "nm-polkit-listener.h"
 
-#include <string.h>
 #include <stdio.h>
 #include <stdlib.h>
 
@@ -198,7 +197,7 @@ choose_identity (GList *identities)
 	GList *elem;
 
 	/* Choose identity. First try current user, then root, and else
-	 * take the firts one */
+	 * take the first one */
 	user = getenv("USER");
 	elem = g_list_find_custom (identities, user, (GCompareFunc) compare_users);
 	if (!elem) {
diff --git a/clients/common/nm-polkit-listener.h b/clients/common/nm-polkit-listener.h
index d69f8eca..ee86104b 100644
--- a/clients/common/nm-polkit-listener.h
+++ b/clients/common/nm-polkit-listener.h
@@ -66,7 +66,7 @@ typedef struct {
 	                       gpointer user_data);
 
 	/*
-	 * @gained_authorization: whether the autorization was successful
+	 * @gained_authorization: whether the authorization was successful
 	 *
 	 * Called as a result of completed signal by polkit.
 	 */
@@ -123,7 +123,7 @@ typedef void (*NMPolkitListenerOnShowInfoFunc) (const char *text);
 typedef void (*NMPolkitListenerOnShowErrorFunc) (const char *text);
 /**
  * NMPolkitListenerCompletedFunc:
- * @gained_authorization: whether the autorization was successful
+ * @gained_authorization: whether the authorization was successful
  *
  * Called as a result of completed signal by polkit.
  */
diff --git a/clients/common/nm-secret-agent-simple.c b/clients/common/nm-secret-agent-simple.c
index cab0c15a..cd3ce2ee 100644
--- a/clients/common/nm-secret-agent-simple.c
+++ b/clients/common/nm-secret-agent-simple.c
@@ -31,16 +31,30 @@
 
 #include "nm-default.h"
 
-#include <string.h>
+#include "nm-secret-agent-simple.h"
 
-#include "nm-vpn-service-plugin.h"
+#include <gio/gunixoutputstream.h>
+#include <gio/gunixinputstream.h>
 
+#include "nm-vpn-service-plugin.h"
 #include "nm-vpn-helpers.h"
-#include "nm-secret-agent-simple.h"
+#include "nm-utils/nm-secret-utils.h"
 
-G_DEFINE_TYPE (NMSecretAgentSimple, nm_secret_agent_simple, NM_TYPE_SECRET_AGENT_OLD)
+/*****************************************************************************/
+
+typedef struct {
+	char                          *request_id;
+
+	NMSecretAgentSimple           *self;
 
-#define NM_SECRET_AGENT_SIMPLE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SECRET_AGENT_SIMPLE, NMSecretAgentSimplePrivate))
+	NMConnection                  *connection;
+	const char                    *setting_name;
+	char                         **hints;
+	NMSecretAgentOldGetSecretsFunc callback;
+	gpointer                       callback_data;
+	GCancellable                  *cancellable;
+	NMSecretAgentGetSecretsFlags   flags;
+} RequestData;
 
 enum {
 	REQUEST_SECRETS,
@@ -51,89 +65,65 @@ enum {
 static guint signals[LAST_SIGNAL] = { 0 };
 
 typedef struct {
-	NMSecretAgentSimple           *self;
-
-	char                          *request_id;
-	NMConnection                  *connection;
-	char                         **hints;
-	NMSecretAgentOldGetSecretsFunc callback;
-	gpointer                       callback_data;
-} NMSecretAgentSimpleRequest;
-
-typedef struct {
-	/* <char *request_id, NMSecretAgentSimpleRequest *request> */
 	GHashTable *requests;
 
 	char *path;
 	gboolean enabled;
 } NMSecretAgentSimplePrivate;
 
-static void
-nm_secret_agent_simple_request_free (gpointer data)
-{
-	NMSecretAgentSimpleRequest *request = data;
+struct _NMSecretAgentSimple {
+	NMSecretAgentOld parent;
+	NMSecretAgentSimplePrivate _priv;
+};
 
-	g_object_unref (request->self);
-	g_object_unref (request->connection);
-	g_strfreev (request->hints);
+struct _NMSecretAgentSimpleClass {
+	NMSecretAgentOldClass parent;
+};
 
-	g_slice_free (NMSecretAgentSimpleRequest, request);
-}
+G_DEFINE_TYPE (NMSecretAgentSimple, nm_secret_agent_simple, NM_TYPE_SECRET_AGENT_OLD)
 
-static void
-nm_secret_agent_simple_init (NMSecretAgentSimple *agent)
-{
-	NMSecretAgentSimplePrivate *priv = NM_SECRET_AGENT_SIMPLE_GET_PRIVATE (agent);
+#define NM_SECRET_AGENT_SIMPLE_GET_PRIVATE(self)  _NM_GET_PRIVATE (self, NMSecretAgentSimple, NM_IS_SECRET_AGENT_SIMPLE, NMSecretAgentOld)
 
-	priv->requests = g_hash_table_new_full (nm_str_hash, g_str_equal,
-	                                        g_free, nm_secret_agent_simple_request_free);
-}
+/*****************************************************************************/
 
 static void
-nm_secret_agent_simple_finalize (GObject *object)
+_request_data_free (gpointer data)
 {
-	NMSecretAgentSimplePrivate *priv = NM_SECRET_AGENT_SIMPLE_GET_PRIVATE (object);
-	GError *error;
-	GHashTableIter iter;
-	gpointer key;
-	gpointer value;
-
-	error = g_error_new (NM_SECRET_AGENT_ERROR,
-	                     NM_SECRET_AGENT_ERROR_AGENT_CANCELED,
-	                     "The secret agent is going away");
-
-	g_hash_table_iter_init (&iter, priv->requests);
-	while (g_hash_table_iter_next (&iter, &key, &value)) {
-		NMSecretAgentSimpleRequest *request = value;
-
-		request->callback (NM_SECRET_AGENT_OLD (object),
-		                   request->connection,
-		                   NULL, error,
-		                   request->callback_data);
-	}
-
-	g_hash_table_destroy (priv->requests);
-	g_error_free (error);
+	RequestData *request = data;
 
-	g_free (priv->path);
+	g_free (request->request_id);
+	nm_clear_g_cancellable (&request->cancellable);
+	g_object_unref (request->connection);
+	g_strfreev (request->hints);
 
-	G_OBJECT_CLASS (nm_secret_agent_simple_parent_class)->finalize (object);
+	g_slice_free (RequestData, request);
 }
 
-static gboolean
-strv_has (char **haystack,
-          char   *needle)
+static void
+_request_data_complete (RequestData *request,
+                        GVariant *secrets,
+                        GError *error,
+                        GHashTableIter *iter_to_remove)
 {
-	char **iter;
+	NMSecretAgentSimple *self = request->self;
+	NMSecretAgentSimplePrivate *priv = NM_SECRET_AGENT_SIMPLE_GET_PRIVATE (self);
 
-	for (iter = haystack; iter && *iter; iter++) {
-		if (g_strcmp0 (*iter, needle) == 0)
-			return TRUE;
-	}
+	nm_assert ((secrets != NULL) != (error != NULL));
 
-	return FALSE;
+	request->callback (NM_SECRET_AGENT_OLD (request->self),
+	                   request->connection,
+	                   secrets,
+	                   error,
+	                   request->callback_data);
+
+	if (iter_to_remove)
+		g_hash_table_iter_remove (iter_to_remove);
+	else
+		g_hash_table_remove (priv->requests, request);
 }
 
+/*****************************************************************************/
+
 /**
  * NMSecretAgentSimpleSecret:
  * @name: the user-visible name of the secret. Eg, "WEP Passphrase".
@@ -148,70 +138,113 @@ typedef struct {
 	NMSecretAgentSimpleSecret base;
 	NMSetting *setting;
 	char *property;
-} NMSecretAgentSimpleSecretReal;
+} SecretReal;
 
 static void
-nm_secret_agent_simple_secret_free (NMSecretAgentSimpleSecret *secret)
+_secret_real_free (NMSecretAgentSimpleSecret *secret)
 {
-	NMSecretAgentSimpleSecretReal *real = (NMSecretAgentSimpleSecretReal *)secret;
+	SecretReal *real = (SecretReal *)secret;
 
 	g_free ((char *) secret->pretty_name);
 	g_free ((char *) secret->entry_id);
-	g_free (secret->value);
+	nm_free_secret (secret->value);
 	g_free ((char *) secret->vpn_type);
 	g_free (real->property);
 	g_clear_object (&real->setting);
 
-	g_slice_free (NMSecretAgentSimpleSecretReal, real);
+	g_slice_free (SecretReal, real);
 }
 
 static NMSecretAgentSimpleSecret *
-nm_secret_agent_simple_secret_new (NMSecretAgentSecretType secret_type,
-                                   const char *pretty_name,
-                                   NMSetting  *setting,
-                                   const char *property,
-                                   const char *vpn_type)
-{
-	NMSecretAgentSimpleSecretReal *real;
-	const char *vpn_prefix;
-	const char *value;
+_secret_real_new_plain (NMSecretAgentSecretType secret_type,
+                       const char *pretty_name,
+                       NMSetting  *setting,
+                       const char *property)
+{
+	SecretReal *real;
+	gs_free char *value= NULL;
 
 	nm_assert (property);
 	nm_assert (NM_IS_SETTING (setting));
+	nm_assert (NM_IN_SET (secret_type, NM_SECRET_AGENT_SECRET_TYPE_PROPERTY, NM_SECRET_AGENT_SECRET_TYPE_SECRET));
+	nm_assert (g_object_class_find_property (G_OBJECT_GET_CLASS (setting), property));
+	nm_assert ((secret_type == NM_SECRET_AGENT_SECRET_TYPE_SECRET) == nm_setting_get_secret_flags (setting, property, NULL, NULL));
+
+	g_object_get (setting, property, &value, NULL);
+
+	real = g_slice_new (SecretReal);
+	*real = (SecretReal) {
+		.base.secret_type = secret_type,
+		.base.pretty_name = g_strdup (pretty_name),
+		.base.entry_id    = g_strdup_printf ("%s.%s", nm_setting_get_name (setting), property),
+		.base.value       = g_steal_pointer (&value),
+		.base.is_secret   = (secret_type != NM_SECRET_AGENT_SECRET_TYPE_PROPERTY),
+		.setting          = g_object_ref (setting),
+		.property         = g_strdup (property),
+	};
+	return &real->base;
+}
 
-	real = g_slice_new0 (NMSecretAgentSimpleSecretReal);
-	*((NMSecretAgentSecretType *) &real->base.secret_type) = secret_type;
-	real->setting = g_object_ref (setting);
-	real->base.pretty_name = g_strdup (pretty_name);
-	real->property = g_strdup (property);
-	switch (secret_type) {
-	case NM_SECRET_AGENT_SECRET_TYPE_PROPERTY:
-	case NM_SECRET_AGENT_SECRET_TYPE_SECRET:
-		nm_assert (!vpn_type);
-		nm_assert (g_object_class_find_property (G_OBJECT_GET_CLASS (setting), property));
-		nm_assert ((secret_type == NM_SECRET_AGENT_SECRET_TYPE_SECRET) == nm_setting_get_secret_flags (setting, property, NULL, NULL));
-		real->base.entry_id = g_strdup_printf ("%s.%s", nm_setting_get_name (setting), property);
-		g_object_get (setting, property, &real->base.value, NULL);
-		real->base.is_secret = (secret_type != NM_SECRET_AGENT_SECRET_TYPE_PROPERTY);
-		break;
-	case NM_SECRET_AGENT_SECRET_TYPE_VPN_SECRET:
-		vpn_prefix = NM_SECRET_AGENT_ENTRY_ID_PREFX_VPN_SECRETS;
-		value = nm_setting_vpn_get_secret (NM_SETTING_VPN (setting), property);
-		real->base.entry_id = g_strdup_printf ("%s%s", vpn_prefix, property);
-		nm_assert (vpn_type);
-		real->base.vpn_type = g_strdup (vpn_type);
-		real->base.value = g_strdup (value);
-		real->base.is_secret = TRUE;
-		break;
-	}
-	nm_assert (real->base.entry_id);
+static NMSecretAgentSimpleSecret *
+_secret_real_new_vpn_secret (const char *pretty_name,
+                             NMSetting  *setting,
+                             const char *property,
+                             const char *vpn_type)
+{
+	SecretReal *real;
+	const char *value;
+
+	nm_assert (property);
+	nm_assert (NM_IS_SETTING_VPN (setting));
+	nm_assert (vpn_type);
+
+	value = nm_setting_vpn_get_secret (NM_SETTING_VPN (setting), property);
+
+	real = g_slice_new (SecretReal);
+	*real = (SecretReal) {
+		.base.secret_type = NM_SECRET_AGENT_SECRET_TYPE_VPN_SECRET,
+		.base.pretty_name = g_strdup (pretty_name),
+		.base.entry_id    = g_strdup_printf ("%s%s", NM_SECRET_AGENT_ENTRY_ID_PREFX_VPN_SECRETS, property),
+		.base.value       = g_strdup (value),
+		.base.is_secret   = TRUE,
+		.base.vpn_type    = g_strdup (vpn_type),
+		.setting          = g_object_ref (setting),
+		.property         = g_strdup (property),
+	};
+	return &real->base;
+}
 
+static NMSecretAgentSimpleSecret *
+_secret_real_new_wireguard_peer_psk (NMSettingWireGuard *s_wg,
+                                     const char *public_key,
+                                     const char *preshared_key)
+{
+	SecretReal *real;
+
+	nm_assert (NM_IS_SETTING_WIREGUARD (s_wg));
+	nm_assert (public_key);
+
+	real = g_slice_new (SecretReal);
+	*real = (SecretReal) {
+		.base.secret_type        = NM_SECRET_AGENT_SECRET_TYPE_WIREGUARD_PEER_PSK,
+		.base.pretty_name        = g_strdup_printf (_("Preshared-key for %s"),
+		                                            public_key),
+		.base.entry_id           = g_strdup_printf (NM_SETTING_WIREGUARD_SETTING_NAME"."NM_SETTING_WIREGUARD_PEERS".%s."NM_WIREGUARD_PEER_ATTR_PRESHARED_KEY,
+		                                            public_key),
+		.base.value              = g_strdup (preshared_key),
+		.base.is_secret          = TRUE,
+		.base.no_prompt_entry_id = TRUE,
+		.setting                 = NM_SETTING (g_object_ref (s_wg)),
+		.property                = g_strdup (public_key),
+	};
 	return &real->base;
 }
 
+/*****************************************************************************/
+
 static gboolean
-add_8021x_secrets (NMSecretAgentSimpleRequest *request,
-                   GPtrArray                  *secrets)
+add_8021x_secrets (RequestData *request,
+                   GPtrArray *secrets)
 {
 	NMSetting8021x *s_8021x = nm_connection_get_setting_802_1x (request->connection);
 	const char *eap_method;
@@ -222,11 +255,10 @@ add_8021x_secrets (NMSecretAgentSimpleRequest *request,
 		char **iter;
 
 		for (iter = request->hints; *iter; iter++) {
-			secret = nm_secret_agent_simple_secret_new (NM_SECRET_AGENT_SECRET_TYPE_SECRET,
-			                                            _(*iter),
-			                                            NM_SETTING (s_8021x),
-			                                            *iter,
-			                                            NULL);
+			secret = _secret_real_new_plain (NM_SECRET_AGENT_SECRET_TYPE_SECRET,
+			                                 _(*iter),
+			                                 NM_SETTING (s_8021x),
+			                                 *iter);
 			g_ptr_array_add (secrets, secret);
 		}
 
@@ -237,41 +269,37 @@ add_8021x_secrets (NMSecretAgentSimpleRequest *request,
 	if (!eap_method)
 		return FALSE;
 
-	if (   !strcmp (eap_method, "md5")
-	    || !strcmp (eap_method, "leap")
-	    || !strcmp (eap_method, "ttls")
-	    || !strcmp (eap_method, "peap")) {
+	if (NM_IN_STRSET (eap_method, "md5",
+	                              "leap",
+	                              "ttls",
+	                              "peap")) {
 		/* TTLS and PEAP are actually much more complicated, but this complication
 		 * is not visible here since we only care about phase2 authentication
 		 * (and don't even care of which one)
 		 */
-		secret = nm_secret_agent_simple_secret_new (NM_SECRET_AGENT_SECRET_TYPE_PROPERTY,
-		                                            _("Username"),
-		                                            NM_SETTING (s_8021x),
-		                                            NM_SETTING_802_1X_IDENTITY,
-		                                            NULL);
+		secret = _secret_real_new_plain (NM_SECRET_AGENT_SECRET_TYPE_PROPERTY,
+		                                 _("Username"),
+		                                 NM_SETTING (s_8021x),
+		                                 NM_SETTING_802_1X_IDENTITY);
 		g_ptr_array_add (secrets, secret);
-		secret = nm_secret_agent_simple_secret_new (NM_SECRET_AGENT_SECRET_TYPE_SECRET,
-		                                            _("Password"),
-		                                            NM_SETTING (s_8021x),
-		                                            NM_SETTING_802_1X_PASSWORD,
-		                                            NULL);
+		secret = _secret_real_new_plain (NM_SECRET_AGENT_SECRET_TYPE_SECRET,
+		                                 _("Password"),
+		                                 NM_SETTING (s_8021x),
+		                                 NM_SETTING_802_1X_PASSWORD);
 		g_ptr_array_add (secrets, secret);
 		return TRUE;
 	}
 
-	if (!strcmp (eap_method, "tls")) {
-		secret = nm_secret_agent_simple_secret_new (NM_SECRET_AGENT_SECRET_TYPE_PROPERTY,
-		                                            _("Identity"),
-		                                            NM_SETTING (s_8021x),
-		                                            NM_SETTING_802_1X_IDENTITY,
-		                                            NULL);
+	if (nm_streq (eap_method, "tls")) {
+		secret = _secret_real_new_plain (NM_SECRET_AGENT_SECRET_TYPE_PROPERTY,
+		                                 _("Identity"),
+		                                 NM_SETTING (s_8021x),
+		                                 NM_SETTING_802_1X_IDENTITY);
 		g_ptr_array_add (secrets, secret);
-		secret = nm_secret_agent_simple_secret_new (NM_SECRET_AGENT_SECRET_TYPE_SECRET,
-		                                            _("Private key password"),
-		                                            NM_SETTING (s_8021x),
-		                                            NM_SETTING_802_1X_PRIVATE_KEY_PASSWORD,
-		                                            NULL);
+		secret = _secret_real_new_plain (NM_SECRET_AGENT_SECRET_TYPE_SECRET,
+		                                 _("Private key password"),
+		                                 NM_SETTING (s_8021x),
+		                                 NM_SETTING_802_1X_PRIVATE_KEY_PASSWORD);
 		g_ptr_array_add (secrets, secret);
 		return TRUE;
 	}
@@ -280,7 +308,7 @@ add_8021x_secrets (NMSecretAgentSimpleRequest *request,
 }
 
 static gboolean
-add_wireless_secrets (NMSecretAgentSimpleRequest *request,
+add_wireless_secrets (RequestData *request,
                       GPtrArray                  *secrets)
 {
 	NMSettingWirelessSecurity *s_wsec = nm_connection_get_setting_wireless_security (request->connection);
@@ -290,76 +318,67 @@ add_wireless_secrets (NMSecretAgentSimpleRequest *request,
 	if (!key_mgmt)
 		return FALSE;
 
-	if (!strcmp (key_mgmt, "wpa-none") || !strcmp (key_mgmt, "wpa-psk")) {
-		secret = nm_secret_agent_simple_secret_new (NM_SECRET_AGENT_SECRET_TYPE_SECRET,
-		                                            _("Password"),
-		                                            NM_SETTING (s_wsec),
-		                                            NM_SETTING_WIRELESS_SECURITY_PSK,
-		                                            NULL);
+	if (NM_IN_STRSET (key_mgmt, "wpa-none", "wpa-psk", "sae")) {
+		secret = _secret_real_new_plain (NM_SECRET_AGENT_SECRET_TYPE_SECRET,
+		                                 _("Password"),
+		                                 NM_SETTING (s_wsec),
+		                                 NM_SETTING_WIRELESS_SECURITY_PSK);
 		g_ptr_array_add (secrets, secret);
 		return TRUE;
 	}
 
-	if (!strcmp (key_mgmt, "none")) {
-		int index;
-		char *key;
+	if (nm_streq (key_mgmt, "none")) {
+		guint32 index;
+		char key[100];
 
 		index = nm_setting_wireless_security_get_wep_tx_keyidx (s_wsec);
-		key = g_strdup_printf ("wep-key%d", index);
-		secret = nm_secret_agent_simple_secret_new (NM_SECRET_AGENT_SECRET_TYPE_SECRET,
-		                                            _("Key"),
-		                                            NM_SETTING (s_wsec),
-		                                            key,
-		                                            NULL);
-		g_free (key);
-
+		secret = _secret_real_new_plain (NM_SECRET_AGENT_SECRET_TYPE_SECRET,
+		                                 _("Key"),
+		                                 NM_SETTING (s_wsec),
+		                                 nm_sprintf_buf (key, "wep-key%u", (guint) index));
 		g_ptr_array_add (secrets, secret);
 		return TRUE;
 	}
 
-	if (!strcmp (key_mgmt, "iee8021x")) {
-		if (!g_strcmp0 (nm_setting_wireless_security_get_auth_alg (s_wsec), "leap")) {
-			secret = nm_secret_agent_simple_secret_new (NM_SECRET_AGENT_SECRET_TYPE_SECRET,
-			                                            _("Password"),
-			                                            NM_SETTING (s_wsec),
-			                                            NM_SETTING_WIRELESS_SECURITY_LEAP_PASSWORD,
-			                                            NULL);
+	if (nm_streq (key_mgmt, "iee8021x")) {
+		if (nm_streq0 (nm_setting_wireless_security_get_auth_alg (s_wsec), "leap")) {
+			secret = _secret_real_new_plain (NM_SECRET_AGENT_SECRET_TYPE_SECRET,
+			                                 _("Password"),
+			                                 NM_SETTING (s_wsec),
+			                                 NM_SETTING_WIRELESS_SECURITY_LEAP_PASSWORD);
 			g_ptr_array_add (secrets, secret);
 			return TRUE;
 		} else
 			return add_8021x_secrets (request, secrets);
 	}
 
-	if (!strcmp (key_mgmt, "wpa-eap"))
+	if (nm_streq (key_mgmt, "wpa-eap"))
 		return add_8021x_secrets (request, secrets);
 
 	return FALSE;
 }
 
 static gboolean
-add_pppoe_secrets (NMSecretAgentSimpleRequest *request,
+add_pppoe_secrets (RequestData *request,
                    GPtrArray                  *secrets)
 {
 	NMSettingPppoe *s_pppoe = nm_connection_get_setting_pppoe (request->connection);
 	NMSecretAgentSimpleSecret *secret;
 
-	secret = nm_secret_agent_simple_secret_new (NM_SECRET_AGENT_SECRET_TYPE_PROPERTY,
-	                                            _("Username"),
-	                                            NM_SETTING (s_pppoe),
-	                                            NM_SETTING_PPPOE_USERNAME,
-	                                            NULL);
+	secret = _secret_real_new_plain (NM_SECRET_AGENT_SECRET_TYPE_PROPERTY,
+	                                 _("Username"),
+	                                 NM_SETTING (s_pppoe),
+	                                 NM_SETTING_PPPOE_USERNAME);
 	g_ptr_array_add (secrets, secret);
-	secret = nm_secret_agent_simple_secret_new (NM_SECRET_AGENT_SECRET_TYPE_PROPERTY,
-	                                            _("Service"),
-	                                            NM_SETTING (s_pppoe),
-	                                            NM_SETTING_PPPOE_SERVICE,
-	                                            NULL);
+	secret = _secret_real_new_plain (NM_SECRET_AGENT_SECRET_TYPE_PROPERTY,
+	                                 _("Service"),
+	                                 NM_SETTING (s_pppoe),
+	                                 NM_SETTING_PPPOE_SERVICE);
 	g_ptr_array_add (secrets, secret);
-	secret = nm_secret_agent_simple_secret_new (NM_SECRET_AGENT_SECRET_TYPE_SECRET,
-	                                            _("Password"),
-	                                            NM_SETTING (s_pppoe),
-	                                            NM_SETTING_PPPOE_PASSWORD,
-	                                            NULL);
+	secret = _secret_real_new_plain (NM_SECRET_AGENT_SECRET_TYPE_SECRET,
+	                                 _("Password"),
+	                                 NM_SETTING (s_pppoe),
+	                                 NM_SETTING_PPPOE_PASSWORD);
 	g_ptr_array_add (secrets, secret);
 	return TRUE;
 }
@@ -387,11 +406,10 @@ add_vpn_secret_helper (GPtrArray *secrets, NMSettingVpn *s_vpn, const char *name
 	flags = get_vpn_secret_flags (s_vpn, name);
 	if (   flags & NM_SETTING_SECRET_FLAG_AGENT_OWNED
 	    || flags & NM_SETTING_SECRET_FLAG_NOT_SAVED) {
-		secret = nm_secret_agent_simple_secret_new (NM_SECRET_AGENT_SECRET_TYPE_VPN_SECRET,
-		                                            ui_name,
-		                                            NM_SETTING (s_vpn),
-		                                            name,
-		                                            nm_setting_vpn_get_service_type (s_vpn));
+		secret = _secret_real_new_vpn_secret (ui_name,
+		                                      NM_SETTING (s_vpn),
+		                                      name,
+		                                      nm_setting_vpn_get_service_type (s_vpn));
 
 		/* Check for duplicates */
 		for (i = 0; i < secrets->len; i++) {
@@ -400,7 +418,7 @@ add_vpn_secret_helper (GPtrArray *secrets, NMSettingVpn *s_vpn, const char *name
 			if (   s->secret_type == secret->secret_type
 			    && nm_streq0 (s->vpn_type, secret->vpn_type)
 			    && nm_streq0 (s->entry_id, secret->entry_id)) {
-				nm_secret_agent_simple_secret_free (secret);
+				_secret_real_free (secret);
 				return;
 			}
 		}
@@ -412,9 +430,9 @@ add_vpn_secret_helper (GPtrArray *secrets, NMSettingVpn *s_vpn, const char *name
 #define VPN_MSG_TAG "x-vpn-message:"
 
 static gboolean
-add_vpn_secrets (NMSecretAgentSimpleRequest *request,
-                 GPtrArray                  *secrets,
-                 char                       **msg)
+add_vpn_secrets (RequestData *request,
+                 GPtrArray *secrets,
+                 char **msg)
 {
 	NMSettingVpn *s_vpn = nm_connection_get_setting_vpn (request->connection);
 	const VpnPasswordName *secret_names, *p;
@@ -443,33 +461,397 @@ add_vpn_secrets (NMSecretAgentSimpleRequest *request,
 	return TRUE;
 }
 
-static void
-request_secrets_from_ui (NMSecretAgentSimpleRequest *request)
+static gboolean
+add_wireguard_secrets (RequestData *request,
+                       GPtrArray *secrets,
+                       char **msg,
+                       GError **error)
 {
+	NMSettingWireGuard *s_wg;
+	NMSecretAgentSimpleSecret *secret;
+	guint i;
+
+	s_wg = NM_SETTING_WIREGUARD (nm_connection_get_setting (request->connection, NM_TYPE_SETTING_WIREGUARD));
+	if (!s_wg) {
+		g_set_error (error, NM_SECRET_AGENT_ERROR, NM_SECRET_AGENT_ERROR_FAILED,
+		             "Cannot service a WireGuard secrets request %s for a connection without WireGuard settings",
+		             request->request_id);
+		return FALSE;
+	}
+
+	if (   !request->hints
+	    || !request->hints[0]
+	    || g_strv_contains (NM_CAST_STRV_CC (request->hints), NM_SETTING_WIREGUARD_PRIVATE_KEY)) {
+		secret = _secret_real_new_plain (NM_SECRET_AGENT_SECRET_TYPE_SECRET,
+		                                 _("WireGuard private-key"),
+		                                 NM_SETTING (s_wg),
+		                                 NM_SETTING_WIREGUARD_PRIVATE_KEY);
+		g_ptr_array_add (secrets, secret);
+	}
+
+	if (request->hints) {
+
+		for (i = 0; request->hints[i]; i++) {
+			NMWireGuardPeer *peer;
+			const char *name = request->hints[i];
+			gs_free char *public_key = NULL;
+
+			if (nm_streq (name, NM_SETTING_WIREGUARD_PRIVATE_KEY))
+				continue;
+
+			if (NM_STR_HAS_PREFIX (name, NM_SETTING_WIREGUARD_PEERS".")) {
+				const char *tmp;
+
+				tmp = &name[NM_STRLEN (NM_SETTING_WIREGUARD_PEERS".")];
+				if (NM_STR_HAS_SUFFIX (tmp, "."NM_WIREGUARD_PEER_ATTR_PRESHARED_KEY)) {
+					public_key = g_strndup (tmp,
+					                       strlen (tmp) - NM_STRLEN ("."NM_WIREGUARD_PEER_ATTR_PRESHARED_KEY));
+				}
+			}
+
+			if (!public_key)
+				continue;
+
+			peer = nm_setting_wireguard_get_peer_by_public_key (s_wg, public_key, NULL);
+
+			g_ptr_array_add (secrets, _secret_real_new_wireguard_peer_psk (s_wg,
+			                                                               (  peer
+			                                                                ? nm_wireguard_peer_get_public_key (peer)
+			                                                                : public_key),
+			                                                               (  peer
+			                                                                ? nm_wireguard_peer_get_preshared_key (peer)
+			                                                                : NULL)));
+		}
+	}
+
+	*msg = g_strdup_printf (_("Secrets are required to connect WireGuard VPN '%s'"),
+	                        nm_connection_get_id (request->connection));
+	return TRUE;
+}
+
+typedef struct {
+	GPid auth_dialog_pid;
+	GString *auth_dialog_response;
+	RequestData *request;
 	GPtrArray *secrets;
+	GCancellable *cancellable;
+	gulong cancellable_id;
+	guint child_watch_id;
+	GInputStream *input_stream;
+	GOutputStream *output_stream;
+	char read_buf[5];
+} AuthDialogData;
+
+static void
+_auth_dialog_data_free (AuthDialogData *data)
+{
+	nm_clear_g_signal_handler (data->cancellable, &data->cancellable_id);
+	g_clear_object (&data->cancellable);
+	nm_clear_g_source (&data->child_watch_id);
+	g_ptr_array_unref (data->secrets);
+	g_spawn_close_pid (data->auth_dialog_pid);
+	g_string_free (data->auth_dialog_response, TRUE);
+	g_object_unref (data->input_stream);
+	g_object_unref (data->output_stream);
+	g_slice_free (AuthDialogData, data);
+}
+
+static void
+_auth_dialog_exited (GPid pid, int status, gpointer user_data)
+{
+	AuthDialogData *data = user_data;
+	RequestData *request = data->request;
+	GPtrArray *secrets = data->secrets;
+	NMSettingVpn *s_vpn = nm_connection_get_setting_vpn (request->connection);
+	gs_unref_keyfile GKeyFile *keyfile = NULL;
+	gs_strfreev char **groups = NULL;
+	gs_free char *title = NULL;
+	gs_free char *message = NULL;
+	int i;
+	gs_free_error GError *error = NULL;
+
+	data->child_watch_id = 0;
+
+	nm_clear_g_cancellable_disconnect (data->cancellable, &data->cancellable_id);
+
+	if (status != 0) {
+		g_set_error (&error, NM_SECRET_AGENT_ERROR, NM_SECRET_AGENT_ERROR_FAILED,
+		             "Auth dialog failed with error code %d\n", status);
+		goto out;
+	}
+
+	keyfile = g_key_file_new ();
+	if (!g_key_file_load_from_data (keyfile,
+	                                data->auth_dialog_response->str,
+	                                data->auth_dialog_response->len, G_KEY_FILE_NONE,
+	                                &error)) {
+		goto out;
+	}
+
+	groups = g_key_file_get_groups (keyfile, NULL);
+	if (!nm_streq0 (groups[0], "VPN Plugin UI")) {
+		g_set_error (&error, NM_SECRET_AGENT_ERROR, NM_SECRET_AGENT_ERROR_FAILED,
+		             "Expected [VPN Plugin UI] in auth dialog response");
+		goto out;
+	}
+
+	title = g_key_file_get_string (keyfile, "VPN Plugin UI", "Title", &error);
+	if (!title)
+		goto out;
+
+	message = g_key_file_get_string (keyfile, "VPN Plugin UI", "Description", &error);
+	if (!message)
+		goto out;
+
+	for (i = 1; groups[i]; i++) {
+		gs_free char *pretty_name = NULL;
+
+		if (!g_key_file_get_boolean (keyfile, groups[i], "IsSecret", NULL))
+			continue;
+		if (!g_key_file_get_boolean (keyfile, groups[i], "ShouldAsk", NULL))
+			continue;
+
+		pretty_name = g_key_file_get_string (keyfile, groups[i], "Label", NULL);
+		g_ptr_array_add (secrets, _secret_real_new_vpn_secret (pretty_name,
+		                                                       NM_SETTING (s_vpn),
+		                                                       groups[i],
+		                                                       nm_setting_vpn_get_service_type (s_vpn)));
+	}
+
+out:
+	/* Try to fall back to the hardwired VPN support if the auth dialog fails.
+	 * We may eventually get rid of the whole hardwired secrets handling at some point,
+	 * when the auth helpers are goode enough.. */
+	if (error && add_vpn_secrets (request, secrets, &message)) {
+		g_clear_error (&error);
+		if (!message) {
+			message = g_strdup_printf (_("A password is required to connect to '%s'."),
+			                           nm_connection_get_id (request->connection));
+		}
+	}
+
+	if (error)
+		_request_data_complete (request, NULL, error, NULL);
+	else {
+		g_signal_emit (request->self, signals[REQUEST_SECRETS], 0,
+		               request->request_id, title, message, secrets);
+	}
+
+	_auth_dialog_data_free (data);
+}
+
+static void
+_request_cancelled (GObject *object, gpointer user_data)
+{
+	_auth_dialog_data_free (user_data);
+}
+
+static void
+_auth_dialog_read_done (GObject *source_object,
+                        GAsyncResult *res,
+                        gpointer user_data)
+{
+	GInputStream *auth_dialog_out = G_INPUT_STREAM (source_object);
+	AuthDialogData *data = user_data;
+	gssize read_size;
+	gs_free_error GError *error = NULL;
+
+	read_size = g_input_stream_read_finish (auth_dialog_out, res, &error);
+	switch (read_size) {
+	case -1:
+		if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+			_request_data_complete (data->request, NULL, error, NULL);
+		_auth_dialog_data_free (data);
+		break;
+	case 0:
+		/* Done reading. Let's wait for the auth dialog to exit so that we're able to collect the status.
+		 * Remember we can be cancelled in between. */
+		data->child_watch_id = g_child_watch_add (data->auth_dialog_pid, _auth_dialog_exited, data);
+		data->cancellable = g_object_ref (data->request->cancellable);
+		data->cancellable_id = g_cancellable_connect (data->cancellable,
+		                                              G_CALLBACK (_request_cancelled), data, NULL);
+		break;
+	default:
+		g_string_append_len (data->auth_dialog_response, data->read_buf, read_size);
+		g_input_stream_read_async (auth_dialog_out,
+		                           data->read_buf,
+		                           sizeof (data->read_buf),
+		                           G_PRIORITY_DEFAULT,
+		                           NULL,
+		                           _auth_dialog_read_done,
+		                           data);
+		return;
+	}
+
+	g_input_stream_close (auth_dialog_out, NULL, NULL);
+}
+
+static void
+_auth_dialog_write_done (GObject *source_object,
+                        GAsyncResult *res,
+                        gpointer user_data)
+{
+	GOutputStream *auth_dialog_out = G_OUTPUT_STREAM (source_object);
+	_nm_unused gs_free char *auth_dialog_request_free = user_data;
+
+	/* We don't care about write errors. If there are any problems, the
+	 * reader shall notice. */
+	g_output_stream_write_finish (auth_dialog_out, res, NULL);
+	g_output_stream_close (auth_dialog_out, NULL, NULL);
+}
+
+static void
+_add_to_string (GString *string, const char *key, const char *value)
+{
+	gs_strfreev char **lines = NULL;
+	int i;
+
+	lines = g_strsplit (value, "\n", -1);
+
+	g_string_append (string, key);
+	for (i = 0; lines[i]; i++) {
+		g_string_append_c (string, '=');
+		g_string_append (string, lines[i]);
+		g_string_append_c (string, '\n');
+	}
+}
+
+static void
+_add_data_item_to_string (const char *key, const char *value, gpointer user_data)
+{
+	GString *string = user_data;
+
+	_add_to_string (string, "DATA_KEY", key);
+	_add_to_string (string, "DATA_VAL", value);
+	g_string_append_c (string, '\n');
+}
+
+static void
+_add_secret_to_string (const char *key, const char *value, gpointer user_data)
+{
+	GString *string = user_data;
+
+	_add_to_string (string, "SECRET_KEY", key);
+	_add_to_string (string, "SECRET_VAL", value);
+	g_string_append_c (string, '\n');
+}
+
+static gboolean
+try_spawn_vpn_auth_helper (RequestData *request,
+                           GPtrArray *secrets)
+{
+	NMSettingVpn *s_vpn = nm_connection_get_setting_vpn (request->connection);
+        NMVpnPluginInfo *plugin_info;
+	gboolean supports_external;
+	const char *auth_dialog_argv[] = { NULL,
+		"-u", nm_connection_get_uuid (request->connection),
+		"-n", nm_connection_get_id (request->connection),
+		"-s", nm_setting_vpn_get_service_type (s_vpn),
+		"--external-ui-mode",
+		"-i",
+		NULL, /* [9], slot for "-r" */
+		NULL };
+	const char *s;
+	GPid auth_dialog_pid;
+	int auth_dialog_in_fd;
+	int auth_dialog_out_fd;
+	GOutputStream *auth_dialog_in;
+	GInputStream *auth_dialog_out;
+	GError *error = NULL;
+	GString *auth_dialog_request;
+	char *auth_dialog_request_str;
+	gsize auth_dialog_request_len;
+	AuthDialogData *data;
+
+	plugin_info = nm_vpn_plugin_info_list_find_by_service (nm_vpn_get_plugin_infos (),
+	                                                       nm_setting_vpn_get_service_type (s_vpn));
+	if (!plugin_info)
+		return FALSE;
+
+	s = nm_vpn_plugin_info_lookup_property (plugin_info, "GNOME", "supports-external-ui-mode");
+	supports_external = _nm_utils_ascii_str_to_bool (s, FALSE);
+	if (!supports_external)
+		return FALSE;
+
+	auth_dialog_argv[0] = nm_vpn_plugin_info_lookup_property (plugin_info, "GNOME", "auth-dialog");
+	g_return_val_if_fail (auth_dialog_argv[0], FALSE);
+
+	if (request->flags & NM_SECRET_AGENT_GET_SECRETS_FLAG_REQUEST_NEW)
+		auth_dialog_argv[9] = "-r";
+
+	if (!g_spawn_async_with_pipes (NULL, (char **)auth_dialog_argv, NULL,
+	                               G_SPAWN_DO_NOT_REAP_CHILD,
+	                               NULL, NULL,
+	                               &auth_dialog_pid,
+	                               &auth_dialog_in_fd,
+	                               &auth_dialog_out_fd,
+	                               NULL,
+	                               &error)) {
+		g_warning ("Failed to spawn the auth dialog%s\n", error->message);
+		return FALSE;
+	}
+
+	auth_dialog_in = g_unix_output_stream_new (auth_dialog_in_fd, TRUE);
+	auth_dialog_out =  g_unix_input_stream_new (auth_dialog_out_fd, TRUE);
+
+	auth_dialog_request = g_string_new_len (NULL, 1024);
+	nm_setting_vpn_foreach_data_item (s_vpn, _add_data_item_to_string, auth_dialog_request);
+	nm_setting_vpn_foreach_secret (s_vpn, _add_secret_to_string, auth_dialog_request);
+	g_string_append (auth_dialog_request, "DONE\nQUIT\n");
+	auth_dialog_request_len = auth_dialog_request->len;
+	auth_dialog_request_str = g_string_free (auth_dialog_request, FALSE);
+
+	data = g_slice_new (AuthDialogData);
+	*data = (AuthDialogData) {
+		.auth_dialog_response = g_string_new_len (NULL, sizeof (data->read_buf)),
+		.auth_dialog_pid = auth_dialog_pid,
+		.request = request,
+		.secrets = g_ptr_array_ref (secrets),
+		.input_stream = auth_dialog_out,
+		.output_stream = auth_dialog_in,
+	};
+
+	g_output_stream_write_async (auth_dialog_in,
+	                             auth_dialog_request_str,
+	                             auth_dialog_request_len,
+	                             G_PRIORITY_DEFAULT,
+	                             request->cancellable,
+	                             _auth_dialog_write_done,
+	                             auth_dialog_request_str);
+
+	g_input_stream_read_async (auth_dialog_out,
+	                           data->read_buf,
+	                           sizeof (data->read_buf),
+	                            G_PRIORITY_DEFAULT,
+	                           request->cancellable,
+	                           _auth_dialog_read_done,
+	                           data);
+
+	return TRUE;
+}
+
+static void
+request_secrets_from_ui (RequestData *request)
+{
+	gs_unref_ptrarray GPtrArray *secrets = NULL;
+	gs_free_error GError *error = NULL;
 	NMSecretAgentSimplePrivate *priv;
 	NMSecretAgentSimpleSecret *secret;
 	const char *title;
-	char *msg;
-	gboolean ok = TRUE;
+	gs_free char *msg = NULL;
 
 	priv = NM_SECRET_AGENT_SIMPLE_GET_PRIVATE (request->self);
 	g_return_if_fail (priv->enabled);
 
 	/* We only handle requests for connection with @path if set. */
 	if (priv->path && !g_str_has_prefix (request->request_id, priv->path)) {
-		gs_free_error GError *error = NULL;
-
-		error = g_error_new (NM_SECRET_AGENT_ERROR, NM_SECRET_AGENT_ERROR_FAILED,
-		                     "Request for %s secrets doesn't match path %s",
-		                     request->request_id, priv->path);
-		request->callback (NM_SECRET_AGENT_OLD (request->self), request->connection,
-		                   NULL, error, request->callback_data);
-		g_hash_table_remove (priv->requests, request->request_id);
-		return;
+		g_set_error (&error, NM_SECRET_AGENT_ERROR, NM_SECRET_AGENT_ERROR_FAILED,
+		             "Request for %s secrets doesn't match path %s",
+		             request->request_id, priv->path);
+		goto out_fail_error;
 	}
 
-	secrets = g_ptr_array_new_with_free_func ((GDestroyNotify) nm_secret_agent_simple_secret_free);
+	secrets = g_ptr_array_new_with_free_func ((GDestroyNotify) _secret_real_free);
 
 	if (nm_connection_is_type (request->connection, NM_SETTING_WIRELESS_SETTING_NAME)) {
 		NMSettingWireless *s_wireless;
@@ -484,42 +866,43 @@ request_secrets_from_ui (NMSecretAgentSimpleRequest *request)
 		title = _("Authentication required by wireless network");
 		msg = g_strdup_printf (_("Passwords or encryption keys are required to access the wireless network '%s'."), ssid_utf8);
 
-		ok = add_wireless_secrets (request, secrets);
+		if (!add_wireless_secrets (request, secrets))
+			goto out_fail;
 	} else if (nm_connection_is_type (request->connection, NM_SETTING_WIRED_SETTING_NAME)) {
 		title = _("Wired 802.1X authentication");
 		msg = g_strdup_printf (_("Secrets are required to access the wired network '%s'"),
 		                       nm_connection_get_id (request->connection));
 
-		ok = add_8021x_secrets (request, secrets);
+		if (!add_8021x_secrets (request, secrets))
+			goto out_fail;
 	} else if (nm_connection_is_type (request->connection, NM_SETTING_PPPOE_SETTING_NAME)) {
 		title = _("DSL authentication");
 		msg = g_strdup_printf (_("Secrets are required for the DSL connection '%s'"),
 		                       nm_connection_get_id (request->connection));
 
-		ok = add_pppoe_secrets (request, secrets);
+		if (!add_pppoe_secrets (request, secrets))
+			goto out_fail;
 	} else if (nm_connection_is_type (request->connection, NM_SETTING_GSM_SETTING_NAME)) {
 		NMSettingGsm *s_gsm = nm_connection_get_setting_gsm (request->connection);
 
-		if (strv_has (request->hints, "pin")) {
+		if (g_strv_contains (NM_CAST_STRV_CC (request->hints), NM_SETTING_GSM_PIN)) {
 			title = _("PIN code required");
 			msg = g_strdup (_("PIN code is needed for the mobile broadband device"));
 
-			secret = nm_secret_agent_simple_secret_new (NM_SECRET_AGENT_SECRET_TYPE_PROPERTY,
-			                                            _("PIN"),
-			                                            NM_SETTING (s_gsm),
-			                                            NM_SETTING_GSM_PIN,
-			                                            NULL);
+			secret = _secret_real_new_plain (NM_SECRET_AGENT_SECRET_TYPE_PROPERTY,
+			                                 _("PIN"),
+			                                 NM_SETTING (s_gsm),
+			                                 NM_SETTING_GSM_PIN);
 			g_ptr_array_add (secrets, secret);
 		} else {
 			title = _("Mobile broadband network password");
 			msg = g_strdup_printf (_("A password is required to connect to '%s'."),
 			                       nm_connection_get_id (request->connection));
 
-			secret = nm_secret_agent_simple_secret_new (NM_SECRET_AGENT_SECRET_TYPE_SECRET,
-			                                            _("Password"),
-			                                            NM_SETTING (s_gsm),
-			                                            NM_SETTING_GSM_PASSWORD,
-			                                            NULL);
+			secret = _secret_real_new_plain (NM_SECRET_AGENT_SECRET_TYPE_SECRET,
+			                                 _("Password"),
+			                                 NM_SETTING (s_gsm),
+			                                 NM_SETTING_GSM_PASSWORD);
 			g_ptr_array_add (secrets, secret);
 		}
 	} else if (nm_connection_is_type (request->connection, NM_SETTING_MACSEC_SETTING_NAME)) {
@@ -530,16 +913,20 @@ request_secrets_from_ui (NMSecretAgentSimpleRequest *request)
 
 		if (nm_setting_macsec_get_mode (s_macsec) == NM_SETTING_MACSEC_MODE_PSK) {
 			title = _("MACsec PSK authentication");
-			secret = nm_secret_agent_simple_secret_new (NM_SECRET_AGENT_SECRET_TYPE_SECRET,
-			                                            _("MKA CAK"),
-			                                            NM_SETTING (s_macsec),
-			                                            NM_SETTING_MACSEC_MKA_CAK,
-			                                            NULL);
+			secret = _secret_real_new_plain (NM_SECRET_AGENT_SECRET_TYPE_SECRET,
+			                                 _("MKA CAK"),
+			                                 NM_SETTING (s_macsec),
+			                                 NM_SETTING_MACSEC_MKA_CAK);
 			g_ptr_array_add (secrets, secret);
 		} else {
 			title = _("MACsec EAP authentication");
-			ok = add_8021x_secrets (request, secrets);
+			if (!add_8021x_secrets (request, secrets))
+				goto out_fail;
 		}
+	} else if (nm_connection_is_type (request->connection, NM_SETTING_WIREGUARD_SETTING_NAME)) {
+		title = _("WireGuard VPN secret");
+		if (!add_wireguard_secrets (request, secrets, &msg, &error))
+			goto out_fail_error;
 	} else if (nm_connection_is_type (request->connection, NM_SETTING_CDMA_SETTING_NAME)) {
 		NMSettingCdma *s_cdma = nm_connection_get_setting_cdma (request->connection);
 
@@ -547,11 +934,10 @@ request_secrets_from_ui (NMSecretAgentSimpleRequest *request)
 		msg = g_strdup_printf (_("A password is required to connect to '%s'."),
 		                       nm_connection_get_id (request->connection));
 
-		secret = nm_secret_agent_simple_secret_new (NM_SECRET_AGENT_SECRET_TYPE_SECRET,
-		                                            _("Password"),
-		                                            NM_SETTING (s_cdma),
-		                                            NM_SETTING_CDMA_PASSWORD,
-		                                            NULL);
+		secret = _secret_real_new_plain (NM_SECRET_AGENT_SECRET_TYPE_SECRET,
+		                                 _("Password"),
+		                                 NM_SETTING (s_cdma),
+		                                 NM_SETTING_CDMA_PASSWORD);
 		g_ptr_array_add (secrets, secret);
 	} else if (nm_connection_is_type (request->connection, NM_SETTING_BLUETOOTH_SETTING_NAME)) {
 		NMSetting *setting = NULL;
@@ -564,73 +950,76 @@ request_secrets_from_ui (NMSecretAgentSimpleRequest *request)
 				setting = nm_connection_get_setting_by_name (request->connection, NM_SETTING_CDMA_SETTING_NAME);
 		}
 
-		if (setting) {
-			title = _("Mobile broadband network password");
-			msg = g_strdup_printf (_("A password is required to connect to '%s'."),
-			                       nm_connection_get_id (request->connection));
+		if (!setting)
+			goto out_fail;
 
-			secret = nm_secret_agent_simple_secret_new (NM_SECRET_AGENT_SECRET_TYPE_SECRET,
-			                                            _("Password"),
-			                                            setting,
-			                                            "password",
-			                                            NULL);
-			g_ptr_array_add (secrets, secret);
-		} else
-			ok = FALSE;
+		title = _("Mobile broadband network password");
+		msg = g_strdup_printf (_("A password is required to connect to '%s'."),
+		                       nm_connection_get_id (request->connection));
+
+		secret = _secret_real_new_plain (NM_SECRET_AGENT_SECRET_TYPE_SECRET,
+		                                 _("Password"),
+		                                 setting,
+		                                 "password");
+		g_ptr_array_add (secrets, secret);
 	} else if (nm_connection_is_type (request->connection, NM_SETTING_VPN_SETTING_NAME)) {
 		title = _("VPN password required");
-		msg = NULL;
 
-		ok = add_vpn_secrets (request, secrets, &msg);
-		if (!msg)
+		if (try_spawn_vpn_auth_helper (request, secrets)) {
+			/* This will emit REQUEST_SECRETS when ready */
+			return;
+		}
+
+		if (!add_vpn_secrets (request, secrets, &msg))
+			goto out_fail;
+		if (!msg) {
 			msg = g_strdup_printf (_("A password is required to connect to '%s'."),
 			                       nm_connection_get_id (request->connection));
+		}
 	} else
-		ok = FALSE;
-
-	if (!ok) {
-		gs_free_error GError *error = NULL;
+		goto out_fail;
 
-		error = g_error_new (NM_SECRET_AGENT_ERROR, NM_SECRET_AGENT_ERROR_FAILED,
-		                     "Cannot service a secrets request %s for a %s connection",
-		                     request->request_id,
-		                     nm_connection_get_connection_type (request->connection));
-		request->callback (NM_SECRET_AGENT_OLD (request->self), request->connection,
-		                   NULL, error, request->callback_data);
-		g_hash_table_remove (priv->requests, request->request_id);
-		g_ptr_array_unref (secrets);
-		return;
-	}
+	if (secrets->len == 0)
+		goto out_fail;
 
 	g_signal_emit (request->self, signals[REQUEST_SECRETS], 0,
 	               request->request_id, title, msg, secrets);
+	return;
+
+out_fail:
+	g_set_error (&error, NM_SECRET_AGENT_ERROR, NM_SECRET_AGENT_ERROR_FAILED,
+	             "Cannot service a secrets request %s for a %s connection",
+	             request->request_id,
+	             nm_connection_get_connection_type (request->connection));
+out_fail_error:
+	_request_data_complete (request, NULL, error, NULL);
+
 }
 
 static void
-nm_secret_agent_simple_get_secrets (NMSecretAgentOld                 *agent,
-                                    NMConnection                     *connection,
-                                    const char                       *connection_path,
-                                    const char                       *setting_name,
-                                    const char                      **hints,
-                                    NMSecretAgentGetSecretsFlags      flags,
-                                    NMSecretAgentOldGetSecretsFunc    callback,
-                                    gpointer                          callback_data)
+get_secrets (NMSecretAgentOld                 *agent,
+             NMConnection                     *connection,
+             const char                       *connection_path,
+             const char                       *setting_name,
+             const char                      **hints,
+             NMSecretAgentGetSecretsFlags      flags,
+             NMSecretAgentOldGetSecretsFunc    callback,
+             gpointer                          callback_data)
 {
 	NMSecretAgentSimple *self = NM_SECRET_AGENT_SIMPLE (agent);
 	NMSecretAgentSimplePrivate *priv = NM_SECRET_AGENT_SIMPLE_GET_PRIVATE (self);
-	NMSecretAgentSimpleRequest *request;
-	char *request_id;
-	GError *error;
+	RequestData *request;
+	gs_free_error GError *error = NULL;
+	gs_free char *request_id = NULL;
+	const char *request_id_setting_name;
 
 	request_id = g_strdup_printf ("%s/%s", connection_path, setting_name);
-	if (g_hash_table_lookup (priv->requests, request_id) != NULL) {
+
+	if (g_hash_table_contains (priv->requests, &request_id)) {
 		/* We already have a request pending for this (connection, setting) */
 		error = g_error_new (NM_SECRET_AGENT_ERROR, NM_SECRET_AGENT_ERROR_FAILED,
 		                     "Request for %s secrets already pending", request_id);
-	nope:
 		callback (agent, connection, NULL, error, callback_data);
-		g_error_free (error);
-		g_free (request_id);
 		return;
 	}
 
@@ -638,17 +1027,27 @@ nm_secret_agent_simple_get_secrets (NMSecretAgentOld                 *agent,
 		/* We don't do stored passwords */
 		error = g_error_new (NM_SECRET_AGENT_ERROR, NM_SECRET_AGENT_ERROR_NO_SECRETS,
 		                     "Stored passwords not supported");
-		goto nope;
+		callback (agent, connection, NULL, error, callback_data);
+		return;
 	}
 
-	request = g_slice_new (NMSecretAgentSimpleRequest);
-	request->self = g_object_ref (self);
-	request->connection = g_object_ref (connection);
-	request->hints = g_strdupv ((char **)hints);
-	request->callback = callback;
-	request->callback_data = callback_data;
-	request->request_id = request_id;
-	g_hash_table_replace (priv->requests, request->request_id, request);
+	nm_assert (g_str_has_suffix (request_id, setting_name));
+	request_id_setting_name = &request_id[strlen (request_id) - strlen (setting_name)];
+	nm_assert (nm_streq (request_id_setting_name, setting_name));
+
+	request = g_slice_new (RequestData);
+	*request = (RequestData) {
+		.self = self,
+		.connection = g_object_ref (connection),
+		.setting_name = request_id_setting_name,
+		.hints = g_strdupv ((char **) hints),
+		.callback = callback,
+		.callback_data = callback_data,
+		.request_id = g_steal_pointer (&request_id),
+		.flags = flags,
+		.cancellable = g_cancellable_new (),
+	};
+	g_hash_table_add (priv->requests, request);
 
 	if (priv->enabled)
 		request_secrets_from_ui (request);
@@ -674,28 +1073,34 @@ nm_secret_agent_simple_response (NMSecretAgentSimple *self,
                                  GPtrArray           *secrets)
 {
 	NMSecretAgentSimplePrivate *priv;
-	NMSecretAgentSimpleRequest *request;
-	GVariant *dict = NULL;
-	GError *error = NULL;
+	RequestData *request;
+	gs_unref_variant GVariant *secrets_dict = NULL;
+	gs_free_error GError *error = NULL;
 	int i;
 
 	g_return_if_fail (NM_IS_SECRET_AGENT_SIMPLE (self));
 
 	priv = NM_SECRET_AGENT_SIMPLE_GET_PRIVATE (self);
-	request = g_hash_table_lookup (priv->requests, request_id);
+	request = g_hash_table_lookup (priv->requests, &request_id);
 	g_return_if_fail (request != NULL);
 
 	if (secrets) {
 		GVariantBuilder conn_builder, *setting_builder;
 		GVariantBuilder vpn_secrets_builder;
+		GVariantBuilder wg_secrets_builder;
+		GVariantBuilder wg_peer_builder;
 		GHashTable *settings;
 		GHashTableIter iter;
 		const char *name;
 		gboolean has_vpn = FALSE;
+		gboolean has_wg = FALSE;
 
-		settings = g_hash_table_new (nm_str_hash, g_str_equal);
+		settings = g_hash_table_new_full (nm_str_hash,
+		                                  g_str_equal,
+		                                  NULL,
+		                                  (GDestroyNotify) g_variant_builder_unref);
 		for (i = 0; i < secrets->len; i++) {
-			NMSecretAgentSimpleSecretReal *secret = secrets->pdata[i];
+			SecretReal *secret = secrets->pdata[i];
 
 			setting_builder = g_hash_table_lookup (settings, nm_setting_get_name (secret->setting));
 			if (!setting_builder) {
@@ -719,6 +1124,19 @@ nm_secret_agent_simple_response (NMSecretAgentSimple *self,
 				g_variant_builder_add (&vpn_secrets_builder, "{ss}",
 				                       secret->property, secret->base.value);
 				break;
+			case NM_SECRET_AGENT_SECRET_TYPE_WIREGUARD_PEER_PSK:
+				if (!has_wg) {
+					g_variant_builder_init (&wg_secrets_builder, G_VARIANT_TYPE ("aa{sv}"));
+					has_wg = TRUE;
+				}
+				g_variant_builder_init (&wg_peer_builder, G_VARIANT_TYPE ("a{sv}"));
+				g_variant_builder_add (&wg_peer_builder, "{sv}",
+				                       NM_WIREGUARD_PEER_ATTR_PUBLIC_KEY, g_variant_new_string (secret->property));
+				g_variant_builder_add (&wg_peer_builder, "{sv}",
+				                       NM_WIREGUARD_PEER_ATTR_PRESHARED_KEY, g_variant_new_string (secret->base.value));
+				g_variant_builder_add (&wg_secrets_builder, "a{sv}",
+				                       &wg_peer_builder);
+				break;
 			}
 		}
 
@@ -728,53 +1146,69 @@ nm_secret_agent_simple_response (NMSecretAgentSimple *self,
 			                       g_variant_builder_end (&vpn_secrets_builder));
 		}
 
+		if (has_wg) {
+			g_variant_builder_add (setting_builder, "{sv}",
+			                       NM_SETTING_WIREGUARD_PEERS,
+			                       g_variant_builder_end (&wg_secrets_builder));
+		}
+
 		g_variant_builder_init (&conn_builder, NM_VARIANT_TYPE_CONNECTION);
 		g_hash_table_iter_init (&iter, settings);
 		while (g_hash_table_iter_next (&iter, (gpointer *) &name, (gpointer *) &setting_builder))
 			g_variant_builder_add (&conn_builder, "{sa{sv}}", name, setting_builder);
-		dict = g_variant_builder_end (&conn_builder);
+		secrets_dict = g_variant_ref_sink (g_variant_builder_end (&conn_builder));
 		g_hash_table_destroy (settings);
 	} else {
 		error = g_error_new (NM_SECRET_AGENT_ERROR, NM_SECRET_AGENT_ERROR_USER_CANCELED,
 		                     "User cancelled");
 	}
 
-	request->callback (NM_SECRET_AGENT_OLD (self), request->connection, dict, error, request->callback_data);
-
-	g_clear_error (&error);
-	g_hash_table_remove (priv->requests, request_id);
+	_request_data_complete (request, secrets_dict, error, NULL);
 }
 
 static void
-nm_secret_agent_simple_cancel_get_secrets (NMSecretAgentOld *agent,
-                                           const char       *connection_path,
-                                           const char       *setting_name)
+cancel_get_secrets (NMSecretAgentOld *agent,
+                    const char       *connection_path,
+                    const char       *setting_name)
 {
 	NMSecretAgentSimple *self = NM_SECRET_AGENT_SIMPLE (agent);
 	NMSecretAgentSimplePrivate *priv = NM_SECRET_AGENT_SIMPLE_GET_PRIVATE (self);
+	gs_free_error GError *error = NULL;
 	gs_free char *request_id = NULL;
+	RequestData *request;
 
 	request_id = g_strdup_printf ("%s/%s", connection_path, setting_name);
-	g_hash_table_remove (priv->requests, request_id);
+	request = g_hash_table_lookup (priv->requests, &request_id);
+	if (!request) {
+		/* this is really a bug of the caller (or us?). We cannot invoke a callback,
+		 * hence the caller cannot cleanup the request. */
+		g_return_if_reached ();
+	}
+
+	g_set_error (&error,
+	             NM_SECRET_AGENT_ERROR,
+	             NM_SECRET_AGENT_ERROR_AGENT_CANCELED,
+	             "The secret agent is going away");
+	_request_data_complete (request, NULL, error, NULL);
 }
 
 static void
-nm_secret_agent_simple_save_secrets (NMSecretAgentOld                *agent,
-                                     NMConnection                    *connection,
-                                     const char                      *connection_path,
-                                     NMSecretAgentOldSaveSecretsFunc  callback,
-                                     gpointer                         callback_data)
+save_secrets (NMSecretAgentOld                *agent,
+              NMConnection                    *connection,
+              const char                      *connection_path,
+              NMSecretAgentOldSaveSecretsFunc  callback,
+              gpointer                         callback_data)
 {
 	/* We don't support secret storage */
 	callback (agent, connection, NULL, callback_data);
 }
 
 static void
-nm_secret_agent_simple_delete_secrets (NMSecretAgentOld                  *agent,
-                                       NMConnection                      *connection,
-                                       const char                        *connection_path,
-                                       NMSecretAgentOldDeleteSecretsFunc  callback,
-                                       gpointer                           callback_data)
+delete_secrets (NMSecretAgentOld                  *agent,
+                NMConnection                      *connection,
+                const char                        *connection_path,
+                NMSecretAgentOldDeleteSecretsFunc  callback,
+                gpointer                           callback_data)
 {
 	/* We don't support secret storage, so there's nothing to delete. */
 	callback (agent, connection, NULL, callback_data);
@@ -794,7 +1228,8 @@ void
 nm_secret_agent_simple_enable (NMSecretAgentSimple *self, const char *path)
 {
 	NMSecretAgentSimplePrivate *priv = NM_SECRET_AGENT_SIMPLE_GET_PRIVATE (self);
-	GList *requests, *iter;
+	gs_free RequestData **requests = NULL;
+	gsize i;
 	gs_free char *path_full = NULL;
 
 	/* The path is only used to match a request_id with the current
@@ -803,10 +1238,9 @@ nm_secret_agent_simple_enable (NMSecretAgentSimple *self, const char *path)
 	 */
 	path_full = path ? g_strdup_printf ("%s/", path) : NULL;
 
-	if (g_strcmp0 (path_full, priv->path) != 0) {
+	if (!nm_streq0 (path_full, priv->path)) {
 		g_free (priv->path);
-		priv->path = path_full;
-		path_full = NULL;
+		priv->path = g_steal_pointer (&path_full);
 	}
 
 	if (priv->enabled)
@@ -814,27 +1248,85 @@ nm_secret_agent_simple_enable (NMSecretAgentSimple *self, const char *path)
 	priv->enabled = TRUE;
 
 	/* Service pending secret requests. */
-	requests = g_hash_table_get_values (priv->requests);
-	for (iter = requests; iter; iter = g_list_next (iter))
-		request_secrets_from_ui (iter->data);
+	requests = (RequestData **) g_hash_table_get_keys_as_array (priv->requests, NULL);
+	for (i = 0; requests[i]; i++)
+		request_secrets_from_ui (requests[i]);
+}
+
+/*****************************************************************************/
+
+static void
+nm_secret_agent_simple_init (NMSecretAgentSimple *agent)
+{
+	NMSecretAgentSimplePrivate *priv = NM_SECRET_AGENT_SIMPLE_GET_PRIVATE (agent);
+
+	G_STATIC_ASSERT_EXPR (G_STRUCT_OFFSET (RequestData, request_id) == 0);
+	priv->requests = g_hash_table_new_full (nm_pstr_hash, nm_pstr_equal,
+	                                        NULL, _request_data_free);
+}
+
+/**
+ * nm_secret_agent_simple_new:
+ * @name: the identifier of secret agent
+ *
+ * Creates a new #NMSecretAgentSimple. It does not serve any requests until
+ * nm_secret_agent_simple_enable() is called.
+ *
+ * Returns: a new #NMSecretAgentSimple if the agent creation is successful
+ * or %NULL in case of a failure.
+ */
+NMSecretAgentSimple *
+nm_secret_agent_simple_new (const char *name)
+{
+	return g_initable_new (NM_TYPE_SECRET_AGENT_SIMPLE, NULL, NULL,
+	                       NM_SECRET_AGENT_OLD_IDENTIFIER, name,
+	                       NM_SECRET_AGENT_OLD_CAPABILITIES, NM_SECRET_AGENT_CAPABILITY_VPN_HINTS,
+	                       NULL);
+}
+
+static void
+dispose (GObject *object)
+{
+	NMSecretAgentSimplePrivate *priv = NM_SECRET_AGENT_SIMPLE_GET_PRIVATE (object);
+	gs_free_error GError *error = NULL;
+	GHashTableIter iter;
+	RequestData *request;
+
+	g_hash_table_iter_init (&iter, priv->requests);
+	while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &request)) {
+		if (!error)
+			nm_utils_error_set_cancelled (&error, TRUE, "NMSecretAgentSimple");
+		_request_data_complete (request, NULL, error, &iter);
+	}
+
+	G_OBJECT_CLASS (nm_secret_agent_simple_parent_class)->dispose (object);
+}
+
+static void
+finalize (GObject *object)
+{
+	NMSecretAgentSimplePrivate *priv = NM_SECRET_AGENT_SIMPLE_GET_PRIVATE (object);
+
+	g_hash_table_destroy (priv->requests);
 
-	g_list_free (requests);
+	g_free (priv->path);
+
+	G_OBJECT_CLASS (nm_secret_agent_simple_parent_class)->finalize (object);
 }
 
 void
 nm_secret_agent_simple_class_init (NMSecretAgentSimpleClass *klass)
 {
-	GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
 	NMSecretAgentOldClass *agent_class = NM_SECRET_AGENT_OLD_CLASS (klass);
 
-	g_type_class_add_private (klass, sizeof (NMSecretAgentSimplePrivate));
-
-	gobject_class->finalize = nm_secret_agent_simple_finalize;
+	object_class->dispose = dispose;
+	object_class->finalize = finalize;
 
-	agent_class->get_secrets = nm_secret_agent_simple_get_secrets;
-	agent_class->cancel_get_secrets = nm_secret_agent_simple_cancel_get_secrets;
-	agent_class->save_secrets = nm_secret_agent_simple_save_secrets;
-	agent_class->delete_secrets = nm_secret_agent_simple_delete_secrets;
+	agent_class->get_secrets        = get_secrets;
+	agent_class->cancel_get_secrets = cancel_get_secrets;
+	agent_class->save_secrets       = save_secrets;
+	agent_class->delete_secrets     = delete_secrets;
 
 	/**
 	 * NMSecretAgentSimple::request-secrets:
@@ -868,22 +1360,3 @@ nm_secret_agent_simple_class_init (NMSecretAgentSimpleClass *klass)
 	                                         G_TYPE_STRING, /* prompt */
 	                                         G_TYPE_PTR_ARRAY);
 }
-
-/**
- * nm_secret_agent_simple_new:
- * @name: the identifier of secret agent
- *
- * Creates a new #NMSecretAgentSimple. It does not serve any requests until
- * nm_secret_agent_simple_enable() is called.
- *
- * Returns: a new #NMSecretAgentSimple if the agent creation is successful
- * or %NULL in case of a failure.
- */
-NMSecretAgentOld *
-nm_secret_agent_simple_new (const char *name)
-{
-	return g_initable_new (NM_TYPE_SECRET_AGENT_SIMPLE, NULL, NULL,
-	                       NM_SECRET_AGENT_OLD_IDENTIFIER, name,
-	                       NM_SECRET_AGENT_OLD_CAPABILITIES, NM_SECRET_AGENT_CAPABILITY_VPN_HINTS,
-	                       NULL);
-}
diff --git a/clients/common/nm-secret-agent-simple.h b/clients/common/nm-secret-agent-simple.h
index 529aaeac..4a666d17 100644
--- a/clients/common/nm-secret-agent-simple.h
+++ b/clients/common/nm-secret-agent-simple.h
@@ -21,48 +21,44 @@
 
 #include "nm-secret-agent-old.h"
 
-#define NM_TYPE_SECRET_AGENT_SIMPLE            (nm_secret_agent_simple_get_type ())
-#define NM_SECRET_AGENT_SIMPLE(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_SECRET_AGENT_SIMPLE, NMSecretAgentSimple))
-#define NM_SECRET_AGENT_SIMPLE_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_SECRET_AGENT_SIMPLE, NMSecretAgentSimpleClass))
-#define NM_IS_SECRET_AGENT_SIMPLE(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_SECRET_AGENT_SIMPLE))
-#define NM_IS_SECRET_AGENT_SIMPLE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_SECRET_AGENT_SIMPLE))
-#define NM_SECRET_AGENT_SIMPLE_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_SECRET_AGENT_SIMPLE, NMSecretAgentSimpleClass))
-
-/* Signals */
-#define NM_SECRET_AGENT_SIMPLE_REQUEST_SECRETS  "request-secrets"
-
-typedef struct {
-	NMSecretAgentOld parent;
-
-} NMSecretAgentSimple;
-
-typedef struct {
-	NMSecretAgentOldClass parent;
-
-} NMSecretAgentSimpleClass;
-
 typedef enum {
 	NM_SECRET_AGENT_SECRET_TYPE_PROPERTY,
 	NM_SECRET_AGENT_SECRET_TYPE_SECRET,
 	NM_SECRET_AGENT_SECRET_TYPE_VPN_SECRET,
+	NM_SECRET_AGENT_SECRET_TYPE_WIREGUARD_PEER_PSK,
 } NMSecretAgentSecretType;
 
 typedef struct {
-	const NMSecretAgentSecretType secret_type;
+	NMSecretAgentSecretType secret_type;
 	const char *pretty_name;
 	const char *entry_id;
 	char *value;
 	const char *vpn_type;
-	gboolean is_secret;
+	bool is_secret:1;
+	bool no_prompt_entry_id:1;
 } NMSecretAgentSimpleSecret;
 
 #define NM_SECRET_AGENT_ENTRY_ID_PREFX_VPN_SECRETS "vpn.secrets."
 
 #define NM_SECRET_AGENT_VPN_TYPE_OPENCONNECT  NM_DBUS_INTERFACE".openconnect"
 
+/*****************************************************************************/
+
+#define NM_TYPE_SECRET_AGENT_SIMPLE            (nm_secret_agent_simple_get_type ())
+#define NM_SECRET_AGENT_SIMPLE(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_SECRET_AGENT_SIMPLE, NMSecretAgentSimple))
+#define NM_SECRET_AGENT_SIMPLE_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_SECRET_AGENT_SIMPLE, NMSecretAgentSimpleClass))
+#define NM_IS_SECRET_AGENT_SIMPLE(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_SECRET_AGENT_SIMPLE))
+#define NM_IS_SECRET_AGENT_SIMPLE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_SECRET_AGENT_SIMPLE))
+#define NM_SECRET_AGENT_SIMPLE_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_SECRET_AGENT_SIMPLE, NMSecretAgentSimpleClass))
+
+#define NM_SECRET_AGENT_SIMPLE_REQUEST_SECRETS  "request-secrets"
+
+typedef struct _NMSecretAgentSimple      NMSecretAgentSimple;
+typedef struct _NMSecretAgentSimpleClass NMSecretAgentSimpleClass;
+
 GType nm_secret_agent_simple_get_type (void);
 
-NMSecretAgentOld *nm_secret_agent_simple_new                 (const char          *name);
+NMSecretAgentSimple *nm_secret_agent_simple_new              (const char          *name);
 
 void              nm_secret_agent_simple_response            (NMSecretAgentSimple *self,
                                                               const char          *request_id,
diff --git a/clients/common/nm-vpn-helpers.c b/clients/common/nm-vpn-helpers.c
index c3237f12..ea905972 100644
--- a/clients/common/nm-vpn-helpers.c
+++ b/clients/common/nm-vpn-helpers.c
@@ -25,9 +25,13 @@
 
 #include "nm-vpn-helpers.h"
 
-#include <string.h>
+#include <arpa/inet.h>
+#include <net/if.h>
 
+#include "nm-client-utils.h"
 #include "nm-utils.h"
+#include "nm-utils/nm-io-utils.h"
+#include "nm-utils/nm-secret-utils.h"
 
 /*****************************************************************************/
 
@@ -115,27 +119,27 @@ nm_vpn_supports_ipv6 (NMConnection *connection)
 const VpnPasswordName *
 nm_vpn_get_secret_names (const char *service_type)
 {
-	static const VpnPasswordName const generic_vpn_secrets[] = {
+	static const VpnPasswordName generic_vpn_secrets[] = {
 		{ "password", N_("Password") },
 		{ 0 }
 	};
-	static const VpnPasswordName const openvpn_secrets[] = {
+	static const VpnPasswordName openvpn_secrets[] = {
 		{ "password", N_("Password") },
 		{ "cert-pass", N_("Certificate password") },
 		{ "http-proxy-password", N_("HTTP proxy password") },
 		{ 0 }
 	};
-	static const VpnPasswordName const vpnc_secrets[] = {
+	static const VpnPasswordName vpnc_secrets[] = {
 		{ "Xauth password", N_("Password") },
 		{ "IPSec secret", N_("Group password") },
 		{ 0 }
 	};
-	static const VpnPasswordName const swan_secrets[] = {
+	static const VpnPasswordName swan_secrets[] = {
 		{ "xauthpassword", N_("Password") },
 		{ "pskvalue", N_("Group password") },
 		{ 0 }
 	};
-	static const VpnPasswordName const openconnect_secrets[] = {
+	static const VpnPasswordName openconnect_secrets[] = {
 		{ "gateway", N_("Gateway") },
 		{ "cookie", N_("Cookie") },
 		{ "gwcert", N_("Gateway certificate hash") },
@@ -249,3 +253,556 @@ nm_vpn_openconnect_authenticate_helper (const char *host,
 	return TRUE;
 }
 
+static gboolean
+_wg_complete_peer (GPtrArray **p_peers,
+                   NMWireGuardPeer *peer_take,
+                   gsize peer_start_line_nr,
+                   const char *filename,
+                   GError **error)
+{
+	nm_auto_unref_wgpeer NMWireGuardPeer *peer = peer_take;
+	gs_free_error GError *local = NULL;
+
+	if (!peer)
+		return TRUE;
+
+	if (!nm_wireguard_peer_is_valid (peer, TRUE, TRUE, &local)) {
+		nm_utils_error_set (error, NM_UTILS_ERROR_UNKNOWN,
+		                    _("Invalid peer starting at %s:%zu: %s"),
+		                    filename,
+		                    peer_start_line_nr,
+		                    local->message);
+		return FALSE;
+	}
+
+	if (!*p_peers)
+		*p_peers = g_ptr_array_new_with_free_func ((GDestroyNotify) nm_wireguard_peer_unref);
+	g_ptr_array_add (*p_peers, g_steal_pointer (&peer));
+	return TRUE;
+}
+
+static gboolean
+_line_match (char *line, const char *key, gsize key_len, const char **out_key, char **out_value)
+{
+	nm_assert (line);
+	nm_assert (key);
+	nm_assert (strlen (key) == key_len);
+	nm_assert (!strchr (key, '='));
+	nm_assert (out_key && !*out_key);
+	nm_assert (out_value && !*out_value);
+
+	/* Note that `wg-quick` (linux.bash) does case-insensitive comparison (shopt -s nocasematch).
+	 * `wg setconf` does case-insensitive comparison too (with strncasecmp, which is locale dependent).
+	 *
+	 * We do a case-insensitive comparison of the key, however in a locale-independent manner. */
+
+	if (g_ascii_strncasecmp (line, key, key_len) != 0)
+		return FALSE;
+
+	if (line[key_len] != '=')
+		return FALSE;
+
+	*out_key = key;
+	*out_value = &line[key_len + 1];
+	return TRUE;
+}
+
+#define line_match(line, key, out_key, out_value) \
+	_line_match ((line), ""key"", NM_STRLEN (key), (out_key), (out_value))
+
+static gboolean
+value_split_word (char **line_remainder, char **out_word)
+{
+	char *str;
+
+	if ((*line_remainder)[0] == '\0')
+		return FALSE;
+
+	*out_word = *line_remainder;
+
+	str = strchrnul (*line_remainder, ',');
+	if (str[0] == ',') {
+		str[0] = '\0';
+		*line_remainder = &str[1];
+	} else
+		*line_remainder = str;
+	return TRUE;
+}
+
+NMConnection *
+nm_vpn_wireguard_import (const char *filename,
+                         GError **error)
+{
+	nm_auto_clear_secret_ptr NMSecretPtr file_content = NM_SECRET_PTR_INIT ();
+	char ifname[IFNAMSIZ];
+	gs_free char *uuid = NULL;
+	gboolean ifname_valid = FALSE;
+	const char *cstr;
+	char *line_remainder;
+	gs_unref_object NMConnection *connection = NULL;
+	NMSettingConnection *s_con;
+	NMSettingIPConfig *s_ip4;
+	NMSettingIPConfig *s_ip6;
+	NMSettingWireGuard *s_wg;
+	gs_free_error GError *local = NULL;
+	enum {
+		LINE_CONTEXT_INIT,
+		LINE_CONTEXT_INTERFACE,
+		LINE_CONTEXT_PEER,
+	} line_context;
+	gsize line_nr;
+	gsize current_peer_start_line_nr = 0;
+	nm_auto_unref_wgpeer NMWireGuardPeer *current_peer = NULL;
+	gs_unref_ptrarray GPtrArray *data_dns_v4 = NULL;
+	gs_unref_ptrarray GPtrArray *data_dns_v6 = NULL;
+	gs_unref_ptrarray GPtrArray *data_addr_v4 = NULL;
+	gs_unref_ptrarray GPtrArray *data_addr_v6 = NULL;
+	gs_unref_ptrarray GPtrArray *data_peers = NULL;
+	const char *data_private_key = NULL;
+	gint64 data_table;
+	guint data_listen_port = 0;
+	guint data_fwmark = 0;
+	guint data_mtu = 0;
+	int is_v4;
+	guint i;
+
+	g_return_val_if_fail (filename, NULL);
+	g_return_val_if_fail (!error || !*error, NULL);
+
+	/* contrary to "wg-quick", we never interpret the filename as "/etc/wireguard/$INTERFACE.conf".
+	 * If the filename has no '/', it is interpreted as relative to the current working directory.
+	 * However, we do require a suitable filename suffix and that the name corresponds to the interface
+	 * name. */
+	cstr = strrchr (filename, '/');
+	cstr = cstr ? &cstr[1] : filename;
+	if (NM_STR_HAS_SUFFIX (cstr, ".conf")) {
+		gsize len = strlen (cstr) - NM_STRLEN (".conf");
+
+		if (len > 0 && len < sizeof (ifname)) {
+			memcpy (ifname, cstr, len);
+			ifname[len] = '\0';
+
+			if (nm_utils_is_valid_iface_name (ifname, NULL))
+				ifname_valid = TRUE;
+		}
+	}
+	if (!ifname_valid) {
+		nm_utils_error_set_literal (error, NM_UTILS_ERROR_UNKNOWN,
+		                            _("The WireGuard config file must be a valid interface name followed by \".conf\""));
+		return FALSE;
+	}
+
+	if (nm_utils_file_get_contents (-1,
+	                                filename,
+	                                10*1024*1024,
+	                                NM_UTILS_FILE_GET_CONTENTS_FLAG_SECRET,
+	                                &file_content.str,
+	                                &file_content.len,
+	                                error) < 0)
+		return NULL;
+
+	/* We interpret the file like `wg-quick up` and `wg setconf` do.
+	 *
+	 * Of course the WireGuard scripts do something fundamentlly different. They
+	 * perform actions to configure the WireGuard link in kernel, add routes and
+	 * addresses, and call resolvconf. It all happens at the time when the script
+	 * run.
+	 *
+	 * This code here instead generates a NetworkManager connection profile so that
+	 * NetworkManager will apply a similar configuration when later activating the profile. */
+
+#define _TABLE_AUTO  ((gint64) -1)
+#define _TABLE_OFF   ((gint64) -2)
+
+	data_table = _TABLE_AUTO;
+
+	line_remainder = file_content.str;
+	line_context = LINE_CONTEXT_INIT;
+	line_nr = 0;
+	while (line_remainder[0] != '\0') {
+		const char *matched_key = NULL;
+		char *value = NULL;
+		char *line;
+		char ch;
+		gint64 i64;
+
+		line_nr++;
+
+		line = line_remainder;
+		line_remainder = strchrnul (line, '\n');
+		if (line_remainder[0] != '\0')
+			(line_remainder++)[0] = '\0';
+
+		/* Drop all spaces and truncate at first '#'.
+		 * See wg's config_read_line().
+		 *
+		 * Note that wg-quick doesn't do that.
+		 *
+		 * Neither `wg setconf` nor `wg-quick` does a strict parsing.
+		 * We don't either. Just try to interpret the file (mostly) the same as
+		 * they would.
+		 */
+		{
+			gsize l, n;
+
+			n = 0;
+			for (l = 0; (ch = line[l]); l++) {
+				if (g_ascii_isspace (ch)) {
+					/* wg-setconf strips all whitespace before parsing the content. That means,
+					 * *[I nterface]" will be accepted. We do that too. */
+					continue;
+				}
+				if (ch == '#')
+					break;
+				line[n++] = line[l];
+			}
+			if (n == 0)
+				continue;
+			line[n] = '\0';
+		}
+
+		if (g_ascii_strcasecmp (line, "[Interface]") == 0) {
+			if (!_wg_complete_peer (&data_peers,
+			                        g_steal_pointer (&current_peer),
+			                        current_peer_start_line_nr,
+			                        filename,
+			                        error))
+				return FALSE;
+			line_context = LINE_CONTEXT_INTERFACE;
+			continue;
+		}
+
+		if (g_ascii_strcasecmp (line, "[Peer]") == 0) {
+			if (!_wg_complete_peer (&data_peers,
+			                        g_steal_pointer (&current_peer),
+			                        current_peer_start_line_nr,
+			                        filename,
+			                        error))
+				return FALSE;
+			current_peer_start_line_nr = line_nr;
+			current_peer = nm_wireguard_peer_new ();
+			line_context = LINE_CONTEXT_PEER;
+			continue;
+		}
+
+		if (line_context == LINE_CONTEXT_INTERFACE) {
+
+			if (line_match (line, "Address", &matched_key, &value)) {
+				char *value_word;
+
+				while (value_split_word (&value, &value_word)) {
+					GPtrArray **p_data_addr;
+					NMIPAddr addr_bin;
+					int addr_family;
+					int prefix_len;
+
+					if (!nm_utils_parse_inaddr_prefix_bin (AF_UNSPEC,
+					                                       value_word,
+					                                       &addr_family,
+					                                       &addr_bin,
+					                                       &prefix_len))
+						goto fail_invalid_value;
+
+					p_data_addr =   (addr_family == AF_INET)
+					              ? &data_addr_v4
+					              : &data_addr_v6;
+
+					if (!*p_data_addr)
+						*p_data_addr = g_ptr_array_new_with_free_func ((GDestroyNotify) nm_ip_address_unref);
+
+					g_ptr_array_add (*p_data_addr,
+					                 nm_ip_address_new_binary (addr_family,
+					                                           &addr_bin,
+					                                             prefix_len == -1
+					                                           ? ((addr_family == AF_INET) ? 32 : 128)
+					                                           : prefix_len,
+					                                           NULL));
+				}
+				continue;
+			}
+
+			if (line_match (line, "MTU", &matched_key, &value)) {
+				i64 = _nm_utils_ascii_str_to_int64 (value, 0, 0, G_MAXUINT32, -1);
+				if (i64 == -1)
+					goto fail_invalid_value;
+
+				/* wg-quick accepts the "MTU" value, but it also fetches routes to
+				 * autodetect it. NetworkManager won't do that, we can only configure
+				 * an explicit MTU or no autodetection will be performed. */
+				data_mtu = i64;
+				continue;
+			}
+
+			if (line_match (line, "DNS", &matched_key, &value)) {
+				char *value_word;
+
+				while (value_split_word (&value, &value_word)) {
+					char addr_s[NM_CONST_MAX (INET_ADDRSTRLEN, INET6_ADDRSTRLEN)];
+					GPtrArray **p_data_dns;
+					NMIPAddr addr_bin;
+					int addr_family;
+
+					if (!nm_utils_parse_inaddr_bin (AF_UNSPEC,
+					                                value_word,
+					                                &addr_family,
+					                                &addr_bin))
+						goto fail_invalid_value;
+
+					p_data_dns =   (addr_family == AF_INET)
+					             ? &data_dns_v4
+					             : &data_dns_v6;
+					if (!*p_data_dns)
+						*p_data_dns = g_ptr_array_new_with_free_func (g_free);
+
+					inet_ntop (addr_family, &addr_bin, addr_s, sizeof (addr_s));
+					g_ptr_array_add (*p_data_dns, g_strdup (addr_s));
+				}
+				continue;
+			}
+
+			if (line_match (line, "Table", &matched_key, &value)) {
+
+				if (nm_streq (value, "auto"))
+					data_table = _TABLE_AUTO;
+				else if (nm_streq (value, "off"))
+					data_table = _TABLE_OFF;
+				else {
+					/* we don't support table names from /etc/iproute2/rt_tables
+					 * But we accept hex like `ip route add` would. */
+					i64 = _nm_utils_ascii_str_to_int64 (value, 0, 0, G_MAXINT32, -1);
+					if (i64 == -1)
+						goto fail_invalid_value;
+					data_table = i64;
+				}
+				continue;
+			}
+
+			if (   line_match (line, "PreUp", &matched_key, &value)
+			    || line_match (line, "PreDown", &matched_key, &value)
+			    || line_match (line, "PostUp", &matched_key, &value)
+			    || line_match (line, "PostDown", &matched_key, &value)) {
+				/* we don't run any scripts. Silently ignore these parameters. */
+				continue;
+			}
+
+			if (line_match (line, "SaveConfig", &matched_key, &value)) {
+				/* we ignore the setting, but enforce that it's either true or false (like
+				 * wg-quick. */
+				if (!NM_IN_STRSET (value, "true", "false"))
+					goto fail_invalid_value;
+				continue;
+			}
+
+			if (line_match (line, "ListenPort", &matched_key, &value)) {
+				/* we don't use getaddrinfo(), unlike `wg setconf`. Just interpret
+				 * the port as plain decimal number. */
+				i64 = _nm_utils_ascii_str_to_int64 (value, 10, 0, 0xFFFF, -1);
+				if (i64 == -1)
+					goto fail_invalid_value;
+				data_listen_port = i64;
+				continue;
+			}
+
+			if (line_match (line, "FwMark", &matched_key, &value)) {
+				if (nm_streq (value, "off"))
+					data_fwmark = 0;
+				else {
+					i64 = _nm_utils_ascii_str_to_int64 (value, 0, 0, G_MAXINT32, -1);
+					if (i64 == -1)
+						goto fail_invalid_value;
+					data_fwmark = i64;
+				}
+				continue;
+			}
+
+			if (line_match (line, "PrivateKey", &matched_key, &value)) {
+				if (!nm_utils_base64secret_decode (value, NM_WIREGUARD_PUBLIC_KEY_LEN, NULL))
+					goto fail_invalid_secret;
+				data_private_key = value;
+				continue;
+			}
+
+			goto fail_invalid_line;
+		}
+
+
+		if (line_context == LINE_CONTEXT_PEER) {
+
+			if (line_match (line, "Endpoint", &matched_key, &value)) {
+				if (!nm_wireguard_peer_set_endpoint (current_peer, value, FALSE))
+					goto fail_invalid_value;
+				continue;
+			}
+
+			if (line_match (line, "PublicKey", &matched_key, &value)) {
+				if (!nm_wireguard_peer_set_public_key (current_peer, value, FALSE))
+					goto fail_invalid_value;
+				continue;
+			}
+
+			if (line_match (line, "AllowedIPs", &matched_key, &value)) {
+				char *value_word;
+
+				while (value_split_word (&value, &value_word)) {
+					if (!nm_wireguard_peer_append_allowed_ip (current_peer,
+					                                          value_word,
+					                                          FALSE))
+						goto fail_invalid_value;
+				}
+				continue;
+			}
+
+			if (line_match (line, "PersistentKeepalive", &matched_key, &value)) {
+				if (nm_streq (value, "off"))
+					i64 = 0;
+				else {
+					i64 = _nm_utils_ascii_str_to_int64 (value, 10, 0, G_MAXUINT16, -1);
+					if (i64 == -1)
+						goto fail_invalid_value;
+				}
+				nm_wireguard_peer_set_persistent_keepalive (current_peer, i64);
+				continue;
+			}
+
+			if (line_match (line, "PresharedKey", &matched_key, &value)) {
+				if (!nm_wireguard_peer_set_preshared_key (current_peer, value, FALSE))
+					goto fail_invalid_secret;
+				nm_wireguard_peer_set_preshared_key_flags (current_peer, NM_SETTING_SECRET_FLAG_NONE);
+				continue;
+			}
+
+			goto fail_invalid_line;
+		}
+
+fail_invalid_line:
+		nm_utils_error_set (error, NM_UTILS_ERROR_INVALID_ARGUMENT,
+		                    _("unrecognized line at %s:%zu"),
+		                    filename, line_nr);
+		return FALSE;
+fail_invalid_value:
+		nm_utils_error_set (error, NM_UTILS_ERROR_INVALID_ARGUMENT,
+		                    _("invalid value for '%s' at %s:%zu"),
+		                    matched_key, filename, line_nr);
+		return FALSE;
+fail_invalid_secret:
+		nm_utils_error_set (error, NM_UTILS_ERROR_INVALID_ARGUMENT,
+		                    _("invalid secret '%s' at %s:%zu"),
+		                    matched_key, filename, line_nr);
+		return FALSE;
+	}
+
+	if (!_wg_complete_peer (&data_peers,
+	                        g_steal_pointer (&current_peer),
+	                        current_peer_start_line_nr,
+	                        filename,
+	                        error))
+		return FALSE;
+
+	connection = nm_simple_connection_new ();
+	s_con = NM_SETTING_CONNECTION (nm_setting_connection_new ());
+	nm_connection_add_setting (connection, NM_SETTING (s_con));
+	s_ip4 = NM_SETTING_IP_CONFIG (nm_setting_ip4_config_new ());
+	nm_connection_add_setting (connection, NM_SETTING (s_ip4));
+	s_ip6 = NM_SETTING_IP_CONFIG (nm_setting_ip6_config_new ());
+	nm_connection_add_setting (connection, NM_SETTING (s_ip6));
+	s_wg = NM_SETTING_WIREGUARD (nm_setting_wireguard_new ());
+	nm_connection_add_setting (connection, NM_SETTING (s_wg));
+
+	uuid = nm_utils_uuid_generate ();
+
+	g_object_set (s_con,
+	              NM_SETTING_CONNECTION_ID,
+	              ifname,
+	              NM_SETTING_CONNECTION_UUID,
+	              uuid,
+	              NM_SETTING_CONNECTION_TYPE,
+	              NM_SETTING_WIREGUARD_SETTING_NAME,
+	              NM_SETTING_CONNECTION_INTERFACE_NAME,
+	              ifname,
+	              NULL);
+
+	g_object_set (s_wg,
+	              NM_SETTING_WIREGUARD_PRIVATE_KEY,
+	              data_private_key,
+	              NM_SETTING_WIREGUARD_LISTEN_PORT,
+	              data_listen_port,
+	              NM_SETTING_WIREGUARD_FWMARK,
+	              data_fwmark,
+	              NM_SETTING_WIREGUARD_MTU,
+	              data_mtu,
+	              NULL);
+
+	if (data_peers) {
+		for (i = 0; i < data_peers->len; i++)
+			nm_setting_wireguard_append_peer (s_wg, data_peers->pdata[i]);
+	}
+
+	for (is_v4 = 0; is_v4 < 2; is_v4++) {
+		const char *method_disabled = is_v4 ? NM_SETTING_IP4_CONFIG_METHOD_DISABLED : NM_SETTING_IP6_CONFIG_METHOD_IGNORE;
+		const char *method_manual   = is_v4 ? NM_SETTING_IP4_CONFIG_METHOD_MANUAL   : NM_SETTING_IP6_CONFIG_METHOD_MANUAL;
+		NMSettingIPConfig *s_ip     = is_v4 ? s_ip4                                 : s_ip6;
+		GPtrArray *data_dns         = is_v4 ? data_dns_v4                           : data_dns_v6;
+		GPtrArray *data_addr        = is_v4 ? data_addr_v4                          : data_addr_v6;
+
+		if (data_dns && !data_addr) {
+			/* When specifying "DNS", we also require an "Address" for the same address
+			 * family. That is because a NMSettingIPConfig cannot have @method_disabled
+			 * and DNS settings at the same time.
+			 *
+			 * We don't have addresses. Silently ignore the DNS setting. */
+			data_dns = NULL;
+		}
+
+		g_object_set (s_ip,
+		              NM_SETTING_IP_CONFIG_METHOD,
+		              data_addr ? method_manual : method_disabled,
+		              NULL);
+
+		if (data_addr) {
+			for (i = 0; i < data_addr->len; i++)
+				nm_setting_ip_config_add_address (s_ip, data_addr->pdata[i]);
+		}
+		if (data_dns) {
+			for (i = 0; i < data_dns->len; i++)
+				nm_setting_ip_config_add_dns (s_ip, data_dns->pdata[i]);
+		}
+
+		if (data_table == _TABLE_AUTO) {
+			/* in the "auto" setting, wg-quick adds peer-routes automatically to the main
+			 * table. NetworkManager will do that too, but there are differences:
+			 *
+			 * - NetworkManager (contrary to wg-quick) does not check whether the peer-route is necessary.
+			 *   It will always add a route for each allowed-ips range, even if there is already another
+			 *   route that would ensure packets to the endpoint are routed via the WireGuard interface.
+			 *   If you don't want that, disable "wireguard.peer-routes", and add the necessary routes
+			 *   yourself to "ipv4.routes" and "ipv6.routes".
+			 *
+			 * - With "auto", wg-quick also configures policy routing to handle default-routes (/0) to
+			 *   avoid routing loops. That is not yet solved by NetworkManager, you need to configure
+			 *   that explicitly (for example, by adding a direct route to the gateway on the interface
+			 *   that has the default-route, or by using a script (possibly dispatcher script).
+			 */
+		} else if (data_table == _TABLE_OFF) {
+			if (is_v4) {
+				g_object_set (s_wg,
+				              NM_SETTING_WIREGUARD_PEER_ROUTES,
+				              FALSE,
+				              NULL);
+			}
+		} else {
+			g_object_set (s_ip,
+			              NM_SETTING_IP_CONFIG_ROUTE_TABLE,
+			              (guint) data_table,
+			              NULL);
+		}
+	}
+
+	if (!nm_connection_normalize (connection, NULL, NULL, &local)) {
+		nm_utils_error_set (error, NM_UTILS_ERROR_INVALID_ARGUMENT,
+		                    _("Failed to create WireGuard connection: %s"),
+		                    local->message);
+		return FALSE;
+	}
+
+	return g_steal_pointer (&connection);
+}
diff --git a/clients/common/nm-vpn-helpers.h b/clients/common/nm-vpn-helpers.h
index 4c15faa1..686687ba 100644
--- a/clients/common/nm-vpn-helpers.h
+++ b/clients/common/nm-vpn-helpers.h
@@ -39,4 +39,7 @@ gboolean nm_vpn_openconnect_authenticate_helper (const char *host,
                                                  int *status,
                                                  GError **error);
 
+NMConnection *nm_vpn_wireguard_import (const char *filename,
+                                       GError **error);
+
 #endif  /* __NM_VPN_HELPERS_H__ */
diff --git a/clients/common/settings-docs.h b/clients/common/settings-docs.h
index bc56edae..7b958faa 100644
--- a/clients/common/settings-docs.h
+++ b/clients/common/settings-docs.h
@@ -24,14 +24,14 @@
 #define DESCRIBE_DOC_NM_SETTING_WIRELESS_SECURITY_AUTH_ALG N_("When WEP is used (ie, key-mgmt = \"none\" or \"ieee8021x\") indicate the 802.11 authentication algorithm required by the AP here.  One of \"open\" for Open System, \"shared\" for Shared Key, or \"leap\" for Cisco LEAP.  When using Cisco LEAP (ie, key-mgmt = \"ieee8021x\" and auth-alg = \"leap\") the \"leap-username\" and \"leap-password\" properties must be specified.")
 #define DESCRIBE_DOC_NM_SETTING_WIRELESS_SECURITY_FILS N_("Indicates whether Fast Initial Link Setup (802.11ai) must be enabled for the connection.  One of NM_SETTING_WIRELESS_SECURITY_FILS_DEFAULT (0) (use global default value), NM_SETTING_WIRELESS_SECURITY_FILS_DISABLE (1) (disable FILS), NM_SETTING_WIRELESS_SECURITY_FILS_OPTIONAL (2) (enable FILS if the supplicant and the access point support it) or NM_SETTING_WIRELESS_SECURITY_FILS_REQUIRED (3) (enable FILS and fail if not supported).  When set to NM_SETTING_WIRELESS_SECURITY_FILS_DEFAULT (0) and no global default is set, FILS will be optionally enabled.")
 #define DESCRIBE_DOC_NM_SETTING_WIRELESS_SECURITY_GROUP N_("A list of group/broadcast encryption algorithms which prevents connections to Wi-Fi networks that do not utilize one of the algorithms in the list.  For maximum compatibility leave this property empty.  Each list element may be one of \"wep40\", \"wep104\", \"tkip\", or \"ccmp\".")
-#define DESCRIBE_DOC_NM_SETTING_WIRELESS_SECURITY_KEY_MGMT N_("Key management used for the connection.  One of \"none\" (WEP), \"ieee8021x\" (Dynamic WEP), \"wpa-none\" (Ad-Hoc WPA-PSK), \"wpa-psk\" (infrastructure WPA-PSK), or \"wpa-eap\" (WPA-Enterprise).  This property must be set for any Wi-Fi connection that uses security.")
+#define DESCRIBE_DOC_NM_SETTING_WIRELESS_SECURITY_KEY_MGMT N_("Key management used for the connection.  One of \"none\" (WEP), \"ieee8021x\" (Dynamic WEP), \"wpa-none\" (Ad-Hoc WPA-PSK), \"wpa-psk\" (infrastructure WPA-PSK), \"sae\" (SAE) or \"wpa-eap\" (WPA-Enterprise). This property must be set for any Wi-Fi connection that uses security.")
 #define DESCRIBE_DOC_NM_SETTING_WIRELESS_SECURITY_LEAP_PASSWORD N_("The login password for legacy LEAP connections (ie, key-mgmt = \"ieee8021x\" and auth-alg = \"leap\").")
 #define DESCRIBE_DOC_NM_SETTING_WIRELESS_SECURITY_LEAP_PASSWORD_FLAGS N_("Flags indicating how to handle the \"leap-password\" property.")
 #define DESCRIBE_DOC_NM_SETTING_WIRELESS_SECURITY_LEAP_USERNAME N_("The login username for legacy LEAP connections (ie, key-mgmt = \"ieee8021x\" and auth-alg = \"leap\").")
 #define DESCRIBE_DOC_NM_SETTING_WIRELESS_SECURITY_PAIRWISE N_("A list of pairwise encryption algorithms which prevents connections to Wi-Fi networks that do not utilize one of the algorithms in the list. For maximum compatibility leave this property empty.  Each list element may be one of \"tkip\" or \"ccmp\".")
 #define DESCRIBE_DOC_NM_SETTING_WIRELESS_SECURITY_PMF N_("Indicates whether Protected Management Frames (802.11w) must be enabled for the connection.  One of NM_SETTING_WIRELESS_SECURITY_PMF_DEFAULT (0) (use global default value), NM_SETTING_WIRELESS_SECURITY_PMF_DISABLE (1) (disable PMF), NM_SETTING_WIRELESS_SECURITY_PMF_OPTIONAL (2) (enable PMF if the supplicant and the access point support it) or NM_SETTING_WIRELESS_SECURITY_PMF_REQUIRED (3) (enable PMF and fail if not supported).  When set to NM_SETTING_WIRELESS_SECURITY_PMF_DEFAULT (0) and no global default is set, PMF will be optionally enabled.")
 #define DESCRIBE_DOC_NM_SETTING_WIRELESS_SECURITY_PROTO N_("List of strings specifying the allowed WPA protocol versions to use. Each element may be one \"wpa\" (allow WPA) or \"rsn\" (allow WPA2/RSN).  If not specified, both WPA and RSN connections are allowed.")
-#define DESCRIBE_DOC_NM_SETTING_WIRELESS_SECURITY_PSK N_("Pre-Shared-Key for WPA networks.  If the key is 64-characters long, it must contain only hexadecimal characters and is interpreted as a hexadecimal WPA key.  Otherwise, the key must be between 8 and 63 ASCII characters (as specified in the 802.11i standard) and is interpreted as a WPA passphrase, and is hashed to derive the actual WPA-PSK used when connecting to the Wi-Fi network.")
+#define DESCRIBE_DOC_NM_SETTING_WIRELESS_SECURITY_PSK N_("Pre-Shared-Key for WPA networks. For WPA-PSK, it's either an ASCII passphrase of 8 to 63 characters that is (as specified in the 802.11i standard) hashed to derive the actual key, or the key in form of 64 hexadecimal character. The WPA3-Personal networks use a passphrase of any length for SAE authentication.")
 #define DESCRIBE_DOC_NM_SETTING_WIRELESS_SECURITY_PSK_FLAGS N_("Flags indicating how to handle the \"psk\" property.")
 #define DESCRIBE_DOC_NM_SETTING_WIRELESS_SECURITY_WEP_KEY_FLAGS N_("Flags indicating how to handle the \"wep-key0\", \"wep-key1\", \"wep-key2\", and \"wep-key3\" properties.")
 #define DESCRIBE_DOC_NM_SETTING_WIRELESS_SECURITY_WEP_KEY_TYPE N_("Controls the interpretation of WEP keys.  Allowed values are NM_WEP_KEY_TYPE_KEY (1), in which case the key is either a 10- or 26-character hexadecimal string, or a 5- or 13-character ASCII password; or NM_WEP_KEY_TYPE_PASSPHRASE (2), in which case the passphrase is provided as a string and will be hashed using the de-facto MD5 method to derive the actual WEP key.")
@@ -144,7 +144,7 @@
 #define DESCRIBE_DOC_NM_SETTING_CONNECTION_READ_ONLY N_("FALSE if the connection can be modified using the provided settings service's D-Bus interface with the right privileges, or TRUE if the connection is read-only and cannot be modified.")
 #define DESCRIBE_DOC_NM_SETTING_CONNECTION_SECONDARIES N_("List of connection UUIDs that should be activated when the base connection itself is activated. Currently only VPN connections are supported.")
 #define DESCRIBE_DOC_NM_SETTING_CONNECTION_SLAVE_TYPE N_("Setting name of the device type of this slave's master connection (eg, \"bond\"), or NULL if this connection is not a slave.")
-#define DESCRIBE_DOC_NM_SETTING_CONNECTION_STABLE_ID N_("This represents the identity of the connection used for various purposes. It allows to configure multiple profiles to share the identity. Also, the stable-id can contain placeholders that are substituted dynamically and deterministically depending on the context. The stable-id is used for generating IPv6 stable private addresses with ipv6.addr-gen-mode=stable-privacy. It is also used to seed the generated cloned MAC address for ethernet.cloned-mac-address=stable and wifi.cloned-mac-address=stable. It is also used as DHCP client identifier with ipv4.dhcp-client-id=stable and to derive the DHCP DUID with ipv6.dhcp-duid=stable-[llt,ll,uuid]. Note that depending on the context where it is used, other parameters are also seeded into the generation algorithm. For example, a per-host key is commonly also included, so that different systems end up generating different IDs. Or with ipv6.addr-gen-mode=stable-privacy, also the device's name is included, so that different interfaces yield different addresses. The '$' character is treated special to perform dynamic substitutions at runtime. Currently supported are \"${CONNECTION}\", \"${DEVICE}\", \"${MAC}\", \"${BOOT}\", \"${RANDOM}\". These effectively create unique IDs per-connection, per-device, per-boot, or every time. Note that \"${DEVICE}\" corresponds the the interface name of the device and \"${MAC}\" is the permanent MAC address of the device. Any unrecognized patterns following '$' are treated verbatim, however are reserved for future use. You are thus advised to avoid '$' or escape it as \"$$\". For example, set it to \"${CONNECTION}-${BOOT}-${DEVICE}\" to create a unique id for this connection that changes with every reboot and differs depending on the interface where the profile activates. If the value is unset, a global connection default is consulted. If the value is still unset, the default is similar to \"${CONNECTION}\" and uses a unique, fixed ID for the connection.")
+#define DESCRIBE_DOC_NM_SETTING_CONNECTION_STABLE_ID N_("This represents the identity of the connection used for various purposes. It allows to configure multiple profiles to share the identity. Also, the stable-id can contain placeholders that are substituted dynamically and deterministically depending on the context. The stable-id is used for generating IPv6 stable private addresses with ipv6.addr-gen-mode=stable-privacy. It is also used to seed the generated cloned MAC address for ethernet.cloned-mac-address=stable and wifi.cloned-mac-address=stable. It is also used as DHCP client identifier with ipv4.dhcp-client-id=stable and to derive the DHCP DUID with ipv6.dhcp-duid=stable-[llt,ll,uuid]. Note that depending on the context where it is used, other parameters are also seeded into the generation algorithm. For example, a per-host key is commonly also included, so that different systems end up generating different IDs. Or with ipv6.addr-gen-mode=stable-privacy, also the device's name is included, so that different interfaces yield different addresses. The '$' character is treated special to perform dynamic substitutions at runtime. Currently supported are \"${CONNECTION}\", \"${DEVICE}\", \"${MAC}\", \"${BOOT}\", \"${RANDOM}\". These effectively create unique IDs per-connection, per-device, per-boot, or every time. Note that \"${DEVICE}\" corresponds to the interface name of the device and \"${MAC}\" is the permanent MAC address of the device. Any unrecognized patterns following '$' are treated verbatim, however are reserved for future use. You are thus advised to avoid '$' or escape it as \"$$\". For example, set it to \"${CONNECTION}-${BOOT}-${DEVICE}\" to create a unique id for this connection that changes with every reboot and differs depending on the interface where the profile activates. If the value is unset, a global connection default is consulted. If the value is still unset, the default is similar to \"${CONNECTION}\" and uses a unique, fixed ID for the connection.")
 #define DESCRIBE_DOC_NM_SETTING_CONNECTION_TIMESTAMP N_("The time, in seconds since the Unix Epoch, that the connection was last _successfully_ fully activated. NetworkManager updates the connection timestamp periodically when the connection is active to ensure that an active connection has the latest timestamp. The property is only meant for reading (changes to this property will not be preserved).")
 #define DESCRIBE_DOC_NM_SETTING_CONNECTION_TYPE N_("Base type of the connection. For hardware-dependent connections, should contain the setting name of the hardware-type specific setting (ie, \"802-3-ethernet\" or \"802-11-wireless\" or \"bluetooth\", etc), and for non-hardware dependent connections like VPN or otherwise, should contain the setting name of that setting type (ie, \"vpn\" or \"bridge\", etc).")
 #define DESCRIBE_DOC_NM_SETTING_CONNECTION_UUID N_("A universally unique identifier for the connection, for example generated with libuuid.  It should be assigned when the connection is created, and never changed as long as the connection still applies to the same network.  For example, it should not be changed when the \"id\" property or NMSettingIP4Config changes, but might need to be re-created when the Wi-Fi SSID, mobile broadband network provider, or \"type\" property changes. The UUID must be in the format \"2815492f-7e56-435e-b2e9-246bd7cdc664\" (ie, contains only hexadecimal characters and \"-\").")
@@ -169,7 +169,7 @@
 #define DESCRIBE_DOC_NM_SETTING_GSM_HOME_ONLY N_("When TRUE, only connections to the home network will be allowed. Connections to roaming networks will not be made.")
 #define DESCRIBE_DOC_NM_SETTING_GSM_MTU N_("If non-zero, only transmit packets of the specified size or smaller, breaking larger packets up into multiple frames.")
 #define DESCRIBE_DOC_NM_SETTING_GSM_NETWORK_ID N_("The Network ID (GSM LAI format, ie MCC-MNC) to force specific network registration.  If the Network ID is specified, NetworkManager will attempt to force the device to register only on the specified network. This can be used to ensure that the device does not roam when direct roaming control of the device is not otherwise possible.")
-#define DESCRIBE_DOC_NM_SETTING_GSM_NUMBER N_("Number to dial when establishing a PPP data session with the GSM-based mobile broadband network.  Many modems do not require PPP for connections to the mobile network and thus this property should be left blank, which allows NetworkManager to select the appropriate settings automatically.")
+#define DESCRIBE_DOC_NM_SETTING_GSM_NUMBER N_("Legacy setting that used to help establishing PPP data sessions for GSM-based modems. Deprecated: 1")
 #define DESCRIBE_DOC_NM_SETTING_GSM_PASSWORD N_("The password used to authenticate with the network, if required.  Many providers do not require a password, or accept any password.  But if a password is required, it is specified here.")
 #define DESCRIBE_DOC_NM_SETTING_GSM_PASSWORD_FLAGS N_("Flags indicating how to handle the \"password\" property.")
 #define DESCRIBE_DOC_NM_SETTING_GSM_PIN N_("If the SIM is locked with a PIN it must be unlocked before any other operations are requested.  Specify the PIN here to allow operation of the device.")
@@ -204,7 +204,7 @@
 #define DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_DHCP_TIMEOUT N_("A timeout for a DHCP transaction in seconds.")
 #define DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_DNS N_("Array of IP addresses of DNS servers.")
 #define DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_DNS_OPTIONS N_("Array of DNS options as described in man 5 resolv.conf. NULL means that the options are unset and left at the default. In this case NetworkManager will use default options. This is distinct from an empty list of properties.")
-#define DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_DNS_PRIORITY N_("DNS servers priority. The relative priority for DNS servers specified by this setting.  A lower value is better (higher priority).  Zero selects the default value, which is 50 for VPNs and 100 for other connections. Note that the priority is to order DNS settings for multiple active connections.  It does not disambiguate multiple DNS servers within the same connection profile. When using dns=default, servers with higher priority will be on top of resolv.conf.  To prioritize a given server over another one within the same connection, just specify them in the desired order.  When multiple devices have configurations with the same priority, the one with an active default route will be preferred.  Negative values have the special effect of excluding other configurations with a greater priority value; so in presence of at least a negative priority, only DNS servers from connections with the lowest priority value will be used. When using a DNS resolver that supports split-DNS as dns=dnsmasq or dns=systemd-resolved, each connection is used to query domains in its search list.  Queries for domains not present in any search list are routed through connections having the '~.' special wildcard domain, which is added automatically to connections with the default route (or can be added manually).  When multiple connections specify the same domain, the one with the highest priority (lowest numerical value) wins.  If a connection specifies a domain which is subdomain of another domain with a negative DNS priority value, the subdomain is ignored.")
+#define DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_DNS_PRIORITY N_("DNS servers priority. The relative priority for DNS servers specified by this setting.  A lower value is better (higher priority). Zero selects a globally configured default value. If the latter is missing or zero too, it defaults to 50 for VPNs and 100 for other connections. Note that the priority is to order DNS settings for multiple active connections.  It does not disambiguate multiple DNS servers within the same connection profile. When using dns=default, servers with higher priority will be on top of resolv.conf.  To prioritize a given server over another one within the same connection, just specify them in the desired order.  When multiple devices have configurations with the same priority, the one with an active default route will be preferred.  Negative values have the special effect of excluding other configurations with a greater priority value; so in presence of at least a negative priority, only DNS servers from connections with the lowest priority value will be used. When using a DNS resolver that supports split-DNS as dns=dnsmasq or dns=systemd-resolved, each connection is used to query domains in its search list.  Queries for domains not present in any search list are routed through connections having the '~.' special wildcard domain, which is added automatically to connections with the default route (or can be added manually).  When multiple connections specify the same domain, the one with the highest priority (lowest numerical value) wins.  If a connection specifies a domain which is subdomain of another domain with a negative DNS priority value, the subdomain is ignored.")
 #define DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_DNS_SEARCH N_("Array of DNS search domains. Domains starting with a tilde ('~') are considered 'routing' domains and are used only to decide the interface over which a query must be forwarded; they are not used to complete unqualified host names.")
 #define DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_GATEWAY N_("The gateway associated with this configuration. This is only meaningful if \"addresses\" is also set.")
 #define DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_IGNORE_AUTO_DNS N_("When \"method\" is set to \"auto\" and this property to TRUE, automatically configured nameservers and search domains are ignored and only nameservers and search domains specified in the \"dns\" and \"dns-search\" properties, if any, are used.")
@@ -224,7 +224,7 @@
 #define DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_DHCP_TIMEOUT N_("A timeout for a DHCP transaction in seconds.")
 #define DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_DNS N_("Array of IP addresses of DNS servers.")
 #define DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_DNS_OPTIONS N_("Array of DNS options as described in man 5 resolv.conf. NULL means that the options are unset and left at the default. In this case NetworkManager will use default options. This is distinct from an empty list of properties.")
-#define DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_DNS_PRIORITY N_("DNS servers priority. The relative priority for DNS servers specified by this setting.  A lower value is better (higher priority).  Zero selects the default value, which is 50 for VPNs and 100 for other connections. Note that the priority is to order DNS settings for multiple active connections.  It does not disambiguate multiple DNS servers within the same connection profile. When using dns=default, servers with higher priority will be on top of resolv.conf.  To prioritize a given server over another one within the same connection, just specify them in the desired order.  When multiple devices have configurations with the same priority, the one with an active default route will be preferred.  Negative values have the special effect of excluding other configurations with a greater priority value; so in presence of at least a negative priority, only DNS servers from connections with the lowest priority value will be used. When using a DNS resolver that supports split-DNS as dns=dnsmasq or dns=systemd-resolved, each connection is used to query domains in its search list.  Queries for domains not present in any search list are routed through connections having the '~.' special wildcard domain, which is added automatically to connections with the default route (or can be added manually).  When multiple connections specify the same domain, the one with the highest priority (lowest numerical value) wins.  If a connection specifies a domain which is subdomain of another domain with a negative DNS priority value, the subdomain is ignored.")
+#define DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_DNS_PRIORITY N_("DNS servers priority. The relative priority for DNS servers specified by this setting.  A lower value is better (higher priority). Zero selects a globally configured default value. If the latter is missing or zero too, it defaults to 50 for VPNs and 100 for other connections. Note that the priority is to order DNS settings for multiple active connections.  It does not disambiguate multiple DNS servers within the same connection profile. When using dns=default, servers with higher priority will be on top of resolv.conf.  To prioritize a given server over another one within the same connection, just specify them in the desired order.  When multiple devices have configurations with the same priority, the one with an active default route will be preferred.  Negative values have the special effect of excluding other configurations with a greater priority value; so in presence of at least a negative priority, only DNS servers from connections with the lowest priority value will be used. When using a DNS resolver that supports split-DNS as dns=dnsmasq or dns=systemd-resolved, each connection is used to query domains in its search list.  Queries for domains not present in any search list are routed through connections having the '~.' special wildcard domain, which is added automatically to connections with the default route (or can be added manually).  When multiple connections specify the same domain, the one with the highest priority (lowest numerical value) wins.  If a connection specifies a domain which is subdomain of another domain with a negative DNS priority value, the subdomain is ignored.")
 #define DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_DNS_SEARCH N_("Array of DNS search domains. Domains starting with a tilde ('~') are considered 'routing' domains and are used only to decide the interface over which a query must be forwarded; they are not used to complete unqualified host names.")
 #define DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_GATEWAY N_("The gateway associated with this configuration. This is only meaningful if \"addresses\" is also set.")
 #define DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_IGNORE_AUTO_DNS N_("When \"method\" is set to \"auto\" and this property to TRUE, automatically configured nameservers and search domains are ignored and only nameservers and search domains specified in the \"dns\" and \"dns-search\" properties, if any, are used.")
@@ -301,7 +301,7 @@
 #define DESCRIBE_DOC_NM_SETTING_TC_CONFIG_QDISCS N_("Array of TC queueing disciplines.")
 #define DESCRIBE_DOC_NM_SETTING_TC_CONFIG_TFILTERS N_("Array of TC traffic filters.")
 #define DESCRIBE_DOC_NM_SETTING_TEAM_CONFIG N_("The JSON configuration for the team network interface.  The property should contain raw JSON configuration data suitable for teamd, because the value is passed directly to teamd. If not specified, the default configuration is used.  See man teamd.conf for the format details.")
-#define DESCRIBE_DOC_NM_SETTING_TEAM_LINK_WATCHERS N_("Link watchers configuration for the connection: each link watcher is defined by a dictionary, whose keys depend upon the selected link watcher. Available link watchers are 'ethtool', 'nsna_ping' and 'arp_ping' and it is specified in the dictionary with the key 'name'. Available keys are:   ethtool: 'delay-up', 'delay-down', 'init-wait'; nsna_ping: 'init-wait', 'interval', 'missed-max', 'target-host'; arp_ping: all the ones in nsna_ping and 'source-host', 'validate-active', 'validate-incative', 'send-always'. See teamd.conf man for more details.")
+#define DESCRIBE_DOC_NM_SETTING_TEAM_LINK_WATCHERS N_("Link watchers configuration for the connection: each link watcher is defined by a dictionary, whose keys depend upon the selected link watcher. Available link watchers are 'ethtool', 'nsna_ping' and 'arp_ping' and it is specified in the dictionary with the key 'name'. Available keys are:   ethtool: 'delay-up', 'delay-down', 'init-wait'; nsna_ping: 'init-wait', 'interval', 'missed-max', 'target-host'; arp_ping: all the ones in nsna_ping and 'source-host', 'validate-active', 'validate-inactive', 'send-always'. See teamd.conf man for more details.")
 #define DESCRIBE_DOC_NM_SETTING_TEAM_MCAST_REJOIN_COUNT N_("Corresponds to the teamd mcast_rejoin.count.")
 #define DESCRIBE_DOC_NM_SETTING_TEAM_MCAST_REJOIN_INTERVAL N_("Corresponds to the teamd mcast_rejoin.interval.")
 #define DESCRIBE_DOC_NM_SETTING_TEAM_NOTIFY_PEERS_COUNT N_("Corresponds to the teamd notify_peers.count.")
@@ -319,7 +319,7 @@
 #define DESCRIBE_DOC_NM_SETTING_TEAM_PORT_CONFIG N_("The JSON configuration for the team port. The property should contain raw JSON configuration data suitable for teamd, because the value is passed directly to teamd. If not specified, the default configuration is used. See man teamd.conf for the format details.")
 #define DESCRIBE_DOC_NM_SETTING_TEAM_PORT_LACP_KEY N_("Corresponds to the teamd ports.PORTIFNAME.lacp_key.")
 #define DESCRIBE_DOC_NM_SETTING_TEAM_PORT_LACP_PRIO N_("Corresponds to the teamd ports.PORTIFNAME.lacp_prio.")
-#define DESCRIBE_DOC_NM_SETTING_TEAM_PORT_LINK_WATCHERS N_("Link watchers configuration for the connection: each link watcher is defined by a dictionary, whose keys depend upon the selected link watcher. Available link watchers are 'ethtool', 'nsna_ping' and 'arp_ping' and it is specified in the dictionary with the key 'name'. Available keys are:   ethtool: 'delay-up', 'delay-down', 'init-wait'; nsna_ping: 'init-wait', 'interval', 'missed-max', 'target-host'; arp_ping: all the ones in nsna_ping and 'source-host', 'validate-active', 'validate-incative', 'send-always'. See teamd.conf man for more details.")
+#define DESCRIBE_DOC_NM_SETTING_TEAM_PORT_LINK_WATCHERS N_("Link watchers configuration for the connection: each link watcher is defined by a dictionary, whose keys depend upon the selected link watcher. Available link watchers are 'ethtool', 'nsna_ping' and 'arp_ping' and it is specified in the dictionary with the key 'name'. Available keys are:   ethtool: 'delay-up', 'delay-down', 'init-wait'; nsna_ping: 'init-wait', 'interval', 'missed-max', 'target-host'; arp_ping: all the ones in nsna_ping and 'source-host', 'validate-active', 'validate-inactive', 'send-always'. See teamd.conf man for more details.")
 #define DESCRIBE_DOC_NM_SETTING_TEAM_PORT_PRIO N_("Corresponds to the teamd ports.PORTIFNAME.prio.")
 #define DESCRIBE_DOC_NM_SETTING_TEAM_PORT_QUEUE_ID N_("Corresponds to the teamd ports.PORTIFNAME.queue_id. When set to -1 means the parameter is skipped from the json config.")
 #define DESCRIBE_DOC_NM_SETTING_TEAM_PORT_STICKY N_("Corresponds to the teamd ports.PORTIFNAME.sticky.")
@@ -357,8 +357,19 @@
 #define DESCRIBE_DOC_NM_SETTING_VXLAN_SOURCE_PORT_MIN N_("Specifies the minimum UDP source port to communicate to the remote VXLAN tunnel endpoint.")
 #define DESCRIBE_DOC_NM_SETTING_VXLAN_TOS N_("Specifies the TOS value to use in outgoing packets.")
 #define DESCRIBE_DOC_NM_SETTING_VXLAN_TTL N_("Specifies the time-to-live value to use in outgoing packets.")
+#define DESCRIBE_DOC_NM_SETTING_WIFI_P2P_PEER N_("The P2P device that should be connected to. Currently this is the only way to create or join a group.")
+#define DESCRIBE_DOC_NM_SETTING_WIFI_P2P_WFD_IES N_("The Wi-Fi Display (WFD) Information Elements (IEs) to set. Wi-Fi Display requires a protocol specific information element to be set in certain Wi-Fi frames. These can be specified here for the purpose of establishing a connection. This setting is only useful when implementing a Wi-Fi Display client.")
+#define DESCRIBE_DOC_NM_SETTING_WIFI_P2P_WPS_METHOD N_("Flags indicating which mode of WPS is to be used. There's little point in changing the default setting as NetworkManager will automatically determine the best method to use.")
 #define DESCRIBE_DOC_NM_SETTING_WIMAX_MAC_ADDRESS N_("If specified, this connection will only apply to the WiMAX device whose MAC address matches. This property does not change the MAC address of the device (known as MAC spoofing). Deprecated: 1")
 #define DESCRIBE_DOC_NM_SETTING_WIMAX_NETWORK_NAME N_("Network Service Provider (NSP) name of the WiMAX network this connection should use. Deprecated: 1")
+#define DESCRIBE_DOC_NM_SETTING_WIREGUARD_FWMARK N_("The use of fwmark is optional and is by default off. Setting it to 0 disables it. Otherwise it is a 32-bit fwmark for outgoing packets.")
+#define DESCRIBE_DOC_NM_SETTING_WIREGUARD_LISTEN_PORT N_("The listen-port. If listen-port is not specified, the port will be chosen randomly when the interface comes up.")
+#define DESCRIBE_DOC_NM_SETTING_WIREGUARD_MTU N_("If non-zero, only transmit packets of the specified size or smaller, breaking larger packets up into multiple fragments. If zero a default MTU is used. Note that contrary to wg-quick's MTU setting, this does not take into account the current routes at the time of activation.")
+#define DESCRIBE_DOC_NM_SETTING_WIREGUARD_PEER_ROUTES N_("Whether to automatically add routes for the AllowedIPs ranges of the peers. If TRUE (the default), NetworkManager will automatically add routes in the routing tables according to ipv4.route-table and ipv6.route-table. If FALSE, no such routes are added automatically. In this case, the user may want to configure static routes in ipv4.routes and ipv6.routes, respectively.")
+#define DESCRIBE_DOC_NM_SETTING_WIREGUARD_PRIVATE_KEY N_("The 256 bit private-key in base64 encoding.")
+#define DESCRIBE_DOC_NM_SETTING_WIREGUARD_PRIVATE_KEY_FLAGS N_("Flags indicating how to handle the \"private-key\" property.")
+#define DESCRIBE_DOC_NM_SETTING_WPAN_CHANNEL N_("IEEE 802.15.4 channel. A positive integer or -1, meaning \"do not set, use whatever the device is already set to\".")
 #define DESCRIBE_DOC_NM_SETTING_WPAN_MAC_ADDRESS N_("If specified, this connection will only apply to the IEEE 802.15.4 (WPAN) MAC layer device whose permanent MAC address matches.")
+#define DESCRIBE_DOC_NM_SETTING_WPAN_PAGE N_("IEEE 802.15.4 channel page. A positive integer or -1, meaning \"do not set, use whatever the device is already set to\".")
 #define DESCRIBE_DOC_NM_SETTING_WPAN_PAN_ID N_("IEEE 802.15.4 Personal Area Network (PAN) identifier.")
 #define DESCRIBE_DOC_NM_SETTING_WPAN_SHORT_ADDRESS N_("Short IEEE 802.15.4 address to be used within a restricted environment.")
diff --git a/clients/common/settings-docs.h.in b/clients/common/settings-docs.h.in
index bc56edae..7b958faa 100644
--- a/clients/common/settings-docs.h.in
+++ b/clients/common/settings-docs.h.in
@@ -24,14 +24,14 @@
 #define DESCRIBE_DOC_NM_SETTING_WIRELESS_SECURITY_AUTH_ALG N_("When WEP is used (ie, key-mgmt = \"none\" or \"ieee8021x\") indicate the 802.11 authentication algorithm required by the AP here.  One of \"open\" for Open System, \"shared\" for Shared Key, or \"leap\" for Cisco LEAP.  When using Cisco LEAP (ie, key-mgmt = \"ieee8021x\" and auth-alg = \"leap\") the \"leap-username\" and \"leap-password\" properties must be specified.")
 #define DESCRIBE_DOC_NM_SETTING_WIRELESS_SECURITY_FILS N_("Indicates whether Fast Initial Link Setup (802.11ai) must be enabled for the connection.  One of NM_SETTING_WIRELESS_SECURITY_FILS_DEFAULT (0) (use global default value), NM_SETTING_WIRELESS_SECURITY_FILS_DISABLE (1) (disable FILS), NM_SETTING_WIRELESS_SECURITY_FILS_OPTIONAL (2) (enable FILS if the supplicant and the access point support it) or NM_SETTING_WIRELESS_SECURITY_FILS_REQUIRED (3) (enable FILS and fail if not supported).  When set to NM_SETTING_WIRELESS_SECURITY_FILS_DEFAULT (0) and no global default is set, FILS will be optionally enabled.")
 #define DESCRIBE_DOC_NM_SETTING_WIRELESS_SECURITY_GROUP N_("A list of group/broadcast encryption algorithms which prevents connections to Wi-Fi networks that do not utilize one of the algorithms in the list.  For maximum compatibility leave this property empty.  Each list element may be one of \"wep40\", \"wep104\", \"tkip\", or \"ccmp\".")
-#define DESCRIBE_DOC_NM_SETTING_WIRELESS_SECURITY_KEY_MGMT N_("Key management used for the connection.  One of \"none\" (WEP), \"ieee8021x\" (Dynamic WEP), \"wpa-none\" (Ad-Hoc WPA-PSK), \"wpa-psk\" (infrastructure WPA-PSK), or \"wpa-eap\" (WPA-Enterprise).  This property must be set for any Wi-Fi connection that uses security.")
+#define DESCRIBE_DOC_NM_SETTING_WIRELESS_SECURITY_KEY_MGMT N_("Key management used for the connection.  One of \"none\" (WEP), \"ieee8021x\" (Dynamic WEP), \"wpa-none\" (Ad-Hoc WPA-PSK), \"wpa-psk\" (infrastructure WPA-PSK), \"sae\" (SAE) or \"wpa-eap\" (WPA-Enterprise). This property must be set for any Wi-Fi connection that uses security.")
 #define DESCRIBE_DOC_NM_SETTING_WIRELESS_SECURITY_LEAP_PASSWORD N_("The login password for legacy LEAP connections (ie, key-mgmt = \"ieee8021x\" and auth-alg = \"leap\").")
 #define DESCRIBE_DOC_NM_SETTING_WIRELESS_SECURITY_LEAP_PASSWORD_FLAGS N_("Flags indicating how to handle the \"leap-password\" property.")
 #define DESCRIBE_DOC_NM_SETTING_WIRELESS_SECURITY_LEAP_USERNAME N_("The login username for legacy LEAP connections (ie, key-mgmt = \"ieee8021x\" and auth-alg = \"leap\").")
 #define DESCRIBE_DOC_NM_SETTING_WIRELESS_SECURITY_PAIRWISE N_("A list of pairwise encryption algorithms which prevents connections to Wi-Fi networks that do not utilize one of the algorithms in the list. For maximum compatibility leave this property empty.  Each list element may be one of \"tkip\" or \"ccmp\".")
 #define DESCRIBE_DOC_NM_SETTING_WIRELESS_SECURITY_PMF N_("Indicates whether Protected Management Frames (802.11w) must be enabled for the connection.  One of NM_SETTING_WIRELESS_SECURITY_PMF_DEFAULT (0) (use global default value), NM_SETTING_WIRELESS_SECURITY_PMF_DISABLE (1) (disable PMF), NM_SETTING_WIRELESS_SECURITY_PMF_OPTIONAL (2) (enable PMF if the supplicant and the access point support it) or NM_SETTING_WIRELESS_SECURITY_PMF_REQUIRED (3) (enable PMF and fail if not supported).  When set to NM_SETTING_WIRELESS_SECURITY_PMF_DEFAULT (0) and no global default is set, PMF will be optionally enabled.")
 #define DESCRIBE_DOC_NM_SETTING_WIRELESS_SECURITY_PROTO N_("List of strings specifying the allowed WPA protocol versions to use. Each element may be one \"wpa\" (allow WPA) or \"rsn\" (allow WPA2/RSN).  If not specified, both WPA and RSN connections are allowed.")
-#define DESCRIBE_DOC_NM_SETTING_WIRELESS_SECURITY_PSK N_("Pre-Shared-Key for WPA networks.  If the key is 64-characters long, it must contain only hexadecimal characters and is interpreted as a hexadecimal WPA key.  Otherwise, the key must be between 8 and 63 ASCII characters (as specified in the 802.11i standard) and is interpreted as a WPA passphrase, and is hashed to derive the actual WPA-PSK used when connecting to the Wi-Fi network.")
+#define DESCRIBE_DOC_NM_SETTING_WIRELESS_SECURITY_PSK N_("Pre-Shared-Key for WPA networks. For WPA-PSK, it's either an ASCII passphrase of 8 to 63 characters that is (as specified in the 802.11i standard) hashed to derive the actual key, or the key in form of 64 hexadecimal character. The WPA3-Personal networks use a passphrase of any length for SAE authentication.")
 #define DESCRIBE_DOC_NM_SETTING_WIRELESS_SECURITY_PSK_FLAGS N_("Flags indicating how to handle the \"psk\" property.")
 #define DESCRIBE_DOC_NM_SETTING_WIRELESS_SECURITY_WEP_KEY_FLAGS N_("Flags indicating how to handle the \"wep-key0\", \"wep-key1\", \"wep-key2\", and \"wep-key3\" properties.")
 #define DESCRIBE_DOC_NM_SETTING_WIRELESS_SECURITY_WEP_KEY_TYPE N_("Controls the interpretation of WEP keys.  Allowed values are NM_WEP_KEY_TYPE_KEY (1), in which case the key is either a 10- or 26-character hexadecimal string, or a 5- or 13-character ASCII password; or NM_WEP_KEY_TYPE_PASSPHRASE (2), in which case the passphrase is provided as a string and will be hashed using the de-facto MD5 method to derive the actual WEP key.")
@@ -144,7 +144,7 @@
 #define DESCRIBE_DOC_NM_SETTING_CONNECTION_READ_ONLY N_("FALSE if the connection can be modified using the provided settings service's D-Bus interface with the right privileges, or TRUE if the connection is read-only and cannot be modified.")
 #define DESCRIBE_DOC_NM_SETTING_CONNECTION_SECONDARIES N_("List of connection UUIDs that should be activated when the base connection itself is activated. Currently only VPN connections are supported.")
 #define DESCRIBE_DOC_NM_SETTING_CONNECTION_SLAVE_TYPE N_("Setting name of the device type of this slave's master connection (eg, \"bond\"), or NULL if this connection is not a slave.")
-#define DESCRIBE_DOC_NM_SETTING_CONNECTION_STABLE_ID N_("This represents the identity of the connection used for various purposes. It allows to configure multiple profiles to share the identity. Also, the stable-id can contain placeholders that are substituted dynamically and deterministically depending on the context. The stable-id is used for generating IPv6 stable private addresses with ipv6.addr-gen-mode=stable-privacy. It is also used to seed the generated cloned MAC address for ethernet.cloned-mac-address=stable and wifi.cloned-mac-address=stable. It is also used as DHCP client identifier with ipv4.dhcp-client-id=stable and to derive the DHCP DUID with ipv6.dhcp-duid=stable-[llt,ll,uuid]. Note that depending on the context where it is used, other parameters are also seeded into the generation algorithm. For example, a per-host key is commonly also included, so that different systems end up generating different IDs. Or with ipv6.addr-gen-mode=stable-privacy, also the device's name is included, so that different interfaces yield different addresses. The '$' character is treated special to perform dynamic substitutions at runtime. Currently supported are \"${CONNECTION}\", \"${DEVICE}\", \"${MAC}\", \"${BOOT}\", \"${RANDOM}\". These effectively create unique IDs per-connection, per-device, per-boot, or every time. Note that \"${DEVICE}\" corresponds the the interface name of the device and \"${MAC}\" is the permanent MAC address of the device. Any unrecognized patterns following '$' are treated verbatim, however are reserved for future use. You are thus advised to avoid '$' or escape it as \"$$\". For example, set it to \"${CONNECTION}-${BOOT}-${DEVICE}\" to create a unique id for this connection that changes with every reboot and differs depending on the interface where the profile activates. If the value is unset, a global connection default is consulted. If the value is still unset, the default is similar to \"${CONNECTION}\" and uses a unique, fixed ID for the connection.")
+#define DESCRIBE_DOC_NM_SETTING_CONNECTION_STABLE_ID N_("This represents the identity of the connection used for various purposes. It allows to configure multiple profiles to share the identity. Also, the stable-id can contain placeholders that are substituted dynamically and deterministically depending on the context. The stable-id is used for generating IPv6 stable private addresses with ipv6.addr-gen-mode=stable-privacy. It is also used to seed the generated cloned MAC address for ethernet.cloned-mac-address=stable and wifi.cloned-mac-address=stable. It is also used as DHCP client identifier with ipv4.dhcp-client-id=stable and to derive the DHCP DUID with ipv6.dhcp-duid=stable-[llt,ll,uuid]. Note that depending on the context where it is used, other parameters are also seeded into the generation algorithm. For example, a per-host key is commonly also included, so that different systems end up generating different IDs. Or with ipv6.addr-gen-mode=stable-privacy, also the device's name is included, so that different interfaces yield different addresses. The '$' character is treated special to perform dynamic substitutions at runtime. Currently supported are \"${CONNECTION}\", \"${DEVICE}\", \"${MAC}\", \"${BOOT}\", \"${RANDOM}\". These effectively create unique IDs per-connection, per-device, per-boot, or every time. Note that \"${DEVICE}\" corresponds to the interface name of the device and \"${MAC}\" is the permanent MAC address of the device. Any unrecognized patterns following '$' are treated verbatim, however are reserved for future use. You are thus advised to avoid '$' or escape it as \"$$\". For example, set it to \"${CONNECTION}-${BOOT}-${DEVICE}\" to create a unique id for this connection that changes with every reboot and differs depending on the interface where the profile activates. If the value is unset, a global connection default is consulted. If the value is still unset, the default is similar to \"${CONNECTION}\" and uses a unique, fixed ID for the connection.")
 #define DESCRIBE_DOC_NM_SETTING_CONNECTION_TIMESTAMP N_("The time, in seconds since the Unix Epoch, that the connection was last _successfully_ fully activated. NetworkManager updates the connection timestamp periodically when the connection is active to ensure that an active connection has the latest timestamp. The property is only meant for reading (changes to this property will not be preserved).")
 #define DESCRIBE_DOC_NM_SETTING_CONNECTION_TYPE N_("Base type of the connection. For hardware-dependent connections, should contain the setting name of the hardware-type specific setting (ie, \"802-3-ethernet\" or \"802-11-wireless\" or \"bluetooth\", etc), and for non-hardware dependent connections like VPN or otherwise, should contain the setting name of that setting type (ie, \"vpn\" or \"bridge\", etc).")
 #define DESCRIBE_DOC_NM_SETTING_CONNECTION_UUID N_("A universally unique identifier for the connection, for example generated with libuuid.  It should be assigned when the connection is created, and never changed as long as the connection still applies to the same network.  For example, it should not be changed when the \"id\" property or NMSettingIP4Config changes, but might need to be re-created when the Wi-Fi SSID, mobile broadband network provider, or \"type\" property changes. The UUID must be in the format \"2815492f-7e56-435e-b2e9-246bd7cdc664\" (ie, contains only hexadecimal characters and \"-\").")
@@ -169,7 +169,7 @@
 #define DESCRIBE_DOC_NM_SETTING_GSM_HOME_ONLY N_("When TRUE, only connections to the home network will be allowed. Connections to roaming networks will not be made.")
 #define DESCRIBE_DOC_NM_SETTING_GSM_MTU N_("If non-zero, only transmit packets of the specified size or smaller, breaking larger packets up into multiple frames.")
 #define DESCRIBE_DOC_NM_SETTING_GSM_NETWORK_ID N_("The Network ID (GSM LAI format, ie MCC-MNC) to force specific network registration.  If the Network ID is specified, NetworkManager will attempt to force the device to register only on the specified network. This can be used to ensure that the device does not roam when direct roaming control of the device is not otherwise possible.")
-#define DESCRIBE_DOC_NM_SETTING_GSM_NUMBER N_("Number to dial when establishing a PPP data session with the GSM-based mobile broadband network.  Many modems do not require PPP for connections to the mobile network and thus this property should be left blank, which allows NetworkManager to select the appropriate settings automatically.")
+#define DESCRIBE_DOC_NM_SETTING_GSM_NUMBER N_("Legacy setting that used to help establishing PPP data sessions for GSM-based modems. Deprecated: 1")
 #define DESCRIBE_DOC_NM_SETTING_GSM_PASSWORD N_("The password used to authenticate with the network, if required.  Many providers do not require a password, or accept any password.  But if a password is required, it is specified here.")
 #define DESCRIBE_DOC_NM_SETTING_GSM_PASSWORD_FLAGS N_("Flags indicating how to handle the \"password\" property.")
 #define DESCRIBE_DOC_NM_SETTING_GSM_PIN N_("If the SIM is locked with a PIN it must be unlocked before any other operations are requested.  Specify the PIN here to allow operation of the device.")
@@ -204,7 +204,7 @@
 #define DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_DHCP_TIMEOUT N_("A timeout for a DHCP transaction in seconds.")
 #define DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_DNS N_("Array of IP addresses of DNS servers.")
 #define DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_DNS_OPTIONS N_("Array of DNS options as described in man 5 resolv.conf. NULL means that the options are unset and left at the default. In this case NetworkManager will use default options. This is distinct from an empty list of properties.")
-#define DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_DNS_PRIORITY N_("DNS servers priority. The relative priority for DNS servers specified by this setting.  A lower value is better (higher priority).  Zero selects the default value, which is 50 for VPNs and 100 for other connections. Note that the priority is to order DNS settings for multiple active connections.  It does not disambiguate multiple DNS servers within the same connection profile. When using dns=default, servers with higher priority will be on top of resolv.conf.  To prioritize a given server over another one within the same connection, just specify them in the desired order.  When multiple devices have configurations with the same priority, the one with an active default route will be preferred.  Negative values have the special effect of excluding other configurations with a greater priority value; so in presence of at least a negative priority, only DNS servers from connections with the lowest priority value will be used. When using a DNS resolver that supports split-DNS as dns=dnsmasq or dns=systemd-resolved, each connection is used to query domains in its search list.  Queries for domains not present in any search list are routed through connections having the '~.' special wildcard domain, which is added automatically to connections with the default route (or can be added manually).  When multiple connections specify the same domain, the one with the highest priority (lowest numerical value) wins.  If a connection specifies a domain which is subdomain of another domain with a negative DNS priority value, the subdomain is ignored.")
+#define DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_DNS_PRIORITY N_("DNS servers priority. The relative priority for DNS servers specified by this setting.  A lower value is better (higher priority). Zero selects a globally configured default value. If the latter is missing or zero too, it defaults to 50 for VPNs and 100 for other connections. Note that the priority is to order DNS settings for multiple active connections.  It does not disambiguate multiple DNS servers within the same connection profile. When using dns=default, servers with higher priority will be on top of resolv.conf.  To prioritize a given server over another one within the same connection, just specify them in the desired order.  When multiple devices have configurations with the same priority, the one with an active default route will be preferred.  Negative values have the special effect of excluding other configurations with a greater priority value; so in presence of at least a negative priority, only DNS servers from connections with the lowest priority value will be used. When using a DNS resolver that supports split-DNS as dns=dnsmasq or dns=systemd-resolved, each connection is used to query domains in its search list.  Queries for domains not present in any search list are routed through connections having the '~.' special wildcard domain, which is added automatically to connections with the default route (or can be added manually).  When multiple connections specify the same domain, the one with the highest priority (lowest numerical value) wins.  If a connection specifies a domain which is subdomain of another domain with a negative DNS priority value, the subdomain is ignored.")
 #define DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_DNS_SEARCH N_("Array of DNS search domains. Domains starting with a tilde ('~') are considered 'routing' domains and are used only to decide the interface over which a query must be forwarded; they are not used to complete unqualified host names.")
 #define DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_GATEWAY N_("The gateway associated with this configuration. This is only meaningful if \"addresses\" is also set.")
 #define DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_IGNORE_AUTO_DNS N_("When \"method\" is set to \"auto\" and this property to TRUE, automatically configured nameservers and search domains are ignored and only nameservers and search domains specified in the \"dns\" and \"dns-search\" properties, if any, are used.")
@@ -224,7 +224,7 @@
 #define DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_DHCP_TIMEOUT N_("A timeout for a DHCP transaction in seconds.")
 #define DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_DNS N_("Array of IP addresses of DNS servers.")
 #define DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_DNS_OPTIONS N_("Array of DNS options as described in man 5 resolv.conf. NULL means that the options are unset and left at the default. In this case NetworkManager will use default options. This is distinct from an empty list of properties.")
-#define DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_DNS_PRIORITY N_("DNS servers priority. The relative priority for DNS servers specified by this setting.  A lower value is better (higher priority).  Zero selects the default value, which is 50 for VPNs and 100 for other connections. Note that the priority is to order DNS settings for multiple active connections.  It does not disambiguate multiple DNS servers within the same connection profile. When using dns=default, servers with higher priority will be on top of resolv.conf.  To prioritize a given server over another one within the same connection, just specify them in the desired order.  When multiple devices have configurations with the same priority, the one with an active default route will be preferred.  Negative values have the special effect of excluding other configurations with a greater priority value; so in presence of at least a negative priority, only DNS servers from connections with the lowest priority value will be used. When using a DNS resolver that supports split-DNS as dns=dnsmasq or dns=systemd-resolved, each connection is used to query domains in its search list.  Queries for domains not present in any search list are routed through connections having the '~.' special wildcard domain, which is added automatically to connections with the default route (or can be added manually).  When multiple connections specify the same domain, the one with the highest priority (lowest numerical value) wins.  If a connection specifies a domain which is subdomain of another domain with a negative DNS priority value, the subdomain is ignored.")
+#define DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_DNS_PRIORITY N_("DNS servers priority. The relative priority for DNS servers specified by this setting.  A lower value is better (higher priority). Zero selects a globally configured default value. If the latter is missing or zero too, it defaults to 50 for VPNs and 100 for other connections. Note that the priority is to order DNS settings for multiple active connections.  It does not disambiguate multiple DNS servers within the same connection profile. When using dns=default, servers with higher priority will be on top of resolv.conf.  To prioritize a given server over another one within the same connection, just specify them in the desired order.  When multiple devices have configurations with the same priority, the one with an active default route will be preferred.  Negative values have the special effect of excluding other configurations with a greater priority value; so in presence of at least a negative priority, only DNS servers from connections with the lowest priority value will be used. When using a DNS resolver that supports split-DNS as dns=dnsmasq or dns=systemd-resolved, each connection is used to query domains in its search list.  Queries for domains not present in any search list are routed through connections having the '~.' special wildcard domain, which is added automatically to connections with the default route (or can be added manually).  When multiple connections specify the same domain, the one with the highest priority (lowest numerical value) wins.  If a connection specifies a domain which is subdomain of another domain with a negative DNS priority value, the subdomain is ignored.")
 #define DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_DNS_SEARCH N_("Array of DNS search domains. Domains starting with a tilde ('~') are considered 'routing' domains and are used only to decide the interface over which a query must be forwarded; they are not used to complete unqualified host names.")
 #define DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_GATEWAY N_("The gateway associated with this configuration. This is only meaningful if \"addresses\" is also set.")
 #define DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_IGNORE_AUTO_DNS N_("When \"method\" is set to \"auto\" and this property to TRUE, automatically configured nameservers and search domains are ignored and only nameservers and search domains specified in the \"dns\" and \"dns-search\" properties, if any, are used.")
@@ -301,7 +301,7 @@
 #define DESCRIBE_DOC_NM_SETTING_TC_CONFIG_QDISCS N_("Array of TC queueing disciplines.")
 #define DESCRIBE_DOC_NM_SETTING_TC_CONFIG_TFILTERS N_("Array of TC traffic filters.")
 #define DESCRIBE_DOC_NM_SETTING_TEAM_CONFIG N_("The JSON configuration for the team network interface.  The property should contain raw JSON configuration data suitable for teamd, because the value is passed directly to teamd. If not specified, the default configuration is used.  See man teamd.conf for the format details.")
-#define DESCRIBE_DOC_NM_SETTING_TEAM_LINK_WATCHERS N_("Link watchers configuration for the connection: each link watcher is defined by a dictionary, whose keys depend upon the selected link watcher. Available link watchers are 'ethtool', 'nsna_ping' and 'arp_ping' and it is specified in the dictionary with the key 'name'. Available keys are:   ethtool: 'delay-up', 'delay-down', 'init-wait'; nsna_ping: 'init-wait', 'interval', 'missed-max', 'target-host'; arp_ping: all the ones in nsna_ping and 'source-host', 'validate-active', 'validate-incative', 'send-always'. See teamd.conf man for more details.")
+#define DESCRIBE_DOC_NM_SETTING_TEAM_LINK_WATCHERS N_("Link watchers configuration for the connection: each link watcher is defined by a dictionary, whose keys depend upon the selected link watcher. Available link watchers are 'ethtool', 'nsna_ping' and 'arp_ping' and it is specified in the dictionary with the key 'name'. Available keys are:   ethtool: 'delay-up', 'delay-down', 'init-wait'; nsna_ping: 'init-wait', 'interval', 'missed-max', 'target-host'; arp_ping: all the ones in nsna_ping and 'source-host', 'validate-active', 'validate-inactive', 'send-always'. See teamd.conf man for more details.")
 #define DESCRIBE_DOC_NM_SETTING_TEAM_MCAST_REJOIN_COUNT N_("Corresponds to the teamd mcast_rejoin.count.")
 #define DESCRIBE_DOC_NM_SETTING_TEAM_MCAST_REJOIN_INTERVAL N_("Corresponds to the teamd mcast_rejoin.interval.")
 #define DESCRIBE_DOC_NM_SETTING_TEAM_NOTIFY_PEERS_COUNT N_("Corresponds to the teamd notify_peers.count.")
@@ -319,7 +319,7 @@
 #define DESCRIBE_DOC_NM_SETTING_TEAM_PORT_CONFIG N_("The JSON configuration for the team port. The property should contain raw JSON configuration data suitable for teamd, because the value is passed directly to teamd. If not specified, the default configuration is used. See man teamd.conf for the format details.")
 #define DESCRIBE_DOC_NM_SETTING_TEAM_PORT_LACP_KEY N_("Corresponds to the teamd ports.PORTIFNAME.lacp_key.")
 #define DESCRIBE_DOC_NM_SETTING_TEAM_PORT_LACP_PRIO N_("Corresponds to the teamd ports.PORTIFNAME.lacp_prio.")
-#define DESCRIBE_DOC_NM_SETTING_TEAM_PORT_LINK_WATCHERS N_("Link watchers configuration for the connection: each link watcher is defined by a dictionary, whose keys depend upon the selected link watcher. Available link watchers are 'ethtool', 'nsna_ping' and 'arp_ping' and it is specified in the dictionary with the key 'name'. Available keys are:   ethtool: 'delay-up', 'delay-down', 'init-wait'; nsna_ping: 'init-wait', 'interval', 'missed-max', 'target-host'; arp_ping: all the ones in nsna_ping and 'source-host', 'validate-active', 'validate-incative', 'send-always'. See teamd.conf man for more details.")
+#define DESCRIBE_DOC_NM_SETTING_TEAM_PORT_LINK_WATCHERS N_("Link watchers configuration for the connection: each link watcher is defined by a dictionary, whose keys depend upon the selected link watcher. Available link watchers are 'ethtool', 'nsna_ping' and 'arp_ping' and it is specified in the dictionary with the key 'name'. Available keys are:   ethtool: 'delay-up', 'delay-down', 'init-wait'; nsna_ping: 'init-wait', 'interval', 'missed-max', 'target-host'; arp_ping: all the ones in nsna_ping and 'source-host', 'validate-active', 'validate-inactive', 'send-always'. See teamd.conf man for more details.")
 #define DESCRIBE_DOC_NM_SETTING_TEAM_PORT_PRIO N_("Corresponds to the teamd ports.PORTIFNAME.prio.")
 #define DESCRIBE_DOC_NM_SETTING_TEAM_PORT_QUEUE_ID N_("Corresponds to the teamd ports.PORTIFNAME.queue_id. When set to -1 means the parameter is skipped from the json config.")
 #define DESCRIBE_DOC_NM_SETTING_TEAM_PORT_STICKY N_("Corresponds to the teamd ports.PORTIFNAME.sticky.")
@@ -357,8 +357,19 @@
 #define DESCRIBE_DOC_NM_SETTING_VXLAN_SOURCE_PORT_MIN N_("Specifies the minimum UDP source port to communicate to the remote VXLAN tunnel endpoint.")
 #define DESCRIBE_DOC_NM_SETTING_VXLAN_TOS N_("Specifies the TOS value to use in outgoing packets.")
 #define DESCRIBE_DOC_NM_SETTING_VXLAN_TTL N_("Specifies the time-to-live value to use in outgoing packets.")
+#define DESCRIBE_DOC_NM_SETTING_WIFI_P2P_PEER N_("The P2P device that should be connected to. Currently this is the only way to create or join a group.")
+#define DESCRIBE_DOC_NM_SETTING_WIFI_P2P_WFD_IES N_("The Wi-Fi Display (WFD) Information Elements (IEs) to set. Wi-Fi Display requires a protocol specific information element to be set in certain Wi-Fi frames. These can be specified here for the purpose of establishing a connection. This setting is only useful when implementing a Wi-Fi Display client.")
+#define DESCRIBE_DOC_NM_SETTING_WIFI_P2P_WPS_METHOD N_("Flags indicating which mode of WPS is to be used. There's little point in changing the default setting as NetworkManager will automatically determine the best method to use.")
 #define DESCRIBE_DOC_NM_SETTING_WIMAX_MAC_ADDRESS N_("If specified, this connection will only apply to the WiMAX device whose MAC address matches. This property does not change the MAC address of the device (known as MAC spoofing). Deprecated: 1")
 #define DESCRIBE_DOC_NM_SETTING_WIMAX_NETWORK_NAME N_("Network Service Provider (NSP) name of the WiMAX network this connection should use. Deprecated: 1")
+#define DESCRIBE_DOC_NM_SETTING_WIREGUARD_FWMARK N_("The use of fwmark is optional and is by default off. Setting it to 0 disables it. Otherwise it is a 32-bit fwmark for outgoing packets.")
+#define DESCRIBE_DOC_NM_SETTING_WIREGUARD_LISTEN_PORT N_("The listen-port. If listen-port is not specified, the port will be chosen randomly when the interface comes up.")
+#define DESCRIBE_DOC_NM_SETTING_WIREGUARD_MTU N_("If non-zero, only transmit packets of the specified size or smaller, breaking larger packets up into multiple fragments. If zero a default MTU is used. Note that contrary to wg-quick's MTU setting, this does not take into account the current routes at the time of activation.")
+#define DESCRIBE_DOC_NM_SETTING_WIREGUARD_PEER_ROUTES N_("Whether to automatically add routes for the AllowedIPs ranges of the peers. If TRUE (the default), NetworkManager will automatically add routes in the routing tables according to ipv4.route-table and ipv6.route-table. If FALSE, no such routes are added automatically. In this case, the user may want to configure static routes in ipv4.routes and ipv6.routes, respectively.")
+#define DESCRIBE_DOC_NM_SETTING_WIREGUARD_PRIVATE_KEY N_("The 256 bit private-key in base64 encoding.")
+#define DESCRIBE_DOC_NM_SETTING_WIREGUARD_PRIVATE_KEY_FLAGS N_("Flags indicating how to handle the \"private-key\" property.")
+#define DESCRIBE_DOC_NM_SETTING_WPAN_CHANNEL N_("IEEE 802.15.4 channel. A positive integer or -1, meaning \"do not set, use whatever the device is already set to\".")
 #define DESCRIBE_DOC_NM_SETTING_WPAN_MAC_ADDRESS N_("If specified, this connection will only apply to the IEEE 802.15.4 (WPAN) MAC layer device whose permanent MAC address matches.")
+#define DESCRIBE_DOC_NM_SETTING_WPAN_PAGE N_("IEEE 802.15.4 channel page. A positive integer or -1, meaning \"do not set, use whatever the device is already set to\".")
 #define DESCRIBE_DOC_NM_SETTING_WPAN_PAN_ID N_("IEEE 802.15.4 Personal Area Network (PAN) identifier.")
 #define DESCRIBE_DOC_NM_SETTING_WPAN_SHORT_ADDRESS N_("Short IEEE 802.15.4 address to be used within a restricted environment.")
diff --git a/clients/common/tests/meson.build b/clients/common/tests/meson.build
index cc357cdc..060bab48 100644
--- a/clients/common/tests/meson.build
+++ b/clients/common/tests/meson.build
@@ -4,7 +4,7 @@ deps = [
   libnm_dep,
   libnmc_dep,
   libnmc_base_dep,
-  nm_core_dep
+  nm_core_dep,
 ]
 
 exe = executable(
@@ -19,5 +19,5 @@ exe = executable(
 test(
   'clients/' + test_unit,
   test_script,
-  args: test_args + [exe.full_path()]
+  args: test_args + [exe.full_path()],
 )
diff --git a/clients/common/tests/test-general.c b/clients/common/tests/test-general.c
index 8f96eb26..c39901ba 100644
--- a/clients/common/tests/test-general.c
+++ b/clients/common/tests/test-general.c
@@ -20,6 +20,7 @@
 #include "nm-default.h"
 
 #include "nm-meta-setting-access.h"
+#include "nm-vpn-helpers.h"
 
 #include "nm-utils/nm-test-utils.h"
 
@@ -145,6 +146,96 @@ test_client_meta_check (void)
 
 /*****************************************************************************/
 
+static void
+test_client_import_wireguard_test0 (void)
+{
+	gs_unref_object NMConnection *connection;
+	NMSettingWireGuard *s_wg;
+	NMWireGuardPeer *peer;
+	gs_free_error GError *error = NULL;
+
+	connection = nm_vpn_wireguard_import (NM_BUILD_SRCDIR"/clients/common/tests/wg-test0.conf",
+	                                      &error);
+
+	g_assert_no_error (error);
+
+	g_assert_cmpstr (nm_connection_get_id (connection), ==, "wg-test0");
+	g_assert_cmpstr (nm_connection_get_interface_name (connection), ==, "wg-test0");
+	g_assert_cmpstr (nm_connection_get_connection_type (connection), ==, NM_SETTING_WIREGUARD_SETTING_NAME);
+
+	s_wg = NM_SETTING_WIREGUARD (nm_connection_get_setting (connection, NM_TYPE_SETTING_WIREGUARD));
+
+	g_assert_cmpint (nm_setting_wireguard_get_listen_port (s_wg), ==, 51820);
+	g_assert_cmpstr (nm_setting_wireguard_get_private_key (s_wg), ==, "yAnz5TF+lXXJte14tji3zlMNq+hd2rYUIgJBgB3fBmk=");
+
+	g_assert_cmpint (nm_setting_wireguard_get_peers_len (s_wg), ==, 3);
+
+	peer = nm_setting_wireguard_get_peer (s_wg, 0);
+	g_assert_cmpstr (nm_wireguard_peer_get_public_key (peer), ==, "xTIBA5rboUvnH4htodjb6e697QjLERt1NAB4mZqp8Dg=");
+	g_assert_cmpstr (nm_wireguard_peer_get_endpoint (peer), ==, "192.95.5.67:1234");
+	g_assert_cmpint (nm_wireguard_peer_get_allowed_ips_len (peer), ==, 2);
+	g_assert_cmpstr (nm_wireguard_peer_get_allowed_ip (peer, 0, NULL), ==, "10.192.122.3/32");
+	g_assert_cmpstr (nm_wireguard_peer_get_allowed_ip (peer, 1, NULL), ==, "10.192.124.1/24");
+
+	peer = nm_setting_wireguard_get_peer (s_wg, 1);
+	g_assert_cmpstr (nm_wireguard_peer_get_public_key (peer), ==, "TrMvSoP4jYQlY6RIzBgbssQqY3vxI2Pi+y71lOWWXX0=");
+	g_assert_cmpstr (nm_wireguard_peer_get_endpoint (peer), ==, "[2607:5300:60:6b0::c05f:543]:2468");
+	g_assert_cmpint (nm_wireguard_peer_get_allowed_ips_len (peer), ==, 2);
+	g_assert_cmpstr (nm_wireguard_peer_get_allowed_ip (peer, 0, NULL), ==, "10.192.122.4/32");
+	g_assert_cmpstr (nm_wireguard_peer_get_allowed_ip (peer, 1, NULL), ==, "192.168.0.0/16");
+
+	peer = nm_setting_wireguard_get_peer (s_wg, 2);
+	g_assert_cmpstr (nm_wireguard_peer_get_public_key (peer), ==, "gN65BkIKy1eCE9pP1wdc8ROUtkHLF2PfAqYdyYBz6EA=");
+	g_assert_cmpstr (nm_wireguard_peer_get_endpoint (peer), ==, "test.wireguard.com:18981");
+	g_assert_cmpint (nm_wireguard_peer_get_allowed_ips_len (peer), ==, 1);
+	g_assert_cmpstr (nm_wireguard_peer_get_allowed_ip (peer, 0, NULL), ==, "10.10.10.230/32");
+}
+
+static void
+test_client_import_wireguard_test1 (void)
+{
+	gs_free_error GError *error = NULL;
+
+	nm_vpn_wireguard_import (NM_BUILD_SRCDIR"/clients/common/tests/wg-test1.conf", &error);
+	g_assert_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_INVALID_ARGUMENT);
+	g_assert (g_str_has_prefix (error->message, "invalid secret 'PrivateKey'"));
+	g_assert (g_str_has_suffix (error->message, "wg-test1.conf:2"));
+}
+
+static void
+test_client_import_wireguard_test2 (void)
+{
+	gs_free_error GError *error = NULL;
+
+	nm_vpn_wireguard_import (NM_BUILD_SRCDIR"/clients/common/tests/wg-test2.conf", &error);
+
+	g_assert_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_INVALID_ARGUMENT);
+	g_assert (g_str_has_prefix (error->message, "unrecognized line at"));
+	g_assert (g_str_has_suffix (error->message, "wg-test2.conf:5"));
+}
+
+static void
+test_client_import_wireguard_test3 (void)
+{
+	gs_free_error GError *error = NULL;
+
+	nm_vpn_wireguard_import (NM_BUILD_SRCDIR"/clients/common/tests/wg-test3.conf", &error);
+	g_assert_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_INVALID_ARGUMENT);
+	g_assert (g_str_has_prefix (error->message, "invalid value for 'ListenPort'"));
+	g_assert (g_str_has_suffix (error->message, "wg-test3.conf:3"));
+}
+
+static void
+test_client_import_wireguard_missing (void)
+{
+	gs_free_error GError *error = NULL;
+
+	nm_vpn_wireguard_import (NM_BUILD_SRCDIR"/clients/common/tests/wg-missing.conf", &error);
+	g_assert_error (error, G_FILE_ERROR, G_FILE_ERROR_NOENT);
+}
+
+/*****************************************************************************/
+
 NMTST_DEFINE ();
 
 int
@@ -153,6 +244,11 @@ main (int argc, char **argv)
 	nmtst_init (&argc, &argv, TRUE);
 
 	g_test_add_func ("/client/meta/check", test_client_meta_check);
+	g_test_add_func ("/client/import/wireguard/test0", test_client_import_wireguard_test0);
+	g_test_add_func ("/client/import/wireguard/test1", test_client_import_wireguard_test1);
+	g_test_add_func ("/client/import/wireguard/test2", test_client_import_wireguard_test2);
+	g_test_add_func ("/client/import/wireguard/test3", test_client_import_wireguard_test3);
+	g_test_add_func ("/client/import/wireguard/missing", test_client_import_wireguard_missing);
 
 	return g_test_run ();
 }
diff --git a/clients/common/tests/wg-test0.conf b/clients/common/tests/wg-test0.conf
new file mode 100644
index 00000000..61438c29
--- /dev/null
+++ b/clients/common/tests/wg-test0.conf
@@ -0,0 +1,18 @@
+[Interface]
+PrivateKey = yAnz5TF+lXXJte14tji3zlMNq+hd2rYUIgJBgB3fBmk=
+ListenPort = 51820
+
+[Peer]
+PublicKey = xTIBA5rboUvnH4htodjb6e697QjLERt1NAB4mZqp8Dg=
+Endpoint = 192.95.5.67:1234
+AllowedIPs = 10.192.122.3/32, 10.192.124.1/24
+
+[Peer]
+PublicKey = TrMvSoP4jYQlY6RIzBgbssQqY3vxI2Pi+y71lOWWXX0=
+Endpoint = [2607:5300:60:6b0::c05f:543]:2468
+AllowedIPs = 10.192.122.4/32, 192.168.0.0/16
+
+[Peer]
+PublicKey = gN65BkIKy1eCE9pP1wdc8ROUtkHLF2PfAqYdyYBz6EA=
+Endpoint = test.wireguard.com:18981
+AllowedIPs = 10.10.10.230/32
diff --git a/clients/common/tests/wg-test1.conf b/clients/common/tests/wg-test1.conf
new file mode 100644
index 00000000..ceb267ac
--- /dev/null
+++ b/clients/common/tests/wg-test1.conf
@@ -0,0 +1,3 @@
+[Interface]
+PrivateKey = bad
+ListenPort = 51820
diff --git a/clients/common/tests/wg-test2.conf b/clients/common/tests/wg-test2.conf
new file mode 100644
index 00000000..f691c2af
--- /dev/null
+++ b/clients/common/tests/wg-test2.conf
@@ -0,0 +1,8 @@
+[Interface]
+PrivateKey = yAnz5TF+lXXJte14tji3zlMNq+hd2rYUIgJBgB3fBmk=
+ListenPort = 51820
+
+[Beer]
+PublicKey = xTIBA5rboUvnH4htodjb6e697QjLERt1NAB4mZqp8Dg=
+Endpoint = 192.95.5.67:1234
+AllowedIPs = 10.192.122.3/32, 10.192.124.1/24
diff --git a/clients/common/tests/wg-test3.conf b/clients/common/tests/wg-test3.conf
new file mode 100644
index 00000000..9956e400
--- /dev/null
+++ b/clients/common/tests/wg-test3.conf
@@ -0,0 +1,3 @@
+[Interface]
+PrivateKey = yAnz5TF+lXXJte14tji3zlMNq+hd2rYUIgJBgB3fBmk=
+ListenPort = 666666666