mirror of
https://gitlab.freedesktop.org/NetworkManager/NetworkManager
synced 2024-07-09 04:05:56 +00:00
libnm-client: Add public nm_conn_wireguard_import() func
This commit moves the `nm_vpn_wireguard_import()` function implementation from `libnmc-base` to `libnm-client-impl`, renaming it to `nm_conn_wireguard_import()`. A new `nm_conn_utils` header file is added in `libnm-client-public`. https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/issues/1031 https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/1299
This commit is contained in:
parent
2fb2a83090
commit
f00e747beb
|
@ -1657,6 +1657,7 @@ libnm_lib_h_pub_real = \
|
|||
src/libnm-client-public/nm-autoptr.h \
|
||||
src/libnm-client-public/nm-checkpoint.h \
|
||||
src/libnm-client-public/nm-client.h \
|
||||
src/libnm-client-public/nm-conn-utils.h \
|
||||
src/libnm-client-public/nm-device-6lowpan.h \
|
||||
src/libnm-client-public/nm-device-adsl.h \
|
||||
src/libnm-client-public/nm-device-bond.h \
|
||||
|
@ -1725,6 +1726,7 @@ libnm_lib_c_real = \
|
|||
\
|
||||
src/libnm-client-impl/nm-access-point.c \
|
||||
src/libnm-client-impl/nm-checkpoint.c \
|
||||
src/libnm-client-impl/nm-conn-utils.c \
|
||||
src/libnm-client-impl/nm-dbus-helpers.c \
|
||||
src/libnm-client-impl/nm-device-6lowpan.c \
|
||||
src/libnm-client-impl/nm-device-adsl.c \
|
||||
|
|
|
@ -1838,6 +1838,7 @@ global:
|
|||
|
||||
libnm_1_40_0 {
|
||||
global:
|
||||
nm_conn_wireguard_import;
|
||||
nm_setting_connection_get_wait_activation_delay;
|
||||
nm_setting_ip4_link_local_get_type;
|
||||
nm_setting_ip6_config_get_mtu;
|
||||
|
|
|
@ -5,6 +5,7 @@ libnm_client_impl_sources = files(
|
|||
'nm-active-connection.c',
|
||||
'nm-checkpoint.c',
|
||||
'nm-client.c',
|
||||
'nm-conn-utils.c',
|
||||
'nm-dbus-helpers.c',
|
||||
'nm-device-6lowpan.c',
|
||||
'nm-device-adsl.c',
|
||||
|
|
593
src/libnm-client-impl/nm-conn-utils.c
Normal file
593
src/libnm-client-impl/nm-conn-utils.c
Normal file
|
@ -0,0 +1,593 @@
|
|||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
/*
|
||||
* Copyright (C) 2013 - 2015 Red Hat, Inc.
|
||||
*/
|
||||
|
||||
#include "libnm-client-impl/nm-default-libnm.h"
|
||||
|
||||
#include "libnm-client-public/nm-conn-utils.h"
|
||||
|
||||
#include <net/if.h>
|
||||
|
||||
#include "libnmc-base/nm-client-utils.h"
|
||||
#include "libnm-glib-aux/nm-io-utils.h"
|
||||
#include "libnm-glib-aux/nm-secret-utils.h"
|
||||
#include "nm-simple-connection.h"
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* nm_conn_wireguard_import:
|
||||
* @filename: name of the file to attempt to read into a new #NMConnection
|
||||
* @error: on return, an error or %NULL
|
||||
*
|
||||
* Returns: a new #NMConnection imported from @path, or %NULL
|
||||
* on error or if the file with @filename was not recognized as a WireGuard config
|
||||
*
|
||||
* Since: 1.40
|
||||
* */
|
||||
NMConnection *
|
||||
nm_conn_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_search = 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_ifname_valid(ifname, NMU_IFACE_KERNEL, NULL))
|
||||
ifname_valid = TRUE;
|
||||
}
|
||||
}
|
||||
if (!ifname_valid) {
|
||||
nm_utils_error_set_literal(error,
|
||||
NM_UTILS_ERROR_UNKNOWN,
|
||||
_("The name of the WireGuard config 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,
|
||||
NULL,
|
||||
error)) {
|
||||
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(¤t_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(¤t_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)) {
|
||||
GPtrArray **p_data_dns;
|
||||
NMIPAddr addr_bin;
|
||||
int addr_family;
|
||||
|
||||
if (nm_utils_parse_inaddr_bin(AF_UNSPEC, value_word, &addr_family, &addr_bin)) {
|
||||
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);
|
||||
|
||||
g_ptr_array_add(*p_data_dns,
|
||||
nm_utils_inet_ntop_dup(addr_family, &addr_bin));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!data_dns_search)
|
||||
data_dns_search = g_ptr_array_new_with_free_func(g_free);
|
||||
g_ptr_array_add(data_dns_search, g_strdup(value_word));
|
||||
}
|
||||
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(¤t_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_DISABLED;
|
||||
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;
|
||||
GPtrArray *data_dns_search2 = data_dns_search;
|
||||
|
||||
if (!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;
|
||||
data_dns_search2 = 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]);
|
||||
|
||||
/* Of the wg-quick doesn't specify a search domain, assume the user
|
||||
* wants to use the domain server for all searches. */
|
||||
if (!data_dns_search2)
|
||||
nm_setting_ip_config_add_dns_search(s_ip, "~");
|
||||
}
|
||||
if (data_dns_search2) {
|
||||
for (i = 0; i < data_dns_search2->len; i++)
|
||||
nm_setting_ip_config_add_dns_search(s_ip, data_dns_search2->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.
|
||||
* The imported connection profile will have wireguard.ip4-auto-default-route and
|
||||
* wireguard.ip6-auto-default-route set to "default". It will thus configure wg-quick's
|
||||
* policy routing if the profile has any AllowedIPs ranges with /0.
|
||||
*/
|
||||
} 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);
|
||||
}
|
|
@ -83,6 +83,7 @@
|
|||
#include "nm-ethtool-utils.h"
|
||||
|
||||
#include "nm-object.h"
|
||||
#include "nm-conn-utils.h"
|
||||
#include "nm-dhcp-config.h"
|
||||
#include "nm-ip-config.h"
|
||||
#include "nm-remote-connection.h"
|
||||
|
|
|
@ -9,6 +9,7 @@ libnm_client_headers = files(
|
|||
'nm-autoptr.h',
|
||||
'nm-checkpoint.h',
|
||||
'nm-client.h',
|
||||
'nm-conn-utils.h',
|
||||
'nm-device-6lowpan.h',
|
||||
'nm-device-adsl.h',
|
||||
'nm-device-bond.h',
|
||||
|
|
22
src/libnm-client-public/nm-conn-utils.h
Normal file
22
src/libnm-client-public/nm-conn-utils.h
Normal file
|
@ -0,0 +1,22 @@
|
|||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
/*
|
||||
* Copyright (C) 2013 - 2015 Red Hat, Inc.
|
||||
*/
|
||||
|
||||
#ifndef __NM_WIREGUARD_IMPORT_H__
|
||||
#define __NM_WIREGUARD_IMPORT_H__
|
||||
|
||||
#if !defined(__NETWORKMANAGER_H_INSIDE__) && !defined(NETWORKMANAGER_COMPILATION)
|
||||
#error "Only <NetworkManager.h> can be included directly."
|
||||
#endif
|
||||
|
||||
#include "nm-object.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
NM_AVAILABLE_IN_1_40
|
||||
NMConnection *nm_conn_wireguard_import(const char *filename, GError **error);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __NM_WIREGUARD_IMPORT_H__ */
|
|
@ -247,570 +247,3 @@ 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_search = 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_ifname_valid(ifname, NMU_IFACE_KERNEL, NULL))
|
||||
ifname_valid = TRUE;
|
||||
}
|
||||
}
|
||||
if (!ifname_valid) {
|
||||
nm_utils_error_set_literal(error,
|
||||
NM_UTILS_ERROR_UNKNOWN,
|
||||
_("The name of the WireGuard config 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,
|
||||
NULL,
|
||||
error))
|
||||
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(¤t_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(¤t_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)) {
|
||||
GPtrArray **p_data_dns;
|
||||
NMIPAddr addr_bin;
|
||||
int addr_family;
|
||||
|
||||
if (nm_utils_parse_inaddr_bin(AF_UNSPEC, value_word, &addr_family, &addr_bin)) {
|
||||
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);
|
||||
|
||||
g_ptr_array_add(*p_data_dns,
|
||||
nm_utils_inet_ntop_dup(addr_family, &addr_bin));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!data_dns_search)
|
||||
data_dns_search = g_ptr_array_new_with_free_func(g_free);
|
||||
g_ptr_array_add(data_dns_search, g_strdup(value_word));
|
||||
}
|
||||
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(¤t_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_DISABLED;
|
||||
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;
|
||||
GPtrArray *data_dns_search2 = data_dns_search;
|
||||
|
||||
if (!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;
|
||||
data_dns_search2 = 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]);
|
||||
|
||||
/* Of the wg-quick doesn't specify a search domain, assume the user
|
||||
* wants to use the domain server for all searches. */
|
||||
if (!data_dns_search2)
|
||||
nm_setting_ip_config_add_dns_search(s_ip, "~");
|
||||
}
|
||||
if (data_dns_search2) {
|
||||
for (i = 0; i < data_dns_search2->len; i++)
|
||||
nm_setting_ip_config_add_dns_search(s_ip, data_dns_search2->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.
|
||||
* The imported connection profile will have wireguard.ip4-auto-default-route and
|
||||
* wireguard.ip6-auto-default-route set to "default". It will thus configure wg-quick's
|
||||
* policy routing if the profile has any AllowedIPs ranges with /0.
|
||||
*/
|
||||
} 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);
|
||||
}
|
||||
|
|
|
@ -26,6 +26,4 @@ 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__ */
|
||||
|
|
|
@ -169,7 +169,7 @@ test_client_import_wireguard_test0(void)
|
|||
gs_free_error GError *error = NULL;
|
||||
|
||||
connection =
|
||||
nm_vpn_wireguard_import(NM_BUILD_SRCDIR "/src/libnmc-setting/tests/wg-test0.conf", &error);
|
||||
nm_conn_wireguard_import(NM_BUILD_SRCDIR "/src/libnmc-setting/tests/wg-test0.conf", &error);
|
||||
|
||||
g_assert_no_error(error);
|
||||
|
||||
|
@ -231,7 +231,7 @@ test_client_import_wireguard_test1(void)
|
|||
{
|
||||
gs_free_error GError *error = NULL;
|
||||
|
||||
nm_vpn_wireguard_import(NM_BUILD_SRCDIR "/src/libnmc-setting/tests/wg-test1.conf", &error);
|
||||
nm_conn_wireguard_import(NM_BUILD_SRCDIR "/src/libnmc-setting/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"));
|
||||
|
@ -242,7 +242,7 @@ test_client_import_wireguard_test2(void)
|
|||
{
|
||||
gs_free_error GError *error = NULL;
|
||||
|
||||
nm_vpn_wireguard_import(NM_BUILD_SRCDIR "/src/libnmc-setting/tests/wg-test2.conf", &error);
|
||||
nm_conn_wireguard_import(NM_BUILD_SRCDIR "/src/libnmc-setting/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"));
|
||||
|
@ -254,7 +254,7 @@ test_client_import_wireguard_test3(void)
|
|||
{
|
||||
gs_free_error GError *error = NULL;
|
||||
|
||||
nm_vpn_wireguard_import(NM_BUILD_SRCDIR "/src/libnmc-setting/tests/wg-test3.conf", &error);
|
||||
nm_conn_wireguard_import(NM_BUILD_SRCDIR "/src/libnmc-setting/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"));
|
||||
|
@ -265,7 +265,7 @@ test_client_import_wireguard_missing(void)
|
|||
{
|
||||
gs_free_error GError *error = NULL;
|
||||
|
||||
nm_vpn_wireguard_import(NM_BUILD_SRCDIR "/src/libnmc-setting/tests/wg-missing.conf", &error);
|
||||
nm_conn_wireguard_import(NM_BUILD_SRCDIR "/src/libnmc-setting/tests/wg-missing.conf", &error);
|
||||
g_assert_error(error, G_FILE_ERROR, G_FILE_ERROR_NOENT);
|
||||
}
|
||||
|
||||
|
|
|
@ -9693,7 +9693,7 @@ do_connection_import(const NMCCommand *cmd, NmCli *nmc, int argc, const char *co
|
|||
}
|
||||
|
||||
if (nm_streq(type, "wireguard"))
|
||||
connection = nm_vpn_wireguard_import(filename, &error);
|
||||
connection = nm_conn_wireguard_import(filename, &error);
|
||||
else {
|
||||
service_type = nm_vpn_plugin_info_list_find_service_type(nm_vpn_get_plugin_infos(), type);
|
||||
if (!service_type) {
|
||||
|
|
Loading…
Reference in New Issue
Block a user