Blame clients/common/nm-vpn-helpers.c

Packit Service 87a54e
/* SPDX-License-Identifier: GPL-2.0-or-later */
Packit 5756e2
/*
Packit 5756e2
 * Copyright (C) 2013 - 2015 Red Hat, Inc.
Packit 5756e2
 */
Packit 5756e2
Packit 5756e2
/**
Packit 5756e2
 * SECTION:nm-vpn-helpers
Packit 5756e2
 * @short_description: VPN-related utilities
Packit 5756e2
 */
Packit 5756e2
Packit 5756e2
#include "nm-default.h"
Packit 5756e2
Packit 5756e2
#include "nm-vpn-helpers.h"
Packit 5756e2
Packit 5756e2
#include <arpa/inet.h>
Packit 5756e2
#include <net/if.h>
Packit 5756e2
Packit 5756e2
#include "nm-client-utils.h"
Packit 5756e2
#include "nm-utils.h"
Packit 5756e2
#include "nm-glib-aux/nm-io-utils.h"
Packit 5756e2
#include "nm-glib-aux/nm-secret-utils.h"
Packit 5756e2
Packit 5756e2
/*****************************************************************************/
Packit 5756e2
Packit 5756e2
NMVpnEditorPlugin *
Packit Service a1bd4f
nm_vpn_get_editor_plugin(const char *service_type, GError **error)
Packit 5756e2
{
Packit Service a1bd4f
    NMVpnEditorPlugin *plugin = NULL;
Packit Service a1bd4f
    NMVpnPluginInfo *  plugin_info;
Packit Service a1bd4f
    gs_free_error GError *local = NULL;
Packit Service a1bd4f
Packit Service a1bd4f
    g_return_val_if_fail(service_type, NULL);
Packit Service a1bd4f
    g_return_val_if_fail(error == NULL || *error == NULL, NULL);
Packit Service a1bd4f
Packit Service a1bd4f
    plugin_info = nm_vpn_plugin_info_list_find_by_service(nm_vpn_get_plugin_infos(), service_type);
Packit Service a1bd4f
Packit Service a1bd4f
    if (!plugin_info) {
Packit Service a1bd4f
        g_set_error(error,
Packit Service a1bd4f
                    NM_VPN_PLUGIN_ERROR,
Packit Service a1bd4f
                    NM_VPN_PLUGIN_ERROR_FAILED,
Packit Service a1bd4f
                    _("unknown VPN plugin \"%s\""),
Packit Service a1bd4f
                    service_type);
Packit Service a1bd4f
        return NULL;
Packit Service a1bd4f
    }
Packit Service a1bd4f
    plugin = nm_vpn_plugin_info_get_editor_plugin(plugin_info);
Packit Service a1bd4f
    if (!plugin)
Packit Service a1bd4f
        plugin = nm_vpn_plugin_info_load_editor_plugin(plugin_info, &local);
Packit Service a1bd4f
Packit Service a1bd4f
    if (!plugin) {
Packit Service a1bd4f
        if (!nm_vpn_plugin_info_get_plugin(plugin_info)
Packit Service a1bd4f
            && nm_vpn_plugin_info_lookup_property(plugin_info,
Packit Service a1bd4f
                                                  NM_VPN_PLUGIN_INFO_KF_GROUP_GNOME,
Packit Service a1bd4f
                                                  "properties")) {
Packit Service a1bd4f
            g_set_error(error,
Packit Service a1bd4f
                        NM_VPN_PLUGIN_ERROR,
Packit Service a1bd4f
                        NM_VPN_PLUGIN_ERROR_FAILED,
Packit Service a1bd4f
                        _("cannot load legacy-only VPN plugin \"%s\" for \"%s\""),
Packit Service a1bd4f
                        nm_vpn_plugin_info_get_name(plugin_info),
Packit Service a1bd4f
                        nm_vpn_plugin_info_get_filename(plugin_info));
Packit Service a1bd4f
        } else if (g_error_matches(local, G_FILE_ERROR, G_FILE_ERROR_NOENT)) {
Packit Service a1bd4f
            g_set_error(
Packit Service a1bd4f
                error,
Packit Service a1bd4f
                NM_VPN_PLUGIN_ERROR,
Packit Service a1bd4f
                NM_VPN_PLUGIN_ERROR_FAILED,
Packit Service a1bd4f
                _("cannot load VPN plugin \"%s\" due to missing \"%s\". Missing client plugin?"),
Packit Service a1bd4f
                nm_vpn_plugin_info_get_name(plugin_info),
Packit Service a1bd4f
                nm_vpn_plugin_info_get_plugin(plugin_info));
Packit Service a1bd4f
        } else {
Packit Service a1bd4f
            g_set_error(error,
Packit Service a1bd4f
                        NM_VPN_PLUGIN_ERROR,
Packit Service a1bd4f
                        NM_VPN_PLUGIN_ERROR_FAILED,
Packit Service a1bd4f
                        _("failed to load VPN plugin \"%s\": %s"),
Packit Service a1bd4f
                        nm_vpn_plugin_info_get_name(plugin_info),
Packit Service a1bd4f
                        local->message);
Packit Service a1bd4f
        }
Packit Service a1bd4f
        return NULL;
Packit Service a1bd4f
    }
Packit Service a1bd4f
Packit Service a1bd4f
    return plugin;
Packit 5756e2
}
Packit 5756e2
Packit 5756e2
GSList *
Packit Service a1bd4f
nm_vpn_get_plugin_infos(void)
Packit 5756e2
{
Packit Service a1bd4f
    static bool    plugins_loaded;
Packit Service a1bd4f
    static GSList *plugins = NULL;
Packit Service a1bd4f
Packit Service a1bd4f
    if (G_LIKELY(plugins_loaded))
Packit Service a1bd4f
        return plugins;
Packit Service a1bd4f
    plugins_loaded = TRUE;
Packit Service a1bd4f
    plugins        = nm_vpn_plugin_info_list_load();
Packit Service a1bd4f
    return plugins;
Packit 5756e2
}
Packit 5756e2
Packit 5756e2
gboolean
Packit Service a1bd4f
nm_vpn_supports_ipv6(NMConnection *connection)
Packit 5756e2
{
Packit Service a1bd4f
    NMSettingVpn *     s_vpn;
Packit Service a1bd4f
    const char *       service_type;
Packit Service a1bd4f
    NMVpnEditorPlugin *plugin;
Packit Service a1bd4f
    guint32            capabilities;
Packit 5756e2
Packit Service a1bd4f
    s_vpn = nm_connection_get_setting_vpn(connection);
Packit Service a1bd4f
    g_return_val_if_fail(s_vpn != NULL, FALSE);
Packit 5756e2
Packit Service a1bd4f
    service_type = nm_setting_vpn_get_service_type(s_vpn);
Packit Service a1bd4f
    if (!service_type)
Packit Service a1bd4f
        return FALSE;
Packit 5756e2
Packit Service a1bd4f
    plugin = nm_vpn_get_editor_plugin(service_type, NULL);
Packit Service a1bd4f
    if (!plugin)
Packit Service a1bd4f
        return FALSE;
Packit 5756e2
Packit Service a1bd4f
    capabilities = nm_vpn_editor_plugin_get_capabilities(plugin);
Packit Service a1bd4f
    return NM_FLAGS_HAS(capabilities, NM_VPN_EDITOR_PLUGIN_CAPABILITY_IPV6);
Packit 5756e2
}
Packit 5756e2
Packit 5756e2
const NmcVpnPasswordName *
Packit Service a1bd4f
nm_vpn_get_secret_names(const char *service_type)
Packit 5756e2
{
Packit Service a1bd4f
    const char *type;
Packit Service a1bd4f
Packit Service a1bd4f
    if (!service_type)
Packit Service a1bd4f
        return NULL;
Packit Service a1bd4f
Packit Service a1bd4f
    if (!NM_STR_HAS_PREFIX(service_type, NM_DBUS_INTERFACE)
Packit Service a1bd4f
        || service_type[NM_STRLEN(NM_DBUS_INTERFACE)] != '.') {
Packit Service a1bd4f
        /* all our well-known, hard-coded vpn-types start with NM_DBUS_INTERFACE. */
Packit Service a1bd4f
        return NULL;
Packit Service a1bd4f
    }
Packit Service a1bd4f
Packit Service a1bd4f
    type = service_type + (NM_STRLEN(NM_DBUS_INTERFACE) + 1);
Packit Service a1bd4f
Packit Service a1bd4f
#define _VPN_PASSWORD_LIST(...)                    \
Packit Service a1bd4f
    ({                                             \
Packit Service a1bd4f
        static const NmcVpnPasswordName _arr[] = { \
Packit Service a1bd4f
            __VA_ARGS__{0},                        \
Packit Service a1bd4f
        };                                         \
Packit Service a1bd4f
        _arr;                                      \
Packit Service a1bd4f
    })
Packit Service a1bd4f
Packit Service a1bd4f
    if (NM_IN_STRSET(type, "pptp", "iodine", "ssh", "l2tp", "fortisslvpn")) {
Packit Service a1bd4f
        return _VPN_PASSWORD_LIST({"password", N_("Password")}, );
Packit Service a1bd4f
    }
Packit Service a1bd4f
Packit Service a1bd4f
    if (NM_IN_STRSET(type, "openvpn")) {
Packit Service a1bd4f
        return _VPN_PASSWORD_LIST({"password", N_("Password")},
Packit Service a1bd4f
                                  {"cert-pass", N_("Certificate password")},
Packit Service a1bd4f
                                  {"http-proxy-password", N_("HTTP proxy password")}, );
Packit Service a1bd4f
    }
Packit Service a1bd4f
Packit Service a1bd4f
    if (NM_IN_STRSET(type, "vpnc")) {
Packit Service a1bd4f
        return _VPN_PASSWORD_LIST({"Xauth password", N_("Password")},
Packit Service a1bd4f
                                  {"IPSec secret", N_("Group password")}, );
Packit Service a1bd4f
    };
Packit Service a1bd4f
Packit Service a1bd4f
    if (NM_IN_STRSET(type, "openswan", "libreswan", "strongswan")) {
Packit Service a1bd4f
        return _VPN_PASSWORD_LIST({"xauthpassword", N_("Password")},
Packit Service a1bd4f
                                  {"pskvalue", N_("Group password")}, );
Packit Service a1bd4f
    };
Packit Service a1bd4f
Packit Service a1bd4f
    if (NM_IN_STRSET(type, "openconnect")) {
Packit Service a1bd4f
        return _VPN_PASSWORD_LIST({"gateway", N_("Gateway")},
Packit Service a1bd4f
                                  {"cookie", N_("Cookie")},
Packit Service a1bd4f
                                  {"gwcert", N_("Gateway certificate hash")}, );
Packit Service a1bd4f
    };
Packit Service a1bd4f
Packit Service a1bd4f
    return NULL;
Packit 5756e2
}
Packit 5756e2
Packit 5756e2
static gboolean
Packit Service a1bd4f
_extract_variable_value(char *line, const char *tag, char **value)
Packit 5756e2
{
Packit Service a1bd4f
    char *p1, *p2;
Packit Service a1bd4f
Packit Service a1bd4f
    if (!g_str_has_prefix(line, tag))
Packit Service a1bd4f
        return FALSE;
Packit Service a1bd4f
Packit Service a1bd4f
    p1 = line + strlen(tag);
Packit Service a1bd4f
    p2 = line + strlen(line) - 1;
Packit Service a1bd4f
    if ((*p1 == '\'' || *p1 == '"') && (*p1 == *p2)) {
Packit Service a1bd4f
        p1++;
Packit Service a1bd4f
        *p2 = '\0';
Packit Service a1bd4f
    }
Packit Service a1bd4f
    NM_SET_OUT(value, g_strdup(p1));
Packit Service a1bd4f
    return TRUE;
Packit 5756e2
}
Packit 5756e2
Packit 5756e2
gboolean
Packit Service a1bd4f
nm_vpn_openconnect_authenticate_helper(const char *host,
Packit Service a1bd4f
                                       char **     cookie,
Packit Service a1bd4f
                                       char **     gateway,
Packit Service a1bd4f
                                       char **     gwcert,
Packit Service a1bd4f
                                       int *       status,
Packit Service a1bd4f
                                       GError **   error)
Packit 5756e2
{
Packit Service a1bd4f
    gs_free char *       output   = NULL;
Packit Service a1bd4f
    gs_free const char **output_v = NULL;
Packit Service a1bd4f
    const char *const *  iter;
Packit Service a1bd4f
    const char *         path;
Packit Service a1bd4f
    const char *const    DEFAULT_PATHS[] = {
Packit Service a1bd4f
        "/sbin/",
Packit Service a1bd4f
        "/usr/sbin/",
Packit Service a1bd4f
        "/usr/local/sbin/",
Packit Service a1bd4f
        "/bin/",
Packit Service a1bd4f
        "/usr/bin/",
Packit Service a1bd4f
        "/usr/local/bin/",
Packit Service a1bd4f
        NULL,
Packit Service a1bd4f
    };
Packit Service a1bd4f
Packit Service a1bd4f
    path = nm_utils_file_search_in_paths("openconnect",
Packit Service a1bd4f
                                         "/usr/sbin/openconnect",
Packit Service a1bd4f
                                         DEFAULT_PATHS,
Packit Service a1bd4f
                                         G_FILE_TEST_IS_EXECUTABLE,
Packit Service a1bd4f
                                         NULL,
Packit Service a1bd4f
                                         NULL,
Packit Service a1bd4f
                                         error);
Packit Service a1bd4f
    if (!path)
Packit Service a1bd4f
        return FALSE;
Packit Service a1bd4f
Packit Service a1bd4f
    if (!g_spawn_sync(NULL,
Packit Service a1bd4f
                      (char **) NM_MAKE_STRV(path, "--authenticate", host),
Packit Service a1bd4f
                      NULL,
Packit Service a1bd4f
                      G_SPAWN_SEARCH_PATH | G_SPAWN_CHILD_INHERITS_STDIN,
Packit Service a1bd4f
                      NULL,
Packit Service a1bd4f
                      NULL,
Packit Service a1bd4f
                      &output,
Packit Service a1bd4f
                      NULL,
Packit Service a1bd4f
                      status,
Packit Service a1bd4f
                      error))
Packit Service a1bd4f
        return FALSE;
Packit Service a1bd4f
Packit Service a1bd4f
    /* Parse output and set cookie, gateway and gwcert
Packit Service a1bd4f
     * output example:
Packit Service a1bd4f
     * COOKIE='loremipsum'
Packit Service a1bd4f
     * HOST='1.2.3.4'
Packit Service a1bd4f
     * FINGERPRINT='sha1:32bac90cf09a722e10ecc1942c67fe2ac8c21e2e'
Packit Service a1bd4f
     */
Packit Service a1bd4f
    output_v = nm_utils_strsplit_set_with_empty(output, "\r\n");
Packit Service a1bd4f
    for (iter = output_v; iter && *iter; iter++) {
Packit Service a1bd4f
        char *s_mutable = (char *) *iter;
Packit Service a1bd4f
Packit Service a1bd4f
        _extract_variable_value(s_mutable, "COOKIE=", cookie);
Packit Service a1bd4f
        _extract_variable_value(s_mutable, "HOST=", gateway);
Packit Service a1bd4f
        _extract_variable_value(s_mutable, "FINGERPRINT=", gwcert);
Packit Service a1bd4f
    }
Packit Service a1bd4f
Packit Service a1bd4f
    return TRUE;
Packit 5756e2
}
Packit 5756e2
Packit 5756e2
static gboolean
Packit Service a1bd4f
_wg_complete_peer(GPtrArray **     p_peers,
Packit Service a1bd4f
                  NMWireGuardPeer *peer_take,
Packit Service a1bd4f
                  gsize            peer_start_line_nr,
Packit Service a1bd4f
                  const char *     filename,
Packit Service a1bd4f
                  GError **        error)
Packit 5756e2
{
Packit Service a1bd4f
    nm_auto_unref_wgpeer NMWireGuardPeer *peer = peer_take;
Packit Service a1bd4f
    gs_free_error GError *local                = NULL;
Packit Service a1bd4f
Packit Service a1bd4f
    if (!peer)
Packit Service a1bd4f
        return TRUE;
Packit Service a1bd4f
Packit Service a1bd4f
    if (!nm_wireguard_peer_is_valid(peer, TRUE, TRUE, &local)) {
Packit Service a1bd4f
        nm_utils_error_set(error,
Packit Service a1bd4f
                           NM_UTILS_ERROR_UNKNOWN,
Packit Service a1bd4f
                           _("Invalid peer starting at %s:%zu: %s"),
Packit Service a1bd4f
                           filename,
Packit Service a1bd4f
                           peer_start_line_nr,
Packit Service a1bd4f
                           local->message);
Packit Service a1bd4f
        return FALSE;
Packit Service a1bd4f
    }
Packit Service a1bd4f
Packit Service a1bd4f
    if (!*p_peers)
Packit Service a1bd4f
        *p_peers = g_ptr_array_new_with_free_func((GDestroyNotify) nm_wireguard_peer_unref);
Packit Service a1bd4f
    g_ptr_array_add(*p_peers, g_steal_pointer(&peer));
Packit Service a1bd4f
    return TRUE;
Packit 5756e2
}
Packit 5756e2
Packit 5756e2
static gboolean
Packit Service a1bd4f
_line_match(char *line, const char *key, gsize key_len, const char **out_key, char **out_value)
Packit 5756e2
{
Packit Service a1bd4f
    nm_assert(line);
Packit Service a1bd4f
    nm_assert(key);
Packit Service a1bd4f
    nm_assert(strlen(key) == key_len);
Packit Service a1bd4f
    nm_assert(!strchr(key, '='));
Packit Service a1bd4f
    nm_assert(out_key && !*out_key);
Packit Service a1bd4f
    nm_assert(out_value && !*out_value);
Packit Service a1bd4f
Packit Service a1bd4f
    /* Note that `wg-quick` (linux.bash) does case-insensitive comparison (shopt -s nocasematch).
Packit Service a1bd4f
     * `wg setconf` does case-insensitive comparison too (with strncasecmp, which is locale dependent).
Packit Service a1bd4f
     *
Packit Service a1bd4f
     * We do a case-insensitive comparison of the key, however in a locale-independent manner. */
Packit Service a1bd4f
Packit Service a1bd4f
    if (g_ascii_strncasecmp(line, key, key_len) != 0)
Packit Service a1bd4f
        return FALSE;
Packit Service a1bd4f
Packit Service a1bd4f
    if (line[key_len] != '=')
Packit Service a1bd4f
        return FALSE;
Packit Service a1bd4f
Packit Service a1bd4f
    *out_key   = key;
Packit Service a1bd4f
    *out_value = &line[key_len + 1];
Packit Service a1bd4f
    return TRUE;
Packit 5756e2
}
Packit 5756e2
Packit 5756e2
#define line_match(line, key, out_key, out_value) \
Packit Service a1bd4f
    _line_match((line), "" key "", NM_STRLEN(key), (out_key), (out_value))
Packit 5756e2
Packit 5756e2
static gboolean
Packit Service a1bd4f
value_split_word(char **line_remainder, char **out_word)
Packit 5756e2
{
Packit Service a1bd4f
    char *str;
Packit 5756e2
Packit Service a1bd4f
    if ((*line_remainder)[0] == '\0')
Packit Service a1bd4f
        return FALSE;
Packit 5756e2
Packit Service a1bd4f
    *out_word = *line_remainder;
Packit 5756e2
Packit Service a1bd4f
    str = strchrnul(*line_remainder, ',');
Packit Service a1bd4f
    if (str[0] == ',') {
Packit Service a1bd4f
        str[0]          = '\0';
Packit Service a1bd4f
        *line_remainder = &str[1];
Packit Service a1bd4f
    } else
Packit Service a1bd4f
        *line_remainder = str;
Packit Service a1bd4f
    return TRUE;
Packit 5756e2
}
Packit 5756e2
Packit 5756e2
NMConnection *
Packit Service a1bd4f
nm_vpn_wireguard_import(const char *filename, GError **error)
Packit 5756e2
{
Packit Service a1bd4f
    nm_auto_clear_secret_ptr NMSecretPtr file_content = NM_SECRET_PTR_INIT();
Packit Service a1bd4f
    char                                 ifname[IFNAMSIZ];
Packit Service a1bd4f
    gs_free char *                       uuid         = NULL;
Packit Service a1bd4f
    gboolean                             ifname_valid = FALSE;
Packit Service a1bd4f
    const char *                         cstr;
Packit Service a1bd4f
    char *                               line_remainder;
Packit Service a1bd4f
    gs_unref_object NMConnection *connection = NULL;
Packit Service a1bd4f
    NMSettingConnection *         s_con;
Packit Service a1bd4f
    NMSettingIPConfig *           s_ip4;
Packit Service a1bd4f
    NMSettingIPConfig *           s_ip6;
Packit Service a1bd4f
    NMSettingWireGuard *          s_wg;
Packit Service a1bd4f
    gs_free_error GError *local = NULL;
Packit Service a1bd4f
    enum {
Packit Service a1bd4f
        LINE_CONTEXT_INIT,
Packit Service a1bd4f
        LINE_CONTEXT_INTERFACE,
Packit Service a1bd4f
        LINE_CONTEXT_PEER,
Packit Service a1bd4f
    } line_context;
Packit Service a1bd4f
    gsize                line_nr;
Packit Service a1bd4f
    gsize                current_peer_start_line_nr    = 0;
Packit Service a1bd4f
    nm_auto_unref_wgpeer NMWireGuardPeer *current_peer = NULL;
Packit Service a1bd4f
    gs_unref_ptrarray GPtrArray *data_dns_search       = NULL;
Packit Service a1bd4f
    gs_unref_ptrarray GPtrArray *data_dns_v4           = NULL;
Packit Service a1bd4f
    gs_unref_ptrarray GPtrArray *data_dns_v6           = NULL;
Packit Service a1bd4f
    gs_unref_ptrarray GPtrArray *data_addr_v4          = NULL;
Packit Service a1bd4f
    gs_unref_ptrarray GPtrArray *data_addr_v6          = NULL;
Packit Service a1bd4f
    gs_unref_ptrarray GPtrArray *data_peers            = NULL;
Packit Service a1bd4f
    const char *                 data_private_key      = NULL;
Packit Service a1bd4f
    gint64                       data_table;
Packit Service a1bd4f
    guint                        data_listen_port = 0;
Packit Service a1bd4f
    guint                        data_fwmark      = 0;
Packit Service a1bd4f
    guint                        data_mtu         = 0;
Packit Service a1bd4f
    int                          is_v4;
Packit Service a1bd4f
    guint                        i;
Packit Service a1bd4f
Packit Service a1bd4f
    g_return_val_if_fail(filename, NULL);
Packit Service a1bd4f
    g_return_val_if_fail(!error || !*error, NULL);
Packit Service a1bd4f
Packit Service a1bd4f
    /* contrary to "wg-quick", we never interpret the filename as "/etc/wireguard/$INTERFACE.conf".
Packit Service a1bd4f
     * If the filename has no '/', it is interpreted as relative to the current working directory.
Packit Service a1bd4f
     * However, we do require a suitable filename suffix and that the name corresponds to the interface
Packit Service a1bd4f
     * name. */
Packit Service a1bd4f
    cstr = strrchr(filename, '/');
Packit Service a1bd4f
    cstr = cstr ? &cstr[1] : filename;
Packit Service a1bd4f
    if (NM_STR_HAS_SUFFIX(cstr, ".conf")) {
Packit Service a1bd4f
        gsize len = strlen(cstr) - NM_STRLEN(".conf");
Packit Service a1bd4f
Packit Service a1bd4f
        if (len > 0 && len < sizeof(ifname)) {
Packit Service a1bd4f
            memcpy(ifname, cstr, len);
Packit Service a1bd4f
            ifname[len] = '\0';
Packit Service a1bd4f
Packit Service a1bd4f
            if (nm_utils_ifname_valid(ifname, NMU_IFACE_KERNEL, NULL))
Packit Service a1bd4f
                ifname_valid = TRUE;
Packit Service a1bd4f
        }
Packit Service a1bd4f
    }
Packit Service a1bd4f
    if (!ifname_valid) {
Packit Service a1bd4f
        nm_utils_error_set_literal(error,
Packit Service a1bd4f
                                   NM_UTILS_ERROR_UNKNOWN,
Packit Service a1bd4f
                                   _("The name of the WireGuard config must be a valid interface "
Packit Service a1bd4f
                                     "name followed by \".conf\""));
Packit Service a1bd4f
        return FALSE;
Packit Service a1bd4f
    }
Packit Service a1bd4f
Packit Service a1bd4f
    if (!nm_utils_file_get_contents(-1,
Packit Service a1bd4f
                                    filename,
Packit Service a1bd4f
                                    10 * 1024 * 1024,
Packit Service a1bd4f
                                    NM_UTILS_FILE_GET_CONTENTS_FLAG_SECRET,
Packit Service a1bd4f
                                    &file_content.str,
Packit Service a1bd4f
                                    &file_content.len,
Packit Service a1bd4f
                                    NULL,
Packit Service a1bd4f
                                    error))
Packit Service a1bd4f
        return NULL;
Packit Service a1bd4f
Packit Service a1bd4f
        /* We interpret the file like `wg-quick up` and `wg setconf` do.
Packit Service a1bd4f
     *
Packit Service a1bd4f
     * Of course the WireGuard scripts do something fundamentlly different. They
Packit Service a1bd4f
     * perform actions to configure the WireGuard link in kernel, add routes and
Packit Service a1bd4f
     * addresses, and call resolvconf. It all happens at the time when the script
Packit Service a1bd4f
     * run.
Packit Service a1bd4f
     *
Packit Service a1bd4f
     * This code here instead generates a NetworkManager connection profile so that
Packit Service a1bd4f
     * NetworkManager will apply a similar configuration when later activating the profile. */
Packit Service a1bd4f
Packit Service a1bd4f
#define _TABLE_AUTO ((gint64) -1)
Packit Service a1bd4f
#define _TABLE_OFF  ((gint64) -2)
Packit Service a1bd4f
Packit Service a1bd4f
    data_table = _TABLE_AUTO;
Packit Service a1bd4f
Packit Service a1bd4f
    line_remainder = file_content.str;
Packit Service a1bd4f
    line_context   = LINE_CONTEXT_INIT;
Packit Service a1bd4f
    line_nr        = 0;
Packit Service a1bd4f
    while (line_remainder[0] != '\0') {
Packit Service a1bd4f
        const char *matched_key = NULL;
Packit Service a1bd4f
        char *      value       = NULL;
Packit Service a1bd4f
        char *      line;
Packit Service a1bd4f
        char        ch;
Packit Service a1bd4f
        gint64      i64;
Packit Service a1bd4f
Packit Service a1bd4f
        line_nr++;
Packit Service a1bd4f
Packit Service a1bd4f
        line           = line_remainder;
Packit Service a1bd4f
        line_remainder = strchrnul(line, '\n');
Packit Service a1bd4f
        if (line_remainder[0] != '\0')
Packit Service a1bd4f
            (line_remainder++)[0] = '\0';
Packit Service a1bd4f
Packit Service a1bd4f
        /* Drop all spaces and truncate at first '#'.
Packit Service a1bd4f
         * See wg's config_read_line().
Packit Service a1bd4f
         *
Packit Service a1bd4f
         * Note that wg-quick doesn't do that.
Packit Service a1bd4f
         *
Packit Service a1bd4f
         * Neither `wg setconf` nor `wg-quick` does a strict parsing.
Packit Service a1bd4f
         * We don't either. Just try to interpret the file (mostly) the same as
Packit Service a1bd4f
         * they would.
Packit Service a1bd4f
         */
Packit Service a1bd4f
        {
Packit Service a1bd4f
            gsize l, n;
Packit Service a1bd4f
Packit Service a1bd4f
            n = 0;
Packit Service a1bd4f
            for (l = 0; (ch = line[l]); l++) {
Packit Service a1bd4f
                if (g_ascii_isspace(ch)) {
Packit Service a1bd4f
                    /* wg-setconf strips all whitespace before parsing the content. That means,
Packit Service a1bd4f
                     * *[I nterface]" will be accepted. We do that too. */
Packit Service a1bd4f
                    continue;
Packit Service a1bd4f
                }
Packit Service a1bd4f
                if (ch == '#')
Packit Service a1bd4f
                    break;
Packit Service a1bd4f
                line[n++] = line[l];
Packit Service a1bd4f
            }
Packit Service a1bd4f
            if (n == 0)
Packit Service a1bd4f
                continue;
Packit Service a1bd4f
            line[n] = '\0';
Packit Service a1bd4f
        }
Packit Service a1bd4f
Packit Service a1bd4f
        if (g_ascii_strcasecmp(line, "[Interface]") == 0) {
Packit Service a1bd4f
            if (!_wg_complete_peer(&data_peers,
Packit Service a1bd4f
                                   g_steal_pointer(&current_peer),
Packit Service a1bd4f
                                   current_peer_start_line_nr,
Packit Service a1bd4f
                                   filename,
Packit Service a1bd4f
                                   error))
Packit Service a1bd4f
                return FALSE;
Packit Service a1bd4f
            line_context = LINE_CONTEXT_INTERFACE;
Packit Service a1bd4f
            continue;
Packit Service a1bd4f
        }
Packit Service a1bd4f
Packit Service a1bd4f
        if (g_ascii_strcasecmp(line, "[Peer]") == 0) {
Packit Service a1bd4f
            if (!_wg_complete_peer(&data_peers,
Packit Service a1bd4f
                                   g_steal_pointer(&current_peer),
Packit Service a1bd4f
                                   current_peer_start_line_nr,
Packit Service a1bd4f
                                   filename,
Packit Service a1bd4f
                                   error))
Packit Service a1bd4f
                return FALSE;
Packit Service a1bd4f
            current_peer_start_line_nr = line_nr;
Packit Service a1bd4f
            current_peer               = nm_wireguard_peer_new();
Packit Service a1bd4f
            line_context               = LINE_CONTEXT_PEER;
Packit Service a1bd4f
            continue;
Packit Service a1bd4f
        }
Packit Service a1bd4f
Packit Service a1bd4f
        if (line_context == LINE_CONTEXT_INTERFACE) {
Packit Service a1bd4f
            if (line_match(line, "Address", &matched_key, &value)) {
Packit Service a1bd4f
                char *value_word;
Packit Service a1bd4f
Packit Service a1bd4f
                while (value_split_word(&value, &value_word)) {
Packit Service a1bd4f
                    GPtrArray **p_data_addr;
Packit Service a1bd4f
                    NMIPAddr    addr_bin;
Packit Service a1bd4f
                    int         addr_family;
Packit Service a1bd4f
                    int         prefix_len;
Packit Service a1bd4f
Packit Service a1bd4f
                    if (!nm_utils_parse_inaddr_prefix_bin(AF_UNSPEC,
Packit Service a1bd4f
                                                          value_word,
Packit Service a1bd4f
                                                          &addr_family,
Packit Service a1bd4f
                                                          &addr_bin,
Packit Service a1bd4f
                                                          &prefix_len))
Packit Service a1bd4f
                        goto fail_invalid_value;
Packit Service a1bd4f
Packit Service a1bd4f
                    p_data_addr = (addr_family == AF_INET) ? &data_addr_v4 : &data_addr_v6;
Packit Service a1bd4f
Packit Service a1bd4f
                    if (!*p_data_addr)
Packit Service a1bd4f
                        *p_data_addr =
Packit Service a1bd4f
                            g_ptr_array_new_with_free_func((GDestroyNotify) nm_ip_address_unref);
Packit Service a1bd4f
Packit Service a1bd4f
                    g_ptr_array_add(
Packit Service a1bd4f
                        *p_data_addr,
Packit Service a1bd4f
                        nm_ip_address_new_binary(
Packit Service a1bd4f
                            addr_family,
Packit Service a1bd4f
                            &addr_bin,
Packit Service a1bd4f
                            prefix_len == -1 ? ((addr_family == AF_INET) ? 32 : 128) : prefix_len,
Packit Service a1bd4f
                            NULL));
Packit Service a1bd4f
                }
Packit Service a1bd4f
                continue;
Packit Service a1bd4f
            }
Packit Service a1bd4f
Packit Service a1bd4f
            if (line_match(line, "MTU", &matched_key, &value)) {
Packit Service a1bd4f
                i64 = _nm_utils_ascii_str_to_int64(value, 0, 0, G_MAXUINT32, -1);
Packit Service a1bd4f
                if (i64 == -1)
Packit Service a1bd4f
                    goto fail_invalid_value;
Packit Service a1bd4f
Packit Service a1bd4f
                /* wg-quick accepts the "MTU" value, but it also fetches routes to
Packit Service a1bd4f
                 * autodetect it. NetworkManager won't do that, we can only configure
Packit Service a1bd4f
                 * an explicit MTU or no autodetection will be performed. */
Packit Service a1bd4f
                data_mtu = i64;
Packit Service a1bd4f
                continue;
Packit Service a1bd4f
            }
Packit Service a1bd4f
Packit Service a1bd4f
            if (line_match(line, "DNS", &matched_key, &value)) {
Packit Service a1bd4f
                char *value_word;
Packit Service a1bd4f
Packit Service a1bd4f
                while (value_split_word(&value, &value_word)) {
Packit Service a1bd4f
                    GPtrArray **p_data_dns;
Packit Service a1bd4f
                    NMIPAddr    addr_bin;
Packit Service a1bd4f
                    int         addr_family;
Packit Service a1bd4f
Packit Service a1bd4f
                    if (nm_utils_parse_inaddr_bin(AF_UNSPEC, value_word, &addr_family, &addr_bin)) {
Packit Service a1bd4f
                        p_data_dns = (addr_family == AF_INET) ? &data_dns_v4 : &data_dns_v6;
Packit Service a1bd4f
                        if (!*p_data_dns)
Packit Service a1bd4f
                            *p_data_dns = g_ptr_array_new_with_free_func(g_free);
Packit Service a1bd4f
Packit Service a1bd4f
                        g_ptr_array_add(*p_data_dns,
Packit Service a1bd4f
                                        nm_utils_inet_ntop_dup(addr_family, &addr_bin));
Packit Service a1bd4f
                        continue;
Packit Service a1bd4f
                    }
Packit Service a1bd4f
Packit Service a1bd4f
                    if (!data_dns_search)
Packit Service a1bd4f
                        data_dns_search = g_ptr_array_new_with_free_func(g_free);
Packit Service a1bd4f
                    g_ptr_array_add(data_dns_search, g_strdup(value_word));
Packit Service a1bd4f
                }
Packit Service a1bd4f
                continue;
Packit Service a1bd4f
            }
Packit Service a1bd4f
Packit Service a1bd4f
            if (line_match(line, "Table", &matched_key, &value)) {
Packit Service a1bd4f
                if (nm_streq(value, "auto"))
Packit Service a1bd4f
                    data_table = _TABLE_AUTO;
Packit Service a1bd4f
                else if (nm_streq(value, "off"))
Packit Service a1bd4f
                    data_table = _TABLE_OFF;
Packit Service a1bd4f
                else {
Packit Service a1bd4f
                    /* we don't support table names from /etc/iproute2/rt_tables
Packit Service a1bd4f
                     * But we accept hex like `ip route add` would. */
Packit Service a1bd4f
                    i64 = _nm_utils_ascii_str_to_int64(value, 0, 0, G_MAXINT32, -1);
Packit Service a1bd4f
                    if (i64 == -1)
Packit Service a1bd4f
                        goto fail_invalid_value;
Packit Service a1bd4f
                    data_table = i64;
Packit Service a1bd4f
                }
Packit Service a1bd4f
                continue;
Packit Service a1bd4f
            }
Packit Service a1bd4f
Packit Service a1bd4f
            if (line_match(line, "PreUp", &matched_key, &value)
Packit Service a1bd4f
                || line_match(line, "PreDown", &matched_key, &value)
Packit Service a1bd4f
                || line_match(line, "PostUp", &matched_key, &value)
Packit Service a1bd4f
                || line_match(line, "PostDown", &matched_key, &value)) {
Packit Service a1bd4f
                /* we don't run any scripts. Silently ignore these parameters. */
Packit Service a1bd4f
                continue;
Packit Service a1bd4f
            }
Packit Service a1bd4f
Packit Service a1bd4f
            if (line_match(line, "SaveConfig", &matched_key, &value)) {
Packit Service a1bd4f
                /* we ignore the setting, but enforce that it's either true or false (like
Packit Service a1bd4f
                 * wg-quick. */
Packit Service a1bd4f
                if (!NM_IN_STRSET(value, "true", "false"))
Packit Service a1bd4f
                    goto fail_invalid_value;
Packit Service a1bd4f
                continue;
Packit Service a1bd4f
            }
Packit Service a1bd4f
Packit Service a1bd4f
            if (line_match(line, "ListenPort", &matched_key, &value)) {
Packit Service a1bd4f
                /* we don't use getaddrinfo(), unlike `wg setconf`. Just interpret
Packit Service a1bd4f
                 * the port as plain decimal number. */
Packit Service a1bd4f
                i64 = _nm_utils_ascii_str_to_int64(value, 10, 0, 0xFFFF, -1);
Packit Service a1bd4f
                if (i64 == -1)
Packit Service a1bd4f
                    goto fail_invalid_value;
Packit Service a1bd4f
                data_listen_port = i64;
Packit Service a1bd4f
                continue;
Packit Service a1bd4f
            }
Packit Service a1bd4f
Packit Service a1bd4f
            if (line_match(line, "FwMark", &matched_key, &value)) {
Packit Service a1bd4f
                if (nm_streq(value, "off"))
Packit Service a1bd4f
                    data_fwmark = 0;
Packit Service a1bd4f
                else {
Packit Service a1bd4f
                    i64 = _nm_utils_ascii_str_to_int64(value, 0, 0, G_MAXINT32, -1);
Packit Service a1bd4f
                    if (i64 == -1)
Packit Service a1bd4f
                        goto fail_invalid_value;
Packit Service a1bd4f
                    data_fwmark = i64;
Packit Service a1bd4f
                }
Packit Service a1bd4f
                continue;
Packit Service a1bd4f
            }
Packit Service a1bd4f
Packit Service a1bd4f
            if (line_match(line, "PrivateKey", &matched_key, &value)) {
Packit Service a1bd4f
                if (!nm_utils_base64secret_decode(value, NM_WIREGUARD_PUBLIC_KEY_LEN, NULL))
Packit Service a1bd4f
                    goto fail_invalid_secret;
Packit Service a1bd4f
                data_private_key = value;
Packit Service a1bd4f
                continue;
Packit Service a1bd4f
            }
Packit Service a1bd4f
Packit Service a1bd4f
            goto fail_invalid_line;
Packit Service a1bd4f
        }
Packit Service a1bd4f
Packit Service a1bd4f
        if (line_context == LINE_CONTEXT_PEER) {
Packit Service a1bd4f
            if (line_match(line, "Endpoint", &matched_key, &value)) {
Packit Service a1bd4f
                if (!nm_wireguard_peer_set_endpoint(current_peer, value, FALSE))
Packit Service a1bd4f
                    goto fail_invalid_value;
Packit Service a1bd4f
                continue;
Packit Service a1bd4f
            }
Packit Service a1bd4f
Packit Service a1bd4f
            if (line_match(line, "PublicKey", &matched_key, &value)) {
Packit Service a1bd4f
                if (!nm_wireguard_peer_set_public_key(current_peer, value, FALSE))
Packit Service a1bd4f
                    goto fail_invalid_value;
Packit Service a1bd4f
                continue;
Packit Service a1bd4f
            }
Packit Service a1bd4f
Packit Service a1bd4f
            if (line_match(line, "AllowedIPs", &matched_key, &value)) {
Packit Service a1bd4f
                char *value_word;
Packit Service a1bd4f
Packit Service a1bd4f
                while (value_split_word(&value, &value_word)) {
Packit Service a1bd4f
                    if (!nm_wireguard_peer_append_allowed_ip(current_peer, value_word, FALSE))
Packit Service a1bd4f
                        goto fail_invalid_value;
Packit Service a1bd4f
                }
Packit Service a1bd4f
                continue;
Packit Service a1bd4f
            }
Packit Service a1bd4f
Packit Service a1bd4f
            if (line_match(line, "PersistentKeepalive", &matched_key, &value)) {
Packit Service a1bd4f
                if (nm_streq(value, "off"))
Packit Service a1bd4f
                    i64 = 0;
Packit Service a1bd4f
                else {
Packit Service a1bd4f
                    i64 = _nm_utils_ascii_str_to_int64(value, 10, 0, G_MAXUINT16, -1);
Packit Service a1bd4f
                    if (i64 == -1)
Packit Service a1bd4f
                        goto fail_invalid_value;
Packit Service a1bd4f
                }
Packit Service a1bd4f
                nm_wireguard_peer_set_persistent_keepalive(current_peer, i64);
Packit Service a1bd4f
                continue;
Packit Service a1bd4f
            }
Packit Service a1bd4f
Packit Service a1bd4f
            if (line_match(line, "PresharedKey", &matched_key, &value)) {
Packit Service a1bd4f
                if (!nm_wireguard_peer_set_preshared_key(current_peer, value, FALSE))
Packit Service a1bd4f
                    goto fail_invalid_secret;
Packit Service a1bd4f
                nm_wireguard_peer_set_preshared_key_flags(current_peer,
Packit Service a1bd4f
                                                          NM_SETTING_SECRET_FLAG_NONE);
Packit Service a1bd4f
                continue;
Packit Service a1bd4f
            }
Packit Service a1bd4f
Packit Service a1bd4f
            goto fail_invalid_line;
Packit Service a1bd4f
        }
Packit 5756e2
Packit 5756e2
fail_invalid_line:
Packit Service a1bd4f
        nm_utils_error_set(error,
Packit Service a1bd4f
                           NM_UTILS_ERROR_INVALID_ARGUMENT,
Packit Service a1bd4f
                           _("unrecognized line at %s:%zu"),
Packit Service a1bd4f
                           filename,
Packit Service a1bd4f
                           line_nr);
Packit Service a1bd4f
        return FALSE;
Packit 5756e2
fail_invalid_value:
Packit Service a1bd4f
        nm_utils_error_set(error,
Packit Service a1bd4f
                           NM_UTILS_ERROR_INVALID_ARGUMENT,
Packit Service a1bd4f
                           _("invalid value for '%s' at %s:%zu"),
Packit Service a1bd4f
                           matched_key,
Packit Service a1bd4f
                           filename,
Packit Service a1bd4f
                           line_nr);
Packit Service a1bd4f
        return FALSE;
Packit 5756e2
fail_invalid_secret:
Packit Service a1bd4f
        nm_utils_error_set(error,
Packit Service a1bd4f
                           NM_UTILS_ERROR_INVALID_ARGUMENT,
Packit Service a1bd4f
                           _("invalid secret '%s' at %s:%zu"),
Packit Service a1bd4f
                           matched_key,
Packit Service a1bd4f
                           filename,
Packit Service a1bd4f
                           line_nr);
Packit Service a1bd4f
        return FALSE;
Packit Service a1bd4f
    }
Packit Service a1bd4f
Packit Service a1bd4f
    if (!_wg_complete_peer(&data_peers,
Packit Service a1bd4f
                           g_steal_pointer(&current_peer),
Packit Service a1bd4f
                           current_peer_start_line_nr,
Packit Service a1bd4f
                           filename,
Packit Service a1bd4f
                           error))
Packit Service a1bd4f
        return FALSE;
Packit Service a1bd4f
Packit Service a1bd4f
    connection = nm_simple_connection_new();
Packit Service a1bd4f
    s_con      = NM_SETTING_CONNECTION(nm_setting_connection_new());
Packit Service a1bd4f
    nm_connection_add_setting(connection, NM_SETTING(s_con));
Packit Service a1bd4f
    s_ip4 = NM_SETTING_IP_CONFIG(nm_setting_ip4_config_new());
Packit Service a1bd4f
    nm_connection_add_setting(connection, NM_SETTING(s_ip4));
Packit Service a1bd4f
    s_ip6 = NM_SETTING_IP_CONFIG(nm_setting_ip6_config_new());
Packit Service a1bd4f
    nm_connection_add_setting(connection, NM_SETTING(s_ip6));
Packit Service a1bd4f
    s_wg = NM_SETTING_WIREGUARD(nm_setting_wireguard_new());
Packit Service a1bd4f
    nm_connection_add_setting(connection, NM_SETTING(s_wg));
Packit Service a1bd4f
Packit Service a1bd4f
    uuid = nm_utils_uuid_generate();
Packit Service a1bd4f
Packit Service a1bd4f
    g_object_set(s_con,
Packit Service a1bd4f
                 NM_SETTING_CONNECTION_ID,
Packit Service a1bd4f
                 ifname,
Packit Service a1bd4f
                 NM_SETTING_CONNECTION_UUID,
Packit Service a1bd4f
                 uuid,
Packit Service a1bd4f
                 NM_SETTING_CONNECTION_TYPE,
Packit Service a1bd4f
                 NM_SETTING_WIREGUARD_SETTING_NAME,
Packit Service a1bd4f
                 NM_SETTING_CONNECTION_INTERFACE_NAME,
Packit Service a1bd4f
                 ifname,
Packit Service a1bd4f
                 NULL);
Packit Service a1bd4f
Packit Service a1bd4f
    g_object_set(s_wg,
Packit Service a1bd4f
                 NM_SETTING_WIREGUARD_PRIVATE_KEY,
Packit Service a1bd4f
                 data_private_key,
Packit Service a1bd4f
                 NM_SETTING_WIREGUARD_LISTEN_PORT,
Packit Service a1bd4f
                 data_listen_port,
Packit Service a1bd4f
                 NM_SETTING_WIREGUARD_FWMARK,
Packit Service a1bd4f
                 data_fwmark,
Packit Service a1bd4f
                 NM_SETTING_WIREGUARD_MTU,
Packit Service a1bd4f
                 data_mtu,
Packit Service a1bd4f
                 NULL);
Packit Service a1bd4f
Packit Service a1bd4f
    if (data_peers) {
Packit Service a1bd4f
        for (i = 0; i < data_peers->len; i++)
Packit Service a1bd4f
            nm_setting_wireguard_append_peer(s_wg, data_peers->pdata[i]);
Packit Service a1bd4f
    }
Packit Service a1bd4f
Packit Service a1bd4f
    for (is_v4 = 0; is_v4 < 2; is_v4++) {
Packit Service a1bd4f
        const char *method_disabled =
Packit Service a1bd4f
            is_v4 ? NM_SETTING_IP4_CONFIG_METHOD_DISABLED : NM_SETTING_IP6_CONFIG_METHOD_DISABLED;
Packit Service a1bd4f
        const char *method_manual =
Packit Service a1bd4f
            is_v4 ? NM_SETTING_IP4_CONFIG_METHOD_MANUAL : NM_SETTING_IP6_CONFIG_METHOD_MANUAL;
Packit Service a1bd4f
        NMSettingIPConfig *s_ip             = is_v4 ? s_ip4 : s_ip6;
Packit Service a1bd4f
        GPtrArray *        data_dns         = is_v4 ? data_dns_v4 : data_dns_v6;
Packit Service a1bd4f
        GPtrArray *        data_addr        = is_v4 ? data_addr_v4 : data_addr_v6;
Packit Service a1bd4f
        GPtrArray *        data_dns_search2 = data_dns_search;
Packit Service a1bd4f
Packit Service a1bd4f
        if (data_dns && !data_addr) {
Packit Service a1bd4f
            /* When specifying "DNS", we also require an "Address" for the same address
Packit Service a1bd4f
             * family. That is because a NMSettingIPConfig cannot have @method_disabled
Packit Service a1bd4f
             * and DNS settings at the same time.
Packit Service a1bd4f
             *
Packit Service a1bd4f
             * We don't have addresses. Silently ignore the DNS setting. */
Packit Service a1bd4f
            data_dns         = NULL;
Packit Service a1bd4f
            data_dns_search2 = NULL;
Packit Service a1bd4f
        }
Packit Service a1bd4f
Packit Service a1bd4f
        g_object_set(s_ip,
Packit Service a1bd4f
                     NM_SETTING_IP_CONFIG_METHOD,
Packit Service a1bd4f
                     data_addr ? method_manual : method_disabled,
Packit Service a1bd4f
                     NULL);
Packit Service a1bd4f
Packit Service a1bd4f
        /* For WireGuard profiles, always set dns-priority to a negative value,
Packit Service a1bd4f
         * so that DNS servers on other profiles get ignored. This is also what
Packit Service a1bd4f
         * wg-quick does, by calling `resolvconf -x`. */
Packit Service a1bd4f
        g_object_set(s_ip, NM_SETTING_IP_CONFIG_DNS_PRIORITY, (int) -50, NULL);
Packit Service a1bd4f
Packit Service a1bd4f
        if (data_addr) {
Packit Service a1bd4f
            for (i = 0; i < data_addr->len; i++)
Packit Service a1bd4f
                nm_setting_ip_config_add_address(s_ip, data_addr->pdata[i]);
Packit Service a1bd4f
        }
Packit Service a1bd4f
        if (data_dns) {
Packit Service a1bd4f
            for (i = 0; i < data_dns->len; i++)
Packit Service a1bd4f
                nm_setting_ip_config_add_dns(s_ip, data_dns->pdata[i]);
Packit Service a1bd4f
Packit Service a1bd4f
            /* Of the wg-quick doesn't specify a search domain, assume the user
Packit Service a1bd4f
             * wants to use the domain server for all searches. */
Packit Service a1bd4f
            if (!data_dns_search2)
Packit Service a1bd4f
                nm_setting_ip_config_add_dns_search(s_ip, "~");
Packit Service a1bd4f
        }
Packit Service a1bd4f
        if (data_dns_search2) {
Packit Service a1bd4f
            for (i = 0; i < data_dns_search2->len; i++)
Packit Service a1bd4f
                nm_setting_ip_config_add_dns_search(s_ip, data_dns_search2->pdata[i]);
Packit Service a1bd4f
        }
Packit Service a1bd4f
Packit Service a1bd4f
        if (data_table == _TABLE_AUTO) {
Packit Service a1bd4f
            /* in the "auto" setting, wg-quick adds peer-routes automatically to the main
Packit Service a1bd4f
             * table. NetworkManager will do that too, but there are differences:
Packit Service a1bd4f
             *
Packit Service a1bd4f
             * - NetworkManager (contrary to wg-quick) does not check whether the peer-route is necessary.
Packit Service a1bd4f
             *   It will always add a route for each allowed-ips range, even if there is already another
Packit Service a1bd4f
             *   route that would ensure packets to the endpoint are routed via the WireGuard interface.
Packit Service a1bd4f
             *   If you don't want that, disable "wireguard.peer-routes", and add the necessary routes
Packit Service a1bd4f
             *   yourself to "ipv4.routes" and "ipv6.routes".
Packit Service a1bd4f
             *
Packit Service a1bd4f
             * - With "auto", wg-quick also configures policy routing to handle default-routes (/0) to
Packit Service a1bd4f
             *   avoid routing loops.
Packit Service a1bd4f
             *   The imported connection profile will have wireguard.ip4-auto-default-route and
Packit Service a1bd4f
             *   wireguard.ip6-auto-default-route set to "default". It will thus configure wg-quick's
Packit Service a1bd4f
             *   policy routing if the profile has any AllowedIPs ranges with /0.
Packit Service a1bd4f
             */
Packit Service a1bd4f
        } else if (data_table == _TABLE_OFF) {
Packit Service a1bd4f
            if (is_v4) {
Packit Service a1bd4f
                g_object_set(s_wg, NM_SETTING_WIREGUARD_PEER_ROUTES, FALSE, NULL);
Packit Service a1bd4f
            }
Packit Service a1bd4f
        } else {
Packit Service a1bd4f
            g_object_set(s_ip, NM_SETTING_IP_CONFIG_ROUTE_TABLE, (guint) data_table, NULL);
Packit Service a1bd4f
        }
Packit Service a1bd4f
    }
Packit Service a1bd4f
Packit Service a1bd4f
    if (!nm_connection_normalize(connection, NULL, NULL, &local)) {
Packit Service a1bd4f
        nm_utils_error_set(error,
Packit Service a1bd4f
                           NM_UTILS_ERROR_INVALID_ARGUMENT,
Packit Service a1bd4f
                           _("Failed to create WireGuard connection: %s"),
Packit Service a1bd4f
                           local->message);
Packit Service a1bd4f
        return FALSE;
Packit Service a1bd4f
    }
Packit Service a1bd4f
Packit Service a1bd4f
    return g_steal_pointer(&connection);
Packit 5756e2
}