diff --git a/NEWS b/NEWS
index 901a8bb9c..9a248c702 100644
--- a/NEWS
+++ b/NEWS
@@ -8,6 +8,7 @@ subject to change and not guaranteed to be compatible with
the later release.
USE AT YOUR OWN RISK. NOT RECOMMENDED FOR PRODUCTION USE!
+* nmcli now supports viewing and managing WireGuard peers.
=============================================
NetworkManager-1.54
diff --git a/src/libnm-client-aux-extern/tests/test-libnm-client-aux.c b/src/libnm-client-aux-extern/tests/test-libnm-client-aux.c
index f727b3d93..6a25b652d 100644
--- a/src/libnm-client-aux-extern/tests/test-libnm-client-aux.c
+++ b/src/libnm-client-aux-extern/tests/test-libnm-client-aux.c
@@ -234,6 +234,227 @@ test_team_link_watcher_tofro_string(void)
NM_TEAM_LINK_WATCHER_ARP_PING_FLAG_NONE);
}
+static void
+test_wireguard_peer(void)
+{
+ guint i;
+ struct {
+ const char *input;
+ const char *canonical; /* canonical string representation */
+
+ gboolean invalid;
+ const char *pubkey;
+ const char *endpoint;
+ guint16 keepalive;
+ guint num_allowed_ips;
+ const char *allowed_ips[2];
+ const char *psk;
+ int psk_flags;
+ } tests[] = {{
+ /* Public key only */
+ .input = "MWEKYcE9MEh5RoGDuJYrJ2YgkoosONGhuHRBAC00e14=",
+ .canonical = "MWEKYcE9MEh5RoGDuJYrJ2YgkoosONGhuHRBAC00e14=",
+ .pubkey = "MWEKYcE9MEh5RoGDuJYrJ2YgkoosONGhuHRBAC00e14=",
+ },
+ {
+ /* IPv4 endpoint */
+ .input = "+DIX0qWKQ4E6hy7MWzsSRXjqAHCtffWrXTdJPe/xS04="
+ " endpoint=1.2.3.4:5555",
+ .canonical = "+DIX0qWKQ4E6hy7MWzsSRXjqAHCtffWrXTdJPe/xS04="
+ " endpoint=1.2.3.4:5555",
+ .pubkey = "+DIX0qWKQ4E6hy7MWzsSRXjqAHCtffWrXTdJPe/xS04=",
+ .endpoint = "1.2.3.4:5555",
+ },
+ {
+ /* IPv6 endpoint */
+ .input = "aPsdPkeqH4l5Nax3g3e8A8f7g0hJk2l3m4N5p6q7R8s="
+ " endpoint=[fd01:db8::1]:8080",
+ .canonical = "aPsdPkeqH4l5Nax3g3e8A8f7g0hJk2l3m4N5p6q7R8s="
+ " endpoint=[fd01:db8::1]:8080",
+ .pubkey = "aPsdPkeqH4l5Nax3g3e8A8f7g0hJk2l3m4N5p6q7R8s=",
+ .endpoint = "[fd01:db8::1]:8080",
+ },
+ {
+ /* IPv6 endpoint, without brackets */
+ .input = "+DIX0qWKQ4E6hy7MWzsSRXjqAHCtffWrXTdJPe/xS04="
+ " endpoint=fd01::12:8080",
+ .canonical = "+DIX0qWKQ4E6hy7MWzsSRXjqAHCtffWrXTdJPe/xS04="
+ " endpoint=fd01::12:8080",
+ .pubkey = "+DIX0qWKQ4E6hy7MWzsSRXjqAHCtffWrXTdJPe/xS04=",
+ .endpoint = "fd01::12:8080",
+ },
+ {
+ /* Single IPv4 allowed-ip */
+ .input = "s4fmZZA3gMGVv8+0hkSwrmeLC6nNd+Pd6DlSaufLKhY="
+ " allowed-ips=172.16.0.0/16",
+ .canonical = "s4fmZZA3gMGVv8+0hkSwrmeLC6nNd+Pd6DlSaufLKhY="
+ " allowed-ips=172.16.0.0/16",
+ .pubkey = "s4fmZZA3gMGVv8+0hkSwrmeLC6nNd+Pd6DlSaufLKhY=",
+ .num_allowed_ips = 1,
+ .allowed_ips = {"172.16.0.0/16"},
+ },
+ {
+ /* Multiple allowed-ips */
+ .input = "V02J2zmCi2LHX2KMK+ZOgDNhZzK4JXjGNr7CYfz9DxQ="
+ " allowed-ips=192.168.2.0/24;2001:db8:a::/48",
+ .canonical = "V02J2zmCi2LHX2KMK+ZOgDNhZzK4JXjGNr7CYfz9DxQ="
+ " allowed-ips=192.168.2.0/24;2001:db8:a::/48",
+ .pubkey = "V02J2zmCi2LHX2KMK+ZOgDNhZzK4JXjGNr7CYfz9DxQ=",
+ .num_allowed_ips = 2,
+ .allowed_ips = {"192.168.2.0/24", "2001:db8:a::/48"},
+ },
+ {
+ /* Persistent-keepalive */
+ .input = "D1FTp8Wy1oJQI045yXo9EMdxJqjXHC3VhTCPTh3lSQM="
+ " persistent-keepalive=25",
+ .canonical = "D1FTp8Wy1oJQI045yXo9EMdxJqjXHC3VhTCPTh3lSQM="
+ " persistent-keepalive=25",
+ .pubkey = "D1FTp8Wy1oJQI045yXo9EMdxJqjXHC3VhTCPTh3lSQM=",
+ .keepalive = 25,
+ },
+ {
+ /* Preshared-key without flags (should default to 0) */
+ .input = "H5cWWgpWgJH+nHFhsuPS3adgZHuc6Z4cRzfiNRTinE0="
+ " preshared-key=16uGwZvROnwyNGoW6Z3pvJB5GKbd6ncYROA/FFleLQA=",
+ .canonical = "H5cWWgpWgJH+nHFhsuPS3adgZHuc6Z4cRzfiNRTinE0="
+ " preshared-key=16uGwZvROnwyNGoW6Z3pvJB5GKbd6ncYROA/FFleLQA="
+ " preshared-key-flags=0",
+ .pubkey = "H5cWWgpWgJH+nHFhsuPS3adgZHuc6Z4cRzfiNRTinE0=",
+ .psk = "16uGwZvROnwyNGoW6Z3pvJB5GKbd6ncYROA/FFleLQA=",
+ .psk_flags = 0,
+ },
+ {
+ /* Preshared-key flags as string */
+ .input = "H5cWWgpWgJH+nHFhsuPS3adgZHuc6Z4cRzfiNRTinE0="
+ " preshared-key=16uGwZvROnwyNGoW6Z3pvJB5GKbd6ncYROA/FFleLQA="
+ " preshared-key-flags=not-saved",
+ .canonical = "H5cWWgpWgJH+nHFhsuPS3adgZHuc6Z4cRzfiNRTinE0="
+ " preshared-key=16uGwZvROnwyNGoW6Z3pvJB5GKbd6ncYROA/FFleLQA="
+ " preshared-key-flags=2",
+ .pubkey = "H5cWWgpWgJH+nHFhsuPS3adgZHuc6Z4cRzfiNRTinE0=",
+ .psk = "16uGwZvROnwyNGoW6Z3pvJB5GKbd6ncYROA/FFleLQA=",
+ .psk_flags = 2,
+ },
+ {
+ /* Non-canonical order and extra whitespaces */
+ .input = "gqQ9dUqKQNfz/KOqELJpS0MKBvRcYWL8sm/LGEWKKQY="
+ " preshared-key=EVVP8pOzn8R3nQtv62/hnGsXzyagEgykSboFe4EFhQc="
+ " endpoint=vpn.example.com:51820 "
+ " preshared-key-flags=1"
+ " persistent-keepalive=45"
+ " allowed-ips=0.0.0.0/0;::/0",
+ .canonical = "gqQ9dUqKQNfz/KOqELJpS0MKBvRcYWL8sm/LGEWKKQY="
+ " allowed-ips=0.0.0.0/0;::/0"
+ " endpoint=vpn.example.com:51820"
+ " persistent-keepalive=45"
+ " preshared-key=EVVP8pOzn8R3nQtv62/hnGsXzyagEgykSboFe4EFhQc="
+ " preshared-key-flags=1",
+ .pubkey = "gqQ9dUqKQNfz/KOqELJpS0MKBvRcYWL8sm/LGEWKKQY=",
+ .endpoint = "vpn.example.com:51820",
+ .keepalive = 45,
+ .num_allowed_ips = 2,
+ .allowed_ips = {"0.0.0.0/0", "::/0"},
+ .psk = "EVVP8pOzn8R3nQtv62/hnGsXzyagEgykSboFe4EFhQc=",
+ .psk_flags = 1,
+ },
+ {
+ /* Empty string */
+ .input = "",
+ .invalid = TRUE,
+ },
+ {
+ /* Invalid public key*/
+ .input = "aaaaaaaaaaaaaaaaaaaaaaa=",
+ .invalid = TRUE,
+ },
+ {
+ /* Missing value*/
+ .input = "gqQ9dUqKQNfz/KOqELJpS0MKBvRcYWL8sm/LGEWKKQY= "
+ "persistent-keepalive=",
+ .invalid = TRUE,
+ },
+ {
+ /* Unknown attribute */
+ .input = "gqQ9dUqKQNfz/KOqELJpS0MKBvRcYWL8sm/LGEWKKQY= "
+ "persistent-keepalive=12 foobarness=13",
+ .invalid = TRUE,
+ },
+ {
+ /* Invalid IPv4 allowed-ip*/
+ .input = "gqQ9dUqKQNfz/KOqELJpS0MKBvRcYWL8sm/LGEWKKQY= "
+ "allowed-ips=192.168.10.256/32",
+ .invalid = TRUE,
+ },
+ {
+ /* Invalid IPv6 allowed-ip */
+ .input = "gqQ9dUqKQNfz/KOqELJpS0MKBvRcYWL8sm/LGEWKKQY= "
+ "allowed-ips=fd01::1::3/64",
+ .invalid = TRUE,
+ },
+ {
+ /* Endpoint with no port */
+ .input = "+DIX0qWKQ4E6hy7MWzsSRXjqAHCtffWrXTdJPe/xS04="
+ " endpoint=1.2.3.4",
+ .invalid = TRUE,
+ },
+ {
+ /* Invalid endpoint */
+ .input = "+DIX0qWKQ4E6hy7MWzsSRXjqAHCtffWrXTdJPe/xS04="
+ " endpoint=1.2.3.5.6",
+ .invalid = TRUE,
+ },
+ {
+ /* Invalid persistent-keepalive */
+ .input = "gqQ9dUqKQNfz/KOqELJpS0MKBvRcYWL8sm/LGEWKKQY= "
+ "persistent-keepalive=yes",
+ .invalid = TRUE,
+ },
+ {
+ /* Invalid PSK */
+ .input = "gqQ9dUqKQNfz/KOqELJpS0MKBvRcYWL8sm/LGEWKKQY="
+ " preshared-key=pskpskpskpskpskpskpskpskpskpskpskpsk",
+ .invalid = TRUE,
+ }};
+
+ for (i = 0; i < G_N_ELEMENTS(tests); i++) {
+ nm_auto_unref_wgpeer NMWireGuardPeer *peer = NULL;
+ gs_free_error GError *error = NULL;
+ gs_free char *newstr = NULL;
+ guint j;
+
+ peer = _nm_utils_wireguard_peer_from_string(tests[i].input, &error);
+ if (tests[i].invalid) {
+ g_assert(!peer);
+ g_assert(error);
+ continue;
+ }
+ g_assert_no_error(error);
+ g_assert_nonnull(peer);
+
+ newstr = _nm_utils_wireguard_peer_to_string(peer);
+ g_assert_nonnull(newstr);
+ g_assert_cmpstr(tests[i].canonical, ==, newstr);
+
+ g_assert_cmpstr(tests[i].pubkey, ==, nm_wireguard_peer_get_public_key(peer));
+ g_assert_cmpstr(tests[i].endpoint, ==, nm_wireguard_peer_get_endpoint(peer));
+
+ g_assert_cmpint(tests[i].num_allowed_ips, ==, nm_wireguard_peer_get_allowed_ips_len(peer));
+ for (j = 0; j < tests[i].num_allowed_ips; j++) {
+ g_assert_cmpstr(tests[i].allowed_ips[j],
+ ==,
+ nm_wireguard_peer_get_allowed_ip(peer, j, NULL));
+ }
+
+ g_assert_cmpint(tests[i].keepalive, ==, nm_wireguard_peer_get_persistent_keepalive(peer));
+ g_assert_cmpstr(tests[i].psk, ==, nm_wireguard_peer_get_preshared_key(peer));
+ if (tests[i].psk) {
+ g_assert_cmpint(tests[i].psk_flags,
+ ==,
+ nm_wireguard_peer_get_preshared_key_flags(peer));
+ }
+ }
+}
+
/*****************************************************************************/
NMTST_DEFINE();
@@ -245,6 +466,7 @@ main(int argc, char **argv)
g_test_add_func("/libnm-core-aux/test_team_link_watcher_tofro_string",
test_team_link_watcher_tofro_string);
+ g_test_add_func("/libnm-core-aux/test-wireguard-peer", test_wireguard_peer);
return g_test_run();
}
diff --git a/src/libnm-core-aux-extern/nm-libnm-core-aux.c b/src/libnm-core-aux-extern/nm-libnm-core-aux.c
index dfc70e35b..b12000fc1 100644
--- a/src/libnm-core-aux-extern/nm-libnm-core-aux.c
+++ b/src/libnm-core-aux-extern/nm-libnm-core-aux.c
@@ -7,6 +7,7 @@
#include "nm-libnm-core-aux.h"
+#include "nm-errors.h"
#include "libnm-core-aux-intern/nm-libnm-core-utils.h"
#include "libnm-glib-aux/nm-str-buf.h"
@@ -475,3 +476,177 @@ _nm_ip_route_to_string(NMIPRoute *route, NMStrBuf *strbuf)
nm_str_buf_append_printf(strbuf, " metric %" G_GINT64_FORMAT, metric);
}
}
+
+/*****************************************************************************/
+
+char *
+_nm_utils_wireguard_peer_to_string(NMWireGuardPeer *peer)
+{
+ GString *str;
+ const char *endpoint;
+ const char *psk;
+ guint16 keepalive;
+ guint i;
+ guint len;
+
+ g_return_val_if_fail(peer, "");
+
+ nm_assert(nm_wireguard_peer_is_valid(peer, TRUE, TRUE, NULL));
+
+ str = g_string_new("");
+ g_string_append(str, nm_wireguard_peer_get_public_key(peer));
+
+ len = nm_wireguard_peer_get_allowed_ips_len(peer);
+ if (len > 0) {
+ g_string_append(str, " allowed-ips=");
+ for (i = 0; i < len; i++) {
+ g_string_append(str, nm_wireguard_peer_get_allowed_ip(peer, i, NULL));
+ if (i < len - 1)
+ g_string_append(str, ";");
+ }
+ }
+
+ endpoint = nm_wireguard_peer_get_endpoint(peer);
+ if (endpoint) {
+ g_string_append_printf(str, " endpoint=%s", endpoint);
+ }
+
+ keepalive = nm_wireguard_peer_get_persistent_keepalive(peer);
+ if (keepalive != 0) {
+ g_string_append_printf(str, " persistent-keepalive=%hu", keepalive);
+ }
+
+ psk = nm_wireguard_peer_get_preshared_key(peer);
+ if (psk) {
+ g_string_append_printf(str, " preshared-key=%s", psk);
+ g_string_append_printf(str,
+ " preshared-key-flags=%u",
+ (guint) nm_wireguard_peer_get_preshared_key_flags(peer));
+ }
+
+ return g_string_free(str, FALSE);
+}
+
+NMWireGuardPeer *
+_nm_utils_wireguard_peer_from_string(const char *str, GError **error)
+{
+ nm_auto_unref_wgpeer NMWireGuardPeer *peer = NULL;
+ gs_strfreev char **tokens = NULL;
+ gboolean has_psk = FALSE;
+ gboolean has_psk_flags = FALSE;
+ char *value;
+ guint i;
+
+ peer = nm_wireguard_peer_new();
+
+ tokens = g_strsplit_set(str, " ", 0);
+ for (i = 0; tokens[i]; i++) {
+ if (i == 0) {
+ if (!nm_wireguard_peer_set_public_key(peer, tokens[i], FALSE)) {
+ g_set_error(error,
+ NM_CONNECTION_ERROR,
+ NM_CONNECTION_ERROR_INVALID_PROPERTY,
+ "invalid public key '%s'",
+ tokens[i]);
+ return NULL;
+ }
+ continue;
+ }
+
+ if (tokens[i][0] == '\0')
+ continue;
+
+ value = strchr(tokens[i], '=');
+ if (!value || value[1] == '\0') {
+ g_set_error(error,
+ NM_CONNECTION_ERROR,
+ NM_CONNECTION_ERROR_INVALID_PROPERTY,
+ "attribute without value '%s'",
+ tokens[i]);
+ return NULL;
+ }
+
+ *value = '\0';
+ value++;
+
+ if (nm_streq(tokens[i], "allowed-ips")) {
+ gs_strfreev char **ips = NULL;
+ guint j;
+
+ ips = g_strsplit_set(value, ";", 0);
+ for (j = 0; ips[j]; j++) {
+ if (!nm_wireguard_peer_append_allowed_ip(peer, ips[j], FALSE)) {
+ g_set_error(error,
+ NM_CONNECTION_ERROR,
+ NM_CONNECTION_ERROR_INVALID_PROPERTY,
+ "invalid allowed-ip '%s'",
+ ips[j]);
+ return NULL;
+ }
+ }
+ } else if (nm_streq(tokens[i], "endpoint")) {
+ if (!nm_wireguard_peer_set_endpoint(peer, value, FALSE)) {
+ g_set_error(error,
+ NM_CONNECTION_ERROR,
+ NM_CONNECTION_ERROR_INVALID_PROPERTY,
+ "invalid endpoint '%s'",
+ value);
+ return NULL;
+ }
+ } else if (nm_streq(tokens[i], "persistent-keepalive")) {
+ gint64 keepalive;
+
+ keepalive = _nm_utils_ascii_str_to_int64(value, 10, 0, G_MAXUINT16, -1);
+ if (keepalive == -1) {
+ g_set_error(error,
+ NM_CONNECTION_ERROR,
+ NM_CONNECTION_ERROR_INVALID_PROPERTY,
+ "invalid persistent-keepalive value '%s'",
+ value);
+ return NULL;
+ }
+ nm_wireguard_peer_set_persistent_keepalive(peer, (guint16) keepalive);
+ } else if (nm_streq(tokens[i], "preshared-key")) {
+ if (!nm_wireguard_peer_set_preshared_key(peer, value, FALSE)) {
+ g_set_error(error,
+ NM_CONNECTION_ERROR,
+ NM_CONNECTION_ERROR_INVALID_PROPERTY,
+ "invalid preshared-key '%s'",
+ value);
+ return NULL;
+ }
+ has_psk = TRUE;
+ } else if (nm_streq(tokens[i], "preshared-key-flags")) {
+ int flags;
+
+ if (!nm_utils_enum_from_str(NM_TYPE_SETTING_SECRET_FLAGS, value, &flags, NULL)) {
+ g_set_error(error,
+ NM_CONNECTION_ERROR,
+ NM_CONNECTION_ERROR_INVALID_PROPERTY,
+ "invalid preshared-key-flags '%s'",
+ value);
+ return NULL;
+ }
+ nm_wireguard_peer_set_preshared_key_flags(peer, (NMSettingSecretFlags) flags);
+ has_psk_flags = TRUE;
+ } else {
+ g_set_error(error,
+ NM_CONNECTION_ERROR,
+ NM_CONNECTION_ERROR_INVALID_PROPERTY,
+ "invalid attribute '%s'",
+ tokens[i]);
+ return NULL;
+ }
+ }
+
+ if (has_psk && !has_psk_flags) {
+ /* The flags are NOT_REQUIRED by default. With this flag, the PSK would not
+ * be saved by default, unless the user explicitly sets a different value. */
+ nm_wireguard_peer_set_preshared_key_flags(peer, NM_SETTING_SECRET_FLAG_NONE);
+ }
+
+ if (!nm_wireguard_peer_is_valid(peer, TRUE, TRUE, error))
+ return NULL;
+
+ return g_steal_pointer(&peer);
+}
diff --git a/src/libnm-core-aux-extern/nm-libnm-core-aux.h b/src/libnm-core-aux-extern/nm-libnm-core-aux.h
index e45e98ab2..aa7ea631e 100644
--- a/src/libnm-core-aux-extern/nm-libnm-core-aux.h
+++ b/src/libnm-core-aux-extern/nm-libnm-core-aux.h
@@ -6,8 +6,9 @@
#ifndef __NM_LIBNM_CORE_AUX_H__
#define __NM_LIBNM_CORE_AUX_H__
-#include "nm-setting-team.h"
#include "nm-setting-ip-config.h"
+#include "nm-setting-team.h"
+#include "nm-setting-wireguard.h"
typedef enum {
NM_TEAM_LINK_WATCHER_TYPE_NONE = 0,
@@ -39,4 +40,7 @@ void _nm_ip_route_to_string(NMIPRoute *route, struct _NMStrBuf *strbuf);
NMTeamLinkWatcher *nm_utils_team_link_watcher_from_string(const char *str, GError **error);
+char *_nm_utils_wireguard_peer_to_string(NMWireGuardPeer *peer);
+NMWireGuardPeer *_nm_utils_wireguard_peer_from_string(const char *str, GError **error);
+
#endif /* __NM_LIBNM_CORE_AUX_H__ */
diff --git a/src/libnm-core-impl/nm-setting-wireguard.c b/src/libnm-core-impl/nm-setting-wireguard.c
index 668af1f6e..a03090865 100644
--- a/src/libnm-core-impl/nm-setting-wireguard.c
+++ b/src/libnm-core-impl/nm-setting-wireguard.c
@@ -2515,6 +2515,73 @@ nm_setting_wireguard_class_init(NMSettingWireGuardClass *klass)
NMSettingWireGuard,
_priv.ip6_auto_default_route);
+ /* ---nmcli---
+ * property: peers
+ * format: a comma-separated list of WireGuard peers
+ * description:
+ * A comma-separated list of WireGuard peers. Each peer has the following syntax:
+ *
+ * PUBLIC_KEY [ATTRIBUTE=VALUE [ATTRIBUTE=VALUE]...]
+ *
+ * The supported attributes are: endpoint, allowed-ips, persistent-keepalive,
+ * preshared-key, preshared-key-flags.
+ * description-docbook:
+ *
+ * A comma-separated list of WireGuard peers. Each peer has the following syntax:
+ *
+ *
+ *
+ * public-key
+ * [attribute=value
+ * [attribute=value]...]
+ *
+ *
+ *
+ * The public key is required and must be encoded as base64; it can be
+ * calculated by running wg pubkey on the private key,
+ * and it is usually transmitted out of band to the author of the configuration
+ * file.
+ *
+ *
+ * The supported attributes are:
+ *
+ *
+ * endpoint
+ * An endpoint IP or hostname, followed by a colon, and then
+ * a port number.
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ * allowed-ips
+ * A semicolon-separated list of IP (v4 or v6) addresses
+ * with CIDR masks from which incoming traffic for this peer is allowed
+ * and to which outgoing traffic for this peer is directed
+ *
+ *
+ *
+ * persistent-keepalive
+ * An interval in seconds, between 1 and 65535, of
+ * how often to send an authenticated empty packet to the peer for the
+ * purpose of keeping a stateful firewall or NAT mapping valid persistently.
+ *
+ *
+ *
+ * preshared-key
+ * A base64 preshared key generated by "wg genpsk". Optional,
+ * and may be omitted.
+ *
+ *
+ * preshared-key-flags
+ * The secret flags for the preshared-key.
+ *
+ *
+ *
+ * ---end---
+ */
/* ---dbus---
* property: peers
* format: array of 'a{sv}'
diff --git a/src/libnmc-setting/nm-meta-setting-desc.c b/src/libnmc-setting/nm-meta-setting-desc.c
index 9b11cd4a1..b025093ad 100644
--- a/src/libnmc-setting/nm-meta-setting-desc.c
+++ b/src/libnmc-setting/nm-meta-setting-desc.c
@@ -4156,6 +4156,50 @@ _optionlist_set_fcn_vpn_secrets(NMSetting *setting,
return TRUE;
}
+static void
+_objlist_obj_to_str_fcn_wireguard_peers(NMMetaAccessorGetType get_type,
+ NMSetting *setting,
+ guint idx,
+ GString *str)
+{
+ NMWireGuardPeer *peer;
+ gs_free char *peer_str = NULL;
+
+ peer = nm_setting_wireguard_get_peer(NM_SETTING_WIREGUARD(setting), idx);
+ peer_str = _nm_utils_wireguard_peer_to_string(peer);
+ g_string_append(str, peer_str);
+}
+
+static gboolean
+_objlist_set_fcn_wireguard_peers(NMSetting *setting,
+ gboolean do_add,
+ const char *value,
+ GError **error)
+{
+ NMSettingWireGuard *s_wg = NM_SETTING_WIREGUARD(setting);
+ nm_auto_unref_wgpeer NMWireGuardPeer *peer = NULL;
+
+ peer = _nm_utils_wireguard_peer_from_string(value, error);
+ if (!peer)
+ return FALSE;
+
+ if (do_add) {
+ nm_setting_wireguard_append_peer(s_wg, peer);
+ } else {
+ NMWireGuardPeer *match;
+ guint idx;
+
+ match = nm_setting_wireguard_get_peer_by_public_key(s_wg,
+ nm_wireguard_peer_get_public_key(peer),
+ &idx);
+ if (match) {
+ nm_setting_wireguard_remove_peer(s_wg, idx);
+ }
+ }
+
+ return TRUE;
+}
+
static gboolean
_set_fcn_wired_s390_subchannels(ARGS_SET_FCN)
{
@@ -8423,6 +8467,21 @@ static const NMMetaPropertyInfo *const property_infos_WIREGUARD[] = {
PROPERTY_INFO_WITH_DESC (NM_SETTING_WIREGUARD_IP6_AUTO_DEFAULT_ROUTE,
.property_type = &_pt_gobject_ternary,
),
+
+ PROPERTY_INFO_WITH_DESC (NM_SETTING_WIREGUARD_PEERS,
+ .property_type = &_pt_objlist,
+ .property_typ_data = DEFINE_PROPERTY_TYP_DATA (
+ PROPERTY_TYP_DATA_SUBTYPE (objlist,
+ .get_num_fcn = OBJLIST_GET_NUM_FCN (NMSettingWireGuard, nm_setting_wireguard_get_peers_len),
+ .clear_all_fcn = (void (*) (NMSetting *))(void (*)(void)) nm_setting_wireguard_clear_peers,
+ .obj_to_str_fcn = _objlist_obj_to_str_fcn_wireguard_peers,
+ .set_fcn = _objlist_set_fcn_wireguard_peers,
+ .remove_by_idx_fcn_u = (void (*) (NMSetting *, guint idx))(void (*)(void)) nm_setting_wireguard_remove_peer,
+ .strsplit_plain = TRUE,
+ ),
+ ),
+ ),
+
NULL
};
diff --git a/src/libnmc-setting/settings-docs.h.in b/src/libnmc-setting/settings-docs.h.in
index 814ca1929..8678dc64e 100644
--- a/src/libnmc-setting/settings-docs.h.in
+++ b/src/libnmc-setting/settings-docs.h.in
@@ -443,6 +443,7 @@
#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. Usually you want this automatism enabled. 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. Note that if the peer's AllowedIPs is \"0.0.0.0/0\" or \"::/0\" and the profile's ipv4.never-default or ipv6.never-default setting is enabled, the peer route for this peer won't be added automatically.")
+#define DESCRIBE_DOC_NM_SETTING_WIREGUARD_PEERS N_("A comma-separated list of WireGuard peers. Each peer has the following syntax: PUBLIC_KEY [ATTRIBUTE=VALUE [ATTRIBUTE=VALUE]...] The supported attributes are: endpoint, allowed-ips, persistent-keepalive, preshared-key, preshared-key-flags.")
#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_WIRELESS_AP_ISOLATION N_("Configures AP isolation, which prevents communication between wireless devices connected to this AP. This property can be set to a value different from \"default\" (-1) only when the interface is configured in AP mode. If set to \"true\" (1), devices are not able to communicate with each other. This increases security because it protects devices against attacks from other clients in the network. At the same time, it prevents devices to access resources on the same wireless networks as file shares, printers, etc. If set to \"false\" (0), devices can talk to each other. When set to \"default\" (-1), the global default is used; in case the global default is unspecified it is assumed to be \"false\" (0).")
diff --git a/src/nmcli/gen-metadata-nm-settings-nmcli.xml.in b/src/nmcli/gen-metadata-nm-settings-nmcli.xml.in
index 155c413cb..957480aea 100644
--- a/src/nmcli/gen-metadata-nm-settings-nmcli.xml.in
+++ b/src/nmcli/gen-metadata-nm-settings-nmcli.xml.in
@@ -2320,6 +2320,9 @@
nmcli-description="Like ip4-auto-default-route, but for the IPv6 default route."
format="ternary"
values="true/yes/on, false/no/off, default/unknown" />
+