summaryrefslogtreecommitdiff
path: root/src/nmtui
diff options
context:
space:
mode:
authorJavier Sánchez Parra <javsanpar@riseup.net>2021-01-27 19:27:12 +0100
committerThomas Haller <thaller@redhat.com>2021-08-17 14:10:12 +0200
commitb0f5b1d97a6502d5e87da617e719c859ea6867e3 (patch)
treece4ea9f650ed92c865f235fe0796a26e76039362 /src/nmtui
parent5d0d8f9e3a11fd14f4dd09b0288efd1cffe78e6a (diff)
downloadNetworkManager-b0f5b1d97a6502d5e87da617e719c859ea6867e3.tar.gz
tui: add WireGuard support to nmtui
https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/736
Diffstat (limited to 'src/nmtui')
-rw-r--r--src/nmtui/meson.build3
-rw-r--r--src/nmtui/nm-editor-bindings.c223
-rw-r--r--src/nmtui/nm-editor-bindings.h50
-rw-r--r--src/nmtui/nm-editor-utils.c25
-rw-r--r--src/nmtui/nmt-connect-connection-list.c1
-rw-r--r--src/nmtui/nmt-editor.c3
-rw-r--r--src/nmtui/nmt-page-wireguard.c125
-rw-r--r--src/nmtui/nmt-page-wireguard.h34
-rw-r--r--src/nmtui/nmt-wireguard-peer-editor.c247
-rw-r--r--src/nmtui/nmt-wireguard-peer-editor.h35
-rw-r--r--src/nmtui/nmt-wireguard-peer-list.c451
-rw-r--r--src/nmtui/nmt-wireguard-peer-list.h42
12 files changed, 1239 insertions, 0 deletions
diff --git a/src/nmtui/meson.build b/src/nmtui/meson.build
index fe4401751c..b4ac068b5b 100644
--- a/src/nmtui/meson.build
+++ b/src/nmtui/meson.build
@@ -31,12 +31,15 @@ executable(
'nmt-page-team-port.c',
'nmt-page-vlan.c',
'nmt-page-wifi.c',
+ 'nmt-page-wireguard.c',
'nmt-password-dialog.c',
'nmt-password-fields.c',
'nmt-route-editor.c',
'nmt-route-entry.c',
'nmt-route-table.c',
'nmt-slave-list.c',
+ 'nmt-wireguard-peer-list.c',
+ 'nmt-wireguard-peer-editor.c',
'nmtui.c',
'nmtui-connect.c',
'nmtui-edit.c',
diff --git a/src/nmtui/nm-editor-bindings.c b/src/nmtui/nm-editor-bindings.c
index 2b1c474847..59474fba42 100644
--- a/src/nmtui/nm-editor-bindings.c
+++ b/src/nmtui/nm-editor-bindings.c
@@ -576,6 +576,229 @@ nm_editor_bind_ip_route_to_strings(int addr_family,
NULL);
}
+gboolean
+peer_transform_to_public_key_string(GBinding * binding,
+ const GValue *source_value,
+ GValue * target_value,
+ gpointer user_data)
+{
+ NMWireGuardPeer *peer;
+ char * string;
+
+ peer = g_value_get_boxed(source_value);
+ if (peer && nm_wireguard_peer_get_public_key(peer)) {
+ string = g_strdup(nm_wireguard_peer_get_public_key(peer));
+ g_value_take_string(target_value, string);
+ } else
+ g_value_set_string(target_value, "");
+ return TRUE;
+}
+
+gboolean
+peer_transform_to_allowed_ips_string(GBinding * binding,
+ const GValue *source_value,
+ GValue * target_value,
+ gpointer user_data)
+{
+ NMWireGuardPeer *peer;
+ GString * string = NULL;
+ guint i, len;
+
+ peer = g_value_get_boxed(source_value);
+ if (!peer)
+ return TRUE;
+
+ len = nm_wireguard_peer_get_allowed_ips_len(peer);
+ for (i = 0; i < len; i++) {
+ if (!string)
+ string = g_string_new("");
+ else
+ g_string_append_c(string, ',');
+ g_string_append(string, nm_wireguard_peer_get_allowed_ip(peer, i, NULL));
+ }
+ if (string)
+ g_value_take_string(target_value, g_string_free(string, FALSE));
+
+ return TRUE;
+}
+
+gboolean
+peer_transform_to_endpoint_string(GBinding * binding,
+ const GValue *source_value,
+ GValue * target_value,
+ gpointer user_data)
+{
+ NMWireGuardPeer *peer;
+ char * string;
+
+ peer = g_value_get_boxed(source_value);
+ if (peer && nm_wireguard_peer_get_endpoint(peer)) {
+ string = g_strdup_printf("%s", nm_wireguard_peer_get_endpoint(peer));
+ g_value_take_string(target_value, string);
+ } else
+ g_value_set_string(target_value, "");
+ return TRUE;
+}
+
+gboolean
+peer_transform_to_preshared_key_string(GBinding * binding,
+ const GValue *source_value,
+ GValue * target_value,
+ gpointer user_data)
+{
+ NMWireGuardPeer *peer;
+ char * string;
+
+ peer = g_value_get_boxed(source_value);
+ if (peer && nm_wireguard_peer_get_preshared_key(peer)) {
+ string = g_strdup_printf("%s", nm_wireguard_peer_get_preshared_key(peer));
+ g_value_take_string(target_value, string);
+ } else
+ g_value_set_string(target_value, "");
+ return TRUE;
+}
+
+gboolean
+peer_transform_to_persistent_keepalive_string(GBinding * binding,
+ const GValue *source_value,
+ GValue * target_value,
+ gpointer user_data)
+{
+ NMWireGuardPeer *peer;
+ char * string;
+
+ peer = g_value_get_boxed(source_value);
+ if (peer && nm_wireguard_peer_get_persistent_keepalive(peer)) {
+ string = g_strdup_printf("%d", nm_wireguard_peer_get_persistent_keepalive(peer));
+ g_value_take_string(target_value, string);
+ } else
+ g_value_set_string(target_value, "");
+ return TRUE;
+}
+
+gboolean
+peer_transform_from_public_key_string(GBinding * binding,
+ const GValue *source_value,
+ GValue * target_value,
+ gpointer user_data)
+{
+ NMWireGuardPeer *peer;
+ const char * text;
+
+ text = g_value_get_string(source_value);
+
+ /* Fetch the original property value */
+ g_object_get(g_binding_get_source(binding),
+ g_binding_get_source_property(binding),
+ &peer,
+ NULL);
+
+ nm_wireguard_peer_set_public_key(peer, text, TRUE);
+
+ g_value_take_boxed(target_value, peer);
+ return TRUE;
+}
+
+gboolean
+peer_transform_from_allowed_ips_string(GBinding * binding,
+ const GValue *source_value,
+ GValue * target_value,
+ gpointer user_data)
+{
+ NMWireGuardPeer *peer;
+ const char * text;
+ char ** strv;
+ guint i;
+
+ text = g_value_get_string(source_value);
+
+ /* Fetch the original property value */
+ g_object_get(g_binding_get_source(binding),
+ g_binding_get_source_property(binding),
+ &peer,
+ NULL);
+
+ nm_wireguard_peer_clear_allowed_ips(peer);
+
+ strv = g_strsplit(text, ",", -1);
+ for (i = 0; strv && strv[i]; i++)
+ nm_wireguard_peer_append_allowed_ip(peer, g_strstrip(strv[i]), TRUE);
+ g_strfreev(strv);
+
+ g_value_take_boxed(target_value, peer);
+ return TRUE;
+}
+
+gboolean
+peer_transform_from_endpoint_string(GBinding * binding,
+ const GValue *source_value,
+ GValue * target_value,
+ gpointer user_data)
+{
+ NMWireGuardPeer *peer;
+ const char * text;
+
+ text = g_value_get_string(source_value);
+
+ /* Fetch the original property value */
+ g_object_get(g_binding_get_source(binding),
+ g_binding_get_source_property(binding),
+ &peer,
+ NULL);
+
+ nm_wireguard_peer_set_endpoint(peer, text, TRUE);
+
+ g_value_take_boxed(target_value, peer);
+ return TRUE;
+}
+
+gboolean
+peer_transform_from_preshared_key_string(GBinding * binding,
+ const GValue *source_value,
+ GValue * target_value,
+ gpointer user_data)
+{
+ NMWireGuardPeer *peer;
+ const char * text;
+
+ text = g_value_get_string(source_value);
+
+ /* Fetch the original property value */
+ g_object_get(g_binding_get_source(binding),
+ g_binding_get_source_property(binding),
+ &peer,
+ NULL);
+
+ nm_wireguard_peer_set_preshared_key(peer, text && text[0] ? text : NULL, TRUE);
+ nm_wireguard_peer_set_preshared_key_flags(peer, NM_SETTING_SECRET_FLAG_NONE);
+
+ g_value_take_boxed(target_value, peer);
+ return TRUE;
+}
+
+gboolean
+peer_transform_from_persistent_keepalive_string(GBinding * binding,
+ const GValue *source_value,
+ GValue * target_value,
+ gpointer user_data)
+{
+ NMWireGuardPeer *peer;
+ const char * text;
+
+ text = g_value_get_string(source_value);
+
+ /* Fetch the original property value */
+ g_object_get(g_binding_get_source(binding),
+ g_binding_get_source_property(binding),
+ &peer,
+ NULL);
+
+ nm_wireguard_peer_set_persistent_keepalive(peer, atoi(text));
+
+ g_value_take_boxed(target_value, peer);
+ return TRUE;
+}
+
/* Wireless security method binding */
typedef struct {
NMConnection * connection;
diff --git a/src/nmtui/nm-editor-bindings.h b/src/nmtui/nm-editor-bindings.h
index 027a59a9bd..a9bf51ed0f 100644
--- a/src/nmtui/nm-editor-bindings.h
+++ b/src/nmtui/nm-editor-bindings.h
@@ -53,4 +53,54 @@ void nm_editor_bind_wireless_security_wep_key(NMSettingWirelessSecurity *s_wsec,
void nm_editor_bind_vlan_name(NMSettingVlan *s_vlan, NMSettingConnection *s_con);
+gboolean peer_transform_to_public_key_string(GBinding * binding,
+ const GValue *source_value,
+ GValue * target_value,
+ gpointer user_data);
+
+gboolean peer_transform_to_allowed_ips_string(GBinding * binding,
+ const GValue *source_value,
+ GValue * target_value,
+ gpointer user_data);
+
+gboolean peer_transform_to_endpoint_string(GBinding * binding,
+ const GValue *source_value,
+ GValue * target_value,
+ gpointer user_data);
+
+gboolean peer_transform_to_preshared_key_string(GBinding * binding,
+ const GValue *source_value,
+ GValue * target_value,
+ gpointer user_data);
+
+gboolean peer_transform_to_persistent_keepalive_string(GBinding * binding,
+ const GValue *source_value,
+ GValue * target_value,
+ gpointer user_data);
+
+gboolean peer_transform_from_public_key_string(GBinding * binding,
+ const GValue *source_value,
+ GValue * target_value,
+ gpointer user_data);
+
+gboolean peer_transform_from_allowed_ips_string(GBinding * binding,
+ const GValue *source_value,
+ GValue * target_value,
+ gpointer user_data);
+
+gboolean peer_transform_from_endpoint_string(GBinding * binding,
+ const GValue *source_value,
+ GValue * target_value,
+ gpointer user_data);
+
+gboolean peer_transform_from_preshared_key_string(GBinding * binding,
+ const GValue *source_value,
+ GValue * target_value,
+ gpointer user_data);
+
+gboolean peer_transform_from_persistent_keepalive_string(GBinding * binding,
+ const GValue *source_value,
+ GValue * target_value,
+ gpointer user_data);
+
#endif /* NM_EDITOR_BINDINGS_H */
diff --git a/src/nmtui/nm-editor-utils.c b/src/nmtui/nm-editor-utils.c
index 02d7d6b263..2b6acbbe24 100644
--- a/src/nmtui/nm-editor-utils.c
+++ b/src/nmtui/nm-editor-utils.c
@@ -66,6 +66,22 @@ bond_connection_setup_func(NMConnection *connection, NMSettingConnection *s_con,
}
}
+static void
+wireguard_connection_setup_func(NMConnection * connection,
+ NMSettingConnection *s_con,
+ NMSetting * s_hw)
+{
+ NMSettingIPConfig *s_ip;
+
+ s_ip = (NMSettingIPConfig *) nm_setting_ip4_config_new();
+ g_object_set(s_ip, NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_DISABLED, NULL);
+ nm_connection_add_setting(connection, (NMSetting *) s_ip);
+
+ s_ip = (NMSettingIPConfig *) nm_setting_ip6_config_new();
+ g_object_set(s_ip, NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_DISABLED, NULL);
+ nm_connection_add_setting(connection, (NMSetting *) s_ip);
+}
+
typedef void (*NMEditorNewConnectionSetupFunc)(NMConnection * connection,
NMSettingConnection *s_con,
NMSetting * s_hw);
@@ -243,6 +259,15 @@ nm_editor_utils_get_connection_type_list(void)
}
#endif
+ item = g_new0(NMEditorConnectionTypeDataReal, 1);
+ item->data.name = _("WireGuard");
+ item->data.setting_type = NM_TYPE_SETTING_WIREGUARD;
+ item->data.device_type = NM_TYPE_DEVICE_WIREGUARD;
+ item->data.virtual = TRUE;
+ item->id_format = _("WireGuard connection %d");
+ item->connection_setup_func = wireguard_connection_setup_func;
+ g_ptr_array_add(array, item);
+
g_ptr_array_sort(array, sort_types);
g_ptr_array_add(array, NULL);
diff --git a/src/nmtui/nmt-connect-connection-list.c b/src/nmtui/nmt-connect-connection-list.c
index 4503dc924b..486e14004f 100644
--- a/src/nmtui/nmt-connect-connection-list.c
+++ b/src/nmtui/nmt-connect-connection-list.c
@@ -100,6 +100,7 @@ static const char *device_sort_order[] = {"NMDeviceEthernet",
NM_SETTING_TEAM_SETTING_NAME,
NM_SETTING_BRIDGE_SETTING_NAME,
NM_SETTING_IP_TUNNEL_SETTING_NAME,
+ NM_SETTING_WIREGUARD_SETTING_NAME,
"NMDeviceModem",
"NMDeviceBt"};
static const int device_sort_order_len = G_N_ELEMENTS(device_sort_order);
diff --git a/src/nmtui/nmt-editor.c b/src/nmtui/nmt-editor.c
index 0ef236938f..baca5b3cf3 100644
--- a/src/nmtui/nmt-editor.c
+++ b/src/nmtui/nmt-editor.c
@@ -39,6 +39,7 @@
#include "nmt-page-team-port.h"
#include "nmt-page-vlan.h"
#include "nmt-page-wifi.h"
+#include "nmt-page-wireguard.h"
#include "libnmc-setting/nm-meta-setting-access.h"
@@ -372,6 +373,8 @@ nmt_editor_constructed(GObject *object)
page = nmt_page_wifi_new(priv->edit_connection, deventry);
else if (nm_connection_is_type(priv->edit_connection, NM_SETTING_IP_TUNNEL_SETTING_NAME))
page = nmt_page_ip_tunnel_new(priv->edit_connection, deventry);
+ else if (nm_connection_is_type(priv->edit_connection, NM_SETTING_WIREGUARD_SETTING_NAME))
+ page = nmt_page_wireguard_new(priv->edit_connection, deventry);
else
g_assert_not_reached();
diff --git a/src/nmtui/nmt-page-wireguard.c b/src/nmtui/nmt-page-wireguard.c
new file mode 100644
index 0000000000..8b797d33c7
--- /dev/null
+++ b/src/nmtui/nmt-page-wireguard.c
@@ -0,0 +1,125 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (C) 2021 Red Hat, Inc.
+ */
+/**
+ * SECTION:nmt-page-wireguard
+ * @short_description: The editor page for WireGuard connections
+ */
+
+#include "libnm-client-aux-extern/nm-default-client.h"
+
+#include "nmt-page-wireguard.h"
+
+#include "nmt-device-entry.h"
+#include "nmt-mtu-entry.h"
+#include "nmt-wireguard-peer-list.h"
+
+G_DEFINE_TYPE(NmtPageWireGuard, nmt_page_wireguard, NMT_TYPE_EDITOR_PAGE_DEVICE)
+
+#define NMT_PAGE_WIREGUARD_GET_PRIVATE(o) \
+ (G_TYPE_INSTANCE_GET_PRIVATE((o), NMT_TYPE_PAGE_WIREGUARD, NmtPageWireGuardPrivate))
+
+typedef struct {
+ NmtWireguardPeerList *peers;
+} NmtPageWireGuardPrivate;
+
+NmtEditorPage *
+nmt_page_wireguard_new(NMConnection *conn, NmtDeviceEntry *deventry)
+{
+ return g_object_new(NMT_TYPE_PAGE_WIREGUARD,
+ "connection",
+ conn,
+ "device-entry",
+ deventry,
+ NULL);
+}
+
+static void
+nmt_page_wireguard_init(NmtPageWireGuard *wireguard)
+{}
+
+static void
+nmt_page_wireguard_constructed(GObject *object)
+{
+ NmtPageWireGuard * wireguard = NMT_PAGE_WIREGUARD(object);
+ NmtPageWireGuardPrivate *priv = NMT_PAGE_WIREGUARD_GET_PRIVATE(wireguard);
+ NmtEditorSection * section;
+ NmtEditorGrid * grid;
+ NMSettingWireGuard * s_wireguard;
+ NmtNewtWidget * widget;
+ NMConnection * conn;
+
+ conn = nmt_editor_page_get_connection(NMT_EDITOR_PAGE(wireguard));
+ s_wireguard = NM_SETTING_WIREGUARD(nm_connection_get_setting(conn, NM_TYPE_SETTING_WIREGUARD));
+ if (!s_wireguard) {
+ nm_connection_add_setting(conn, nm_setting_wireguard_new());
+ s_wireguard =
+ NM_SETTING_WIREGUARD(nm_connection_get_setting(conn, NM_TYPE_SETTING_WIREGUARD));
+ }
+
+ section = nmt_editor_section_new(_("WireGuard"), NULL, TRUE);
+ grid = nmt_editor_section_get_body(section);
+
+ widget = nmt_newt_entry_new(40, 0);
+ nmt_editor_grid_append(grid, _("Private key"), widget, NULL);
+ g_object_bind_property(s_wireguard,
+ NM_SETTING_WIREGUARD_PRIVATE_KEY,
+ widget,
+ "text",
+ G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
+
+ widget = nmt_newt_entry_new(40, 0);
+ nmt_editor_grid_append(grid, _("Listen port"), widget, NULL);
+ g_object_bind_property(s_wireguard,
+ NM_SETTING_WIREGUARD_LISTEN_PORT,
+ widget,
+ "text",
+ G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
+
+ widget = nmt_newt_entry_new(40, 0);
+ nmt_editor_grid_append(grid, _("Fwmark"), widget, NULL);
+ g_object_bind_property(s_wireguard,
+ NM_SETTING_WIREGUARD_FWMARK,
+ widget,
+ "text",
+ G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
+
+ widget = nmt_mtu_entry_new();
+ nmt_editor_grid_append(grid, _("MTU"), widget, NULL);
+ g_object_bind_property(s_wireguard,
+ NM_SETTING_WIREGUARD_MTU,
+ widget,
+ "mtu",
+ G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
+
+ widget = nmt_newt_checkbox_new(_("Add peer routes"));
+ nmt_editor_grid_append(grid, NULL, widget, NULL);
+ g_object_bind_property(s_wireguard,
+ NM_SETTING_WIREGUARD_PEER_ROUTES,
+ widget,
+ "active",
+ G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
+
+ widget = nmt_newt_separator_new();
+ nmt_editor_grid_append(grid, _("Peers"), widget, NULL);
+ nmt_editor_grid_set_row_flags(grid, widget, NMT_EDITOR_GRID_ROW_LABEL_ALIGN_LEFT);
+
+ widget = nmt_wireguard_peer_list_new(s_wireguard);
+ nmt_editor_grid_append(grid, NULL, widget, NULL);
+ priv->peers = NMT_WIREGUARD_PEER_LIST(widget);
+
+ nmt_editor_page_add_section(NMT_EDITOR_PAGE(wireguard), section);
+
+ G_OBJECT_CLASS(nmt_page_wireguard_parent_class)->constructed(object);
+}
+
+static void
+nmt_page_wireguard_class_init(NmtPageWireGuardClass *wireguard_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS(wireguard_class);
+
+ g_type_class_add_private(wireguard_class, sizeof(NmtPageWireGuardPrivate));
+
+ object_class->constructed = nmt_page_wireguard_constructed;
+}
diff --git a/src/nmtui/nmt-page-wireguard.h b/src/nmtui/nmt-page-wireguard.h
new file mode 100644
index 0000000000..8e39139c80
--- /dev/null
+++ b/src/nmtui/nmt-page-wireguard.h
@@ -0,0 +1,34 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (C) 2021 Red Hat, Inc.
+ */
+
+#ifndef NMT_PAGE_WIREGUARD_H
+#define NMT_PAGE_WIREGUARD_H
+
+#include "nmt-editor-page-device.h"
+
+#define NMT_TYPE_PAGE_WIREGUARD (nmt_page_wireguard_get_type())
+#define NMT_PAGE_WIREGUARD(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST((obj), NMT_TYPE_PAGE_WIREGUARD, NmtPageWireGuard))
+#define NMT_PAGE_WIREGUARD_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST((klass), NMT_TYPE_PAGE_WIREGUARD, NmtPageWireGuardClass))
+#define NMT_IS_PAGE_WIREGUARD(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NMT_TYPE_PAGE_WIREGUARD))
+#define NMT_IS_PAGE_WIREGUARD_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE((klass), NMT_TYPE_PAGE_WIREGUARD))
+#define NMT_PAGE_WIREGUARD_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS((obj), NMT_TYPE_PAGE_WIREGUARD, NmtPageWireGuardClass))
+
+typedef struct {
+ NmtEditorPageDevice parent;
+} NmtPageWireGuard;
+
+typedef struct {
+ NmtEditorPageDeviceClass parent;
+} NmtPageWireGuardClass;
+
+GType nmt_page_wireguard_get_type(void);
+
+NmtEditorPage *nmt_page_wireguard_new(NMConnection *conn, NmtDeviceEntry *deventry);
+
+#endif /* NMT_PAGE_WIREGUARD_H */
diff --git a/src/nmtui/nmt-wireguard-peer-editor.c b/src/nmtui/nmt-wireguard-peer-editor.c
new file mode 100644
index 0000000000..810bdf6966
--- /dev/null
+++ b/src/nmtui/nmt-wireguard-peer-editor.c
@@ -0,0 +1,247 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (C) 2021 Red Hat, Inc.
+ */
+/**
+ * SECTION:nmt-wireguard-peer-editor
+ * @short_description: The editor page for Peer connections
+ */
+
+#include "libnm-client-aux-extern/nm-default-client.h"
+
+#include "nmt-wireguard-peer-editor.h"
+#include "nmt-page-wireguard.h"
+
+#include "nmt-device-entry.h"
+#include "nmt-mtu-entry.h"
+#include "nmt-wireguard-peer-list.h"
+
+#include "nm-editor-bindings.h"
+
+G_DEFINE_TYPE(NmtWireguardPeerEditor, nmt_wireguard_peer_editor, NMT_TYPE_NEWT_FORM)
+
+#define NMT_WIREGUARD_PEER_EDITOR_GET_PRIVATE(o) \
+ (G_TYPE_INSTANCE_GET_PRIVATE((o), \
+ NMT_TYPE_WIREGUARD_PEER_EDITOR, \
+ NmtWireguardPeerEditorPrivate))
+
+typedef struct {
+ NMSettingWireGuard *orig_setting;
+ NMSettingWireGuard *edit_setting;
+ NMWireGuardPeer * peer;
+ NmtNewtEntry * private_key;
+} NmtWireguardPeerEditorPrivate;
+
+enum {
+ PROP_0,
+
+ PROP_SETTING,
+ PROP_PEER,
+ PROP_PUBLIC_KEY,
+
+ LAST_PROP
+};
+
+NmtNewtForm *
+nmt_wireguard_peer_editor_new(NMSettingWireGuard *setting, NMWireGuardPeer *peer)
+{
+ return g_object_new(NMT_TYPE_WIREGUARD_PEER_EDITOR, "setting", setting, "peer", peer, NULL);
+}
+
+static void
+nmt_wireguard_peer_editor_init(NmtWireguardPeerEditor *peer)
+{}
+
+static void
+save_peer_and_exit(NmtNewtButton *button, gpointer user_data)
+{
+ NmtWireguardPeerEditor * editor = user_data;
+ NmtWireguardPeerEditorPrivate *priv = NMT_WIREGUARD_PEER_EDITOR_GET_PRIVATE(editor);
+
+ nm_setting_wireguard_append_peer(priv->orig_setting, priv->peer);
+
+ nmt_newt_form_quit(NMT_NEWT_FORM(editor));
+}
+
+static void
+nmt_wireguard_peer_editor_constructed(GObject *object)
+{
+ NmtWireguardPeerEditor *peer = NMT_WIREGUARD_PEER_EDITOR(object);
+ NmtEditorSection * section;
+ NmtEditorGrid * grid;
+ NmtNewtWidget * widget, *label;
+ NmtNewtWidget * buttons, *ok, *cancel;
+
+ if (G_OBJECT_CLASS(nmt_wireguard_peer_editor_parent_class)->constructed)
+ G_OBJECT_CLASS(nmt_wireguard_peer_editor_parent_class)->constructed(object);
+
+ section = nmt_editor_section_new(_("Peer"), NULL, TRUE);
+ grid = nmt_editor_section_get_body(section);
+
+ widget = nmt_newt_entry_new(40, 0);
+ nmt_editor_grid_append(grid, _("Public key"), widget, NULL);
+ g_object_bind_property_full(object,
+ "peer",
+ widget,
+ "text",
+ G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL,
+ peer_transform_to_public_key_string,
+ peer_transform_from_public_key_string,
+ NULL,
+ NULL);
+
+ widget = nmt_newt_entry_new(40, 0);
+ nmt_editor_grid_append(grid, _("Allowed IPs"), widget, NULL);
+ g_object_bind_property_full(peer,
+ "peer",
+ widget,
+ "text",
+ G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL,
+ peer_transform_to_allowed_ips_string,
+ peer_transform_from_allowed_ips_string,
+ NULL,
+ NULL);
+
+ widget = nmt_newt_entry_new(40, 0);
+ nmt_editor_grid_append(grid, _("Endpoint"), widget, NULL);
+ g_object_bind_property_full(peer,
+ "peer",
+ widget,
+ "text",
+ G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL,
+ peer_transform_to_endpoint_string,
+ peer_transform_from_endpoint_string,
+ NULL,
+ NULL);
+
+ widget = nmt_newt_entry_new(40, 0);
+ nmt_editor_grid_append(grid, _("Preshared key"), widget, NULL);
+ g_object_bind_property_full(peer,
+ "peer",
+ widget,
+ "text",
+ G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL,
+ peer_transform_to_preshared_key_string,
+ peer_transform_from_preshared_key_string,
+ NULL,
+ NULL);
+
+ widget = nmt_newt_entry_numeric_new(10, 0, G_MAXINT);
+ label = nmt_newt_label_new(C_("seconds", "seconds"));
+ nmt_editor_grid_append(grid, _("Persistent keepalive"), widget, label);
+ g_object_bind_property_full(peer,
+ "peer",
+ widget,
+ "text",
+ G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL,
+ peer_transform_to_persistent_keepalive_string,
+ peer_transform_from_persistent_keepalive_string,
+ NULL,
+ NULL);
+
+ buttons = nmt_newt_grid_new();
+ nmt_editor_grid_append(grid, NULL, buttons, NULL);
+ nmt_newt_widget_set_padding(buttons, 0, 1, 0, 0);
+
+ cancel = g_object_ref_sink(nmt_newt_button_new(_("Cancel")));
+ nmt_newt_widget_set_exit_on_activate(cancel, TRUE);
+ nmt_newt_grid_add(NMT_NEWT_GRID(buttons), cancel, 0, 0);
+ nmt_newt_grid_set_flags(NMT_NEWT_GRID(buttons),
+ cancel,
+ NMT_NEWT_GRID_EXPAND_X | NMT_NEWT_GRID_ANCHOR_RIGHT
+ | NMT_NEWT_GRID_FILL_Y);
+
+ ok = g_object_ref_sink(nmt_newt_button_new(_("OK")));
+ g_signal_connect(ok, "clicked", G_CALLBACK(save_peer_and_exit), peer);
+ nmt_newt_grid_add(NMT_NEWT_GRID(buttons), ok, 1, 0);
+ nmt_newt_widget_set_padding(ok, 1, 0, 0, 0);
+ g_object_bind_property(NMT_NEWT_GRID(buttons), "valid", ok, "sensitive", G_BINDING_SYNC_CREATE);
+
+ nmt_newt_form_set_content(NMT_NEWT_FORM(peer), NMT_NEWT_WIDGET(section));
+}
+
+static void
+nmt_wireguard_peer_editor_set_property(GObject * object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec * pspec)
+{
+ NmtWireguardPeerEditorPrivate *priv = NMT_WIREGUARD_PEER_EDITOR_GET_PRIVATE(object);
+
+ switch (prop_id) {
+ case PROP_SETTING:
+ priv->orig_setting = g_value_dup_object(value);
+ priv->edit_setting =
+ NM_SETTING_WIREGUARD(nm_setting_duplicate(NM_SETTING(priv->orig_setting)));
+ break;
+ case PROP_PEER:
+ priv->peer = g_value_dup_boxed(value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+nmt_wireguard_peer_editor_get_property(GObject * object,
+ guint prop_id,
+ GValue * value,
+ GParamSpec *pspec)
+{
+ NmtWireguardPeerEditorPrivate *priv = NMT_WIREGUARD_PEER_EDITOR_GET_PRIVATE(object);
+
+ switch (prop_id) {
+ case PROP_SETTING:
+ g_value_set_object(value, priv->edit_setting);
+ break;
+ case PROP_PEER:
+ g_value_set_boxed(value, priv->peer);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+nmt_wireguard_peer_editor_class_init(NmtWireguardPeerEditorClass *peer_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS(peer_class);
+
+ g_type_class_add_private(peer_class, sizeof(NmtWireguardPeerEditorPrivate));
+
+ /* virtual methods */
+ object_class->constructed = nmt_wireguard_peer_editor_constructed;
+ object_class->set_property = nmt_wireguard_peer_editor_set_property;
+ object_class->get_property = nmt_wireguard_peer_editor_get_property;
+
+ /* properties */
+
+ /**
+ * NmtPeerPage:setting:
+ *
+ * The page's #NMSettingWireGuard.
+ */
+ g_object_class_install_property(
+ object_class,
+ PROP_SETTING,
+ g_param_spec_object("setting",
+ "",
+ "",
+ NM_TYPE_SETTING_WIREGUARD,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NmtPeerPage:peer:
+ *
+ * The page's #NMWireGuardPeer.
+ */
+ g_object_class_install_property(object_class,
+ PROP_PEER,
+ g_param_spec_boxed("peer",
+ "",
+ "",
+ nm_wireguard_peer_get_type(),
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+}
diff --git a/src/nmtui/nmt-wireguard-peer-editor.h b/src/nmtui/nmt-wireguard-peer-editor.h
new file mode 100644
index 0000000000..ffe76c8cf9
--- /dev/null
+++ b/src/nmtui/nmt-wireguard-peer-editor.h
@@ -0,0 +1,35 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (C) 2021 Red Hat, Inc.
+ */
+
+#ifndef NMT_WIREGUARD_PEER_EDITOR_H
+#define NMT_WIREGUARD_PEER_EDITOR_H
+
+#include "libnmt-newt/nmt-newt.h"
+
+#define NMT_TYPE_WIREGUARD_PEER_EDITOR (nmt_wireguard_peer_editor_get_type())
+#define NMT_WIREGUARD_PEER_EDITOR(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST((obj), NMT_TYPE_WIREGUARD_PEER_EDITOR, NmtWireguardPeerEditor))
+#define NMT_WIREGUARD_PEER_EDITOR_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST((klass), NMT_TYPE_WIREGUARD_PEER_EDITOR, NmtWireguardPeerEditorClass))
+#define NMT_IS_WIREGUARD_PEER_EDITOR(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE((obj), NMT_TYPE_WIREGUARD_PEER_EDITOR))
+#define NMT_IS_WIREGUARD_PEER_EDITOR_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE((klass), NMT_TYPE_WIREGUARD_PEER_EDITOR))
+#define NMT_WIREGUARD_PEER_EDITOR_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS((obj), NMT_TYPE_WIREGUARD_PEER_EDITOR, NmtWireguardPeerEditorClass))
+
+typedef struct {
+ NmtNewtForm parent;
+} NmtWireguardPeerEditor;
+
+typedef struct {
+ NmtNewtFormClass parent;
+} NmtWireguardPeerEditorClass;
+
+GType nmt_wireguard_peer_editor_get_type(void);
+
+NmtNewtForm *nmt_wireguard_peer_editor_new(NMSettingWireGuard *setting, NMWireGuardPeer *peer);
+
+#endif /* NMT_WIREGUARD_PEER_EDITOR_H */
diff --git a/src/nmtui/nmt-wireguard-peer-list.c b/src/nmtui/nmt-wireguard-peer-list.c
new file mode 100644
index 0000000000..03f3ef9aaa
--- /dev/null
+++ b/src/nmtui/nmt-wireguard-peer-list.c
@@ -0,0 +1,451 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (C) 2021 Red Hat, Inc.
+ */
+
+/**
+ * SECTION:nmt-wireguard-peer-list:
+ * @short_description: An editable list of a connection's peers
+ *
+ * #NmtWireguardPeerList implements an #NmtNewtGrid for the
+ * peers of a connection.
+ */
+
+#include "libnm-client-aux-extern/nm-default-client.h"
+
+#include "nmtui.h"
+#include "nmt-wireguard-peer-list.h"
+#include "nmt-wireguard-peer-editor.h"
+
+G_DEFINE_TYPE(NmtWireguardPeerList, nmt_wireguard_peer_list, NMT_TYPE_NEWT_GRID)
+
+#define NMT_WIREGUARD_PEER_LIST_GET_PRIVATE(o) \
+ (G_TYPE_INSTANCE_GET_PRIVATE((o), NMT_TYPE_WIREGUARD_PEER_LIST, NmtWireguardPeerListPrivate))
+
+typedef struct {
+ NMSettingWireGuard *setting;
+ GSList * peers;
+
+ NmtNewtListbox * listbox;
+ NmtNewtButtonBox *buttons;
+
+ NmtNewtWidget *add;
+ NmtNewtWidget *edit;
+ NmtNewtWidget *delete;
+} NmtWireguardPeerListPrivate;
+
+enum {
+ PROP_0,
+
+ PROP_SETTING,
+ PROP_PEERS,
+ PROP_NUM_PEERS,
+
+ LAST_PROP
+};
+
+enum {
+ ADD_PEER,
+ EDIT_PEER,
+ REMOVE_PEER,
+
+ LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = {0};
+
+/**
+ * nmt_wireguard_peer_list_new:
+ * @master: the master #NMSettingWireGuard whose peers are being listed
+ *
+ * Creates a new #NmtWireguardPeerList.
+ *
+ * Returns: a new #NmtWireguardPeerList.
+ */
+NmtNewtWidget *
+nmt_wireguard_peer_list_new(NMSettingWireGuard *setting)
+{
+ return g_object_new(NMT_TYPE_WIREGUARD_PEER_LIST, "setting", setting, NULL);
+}
+
+static void
+add_clicked(NmtNewtButton *button, gpointer list)
+{
+ g_signal_emit(list, signals[ADD_PEER], 0);
+}
+
+static void
+edit_clicked(NmtNewtButton *button, gpointer list)
+{
+ g_signal_emit(list, signals[EDIT_PEER], 0);
+}
+
+static void
+delete_clicked(NmtNewtButton *button, gpointer list)
+{
+ g_signal_emit(list, signals[REMOVE_PEER], 0);
+}
+
+static void
+listbox_activated(NmtNewtWidget *listbox, gpointer list)
+{
+ NmtWireguardPeerListPrivate *priv = NMT_WIREGUARD_PEER_LIST_GET_PRIVATE(list);
+
+ edit_clicked(NMT_NEWT_BUTTON(priv->edit), list);
+}
+
+static void
+nmt_wireguard_peer_list_init(NmtWireguardPeerList *list)
+{
+ NmtWireguardPeerListPrivate *priv = NMT_WIREGUARD_PEER_LIST_GET_PRIVATE(list);
+ NmtNewtWidget * listbox, *buttons;
+ NmtNewtGrid * grid = NMT_NEWT_GRID(list);
+
+ listbox = g_object_new(NMT_TYPE_NEWT_LISTBOX,
+ "flags",
+ NMT_NEWT_LISTBOX_SCROLL | NMT_NEWT_LISTBOX_BORDER,
+ "skip-null-keys",
+ TRUE,
+ NULL);
+ priv->listbox = NMT_NEWT_LISTBOX(listbox);
+ nmt_newt_grid_add(grid, listbox, 0, 0);
+ nmt_newt_grid_set_flags(grid,
+ listbox,
+ NMT_NEWT_GRID_FILL_X | NMT_NEWT_GRID_FILL_Y | NMT_NEWT_GRID_EXPAND_X
+ | NMT_NEWT_GRID_EXPAND_Y);
+ g_signal_connect(priv->listbox, "activated", G_CALLBACK(listbox_activated), list);
+
+ buttons = nmt_newt_button_box_new(NMT_NEWT_BUTTON_BOX_VERTICAL);
+ priv->buttons = NMT_NEWT_BUTTON_BOX(buttons);
+ nmt_newt_grid_add(grid, buttons, 1, 0);
+ nmt_newt_widget_set_padding(buttons, 1, 1, 0, 1);
+ nmt_newt_grid_set_flags(grid,
+ buttons,
+ NMT_NEWT_GRID_FILL_X | NMT_NEWT_GRID_FILL_Y | NMT_NEWT_GRID_EXPAND_Y);
+
+ priv->add = nmt_newt_button_box_add_start(priv->buttons, _("Add"));
+ g_signal_connect(priv->add, "clicked", G_CALLBACK(add_clicked), list);
+
+ priv->edit = nmt_newt_button_box_add_start(priv->buttons, _("Edit..."));
+ g_signal_connect(priv->edit, "clicked", G_CALLBACK(edit_clicked), list);
+
+ priv->delete = nmt_newt_button_box_add_start(priv->buttons, _("Delete"));
+ g_signal_connect(priv->delete, "clicked", G_CALLBACK(delete_clicked), list);
+}
+
+static void nmt_wireguard_peer_list_rebuild(NmtWireguardPeerList *list);
+
+static void
+rebuild_on_peer_changed(gpointer list)
+{
+ nmt_wireguard_peer_list_rebuild(list);
+}
+
+static void
+free_peers(NmtWireguardPeerList *list)
+{
+ NmtWireguardPeerListPrivate *priv = NMT_WIREGUARD_PEER_LIST_GET_PRIVATE(list);
+
+ g_slist_free(priv->peers);
+ priv->peers = NULL;
+}
+
+static void
+nmt_wireguard_peer_list_finalize(GObject *object)
+{
+ NmtWireguardPeerListPrivate *priv = NMT_WIREGUARD_PEER_LIST_GET_PRIVATE(object);
+
+ free_peers(NMT_WIREGUARD_PEER_LIST(object));
+ g_object_unref(priv->setting);
+ g_object_unref(priv->peers);
+
+ G_OBJECT_CLASS(nmt_wireguard_peer_list_parent_class)->finalize(object);
+}
+
+static void
+nmt_wireguard_peer_list_add_peer(NmtWireguardPeerList *list)
+{
+ NmtWireguardPeerListPrivate *priv = NMT_WIREGUARD_PEER_LIST_GET_PRIVATE(list);
+ NMWireGuardPeer * peer = nm_wireguard_peer_new();
+ NmtNewtForm * editor;
+
+ editor = nmt_wireguard_peer_editor_new(priv->setting, peer);
+
+ if (!editor)
+ return;
+
+ nmt_newt_form_run_sync(editor);
+ g_object_unref(editor);
+ rebuild_on_peer_changed(list);
+}
+
+static void
+nmt_wireguard_peer_list_edit_peer(NmtWireguardPeerList *list)
+{
+ NmtWireguardPeerListPrivate *priv = NMT_WIREGUARD_PEER_LIST_GET_PRIVATE(list);
+ NMWireGuardPeer * orig_peer, *edit_peer;
+ NmtNewtForm * editor;
+ int selected_row;
+
+ selected_row = nmt_newt_listbox_get_active(priv->listbox);
+
+ if (selected_row >= 0) {
+ orig_peer = nm_setting_wireguard_get_peer(priv->setting, (guint) selected_row);
+ edit_peer = nm_wireguard_peer_new_clone(orig_peer, TRUE);
+ editor = nmt_wireguard_peer_editor_new(priv->setting, edit_peer);
+ if (!editor)
+ return;
+ nmt_newt_form_run_sync(editor);
+ g_object_unref(editor);
+ rebuild_on_peer_changed(list);
+ }
+}
+
+static void
+nmt_wireguard_peer_list_remove_peer(NmtWireguardPeerList *list)
+{
+ NmtWireguardPeerListPrivate *priv = NMT_WIREGUARD_PEER_LIST_GET_PRIVATE(list);
+ int selected_row;
+
+ selected_row = nmt_newt_listbox_get_active(priv->listbox);
+
+ if (selected_row >= 0) {
+ nm_setting_wireguard_remove_peer(priv->setting, (guint) selected_row);
+ rebuild_on_peer_changed(list);
+ }
+}
+
+static void
+nmt_wireguard_peer_list_set_property(GObject * object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec * pspec)
+{
+ NmtWireguardPeerListPrivate *priv = NMT_WIREGUARD_PEER_LIST_GET_PRIVATE(object);
+
+ switch (prop_id) {
+ case PROP_SETTING:
+ priv->setting = g_value_get_pointer(value);
+ break;
+ case PROP_PEERS:
+ priv->peers = g_value_get_pointer(value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+nmt_wireguard_peer_list_get_property(GObject * object,
+ guint prop_id,
+ GValue * value,
+ GParamSpec *pspec)
+{
+ NmtWireguardPeerListPrivate *priv = NMT_WIREGUARD_PEER_LIST_GET_PRIVATE(object);
+ GPtrArray * peers;
+ GSList * iter;
+
+ switch (prop_id) {
+ case PROP_SETTING:
+ g_value_set_pointer(value, priv->setting);
+ break;
+ case PROP_PEERS:
+ peers = g_ptr_array_new_with_free_func(g_object_unref);
+ for (iter = priv->peers; iter; iter = iter->next)
+ g_ptr_array_add(peers, g_object_ref(iter->data));
+ g_value_take_boxed(value, peers);
+ break;
+ case PROP_NUM_PEERS:
+ g_value_set_int(value, g_slist_length(priv->peers));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+nmt_wireguard_peer_list_rebuild(NmtWireguardPeerList *list)
+{
+ NmtWireguardPeerListPrivate *priv = NMT_WIREGUARD_PEER_LIST_GET_PRIVATE(list);
+ GSList * iter;
+ NMWireGuardPeer * peer, *selected_peer;
+ int i, row, selected_row, num;
+ NMSettingWireGuard * setting = priv->setting;
+
+ selected_row = nmt_newt_listbox_get_active(priv->listbox);
+ selected_peer = nmt_newt_listbox_get_active_key(priv->listbox);
+
+ free_peers(list);
+ num = nm_setting_wireguard_get_peers_len(setting);
+ for (i = 0; i < num; i++) {
+ peer = nm_setting_wireguard_get_peer(setting, i);
+ priv->peers = g_slist_append(priv->peers, peer);
+ }
+ g_object_notify(G_OBJECT(list), "peers");
+ g_object_notify(G_OBJECT(list), "num-peers");
+
+ nmt_newt_component_set_sensitive(NMT_NEWT_COMPONENT(priv->edit), priv->peers != NULL);
+ nmt_newt_component_set_sensitive(NMT_NEWT_COMPONENT(priv->delete), priv->peers != NULL);
+
+ nmt_newt_listbox_clear(priv->listbox);
+
+ for (iter = priv->peers, row = 0; iter; iter = iter->next, row++) {
+ peer = iter->data;
+ nmt_newt_listbox_append(priv->listbox, nm_wireguard_peer_get_public_key(peer), peer);
+ if (peer == selected_peer)
+ selected_row = row;
+ }
+ if (selected_row >= row)
+ selected_row = row - 1;
+ nmt_newt_listbox_set_active(priv->listbox, selected_row);
+}
+
+static void
+rebuild_on_peers_changed(GObject *object, GParamSpec *pspec, gpointer list)
+{
+ nmt_wireguard_peer_list_rebuild(list);
+}
+
+static void
+nmt_wireguard_peer_list_constructed(GObject *object)
+{
+ NmtWireguardPeerList *list = NMT_WIREGUARD_PEER_LIST(object);
+
+ g_signal_connect(nm_client,
+ "notify::" NM_CLIENT_CONNECTIONS,
+ G_CALLBACK(rebuild_on_peers_changed),
+ list);
+
+ nmt_wireguard_peer_list_rebuild(list);
+
+ G_OBJECT_CLASS(nmt_wireguard_peer_list_parent_class)->constructed(object);
+}
+
+static void
+nmt_wireguard_peer_list_class_init(NmtWireguardPeerListClass *list_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS(list_class);
+
+ g_type_class_add_private(list_class, sizeof(NmtWireguardPeerListPrivate));
+
+ /* virtual methods */
+ object_class->constructed = nmt_wireguard_peer_list_constructed;
+ object_class->set_property = nmt_wireguard_peer_list_set_property;
+ object_class->get_property = nmt_wireguard_peer_list_get_property;
+ object_class->finalize = nmt_wireguard_peer_list_finalize;
+
+ list_class->add_peer = nmt_wireguard_peer_list_add_peer;
+ list_class->edit_peer = nmt_wireguard_peer_list_edit_peer;
+ list_class->remove_peer = nmt_wireguard_peer_list_remove_peer;
+
+ /* signals */
+
+ /**
+ * NmtWireguardPeerList::add-connection:
+ * @list: the #NmtWireguardPeerList
+ *
+ * Emitted when the user clicks the list's "Add" button.
+ */
+ signals[ADD_PEER] = g_signal_new("add-peer",
+ G_OBJECT_CLASS_TYPE(object_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET(NmtWireguardPeerListClass, add_peer),
+ NULL,
+ NULL,
+ NULL,
+ G_TYPE_NONE,
+ 0);
+
+ /**
+ * NmtWireguardPeerList::edit-connection:
+ * @list: the #NmtWireguardPeerList
+ * @connection: the connection to edit
+ *
+ * Emitted when the user clicks the list's "Edit" button, or
+ * hits "Return" on the listbox.
+ */
+ signals[EDIT_PEER] = g_signal_new("edit-peer",
+ G_OBJECT_CLASS_TYPE(object_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET(NmtWireguardPeerListClass, edit_peer),
+ NULL,
+ NULL,
+ NULL,
+ G_TYPE_NONE,
+ 0);
+
+ /**
+ * NmtWireguardPeerList::remove-connection:
+ * @list: the #NmtWireguardPeerList
+ * @connection: the connection to remove
+ *
+ * Emitted when the user clicks the list's "Delete" button.
+ */
+ signals[REMOVE_PEER] = g_signal_new("remove-peer",
+ G_OBJECT_CLASS_TYPE(object_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET(NmtWireguardPeerListClass, remove_peer),
+ NULL,
+ NULL,
+ NULL,
+ G_TYPE_NONE,
+ 0);
+
+ /* properties */
+
+ /**
+ * NmtWireguardPeerListFilter:
+ * @list: the #NmtWireguardPeerList
+ * @connection: an #NMConnection
+ * @user_data: the user data
+ *
+ * Decides whether @connection should be displayed in @list.
+ *
+ * Returns: %TRUE or %FALSE
+ */
+ /**
+ * NmtWireguardPeerList:connection-filter:
+ *
+ * A callback function for filtering which connections appear in
+ * the list.
+ */
+ g_object_class_install_property(
+ object_class,
+ PROP_SETTING,
+ g_param_spec_pointer("setting",
+ "",
+ "",
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NmtWireguardPeerList:connections:
+ *
+ * The list of connections in the widget.
+ *
+ * Element-Type: #NMConnection
+ */
+ g_object_class_install_property(object_class,
+ PROP_PEERS,
+ g_param_spec_boxed("peers",
+ "",
+ "",
+ G_TYPE_PTR_ARRAY,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NmtWireguardPeerList:num-connections:
+ *
+ * The number of connections in the widget.
+ */
+ g_object_class_install_property(object_class,
+ PROP_NUM_PEERS,
+ g_param_spec_int("num-peers",
+ "",
+ "",
+ 0,
+ G_MAXINT,
+ 0,
+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+}
diff --git a/src/nmtui/nmt-wireguard-peer-list.h b/src/nmtui/nmt-wireguard-peer-list.h
new file mode 100644
index 0000000000..de3faf1206
--- /dev/null
+++ b/src/nmtui/nmt-wireguard-peer-list.h
@@ -0,0 +1,42 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (C) 2021 Red Hat, Inc.
+ */
+
+#ifndef NMT_WIREGUARD_PEER_LIST_H
+#define NMT_WIREGUARD_PEER_LIST_H
+
+#include "libnmt-newt/nmt-newt.h"
+#include "nmtui-edit.h"
+
+#define NMT_TYPE_WIREGUARD_PEER_LIST (nmt_wireguard_peer_list_get_type())
+#define NMT_WIREGUARD_PEER_LIST(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST((obj), NMT_TYPE_WIREGUARD_PEER_LIST, NmtWireguardPeerList))
+#define NMT_WIREGUARD_PEER_LIST_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST((klass), NMT_TYPE_WIREGUARD_PEER_LIST, NmtWireguardPeerListClass))
+#define NMT_IS_WIREGUARD_PEER_LIST(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE((obj), NMT_TYPE_WIREGUARD_PEER_LIST))
+#define NMT_IS_WIREGUARD_PEER_LIST_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE((klass), NMT_TYPE_WIREGUARD_PEER_LIST))
+#define NMT_WIREGUARD_PEER_LIST_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS((obj), NMT_TYPE_WIREGUARD_PEER_LIST, NmtWireguardPeerListClass))
+
+typedef struct {
+ NmtNewtGrid parent;
+
+} NmtWireguardPeerList;
+
+typedef struct {
+ NmtNewtGridClass parent;
+
+ /* signals */
+ void (*add_peer)(NmtWireguardPeerList *list);
+ void (*edit_peer)(NmtWireguardPeerList *list);
+ void (*remove_peer)(NmtWireguardPeerList *list);
+} NmtWireguardPeerListClass;
+
+GType nmt_wireguard_peer_list_get_type(void);
+
+NmtNewtWidget *nmt_wireguard_peer_list_new(NMSettingWireGuard *setting);
+
+#endif /* NMT_WIREGUARD_PEER_LIST_H */