2020-12-23 22:21:36 +01:00
|
|
|
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
2018-03-13 13:42:38 +00:00
|
|
|
/*
|
2019-10-01 09:20:35 +02:00
|
|
|
* Copyright (C) 2018 Javier Arteaga <jarteaga@jbeta.is>
|
2018-03-13 13:42:38 +00:00
|
|
|
*/
|
|
|
|
|
|
2021-02-04 18:04:13 +01:00
|
|
|
#include "src/core/nm-default-daemon.h"
|
2018-03-13 13:42:38 +00:00
|
|
|
|
|
|
|
|
#include "nm-device-wireguard.h"
|
|
|
|
|
|
wireguard: support configuring policy routing to avoid routing loops
For WireGuard (like for all IP-tunnels and IP-based VPNs), the IP addresses of
the peers must be reached outside the tunnel/VPN itself.
For VPN connections, NetworkManager usually adds a direct /32 route to
the external VPN gateway to the underlying device. For WireGuard that is
not done, because injecting a route to another device is ugly and error
prone. Worse: WireGuard with automatic roaming and multiple peers makes this
more complicated.
This is commonly a problem when setting the default-route via the VPN,
but there are also other subtle setups where special care must be taken
to prevent such routing loops.
WireGuard's wg-quick provides a simple, automatic solution by adding two policy
routing rules and relying on the WireGuard packets having a fwmark set (see [1]).
Let's also do that. Add new properties "wireguard.ip4-auto-default-route"
and "wireguard.ip6-auto-default-route" to enable/disable this. Note that
the default value lets NetworkManager automatically choose whether to
enable it (depending on whether there are any peers that have a default
route). This means, common scenarios should now work well without additional
configuration.
Note that this is also a change in behavior and upon package upgrade
NetworkManager may start adding policy routes (if there are peers that
have a default-route). This is a change in behavior, as the user already
clearly had this setup working and configured some working solution
already.
The new automatism picks the rule priority automatically and adds the
default-route to the routing table that has the same number as the fwmark.
If any of this is unsuitable, then the user is free to disable this
automatism. Note that since 1.18.0 NetworkManager supports policy routing (*).
That means, what this automatism does can be also achieved via explicit
configuration of the profile, which gives the user more flexibility to
adjust all parameters explicitly).
(*) but only since 1.20.0 NetworkManager supports the "suppress_prefixlength"
rule attribute, which makes it impossible to configure exactly this rule-based
solution with 1.18.0 NetworkManager.
[1] https://www.wireguard.com/netns/#improved-rule-based-routing
2019-04-30 17:48:46 +02:00
|
|
|
#include <linux/rtnetlink.h>
|
|
|
|
|
#include <linux/fib_rules.h>
|
|
|
|
|
|
2018-12-27 16:48:30 +01:00
|
|
|
#include "nm-setting-wireguard.h"
|
2021-08-20 18:40:21 +08:00
|
|
|
#include "libnm-core-aux-intern/nm-libnm-core-utils.h"
|
2021-08-06 15:17:05 +02:00
|
|
|
#include "nm-l3-config-data.h"
|
2021-02-12 15:01:09 +01:00
|
|
|
#include "libnm-core-intern/nm-core-internal.h"
|
2021-02-18 17:37:47 +01:00
|
|
|
#include "libnm-glib-aux/nm-secret-utils.h"
|
2018-03-13 13:42:38 +00:00
|
|
|
#include "nm-device-private.h"
|
2021-03-04 11:29:39 +01:00
|
|
|
#include "libnm-platform/nm-platform.h"
|
|
|
|
|
#include "libnm-platform/nmp-object.h"
|
|
|
|
|
#include "libnm-platform/nmp-rules-manager.h"
|
2018-03-13 13:42:38 +00:00
|
|
|
#include "nm-device-factory.h"
|
2018-12-29 00:04:20 +01:00
|
|
|
#include "nm-active-connection.h"
|
|
|
|
|
#include "nm-act-request.h"
|
2019-01-12 12:33:35 +01:00
|
|
|
#include "dns/nm-dns-manager.h"
|
2018-03-13 13:42:38 +00:00
|
|
|
|
2020-11-06 18:22:11 +01:00
|
|
|
#define _NMLOG_DEVICE_TYPE NMDeviceWireGuard
|
2018-03-13 13:42:38 +00:00
|
|
|
#include "nm-device-logging.h"
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2019-01-12 12:33:35 +01:00
|
|
|
/* TODO: activate profile with peer preshared-key-flags=2. On first activation, the secret is
|
|
|
|
|
* requested (good). Enter it and connect. Reactivate the profile, now there is no password
|
|
|
|
|
* prompt, as the secret is cached (good??). */
|
|
|
|
|
|
|
|
|
|
/* TODO: unlike for other VPNs, we don't inject a direct route to the peers. That means,
|
2020-07-04 11:37:01 +03:00
|
|
|
* you might get a routing scenario where the peer (VPN server) is reachable via the VPN.
|
2019-01-12 12:33:35 +01:00
|
|
|
* How we handle adding routes to external gateway for other peers, has severe issues
|
2020-07-04 11:37:01 +03:00
|
|
|
* as well. We may use policy-routing like wg-quick does. See also discussions at
|
2019-03-01 09:02:20 +01:00
|
|
|
* https://www.wireguard.com/netns/#improving-the-classic-solutions */
|
|
|
|
|
|
|
|
|
|
/* TODO: honor the TTL of DNS to determine when to retry resolving endpoints. */
|
|
|
|
|
|
|
|
|
|
/* TODO: when we get multiple IP addresses when resolving a peer endpoint. We currently
|
|
|
|
|
* just take the first from GAI. We should only accept AAAA/IPv6 if we also have a suitable
|
|
|
|
|
* IPv6 address. The problem is, that we have to recheck that when IP addressing on other
|
|
|
|
|
* interfaces changes. This makes it almost too cumbersome to implement. */
|
2019-01-12 12:33:35 +01:00
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2018-12-27 16:48:30 +01:00
|
|
|
G_STATIC_ASSERT(NM_WIREGUARD_PUBLIC_KEY_LEN == NMP_WIREGUARD_PUBLIC_KEY_LEN);
|
|
|
|
|
G_STATIC_ASSERT(NM_WIREGUARD_SYMMETRIC_KEY_LEN == NMP_WIREGUARD_SYMMETRIC_KEY_LEN);
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2019-12-13 16:54:30 +01:00
|
|
|
#define LINK_CONFIG_RATE_LIMIT_NSEC (50 * NM_UTILS_NSEC_PER_MSEC)
|
2019-01-12 12:33:35 +01:00
|
|
|
|
|
|
|
|
/* a special @next_try_at_nsec timestamp indicating that we should try again as soon as possible. */
|
|
|
|
|
#define NEXT_TRY_AT_NSEC_ASAP ((gint64) G_MAXINT64)
|
|
|
|
|
|
|
|
|
|
/* a special @next_try_at_nsec timestamp that is
|
|
|
|
|
* - positive (indicating resolve-checks are enabled)
|
|
|
|
|
* - already in the past (we use the absolute timestamp of 1nsec for that). */
|
|
|
|
|
#define NEXT_TRY_AT_NSEC_PAST ((gint64) 1)
|
|
|
|
|
|
|
|
|
|
/* like %NEXT_TRY_AT_NSEC_ASAP, but used for indicating to retry ASAP for a @retry_in_msec value.
|
2020-07-04 11:37:01 +03:00
|
|
|
* That is a relative time duration, contrary to @next_try_at_nsec which is an absolute
|
2019-01-12 12:33:35 +01:00
|
|
|
* timestamp. */
|
|
|
|
|
#define RETRY_IN_MSEC_ASAP ((gint64) G_MAXINT64)
|
|
|
|
|
|
2021-05-03 21:48:58 +02:00
|
|
|
#define RETRY_IN_MSEC_MAX ((gint64) (30 * 60 * 1000))
|
2019-01-12 12:33:35 +01:00
|
|
|
|
|
|
|
|
typedef enum {
|
|
|
|
|
LINK_CONFIG_MODE_FULL,
|
|
|
|
|
LINK_CONFIG_MODE_REAPPLY,
|
|
|
|
|
LINK_CONFIG_MODE_ASSUME,
|
|
|
|
|
LINK_CONFIG_MODE_ENDPOINTS,
|
|
|
|
|
} LinkConfigMode;
|
|
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
|
GCancellable *cancellable;
|
|
|
|
|
|
|
|
|
|
NMSockAddrUnion sockaddr;
|
|
|
|
|
|
2019-12-13 16:54:30 +01:00
|
|
|
/* the timestamp (in nm_utils_get_monotonic_timestamp_nsec() scale) when we want
|
2019-01-12 12:33:35 +01:00
|
|
|
* to retry resolving the endpoint (again).
|
|
|
|
|
*
|
|
|
|
|
* It may be set to %NEXT_TRY_AT_NSEC_ASAP to indicate to re-resolve as soon as possible.
|
|
|
|
|
*
|
|
|
|
|
* A @sockaddr is either fixed or it has
|
|
|
|
|
* - @cancellable set to indicate an ongoing request
|
|
|
|
|
* - @next_try_at_nsec set to a positive value, indicating when
|
|
|
|
|
* we ought to retry. */
|
|
|
|
|
gint64 next_try_at_nsec;
|
|
|
|
|
|
|
|
|
|
guint resolv_fail_count;
|
|
|
|
|
} PeerEndpointResolveData;
|
|
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
|
NMWireGuardPeer *peer;
|
|
|
|
|
|
|
|
|
|
NMDeviceWireGuard *self;
|
|
|
|
|
|
|
|
|
|
CList lst_peers;
|
|
|
|
|
|
|
|
|
|
PeerEndpointResolveData ep_resolv;
|
|
|
|
|
|
|
|
|
|
/* dirty flag used during _peers_update_all(). */
|
|
|
|
|
bool dirty_update_all : 1;
|
|
|
|
|
} PeerData;
|
|
|
|
|
|
2018-03-13 13:42:38 +00:00
|
|
|
NM_GOBJECT_PROPERTIES_DEFINE(NMDeviceWireGuard, PROP_PUBLIC_KEY, PROP_LISTEN_PORT, PROP_FWMARK, );
|
|
|
|
|
|
2018-12-29 00:04:20 +01:00
|
|
|
typedef struct {
|
2019-01-12 12:33:35 +01:00
|
|
|
NMDnsManager *dns_manager;
|
|
|
|
|
|
|
|
|
|
NMPlatformLnkWireGuard lnk_curr;
|
|
|
|
|
NMActRequestGetSecretsCallId *secrets_call_id;
|
|
|
|
|
|
2018-12-29 00:04:20 +01:00
|
|
|
CList lst_peers_head;
|
|
|
|
|
GHashTable *peers;
|
2019-01-12 12:33:35 +01:00
|
|
|
|
wireguard: delay activation while resolving DNS names for WireGuard peers to avoid race
The endpoints of WireGuard peers can be configured as DNS name, which
NetworkManager will resolve.
Since activating a profile might affect now names get resolved, we must
first resolve names before completing the activation of the WireGuard
device (and before reconfiguring DNS accordingly).
For example, if you configure exclusive DNS resolution via the WireGuard
device, and if the peer needs to be resolved via DNS, then resolving the
peer name must happen before the reconfiguration of DNS. Otherwise the
new DNS configuration will be broken due to being unable to reach the
WireGuard peer.
Fix that by waiting.
There is still an unfixed problem. If resolving any peers fails,
activation silently proceeds -- again possibly breaking the network
setup. Of course, NetworkManager will repeatedly try to re-resolve
the name, but that may never succeed if DNS would be resolved via
the VPN itself.
That is different from `wg set` which resolves hostnames and fails.
Consequently `wg-quick up` would also fail. But these are both one shot
applications, they are not around and basically let the user handle the
error (by reading the log and invoking the command again). NetworkManager
can do something different and proceed activation (as it will also
periodically re-resolve the hostnames again). Note that it's also valid
to activate a WireGuard device without any peers (and to modify the
activated device later with Reapply()). As such, having no peers (or
being unable to resolve a hostname) may be a valid configuration.
I think we should add an option/flag that when enabled will cause
the activation to fail of names cannot be resolved.
https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/issues/535
https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/issues/616
https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/721
2021-01-08 11:09:03 +01:00
|
|
|
/* counts the numbers of peers that are currently resolving. */
|
|
|
|
|
guint peers_resolving_cnt;
|
|
|
|
|
|
2019-01-12 12:33:35 +01:00
|
|
|
gint64 resolve_next_try_at;
|
|
|
|
|
gint64 link_config_last_at;
|
|
|
|
|
|
|
|
|
|
guint resolve_next_try_id;
|
|
|
|
|
guint link_config_delayed_id;
|
wireguard: support configuring policy routing to avoid routing loops
For WireGuard (like for all IP-tunnels and IP-based VPNs), the IP addresses of
the peers must be reached outside the tunnel/VPN itself.
For VPN connections, NetworkManager usually adds a direct /32 route to
the external VPN gateway to the underlying device. For WireGuard that is
not done, because injecting a route to another device is ugly and error
prone. Worse: WireGuard with automatic roaming and multiple peers makes this
more complicated.
This is commonly a problem when setting the default-route via the VPN,
but there are also other subtle setups where special care must be taken
to prevent such routing loops.
WireGuard's wg-quick provides a simple, automatic solution by adding two policy
routing rules and relying on the WireGuard packets having a fwmark set (see [1]).
Let's also do that. Add new properties "wireguard.ip4-auto-default-route"
and "wireguard.ip6-auto-default-route" to enable/disable this. Note that
the default value lets NetworkManager automatically choose whether to
enable it (depending on whether there are any peers that have a default
route). This means, common scenarios should now work well without additional
configuration.
Note that this is also a change in behavior and upon package upgrade
NetworkManager may start adding policy routes (if there are peers that
have a default-route). This is a change in behavior, as the user already
clearly had this setup working and configured some working solution
already.
The new automatism picks the rule priority automatically and adds the
default-route to the routing table that has the same number as the fwmark.
If any of this is unsuitable, then the user is free to disable this
automatism. Note that since 1.18.0 NetworkManager supports policy routing (*).
That means, what this automatism does can be also achieved via explicit
configuration of the profile, which gives the user more flexibility to
adjust all parameters explicitly).
(*) but only since 1.20.0 NetworkManager supports the "suppress_prefixlength"
rule attribute, which makes it impossible to configure exactly this rule-based
solution with 1.18.0 NetworkManager.
[1] https://www.wireguard.com/netns/#improved-rule-based-routing
2019-04-30 17:48:46 +02:00
|
|
|
|
2019-01-12 12:33:35 +01:00
|
|
|
guint32 auto_default_route_fwmark;
|
wireguard: support configuring policy routing to avoid routing loops
For WireGuard (like for all IP-tunnels and IP-based VPNs), the IP addresses of
the peers must be reached outside the tunnel/VPN itself.
For VPN connections, NetworkManager usually adds a direct /32 route to
the external VPN gateway to the underlying device. For WireGuard that is
not done, because injecting a route to another device is ugly and error
prone. Worse: WireGuard with automatic roaming and multiple peers makes this
more complicated.
This is commonly a problem when setting the default-route via the VPN,
but there are also other subtle setups where special care must be taken
to prevent such routing loops.
WireGuard's wg-quick provides a simple, automatic solution by adding two policy
routing rules and relying on the WireGuard packets having a fwmark set (see [1]).
Let's also do that. Add new properties "wireguard.ip4-auto-default-route"
and "wireguard.ip6-auto-default-route" to enable/disable this. Note that
the default value lets NetworkManager automatically choose whether to
enable it (depending on whether there are any peers that have a default
route). This means, common scenarios should now work well without additional
configuration.
Note that this is also a change in behavior and upon package upgrade
NetworkManager may start adding policy routes (if there are peers that
have a default-route). This is a change in behavior, as the user already
clearly had this setup working and configured some working solution
already.
The new automatism picks the rule priority automatically and adds the
default-route to the routing table that has the same number as the fwmark.
If any of this is unsuitable, then the user is free to disable this
automatism. Note that since 1.18.0 NetworkManager supports policy routing (*).
That means, what this automatism does can be also achieved via explicit
configuration of the profile, which gives the user more flexibility to
adjust all parameters explicitly).
(*) but only since 1.20.0 NetworkManager supports the "suppress_prefixlength"
rule attribute, which makes it impossible to configure exactly this rule-based
solution with 1.18.0 NetworkManager.
[1] https://www.wireguard.com/netns/#improved-rule-based-routing
2019-04-30 17:48:46 +02:00
|
|
|
|
|
|
|
|
guint32 auto_default_route_priority;
|
|
|
|
|
|
|
|
|
|
bool auto_default_route_enabled_4 : 1;
|
|
|
|
|
bool auto_default_route_enabled_6 : 1;
|
|
|
|
|
bool auto_default_route_initialized : 1;
|
|
|
|
|
bool auto_default_route_refresh : 1;
|
|
|
|
|
bool auto_default_route_priority_initialized : 1;
|
|
|
|
|
|
2018-12-29 00:04:20 +01:00
|
|
|
} NMDeviceWireGuardPrivate;
|
|
|
|
|
|
2018-03-13 13:42:38 +00:00
|
|
|
struct _NMDeviceWireGuard {
|
|
|
|
|
NMDevice parent;
|
2018-12-29 00:04:20 +01:00
|
|
|
NMDeviceWireGuardPrivate _priv;
|
2018-03-13 13:42:38 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
struct _NMDeviceWireGuardClass {
|
|
|
|
|
NMDeviceClass parent;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
G_DEFINE_TYPE(NMDeviceWireGuard, nm_device_wireguard, NM_TYPE_DEVICE)
|
|
|
|
|
|
2018-12-29 00:04:20 +01:00
|
|
|
#define NM_DEVICE_WIREGUARD_GET_PRIVATE(self) \
|
|
|
|
|
_NM_GET_PRIVATE(self, NMDeviceWireGuard, NM_IS_DEVICE_WIREGUARD, NMDevice)
|
2018-03-13 13:42:38 +00:00
|
|
|
|
2018-12-29 00:04:20 +01:00
|
|
|
/*****************************************************************************/
|
2018-03-13 13:42:38 +00:00
|
|
|
|
2019-01-12 12:33:35 +01:00
|
|
|
static void _peers_resolve_start(NMDeviceWireGuard *self, PeerData *peer_data);
|
|
|
|
|
|
|
|
|
|
static void _peers_resolve_retry_reschedule(NMDeviceWireGuard *self, gint64 new_next_try_at_nsec);
|
|
|
|
|
|
|
|
|
|
static gboolean link_config_delayed_resolver_cb(gpointer user_data);
|
|
|
|
|
|
|
|
|
|
static gboolean link_config_delayed_ratelimit_cb(gpointer user_data);
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2020-02-13 14:55:26 +01:00
|
|
|
static NM_UTILS_LOOKUP_STR_DEFINE(_link_config_mode_to_string,
|
|
|
|
|
LinkConfigMode,
|
2019-01-12 12:33:35 +01:00
|
|
|
NM_UTILS_LOOKUP_DEFAULT_NM_ASSERT(NULL),
|
|
|
|
|
NM_UTILS_LOOKUP_ITEM(LINK_CONFIG_MODE_FULL, "full"),
|
|
|
|
|
NM_UTILS_LOOKUP_ITEM(LINK_CONFIG_MODE_REAPPLY, "reapply"),
|
|
|
|
|
NM_UTILS_LOOKUP_ITEM(LINK_CONFIG_MODE_ASSUME, "assume"),
|
|
|
|
|
NM_UTILS_LOOKUP_ITEM(LINK_CONFIG_MODE_ENDPOINTS, "endpoints"), );
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
wireguard: support configuring policy routing to avoid routing loops
For WireGuard (like for all IP-tunnels and IP-based VPNs), the IP addresses of
the peers must be reached outside the tunnel/VPN itself.
For VPN connections, NetworkManager usually adds a direct /32 route to
the external VPN gateway to the underlying device. For WireGuard that is
not done, because injecting a route to another device is ugly and error
prone. Worse: WireGuard with automatic roaming and multiple peers makes this
more complicated.
This is commonly a problem when setting the default-route via the VPN,
but there are also other subtle setups where special care must be taken
to prevent such routing loops.
WireGuard's wg-quick provides a simple, automatic solution by adding two policy
routing rules and relying on the WireGuard packets having a fwmark set (see [1]).
Let's also do that. Add new properties "wireguard.ip4-auto-default-route"
and "wireguard.ip6-auto-default-route" to enable/disable this. Note that
the default value lets NetworkManager automatically choose whether to
enable it (depending on whether there are any peers that have a default
route). This means, common scenarios should now work well without additional
configuration.
Note that this is also a change in behavior and upon package upgrade
NetworkManager may start adding policy routes (if there are peers that
have a default-route). This is a change in behavior, as the user already
clearly had this setup working and configured some working solution
already.
The new automatism picks the rule priority automatically and adds the
default-route to the routing table that has the same number as the fwmark.
If any of this is unsuitable, then the user is free to disable this
automatism. Note that since 1.18.0 NetworkManager supports policy routing (*).
That means, what this automatism does can be also achieved via explicit
configuration of the profile, which gives the user more flexibility to
adjust all parameters explicitly).
(*) but only since 1.20.0 NetworkManager supports the "suppress_prefixlength"
rule attribute, which makes it impossible to configure exactly this rule-based
solution with 1.18.0 NetworkManager.
[1] https://www.wireguard.com/netns/#improved-rule-based-routing
2019-04-30 17:48:46 +02:00
|
|
|
static void
|
|
|
|
|
_auto_default_route_get_enabled(NMSettingWireGuard *s_wg,
|
2021-11-09 13:28:54 +01:00
|
|
|
NMConnection *connection,
|
|
|
|
|
gboolean *out_enabled_v4,
|
|
|
|
|
gboolean *out_enabled_v6)
|
wireguard: support configuring policy routing to avoid routing loops
For WireGuard (like for all IP-tunnels and IP-based VPNs), the IP addresses of
the peers must be reached outside the tunnel/VPN itself.
For VPN connections, NetworkManager usually adds a direct /32 route to
the external VPN gateway to the underlying device. For WireGuard that is
not done, because injecting a route to another device is ugly and error
prone. Worse: WireGuard with automatic roaming and multiple peers makes this
more complicated.
This is commonly a problem when setting the default-route via the VPN,
but there are also other subtle setups where special care must be taken
to prevent such routing loops.
WireGuard's wg-quick provides a simple, automatic solution by adding two policy
routing rules and relying on the WireGuard packets having a fwmark set (see [1]).
Let's also do that. Add new properties "wireguard.ip4-auto-default-route"
and "wireguard.ip6-auto-default-route" to enable/disable this. Note that
the default value lets NetworkManager automatically choose whether to
enable it (depending on whether there are any peers that have a default
route). This means, common scenarios should now work well without additional
configuration.
Note that this is also a change in behavior and upon package upgrade
NetworkManager may start adding policy routes (if there are peers that
have a default-route). This is a change in behavior, as the user already
clearly had this setup working and configured some working solution
already.
The new automatism picks the rule priority automatically and adds the
default-route to the routing table that has the same number as the fwmark.
If any of this is unsuitable, then the user is free to disable this
automatism. Note that since 1.18.0 NetworkManager supports policy routing (*).
That means, what this automatism does can be also achieved via explicit
configuration of the profile, which gives the user more flexibility to
adjust all parameters explicitly).
(*) but only since 1.20.0 NetworkManager supports the "suppress_prefixlength"
rule attribute, which makes it impossible to configure exactly this rule-based
solution with 1.18.0 NetworkManager.
[1] https://www.wireguard.com/netns/#improved-rule-based-routing
2019-04-30 17:48:46 +02:00
|
|
|
{
|
|
|
|
|
NMTernary enabled_v4;
|
|
|
|
|
NMTernary enabled_v6;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
wireguard: support configuring policy routing to avoid routing loops
For WireGuard (like for all IP-tunnels and IP-based VPNs), the IP addresses of
the peers must be reached outside the tunnel/VPN itself.
For VPN connections, NetworkManager usually adds a direct /32 route to
the external VPN gateway to the underlying device. For WireGuard that is
not done, because injecting a route to another device is ugly and error
prone. Worse: WireGuard with automatic roaming and multiple peers makes this
more complicated.
This is commonly a problem when setting the default-route via the VPN,
but there are also other subtle setups where special care must be taken
to prevent such routing loops.
WireGuard's wg-quick provides a simple, automatic solution by adding two policy
routing rules and relying on the WireGuard packets having a fwmark set (see [1]).
Let's also do that. Add new properties "wireguard.ip4-auto-default-route"
and "wireguard.ip6-auto-default-route" to enable/disable this. Note that
the default value lets NetworkManager automatically choose whether to
enable it (depending on whether there are any peers that have a default
route). This means, common scenarios should now work well without additional
configuration.
Note that this is also a change in behavior and upon package upgrade
NetworkManager may start adding policy routes (if there are peers that
have a default-route). This is a change in behavior, as the user already
clearly had this setup working and configured some working solution
already.
The new automatism picks the rule priority automatically and adds the
default-route to the routing table that has the same number as the fwmark.
If any of this is unsuitable, then the user is free to disable this
automatism. Note that since 1.18.0 NetworkManager supports policy routing (*).
That means, what this automatism does can be also achieved via explicit
configuration of the profile, which gives the user more flexibility to
adjust all parameters explicitly).
(*) but only since 1.20.0 NetworkManager supports the "suppress_prefixlength"
rule attribute, which makes it impossible to configure exactly this rule-based
solution with 1.18.0 NetworkManager.
[1] https://www.wireguard.com/netns/#improved-rule-based-routing
2019-04-30 17:48:46 +02:00
|
|
|
enabled_v4 = nm_setting_wireguard_get_ip4_auto_default_route(s_wg);
|
|
|
|
|
enabled_v6 = nm_setting_wireguard_get_ip6_auto_default_route(s_wg);
|
2020-09-28 16:03:33 +02:00
|
|
|
|
wireguard: support configuring policy routing to avoid routing loops
For WireGuard (like for all IP-tunnels and IP-based VPNs), the IP addresses of
the peers must be reached outside the tunnel/VPN itself.
For VPN connections, NetworkManager usually adds a direct /32 route to
the external VPN gateway to the underlying device. For WireGuard that is
not done, because injecting a route to another device is ugly and error
prone. Worse: WireGuard with automatic roaming and multiple peers makes this
more complicated.
This is commonly a problem when setting the default-route via the VPN,
but there are also other subtle setups where special care must be taken
to prevent such routing loops.
WireGuard's wg-quick provides a simple, automatic solution by adding two policy
routing rules and relying on the WireGuard packets having a fwmark set (see [1]).
Let's also do that. Add new properties "wireguard.ip4-auto-default-route"
and "wireguard.ip6-auto-default-route" to enable/disable this. Note that
the default value lets NetworkManager automatically choose whether to
enable it (depending on whether there are any peers that have a default
route). This means, common scenarios should now work well without additional
configuration.
Note that this is also a change in behavior and upon package upgrade
NetworkManager may start adding policy routes (if there are peers that
have a default-route). This is a change in behavior, as the user already
clearly had this setup working and configured some working solution
already.
The new automatism picks the rule priority automatically and adds the
default-route to the routing table that has the same number as the fwmark.
If any of this is unsuitable, then the user is free to disable this
automatism. Note that since 1.18.0 NetworkManager supports policy routing (*).
That means, what this automatism does can be also achieved via explicit
configuration of the profile, which gives the user more flexibility to
adjust all parameters explicitly).
(*) but only since 1.20.0 NetworkManager supports the "suppress_prefixlength"
rule attribute, which makes it impossible to configure exactly this rule-based
solution with 1.18.0 NetworkManager.
[1] https://www.wireguard.com/netns/#improved-rule-based-routing
2019-04-30 17:48:46 +02:00
|
|
|
if (enabled_v4 == NM_TERNARY_DEFAULT) {
|
|
|
|
|
if (nm_setting_ip_config_get_never_default(
|
|
|
|
|
nm_connection_get_setting_ip_config(connection, AF_INET)))
|
|
|
|
|
enabled_v4 = FALSE;
|
|
|
|
|
}
|
|
|
|
|
if (enabled_v6 == NM_TERNARY_DEFAULT) {
|
|
|
|
|
if (nm_setting_ip_config_get_never_default(
|
|
|
|
|
nm_connection_get_setting_ip_config(connection, AF_INET6)))
|
|
|
|
|
enabled_v6 = FALSE;
|
|
|
|
|
}
|
2020-09-28 16:03:33 +02:00
|
|
|
|
wireguard: support configuring policy routing to avoid routing loops
For WireGuard (like for all IP-tunnels and IP-based VPNs), the IP addresses of
the peers must be reached outside the tunnel/VPN itself.
For VPN connections, NetworkManager usually adds a direct /32 route to
the external VPN gateway to the underlying device. For WireGuard that is
not done, because injecting a route to another device is ugly and error
prone. Worse: WireGuard with automatic roaming and multiple peers makes this
more complicated.
This is commonly a problem when setting the default-route via the VPN,
but there are also other subtle setups where special care must be taken
to prevent such routing loops.
WireGuard's wg-quick provides a simple, automatic solution by adding two policy
routing rules and relying on the WireGuard packets having a fwmark set (see [1]).
Let's also do that. Add new properties "wireguard.ip4-auto-default-route"
and "wireguard.ip6-auto-default-route" to enable/disable this. Note that
the default value lets NetworkManager automatically choose whether to
enable it (depending on whether there are any peers that have a default
route). This means, common scenarios should now work well without additional
configuration.
Note that this is also a change in behavior and upon package upgrade
NetworkManager may start adding policy routes (if there are peers that
have a default-route). This is a change in behavior, as the user already
clearly had this setup working and configured some working solution
already.
The new automatism picks the rule priority automatically and adds the
default-route to the routing table that has the same number as the fwmark.
If any of this is unsuitable, then the user is free to disable this
automatism. Note that since 1.18.0 NetworkManager supports policy routing (*).
That means, what this automatism does can be also achieved via explicit
configuration of the profile, which gives the user more flexibility to
adjust all parameters explicitly).
(*) but only since 1.20.0 NetworkManager supports the "suppress_prefixlength"
rule attribute, which makes it impossible to configure exactly this rule-based
solution with 1.18.0 NetworkManager.
[1] https://www.wireguard.com/netns/#improved-rule-based-routing
2019-04-30 17:48:46 +02:00
|
|
|
if (enabled_v4 == NM_TERNARY_DEFAULT || enabled_v6 == NM_TERNARY_DEFAULT) {
|
|
|
|
|
guint i, n_peers;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
wireguard: support configuring policy routing to avoid routing loops
For WireGuard (like for all IP-tunnels and IP-based VPNs), the IP addresses of
the peers must be reached outside the tunnel/VPN itself.
For VPN connections, NetworkManager usually adds a direct /32 route to
the external VPN gateway to the underlying device. For WireGuard that is
not done, because injecting a route to another device is ugly and error
prone. Worse: WireGuard with automatic roaming and multiple peers makes this
more complicated.
This is commonly a problem when setting the default-route via the VPN,
but there are also other subtle setups where special care must be taken
to prevent such routing loops.
WireGuard's wg-quick provides a simple, automatic solution by adding two policy
routing rules and relying on the WireGuard packets having a fwmark set (see [1]).
Let's also do that. Add new properties "wireguard.ip4-auto-default-route"
and "wireguard.ip6-auto-default-route" to enable/disable this. Note that
the default value lets NetworkManager automatically choose whether to
enable it (depending on whether there are any peers that have a default
route). This means, common scenarios should now work well without additional
configuration.
Note that this is also a change in behavior and upon package upgrade
NetworkManager may start adding policy routes (if there are peers that
have a default-route). This is a change in behavior, as the user already
clearly had this setup working and configured some working solution
already.
The new automatism picks the rule priority automatically and adds the
default-route to the routing table that has the same number as the fwmark.
If any of this is unsuitable, then the user is free to disable this
automatism. Note that since 1.18.0 NetworkManager supports policy routing (*).
That means, what this automatism does can be also achieved via explicit
configuration of the profile, which gives the user more flexibility to
adjust all parameters explicitly).
(*) but only since 1.20.0 NetworkManager supports the "suppress_prefixlength"
rule attribute, which makes it impossible to configure exactly this rule-based
solution with 1.18.0 NetworkManager.
[1] https://www.wireguard.com/netns/#improved-rule-based-routing
2019-04-30 17:48:46 +02:00
|
|
|
n_peers = nm_setting_wireguard_get_peers_len(s_wg);
|
|
|
|
|
for (i = 0; i < n_peers; i++) {
|
|
|
|
|
NMWireGuardPeer *peer = nm_setting_wireguard_get_peer(s_wg, i);
|
|
|
|
|
guint n_aips;
|
|
|
|
|
guint j;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
wireguard: support configuring policy routing to avoid routing loops
For WireGuard (like for all IP-tunnels and IP-based VPNs), the IP addresses of
the peers must be reached outside the tunnel/VPN itself.
For VPN connections, NetworkManager usually adds a direct /32 route to
the external VPN gateway to the underlying device. For WireGuard that is
not done, because injecting a route to another device is ugly and error
prone. Worse: WireGuard with automatic roaming and multiple peers makes this
more complicated.
This is commonly a problem when setting the default-route via the VPN,
but there are also other subtle setups where special care must be taken
to prevent such routing loops.
WireGuard's wg-quick provides a simple, automatic solution by adding two policy
routing rules and relying on the WireGuard packets having a fwmark set (see [1]).
Let's also do that. Add new properties "wireguard.ip4-auto-default-route"
and "wireguard.ip6-auto-default-route" to enable/disable this. Note that
the default value lets NetworkManager automatically choose whether to
enable it (depending on whether there are any peers that have a default
route). This means, common scenarios should now work well without additional
configuration.
Note that this is also a change in behavior and upon package upgrade
NetworkManager may start adding policy routes (if there are peers that
have a default-route). This is a change in behavior, as the user already
clearly had this setup working and configured some working solution
already.
The new automatism picks the rule priority automatically and adds the
default-route to the routing table that has the same number as the fwmark.
If any of this is unsuitable, then the user is free to disable this
automatism. Note that since 1.18.0 NetworkManager supports policy routing (*).
That means, what this automatism does can be also achieved via explicit
configuration of the profile, which gives the user more flexibility to
adjust all parameters explicitly).
(*) but only since 1.20.0 NetworkManager supports the "suppress_prefixlength"
rule attribute, which makes it impossible to configure exactly this rule-based
solution with 1.18.0 NetworkManager.
[1] https://www.wireguard.com/netns/#improved-rule-based-routing
2019-04-30 17:48:46 +02:00
|
|
|
n_aips = nm_wireguard_peer_get_allowed_ips_len(peer);
|
|
|
|
|
for (j = 0; j < n_aips; j++) {
|
|
|
|
|
const char *aip;
|
|
|
|
|
gboolean valid;
|
|
|
|
|
int prefix;
|
|
|
|
|
int addr_family;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
wireguard: support configuring policy routing to avoid routing loops
For WireGuard (like for all IP-tunnels and IP-based VPNs), the IP addresses of
the peers must be reached outside the tunnel/VPN itself.
For VPN connections, NetworkManager usually adds a direct /32 route to
the external VPN gateway to the underlying device. For WireGuard that is
not done, because injecting a route to another device is ugly and error
prone. Worse: WireGuard with automatic roaming and multiple peers makes this
more complicated.
This is commonly a problem when setting the default-route via the VPN,
but there are also other subtle setups where special care must be taken
to prevent such routing loops.
WireGuard's wg-quick provides a simple, automatic solution by adding two policy
routing rules and relying on the WireGuard packets having a fwmark set (see [1]).
Let's also do that. Add new properties "wireguard.ip4-auto-default-route"
and "wireguard.ip6-auto-default-route" to enable/disable this. Note that
the default value lets NetworkManager automatically choose whether to
enable it (depending on whether there are any peers that have a default
route). This means, common scenarios should now work well without additional
configuration.
Note that this is also a change in behavior and upon package upgrade
NetworkManager may start adding policy routes (if there are peers that
have a default-route). This is a change in behavior, as the user already
clearly had this setup working and configured some working solution
already.
The new automatism picks the rule priority automatically and adds the
default-route to the routing table that has the same number as the fwmark.
If any of this is unsuitable, then the user is free to disable this
automatism. Note that since 1.18.0 NetworkManager supports policy routing (*).
That means, what this automatism does can be also achieved via explicit
configuration of the profile, which gives the user more flexibility to
adjust all parameters explicitly).
(*) but only since 1.20.0 NetworkManager supports the "suppress_prefixlength"
rule attribute, which makes it impossible to configure exactly this rule-based
solution with 1.18.0 NetworkManager.
[1] https://www.wireguard.com/netns/#improved-rule-based-routing
2019-04-30 17:48:46 +02:00
|
|
|
aip = nm_wireguard_peer_get_allowed_ip(peer, j, &valid);
|
|
|
|
|
if (!valid)
|
|
|
|
|
continue;
|
|
|
|
|
if (!nm_utils_parse_inaddr_prefix_bin(AF_UNSPEC, aip, &addr_family, NULL, &prefix))
|
|
|
|
|
continue;
|
|
|
|
|
if (prefix != 0)
|
|
|
|
|
continue;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
wireguard: support configuring policy routing to avoid routing loops
For WireGuard (like for all IP-tunnels and IP-based VPNs), the IP addresses of
the peers must be reached outside the tunnel/VPN itself.
For VPN connections, NetworkManager usually adds a direct /32 route to
the external VPN gateway to the underlying device. For WireGuard that is
not done, because injecting a route to another device is ugly and error
prone. Worse: WireGuard with automatic roaming and multiple peers makes this
more complicated.
This is commonly a problem when setting the default-route via the VPN,
but there are also other subtle setups where special care must be taken
to prevent such routing loops.
WireGuard's wg-quick provides a simple, automatic solution by adding two policy
routing rules and relying on the WireGuard packets having a fwmark set (see [1]).
Let's also do that. Add new properties "wireguard.ip4-auto-default-route"
and "wireguard.ip6-auto-default-route" to enable/disable this. Note that
the default value lets NetworkManager automatically choose whether to
enable it (depending on whether there are any peers that have a default
route). This means, common scenarios should now work well without additional
configuration.
Note that this is also a change in behavior and upon package upgrade
NetworkManager may start adding policy routes (if there are peers that
have a default-route). This is a change in behavior, as the user already
clearly had this setup working and configured some working solution
already.
The new automatism picks the rule priority automatically and adds the
default-route to the routing table that has the same number as the fwmark.
If any of this is unsuitable, then the user is free to disable this
automatism. Note that since 1.18.0 NetworkManager supports policy routing (*).
That means, what this automatism does can be also achieved via explicit
configuration of the profile, which gives the user more flexibility to
adjust all parameters explicitly).
(*) but only since 1.20.0 NetworkManager supports the "suppress_prefixlength"
rule attribute, which makes it impossible to configure exactly this rule-based
solution with 1.18.0 NetworkManager.
[1] https://www.wireguard.com/netns/#improved-rule-based-routing
2019-04-30 17:48:46 +02:00
|
|
|
if (addr_family == AF_INET) {
|
|
|
|
|
if (enabled_v4 == NM_TERNARY_DEFAULT) {
|
|
|
|
|
enabled_v4 = TRUE;
|
|
|
|
|
if (enabled_v6 != NM_TERNARY_DEFAULT)
|
|
|
|
|
goto done;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
if (enabled_v6 == NM_TERNARY_DEFAULT) {
|
|
|
|
|
enabled_v6 = TRUE;
|
|
|
|
|
if (enabled_v4 != NM_TERNARY_DEFAULT)
|
|
|
|
|
goto done;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-09-28 16:03:33 +02:00
|
|
|
}
|
wireguard: support configuring policy routing to avoid routing loops
For WireGuard (like for all IP-tunnels and IP-based VPNs), the IP addresses of
the peers must be reached outside the tunnel/VPN itself.
For VPN connections, NetworkManager usually adds a direct /32 route to
the external VPN gateway to the underlying device. For WireGuard that is
not done, because injecting a route to another device is ugly and error
prone. Worse: WireGuard with automatic roaming and multiple peers makes this
more complicated.
This is commonly a problem when setting the default-route via the VPN,
but there are also other subtle setups where special care must be taken
to prevent such routing loops.
WireGuard's wg-quick provides a simple, automatic solution by adding two policy
routing rules and relying on the WireGuard packets having a fwmark set (see [1]).
Let's also do that. Add new properties "wireguard.ip4-auto-default-route"
and "wireguard.ip6-auto-default-route" to enable/disable this. Note that
the default value lets NetworkManager automatically choose whether to
enable it (depending on whether there are any peers that have a default
route). This means, common scenarios should now work well without additional
configuration.
Note that this is also a change in behavior and upon package upgrade
NetworkManager may start adding policy routes (if there are peers that
have a default-route). This is a change in behavior, as the user already
clearly had this setup working and configured some working solution
already.
The new automatism picks the rule priority automatically and adds the
default-route to the routing table that has the same number as the fwmark.
If any of this is unsuitable, then the user is free to disable this
automatism. Note that since 1.18.0 NetworkManager supports policy routing (*).
That means, what this automatism does can be also achieved via explicit
configuration of the profile, which gives the user more flexibility to
adjust all parameters explicitly).
(*) but only since 1.20.0 NetworkManager supports the "suppress_prefixlength"
rule attribute, which makes it impossible to configure exactly this rule-based
solution with 1.18.0 NetworkManager.
[1] https://www.wireguard.com/netns/#improved-rule-based-routing
2019-04-30 17:48:46 +02:00
|
|
|
done:;
|
|
|
|
|
}
|
2020-09-28 16:03:33 +02:00
|
|
|
|
wireguard: support configuring policy routing to avoid routing loops
For WireGuard (like for all IP-tunnels and IP-based VPNs), the IP addresses of
the peers must be reached outside the tunnel/VPN itself.
For VPN connections, NetworkManager usually adds a direct /32 route to
the external VPN gateway to the underlying device. For WireGuard that is
not done, because injecting a route to another device is ugly and error
prone. Worse: WireGuard with automatic roaming and multiple peers makes this
more complicated.
This is commonly a problem when setting the default-route via the VPN,
but there are also other subtle setups where special care must be taken
to prevent such routing loops.
WireGuard's wg-quick provides a simple, automatic solution by adding two policy
routing rules and relying on the WireGuard packets having a fwmark set (see [1]).
Let's also do that. Add new properties "wireguard.ip4-auto-default-route"
and "wireguard.ip6-auto-default-route" to enable/disable this. Note that
the default value lets NetworkManager automatically choose whether to
enable it (depending on whether there are any peers that have a default
route). This means, common scenarios should now work well without additional
configuration.
Note that this is also a change in behavior and upon package upgrade
NetworkManager may start adding policy routes (if there are peers that
have a default-route). This is a change in behavior, as the user already
clearly had this setup working and configured some working solution
already.
The new automatism picks the rule priority automatically and adds the
default-route to the routing table that has the same number as the fwmark.
If any of this is unsuitable, then the user is free to disable this
automatism. Note that since 1.18.0 NetworkManager supports policy routing (*).
That means, what this automatism does can be also achieved via explicit
configuration of the profile, which gives the user more flexibility to
adjust all parameters explicitly).
(*) but only since 1.20.0 NetworkManager supports the "suppress_prefixlength"
rule attribute, which makes it impossible to configure exactly this rule-based
solution with 1.18.0 NetworkManager.
[1] https://www.wireguard.com/netns/#improved-rule-based-routing
2019-04-30 17:48:46 +02:00
|
|
|
*out_enabled_v4 = (enabled_v4 == TRUE);
|
|
|
|
|
*out_enabled_v6 = (enabled_v6 == TRUE);
|
|
|
|
|
}
|
|
|
|
|
|
wireguard: use fixed fwmark/rule-priority for auto-default-route
With "wireguard.ip4-auto-default-route" and "wireguard.ip6-auto-default-route",
NetworkManager automatically adds policy routing rules for the default
route.
For that, it needs to pick (up to) two numbers:
- the fwmark. This is used both for WireGuard's fwmark setting and
is also the number of the routing table where the default-route is
added.
- the rule priority. NetworkManager adds two policy routing rules, and
we need to place them somewhere before the default rules (at 32766).
Previously, we looked at exiting platform configuration and picked
numbers that were not yet used. However, during restart of
NetworkManager, we leave the interface up and after restart we will
take over the previous configuration. At that point, we need to choose
the same fwmark/priority, otherwise the configuration changes.
But since we picked numbers that were not yet used, we would always choose
different numbers. For routing rules that meant that after restart a second
pair of rules was added.
We possibly could store this data in the device's state-file. But that
is complex. Instead, just pick numbers deterministically based on the
connection's UUID.
If the picked numbers are not suitable, then the user can still work
around that:
- for fwmark, the user can explicitly configure wireguard.fwmark
setting.
- for the policy routes, the user can explicitly add the rules with
the desired priorirites (arguably, currently the default-route cannot
be added like a regular route, so the table cannot be set. Possibly
the user would have to add two /1 routes instead with
suppress_prefixlength=1).
(cherry picked from commit cfb497e49936d36867ae3175537c7d4f77259655)
2019-07-31 10:01:04 +02:00
|
|
|
#define AUTO_RANDOM_RANGE 500u
|
|
|
|
|
|
wireguard: support configuring policy routing to avoid routing loops
For WireGuard (like for all IP-tunnels and IP-based VPNs), the IP addresses of
the peers must be reached outside the tunnel/VPN itself.
For VPN connections, NetworkManager usually adds a direct /32 route to
the external VPN gateway to the underlying device. For WireGuard that is
not done, because injecting a route to another device is ugly and error
prone. Worse: WireGuard with automatic roaming and multiple peers makes this
more complicated.
This is commonly a problem when setting the default-route via the VPN,
but there are also other subtle setups where special care must be taken
to prevent such routing loops.
WireGuard's wg-quick provides a simple, automatic solution by adding two policy
routing rules and relying on the WireGuard packets having a fwmark set (see [1]).
Let's also do that. Add new properties "wireguard.ip4-auto-default-route"
and "wireguard.ip6-auto-default-route" to enable/disable this. Note that
the default value lets NetworkManager automatically choose whether to
enable it (depending on whether there are any peers that have a default
route). This means, common scenarios should now work well without additional
configuration.
Note that this is also a change in behavior and upon package upgrade
NetworkManager may start adding policy routes (if there are peers that
have a default-route). This is a change in behavior, as the user already
clearly had this setup working and configured some working solution
already.
The new automatism picks the rule priority automatically and adds the
default-route to the routing table that has the same number as the fwmark.
If any of this is unsuitable, then the user is free to disable this
automatism. Note that since 1.18.0 NetworkManager supports policy routing (*).
That means, what this automatism does can be also achieved via explicit
configuration of the profile, which gives the user more flexibility to
adjust all parameters explicitly).
(*) but only since 1.20.0 NetworkManager supports the "suppress_prefixlength"
rule attribute, which makes it impossible to configure exactly this rule-based
solution with 1.18.0 NetworkManager.
[1] https://www.wireguard.com/netns/#improved-rule-based-routing
2019-04-30 17:48:46 +02:00
|
|
|
static guint32
|
wireguard: use fixed fwmark/rule-priority for auto-default-route
With "wireguard.ip4-auto-default-route" and "wireguard.ip6-auto-default-route",
NetworkManager automatically adds policy routing rules for the default
route.
For that, it needs to pick (up to) two numbers:
- the fwmark. This is used both for WireGuard's fwmark setting and
is also the number of the routing table where the default-route is
added.
- the rule priority. NetworkManager adds two policy routing rules, and
we need to place them somewhere before the default rules (at 32766).
Previously, we looked at exiting platform configuration and picked
numbers that were not yet used. However, during restart of
NetworkManager, we leave the interface up and after restart we will
take over the previous configuration. At that point, we need to choose
the same fwmark/priority, otherwise the configuration changes.
But since we picked numbers that were not yet used, we would always choose
different numbers. For routing rules that meant that after restart a second
pair of rules was added.
We possibly could store this data in the device's state-file. But that
is complex. Instead, just pick numbers deterministically based on the
connection's UUID.
If the picked numbers are not suitable, then the user can still work
around that:
- for fwmark, the user can explicitly configure wireguard.fwmark
setting.
- for the policy routes, the user can explicitly add the rules with
the desired priorirites (arguably, currently the default-route cannot
be added like a regular route, so the table cannot be set. Possibly
the user would have to add two /1 routes instead with
suppress_prefixlength=1).
(cherry picked from commit cfb497e49936d36867ae3175537c7d4f77259655)
2019-07-31 10:01:04 +02:00
|
|
|
_auto_default_route_get_auto_fwmark(const char *uuid)
|
wireguard: support configuring policy routing to avoid routing loops
For WireGuard (like for all IP-tunnels and IP-based VPNs), the IP addresses of
the peers must be reached outside the tunnel/VPN itself.
For VPN connections, NetworkManager usually adds a direct /32 route to
the external VPN gateway to the underlying device. For WireGuard that is
not done, because injecting a route to another device is ugly and error
prone. Worse: WireGuard with automatic roaming and multiple peers makes this
more complicated.
This is commonly a problem when setting the default-route via the VPN,
but there are also other subtle setups where special care must be taken
to prevent such routing loops.
WireGuard's wg-quick provides a simple, automatic solution by adding two policy
routing rules and relying on the WireGuard packets having a fwmark set (see [1]).
Let's also do that. Add new properties "wireguard.ip4-auto-default-route"
and "wireguard.ip6-auto-default-route" to enable/disable this. Note that
the default value lets NetworkManager automatically choose whether to
enable it (depending on whether there are any peers that have a default
route). This means, common scenarios should now work well without additional
configuration.
Note that this is also a change in behavior and upon package upgrade
NetworkManager may start adding policy routes (if there are peers that
have a default-route). This is a change in behavior, as the user already
clearly had this setup working and configured some working solution
already.
The new automatism picks the rule priority automatically and adds the
default-route to the routing table that has the same number as the fwmark.
If any of this is unsuitable, then the user is free to disable this
automatism. Note that since 1.18.0 NetworkManager supports policy routing (*).
That means, what this automatism does can be also achieved via explicit
configuration of the profile, which gives the user more flexibility to
adjust all parameters explicitly).
(*) but only since 1.20.0 NetworkManager supports the "suppress_prefixlength"
rule attribute, which makes it impossible to configure exactly this rule-based
solution with 1.18.0 NetworkManager.
[1] https://www.wireguard.com/netns/#improved-rule-based-routing
2019-04-30 17:48:46 +02:00
|
|
|
{
|
wireguard: use fixed fwmark/rule-priority for auto-default-route
With "wireguard.ip4-auto-default-route" and "wireguard.ip6-auto-default-route",
NetworkManager automatically adds policy routing rules for the default
route.
For that, it needs to pick (up to) two numbers:
- the fwmark. This is used both for WireGuard's fwmark setting and
is also the number of the routing table where the default-route is
added.
- the rule priority. NetworkManager adds two policy routing rules, and
we need to place them somewhere before the default rules (at 32766).
Previously, we looked at exiting platform configuration and picked
numbers that were not yet used. However, during restart of
NetworkManager, we leave the interface up and after restart we will
take over the previous configuration. At that point, we need to choose
the same fwmark/priority, otherwise the configuration changes.
But since we picked numbers that were not yet used, we would always choose
different numbers. For routing rules that meant that after restart a second
pair of rules was added.
We possibly could store this data in the device's state-file. But that
is complex. Instead, just pick numbers deterministically based on the
connection's UUID.
If the picked numbers are not suitable, then the user can still work
around that:
- for fwmark, the user can explicitly configure wireguard.fwmark
setting.
- for the policy routes, the user can explicitly add the rules with
the desired priorirites (arguably, currently the default-route cannot
be added like a regular route, so the table cannot be set. Possibly
the user would have to add two /1 routes instead with
suppress_prefixlength=1).
(cherry picked from commit cfb497e49936d36867ae3175537c7d4f77259655)
2019-07-31 10:01:04 +02:00
|
|
|
guint64 rnd_seed;
|
wireguard: support configuring policy routing to avoid routing loops
For WireGuard (like for all IP-tunnels and IP-based VPNs), the IP addresses of
the peers must be reached outside the tunnel/VPN itself.
For VPN connections, NetworkManager usually adds a direct /32 route to
the external VPN gateway to the underlying device. For WireGuard that is
not done, because injecting a route to another device is ugly and error
prone. Worse: WireGuard with automatic roaming and multiple peers makes this
more complicated.
This is commonly a problem when setting the default-route via the VPN,
but there are also other subtle setups where special care must be taken
to prevent such routing loops.
WireGuard's wg-quick provides a simple, automatic solution by adding two policy
routing rules and relying on the WireGuard packets having a fwmark set (see [1]).
Let's also do that. Add new properties "wireguard.ip4-auto-default-route"
and "wireguard.ip6-auto-default-route" to enable/disable this. Note that
the default value lets NetworkManager automatically choose whether to
enable it (depending on whether there are any peers that have a default
route). This means, common scenarios should now work well without additional
configuration.
Note that this is also a change in behavior and upon package upgrade
NetworkManager may start adding policy routes (if there are peers that
have a default-route). This is a change in behavior, as the user already
clearly had this setup working and configured some working solution
already.
The new automatism picks the rule priority automatically and adds the
default-route to the routing table that has the same number as the fwmark.
If any of this is unsuitable, then the user is free to disable this
automatism. Note that since 1.18.0 NetworkManager supports policy routing (*).
That means, what this automatism does can be also achieved via explicit
configuration of the profile, which gives the user more flexibility to
adjust all parameters explicitly).
(*) but only since 1.20.0 NetworkManager supports the "suppress_prefixlength"
rule attribute, which makes it impossible to configure exactly this rule-based
solution with 1.18.0 NetworkManager.
[1] https://www.wireguard.com/netns/#improved-rule-based-routing
2019-04-30 17:48:46 +02:00
|
|
|
|
wireguard: use fixed fwmark/rule-priority for auto-default-route
With "wireguard.ip4-auto-default-route" and "wireguard.ip6-auto-default-route",
NetworkManager automatically adds policy routing rules for the default
route.
For that, it needs to pick (up to) two numbers:
- the fwmark. This is used both for WireGuard's fwmark setting and
is also the number of the routing table where the default-route is
added.
- the rule priority. NetworkManager adds two policy routing rules, and
we need to place them somewhere before the default rules (at 32766).
Previously, we looked at exiting platform configuration and picked
numbers that were not yet used. However, during restart of
NetworkManager, we leave the interface up and after restart we will
take over the previous configuration. At that point, we need to choose
the same fwmark/priority, otherwise the configuration changes.
But since we picked numbers that were not yet used, we would always choose
different numbers. For routing rules that meant that after restart a second
pair of rules was added.
We possibly could store this data in the device's state-file. But that
is complex. Instead, just pick numbers deterministically based on the
connection's UUID.
If the picked numbers are not suitable, then the user can still work
around that:
- for fwmark, the user can explicitly configure wireguard.fwmark
setting.
- for the policy routes, the user can explicitly add the rules with
the desired priorirites (arguably, currently the default-route cannot
be added like a regular route, so the table cannot be set. Possibly
the user would have to add two /1 routes instead with
suppress_prefixlength=1).
(cherry picked from commit cfb497e49936d36867ae3175537c7d4f77259655)
2019-07-31 10:01:04 +02:00
|
|
|
/* we use the generated number as fwmark but also as routing table for
|
|
|
|
|
* the default-route.
|
|
|
|
|
*
|
|
|
|
|
* We pick a number
|
|
|
|
|
*
|
|
|
|
|
* - based on the connection's UUID (as stable seed).
|
|
|
|
|
* - larger than 51820u (arbitrarily)
|
|
|
|
|
* - one out of AUTO_RANDOM_RANGE
|
|
|
|
|
*/
|
wireguard: support configuring policy routing to avoid routing loops
For WireGuard (like for all IP-tunnels and IP-based VPNs), the IP addresses of
the peers must be reached outside the tunnel/VPN itself.
For VPN connections, NetworkManager usually adds a direct /32 route to
the external VPN gateway to the underlying device. For WireGuard that is
not done, because injecting a route to another device is ugly and error
prone. Worse: WireGuard with automatic roaming and multiple peers makes this
more complicated.
This is commonly a problem when setting the default-route via the VPN,
but there are also other subtle setups where special care must be taken
to prevent such routing loops.
WireGuard's wg-quick provides a simple, automatic solution by adding two policy
routing rules and relying on the WireGuard packets having a fwmark set (see [1]).
Let's also do that. Add new properties "wireguard.ip4-auto-default-route"
and "wireguard.ip6-auto-default-route" to enable/disable this. Note that
the default value lets NetworkManager automatically choose whether to
enable it (depending on whether there are any peers that have a default
route). This means, common scenarios should now work well without additional
configuration.
Note that this is also a change in behavior and upon package upgrade
NetworkManager may start adding policy routes (if there are peers that
have a default-route). This is a change in behavior, as the user already
clearly had this setup working and configured some working solution
already.
The new automatism picks the rule priority automatically and adds the
default-route to the routing table that has the same number as the fwmark.
If any of this is unsuitable, then the user is free to disable this
automatism. Note that since 1.18.0 NetworkManager supports policy routing (*).
That means, what this automatism does can be also achieved via explicit
configuration of the profile, which gives the user more flexibility to
adjust all parameters explicitly).
(*) but only since 1.20.0 NetworkManager supports the "suppress_prefixlength"
rule attribute, which makes it impossible to configure exactly this rule-based
solution with 1.18.0 NetworkManager.
[1] https://www.wireguard.com/netns/#improved-rule-based-routing
2019-04-30 17:48:46 +02:00
|
|
|
|
wireguard: use fixed fwmark/rule-priority for auto-default-route
With "wireguard.ip4-auto-default-route" and "wireguard.ip6-auto-default-route",
NetworkManager automatically adds policy routing rules for the default
route.
For that, it needs to pick (up to) two numbers:
- the fwmark. This is used both for WireGuard's fwmark setting and
is also the number of the routing table where the default-route is
added.
- the rule priority. NetworkManager adds two policy routing rules, and
we need to place them somewhere before the default rules (at 32766).
Previously, we looked at exiting platform configuration and picked
numbers that were not yet used. However, during restart of
NetworkManager, we leave the interface up and after restart we will
take over the previous configuration. At that point, we need to choose
the same fwmark/priority, otherwise the configuration changes.
But since we picked numbers that were not yet used, we would always choose
different numbers. For routing rules that meant that after restart a second
pair of rules was added.
We possibly could store this data in the device's state-file. But that
is complex. Instead, just pick numbers deterministically based on the
connection's UUID.
If the picked numbers are not suitable, then the user can still work
around that:
- for fwmark, the user can explicitly configure wireguard.fwmark
setting.
- for the policy routes, the user can explicitly add the rules with
the desired priorirites (arguably, currently the default-route cannot
be added like a regular route, so the table cannot be set. Possibly
the user would have to add two /1 routes instead with
suppress_prefixlength=1).
(cherry picked from commit cfb497e49936d36867ae3175537c7d4f77259655)
2019-07-31 10:01:04 +02:00
|
|
|
rnd_seed = c_siphash_hash(NM_HASH_SEED_16(0xb9,
|
|
|
|
|
0x39,
|
|
|
|
|
0x8e,
|
|
|
|
|
0xed,
|
|
|
|
|
0x15,
|
|
|
|
|
0xb3,
|
|
|
|
|
0xd1,
|
|
|
|
|
0xc4,
|
|
|
|
|
0x5f,
|
|
|
|
|
0x45,
|
|
|
|
|
0x00,
|
|
|
|
|
0x4f,
|
|
|
|
|
0xec,
|
|
|
|
|
0xc2,
|
|
|
|
|
0x2b,
|
|
|
|
|
0x7e),
|
|
|
|
|
(const guint8 *) uuid,
|
|
|
|
|
uuid ? strlen(uuid) + 1u : 0u);
|
2020-09-28 16:03:33 +02:00
|
|
|
|
wireguard: use fixed fwmark/rule-priority for auto-default-route
With "wireguard.ip4-auto-default-route" and "wireguard.ip6-auto-default-route",
NetworkManager automatically adds policy routing rules for the default
route.
For that, it needs to pick (up to) two numbers:
- the fwmark. This is used both for WireGuard's fwmark setting and
is also the number of the routing table where the default-route is
added.
- the rule priority. NetworkManager adds two policy routing rules, and
we need to place them somewhere before the default rules (at 32766).
Previously, we looked at exiting platform configuration and picked
numbers that were not yet used. However, during restart of
NetworkManager, we leave the interface up and after restart we will
take over the previous configuration. At that point, we need to choose
the same fwmark/priority, otherwise the configuration changes.
But since we picked numbers that were not yet used, we would always choose
different numbers. For routing rules that meant that after restart a second
pair of rules was added.
We possibly could store this data in the device's state-file. But that
is complex. Instead, just pick numbers deterministically based on the
connection's UUID.
If the picked numbers are not suitable, then the user can still work
around that:
- for fwmark, the user can explicitly configure wireguard.fwmark
setting.
- for the policy routes, the user can explicitly add the rules with
the desired priorirites (arguably, currently the default-route cannot
be added like a regular route, so the table cannot be set. Possibly
the user would have to add two /1 routes instead with
suppress_prefixlength=1).
(cherry picked from commit cfb497e49936d36867ae3175537c7d4f77259655)
2019-07-31 10:01:04 +02:00
|
|
|
return 51820u + (rnd_seed % AUTO_RANDOM_RANGE);
|
wireguard: support configuring policy routing to avoid routing loops
For WireGuard (like for all IP-tunnels and IP-based VPNs), the IP addresses of
the peers must be reached outside the tunnel/VPN itself.
For VPN connections, NetworkManager usually adds a direct /32 route to
the external VPN gateway to the underlying device. For WireGuard that is
not done, because injecting a route to another device is ugly and error
prone. Worse: WireGuard with automatic roaming and multiple peers makes this
more complicated.
This is commonly a problem when setting the default-route via the VPN,
but there are also other subtle setups where special care must be taken
to prevent such routing loops.
WireGuard's wg-quick provides a simple, automatic solution by adding two policy
routing rules and relying on the WireGuard packets having a fwmark set (see [1]).
Let's also do that. Add new properties "wireguard.ip4-auto-default-route"
and "wireguard.ip6-auto-default-route" to enable/disable this. Note that
the default value lets NetworkManager automatically choose whether to
enable it (depending on whether there are any peers that have a default
route). This means, common scenarios should now work well without additional
configuration.
Note that this is also a change in behavior and upon package upgrade
NetworkManager may start adding policy routes (if there are peers that
have a default-route). This is a change in behavior, as the user already
clearly had this setup working and configured some working solution
already.
The new automatism picks the rule priority automatically and adds the
default-route to the routing table that has the same number as the fwmark.
If any of this is unsuitable, then the user is free to disable this
automatism. Note that since 1.18.0 NetworkManager supports policy routing (*).
That means, what this automatism does can be also achieved via explicit
configuration of the profile, which gives the user more flexibility to
adjust all parameters explicitly).
(*) but only since 1.20.0 NetworkManager supports the "suppress_prefixlength"
rule attribute, which makes it impossible to configure exactly this rule-based
solution with 1.18.0 NetworkManager.
[1] https://www.wireguard.com/netns/#improved-rule-based-routing
2019-04-30 17:48:46 +02:00
|
|
|
}
|
|
|
|
|
|
wireguard: use fixed fwmark/rule-priority for auto-default-route
With "wireguard.ip4-auto-default-route" and "wireguard.ip6-auto-default-route",
NetworkManager automatically adds policy routing rules for the default
route.
For that, it needs to pick (up to) two numbers:
- the fwmark. This is used both for WireGuard's fwmark setting and
is also the number of the routing table where the default-route is
added.
- the rule priority. NetworkManager adds two policy routing rules, and
we need to place them somewhere before the default rules (at 32766).
Previously, we looked at exiting platform configuration and picked
numbers that were not yet used. However, during restart of
NetworkManager, we leave the interface up and after restart we will
take over the previous configuration. At that point, we need to choose
the same fwmark/priority, otherwise the configuration changes.
But since we picked numbers that were not yet used, we would always choose
different numbers. For routing rules that meant that after restart a second
pair of rules was added.
We possibly could store this data in the device's state-file. But that
is complex. Instead, just pick numbers deterministically based on the
connection's UUID.
If the picked numbers are not suitable, then the user can still work
around that:
- for fwmark, the user can explicitly configure wireguard.fwmark
setting.
- for the policy routes, the user can explicitly add the rules with
the desired priorirites (arguably, currently the default-route cannot
be added like a regular route, so the table cannot be set. Possibly
the user would have to add two /1 routes instead with
suppress_prefixlength=1).
(cherry picked from commit cfb497e49936d36867ae3175537c7d4f77259655)
2019-07-31 10:01:04 +02:00
|
|
|
#define PRIO_WIDTH 2u
|
wireguard: support configuring policy routing to avoid routing loops
For WireGuard (like for all IP-tunnels and IP-based VPNs), the IP addresses of
the peers must be reached outside the tunnel/VPN itself.
For VPN connections, NetworkManager usually adds a direct /32 route to
the external VPN gateway to the underlying device. For WireGuard that is
not done, because injecting a route to another device is ugly and error
prone. Worse: WireGuard with automatic roaming and multiple peers makes this
more complicated.
This is commonly a problem when setting the default-route via the VPN,
but there are also other subtle setups where special care must be taken
to prevent such routing loops.
WireGuard's wg-quick provides a simple, automatic solution by adding two policy
routing rules and relying on the WireGuard packets having a fwmark set (see [1]).
Let's also do that. Add new properties "wireguard.ip4-auto-default-route"
and "wireguard.ip6-auto-default-route" to enable/disable this. Note that
the default value lets NetworkManager automatically choose whether to
enable it (depending on whether there are any peers that have a default
route). This means, common scenarios should now work well without additional
configuration.
Note that this is also a change in behavior and upon package upgrade
NetworkManager may start adding policy routes (if there are peers that
have a default-route). This is a change in behavior, as the user already
clearly had this setup working and configured some working solution
already.
The new automatism picks the rule priority automatically and adds the
default-route to the routing table that has the same number as the fwmark.
If any of this is unsuitable, then the user is free to disable this
automatism. Note that since 1.18.0 NetworkManager supports policy routing (*).
That means, what this automatism does can be also achieved via explicit
configuration of the profile, which gives the user more flexibility to
adjust all parameters explicitly).
(*) but only since 1.20.0 NetworkManager supports the "suppress_prefixlength"
rule attribute, which makes it impossible to configure exactly this rule-based
solution with 1.18.0 NetworkManager.
[1] https://www.wireguard.com/netns/#improved-rule-based-routing
2019-04-30 17:48:46 +02:00
|
|
|
|
|
|
|
|
static guint32
|
wireguard: use fixed fwmark/rule-priority for auto-default-route
With "wireguard.ip4-auto-default-route" and "wireguard.ip6-auto-default-route",
NetworkManager automatically adds policy routing rules for the default
route.
For that, it needs to pick (up to) two numbers:
- the fwmark. This is used both for WireGuard's fwmark setting and
is also the number of the routing table where the default-route is
added.
- the rule priority. NetworkManager adds two policy routing rules, and
we need to place them somewhere before the default rules (at 32766).
Previously, we looked at exiting platform configuration and picked
numbers that were not yet used. However, during restart of
NetworkManager, we leave the interface up and after restart we will
take over the previous configuration. At that point, we need to choose
the same fwmark/priority, otherwise the configuration changes.
But since we picked numbers that were not yet used, we would always choose
different numbers. For routing rules that meant that after restart a second
pair of rules was added.
We possibly could store this data in the device's state-file. But that
is complex. Instead, just pick numbers deterministically based on the
connection's UUID.
If the picked numbers are not suitable, then the user can still work
around that:
- for fwmark, the user can explicitly configure wireguard.fwmark
setting.
- for the policy routes, the user can explicitly add the rules with
the desired priorirites (arguably, currently the default-route cannot
be added like a regular route, so the table cannot be set. Possibly
the user would have to add two /1 routes instead with
suppress_prefixlength=1).
(cherry picked from commit cfb497e49936d36867ae3175537c7d4f77259655)
2019-07-31 10:01:04 +02:00
|
|
|
_auto_default_route_get_auto_priority(const char *uuid)
|
wireguard: support configuring policy routing to avoid routing loops
For WireGuard (like for all IP-tunnels and IP-based VPNs), the IP addresses of
the peers must be reached outside the tunnel/VPN itself.
For VPN connections, NetworkManager usually adds a direct /32 route to
the external VPN gateway to the underlying device. For WireGuard that is
not done, because injecting a route to another device is ugly and error
prone. Worse: WireGuard with automatic roaming and multiple peers makes this
more complicated.
This is commonly a problem when setting the default-route via the VPN,
but there are also other subtle setups where special care must be taken
to prevent such routing loops.
WireGuard's wg-quick provides a simple, automatic solution by adding two policy
routing rules and relying on the WireGuard packets having a fwmark set (see [1]).
Let's also do that. Add new properties "wireguard.ip4-auto-default-route"
and "wireguard.ip6-auto-default-route" to enable/disable this. Note that
the default value lets NetworkManager automatically choose whether to
enable it (depending on whether there are any peers that have a default
route). This means, common scenarios should now work well without additional
configuration.
Note that this is also a change in behavior and upon package upgrade
NetworkManager may start adding policy routes (if there are peers that
have a default-route). This is a change in behavior, as the user already
clearly had this setup working and configured some working solution
already.
The new automatism picks the rule priority automatically and adds the
default-route to the routing table that has the same number as the fwmark.
If any of this is unsuitable, then the user is free to disable this
automatism. Note that since 1.18.0 NetworkManager supports policy routing (*).
That means, what this automatism does can be also achieved via explicit
configuration of the profile, which gives the user more flexibility to
adjust all parameters explicitly).
(*) but only since 1.20.0 NetworkManager supports the "suppress_prefixlength"
rule attribute, which makes it impossible to configure exactly this rule-based
solution with 1.18.0 NetworkManager.
[1] https://www.wireguard.com/netns/#improved-rule-based-routing
2019-04-30 17:48:46 +02:00
|
|
|
{
|
wireguard: use fixed fwmark/rule-priority for auto-default-route
With "wireguard.ip4-auto-default-route" and "wireguard.ip6-auto-default-route",
NetworkManager automatically adds policy routing rules for the default
route.
For that, it needs to pick (up to) two numbers:
- the fwmark. This is used both for WireGuard's fwmark setting and
is also the number of the routing table where the default-route is
added.
- the rule priority. NetworkManager adds two policy routing rules, and
we need to place them somewhere before the default rules (at 32766).
Previously, we looked at exiting platform configuration and picked
numbers that were not yet used. However, during restart of
NetworkManager, we leave the interface up and after restart we will
take over the previous configuration. At that point, we need to choose
the same fwmark/priority, otherwise the configuration changes.
But since we picked numbers that were not yet used, we would always choose
different numbers. For routing rules that meant that after restart a second
pair of rules was added.
We possibly could store this data in the device's state-file. But that
is complex. Instead, just pick numbers deterministically based on the
connection's UUID.
If the picked numbers are not suitable, then the user can still work
around that:
- for fwmark, the user can explicitly configure wireguard.fwmark
setting.
- for the policy routes, the user can explicitly add the rules with
the desired priorirites (arguably, currently the default-route cannot
be added like a regular route, so the table cannot be set. Possibly
the user would have to add two /1 routes instead with
suppress_prefixlength=1).
(cherry picked from commit cfb497e49936d36867ae3175537c7d4f77259655)
2019-07-31 10:01:04 +02:00
|
|
|
const guint32 RANGE_TOP = 32766u - 1000u;
|
wireguard: support configuring policy routing to avoid routing loops
For WireGuard (like for all IP-tunnels and IP-based VPNs), the IP addresses of
the peers must be reached outside the tunnel/VPN itself.
For VPN connections, NetworkManager usually adds a direct /32 route to
the external VPN gateway to the underlying device. For WireGuard that is
not done, because injecting a route to another device is ugly and error
prone. Worse: WireGuard with automatic roaming and multiple peers makes this
more complicated.
This is commonly a problem when setting the default-route via the VPN,
but there are also other subtle setups where special care must be taken
to prevent such routing loops.
WireGuard's wg-quick provides a simple, automatic solution by adding two policy
routing rules and relying on the WireGuard packets having a fwmark set (see [1]).
Let's also do that. Add new properties "wireguard.ip4-auto-default-route"
and "wireguard.ip6-auto-default-route" to enable/disable this. Note that
the default value lets NetworkManager automatically choose whether to
enable it (depending on whether there are any peers that have a default
route). This means, common scenarios should now work well without additional
configuration.
Note that this is also a change in behavior and upon package upgrade
NetworkManager may start adding policy routes (if there are peers that
have a default-route). This is a change in behavior, as the user already
clearly had this setup working and configured some working solution
already.
The new automatism picks the rule priority automatically and adds the
default-route to the routing table that has the same number as the fwmark.
If any of this is unsuitable, then the user is free to disable this
automatism. Note that since 1.18.0 NetworkManager supports policy routing (*).
That means, what this automatism does can be also achieved via explicit
configuration of the profile, which gives the user more flexibility to
adjust all parameters explicitly).
(*) but only since 1.20.0 NetworkManager supports the "suppress_prefixlength"
rule attribute, which makes it impossible to configure exactly this rule-based
solution with 1.18.0 NetworkManager.
[1] https://www.wireguard.com/netns/#improved-rule-based-routing
2019-04-30 17:48:46 +02:00
|
|
|
guint64 rnd_seed;
|
|
|
|
|
|
wireguard: use fixed fwmark/rule-priority for auto-default-route
With "wireguard.ip4-auto-default-route" and "wireguard.ip6-auto-default-route",
NetworkManager automatically adds policy routing rules for the default
route.
For that, it needs to pick (up to) two numbers:
- the fwmark. This is used both for WireGuard's fwmark setting and
is also the number of the routing table where the default-route is
added.
- the rule priority. NetworkManager adds two policy routing rules, and
we need to place them somewhere before the default rules (at 32766).
Previously, we looked at exiting platform configuration and picked
numbers that were not yet used. However, during restart of
NetworkManager, we leave the interface up and after restart we will
take over the previous configuration. At that point, we need to choose
the same fwmark/priority, otherwise the configuration changes.
But since we picked numbers that were not yet used, we would always choose
different numbers. For routing rules that meant that after restart a second
pair of rules was added.
We possibly could store this data in the device's state-file. But that
is complex. Instead, just pick numbers deterministically based on the
connection's UUID.
If the picked numbers are not suitable, then the user can still work
around that:
- for fwmark, the user can explicitly configure wireguard.fwmark
setting.
- for the policy routes, the user can explicitly add the rules with
the desired priorirites (arguably, currently the default-route cannot
be added like a regular route, so the table cannot be set. Possibly
the user would have to add two /1 routes instead with
suppress_prefixlength=1).
(cherry picked from commit cfb497e49936d36867ae3175537c7d4f77259655)
2019-07-31 10:01:04 +02:00
|
|
|
/* we pick a priority for the routing rules as follows:
|
|
|
|
|
*
|
|
|
|
|
* - use the connection's UUID as stable seed for the "random" number.
|
|
|
|
|
* - have it smaller than RANGE_TOP (32766u - 1000u), where 32766u is the priority of the default
|
|
|
|
|
* rules
|
2020-07-04 11:37:01 +03:00
|
|
|
* - we add 2 rules (PRIO_WIDTH). Hence only pick even priorities.
|
wireguard: use fixed fwmark/rule-priority for auto-default-route
With "wireguard.ip4-auto-default-route" and "wireguard.ip6-auto-default-route",
NetworkManager automatically adds policy routing rules for the default
route.
For that, it needs to pick (up to) two numbers:
- the fwmark. This is used both for WireGuard's fwmark setting and
is also the number of the routing table where the default-route is
added.
- the rule priority. NetworkManager adds two policy routing rules, and
we need to place them somewhere before the default rules (at 32766).
Previously, we looked at exiting platform configuration and picked
numbers that were not yet used. However, during restart of
NetworkManager, we leave the interface up and after restart we will
take over the previous configuration. At that point, we need to choose
the same fwmark/priority, otherwise the configuration changes.
But since we picked numbers that were not yet used, we would always choose
different numbers. For routing rules that meant that after restart a second
pair of rules was added.
We possibly could store this data in the device's state-file. But that
is complex. Instead, just pick numbers deterministically based on the
connection's UUID.
If the picked numbers are not suitable, then the user can still work
around that:
- for fwmark, the user can explicitly configure wireguard.fwmark
setting.
- for the policy routes, the user can explicitly add the rules with
the desired priorirites (arguably, currently the default-route cannot
be added like a regular route, so the table cannot be set. Possibly
the user would have to add two /1 routes instead with
suppress_prefixlength=1).
(cherry picked from commit cfb497e49936d36867ae3175537c7d4f77259655)
2019-07-31 10:01:04 +02:00
|
|
|
* - pick one out of AUTO_RANDOM_RANGE. */
|
|
|
|
|
|
|
|
|
|
rnd_seed = c_siphash_hash(NM_HASH_SEED_16(0x99,
|
|
|
|
|
0x22,
|
|
|
|
|
0x4d,
|
|
|
|
|
0x7c,
|
|
|
|
|
0x37,
|
|
|
|
|
0xda,
|
|
|
|
|
0x8e,
|
|
|
|
|
0x7b,
|
|
|
|
|
0x2f,
|
|
|
|
|
0x55,
|
|
|
|
|
0x16,
|
|
|
|
|
0x7b,
|
|
|
|
|
0x75,
|
|
|
|
|
0xda,
|
|
|
|
|
0x42,
|
|
|
|
|
0xdc),
|
wireguard: support configuring policy routing to avoid routing loops
For WireGuard (like for all IP-tunnels and IP-based VPNs), the IP addresses of
the peers must be reached outside the tunnel/VPN itself.
For VPN connections, NetworkManager usually adds a direct /32 route to
the external VPN gateway to the underlying device. For WireGuard that is
not done, because injecting a route to another device is ugly and error
prone. Worse: WireGuard with automatic roaming and multiple peers makes this
more complicated.
This is commonly a problem when setting the default-route via the VPN,
but there are also other subtle setups where special care must be taken
to prevent such routing loops.
WireGuard's wg-quick provides a simple, automatic solution by adding two policy
routing rules and relying on the WireGuard packets having a fwmark set (see [1]).
Let's also do that. Add new properties "wireguard.ip4-auto-default-route"
and "wireguard.ip6-auto-default-route" to enable/disable this. Note that
the default value lets NetworkManager automatically choose whether to
enable it (depending on whether there are any peers that have a default
route). This means, common scenarios should now work well without additional
configuration.
Note that this is also a change in behavior and upon package upgrade
NetworkManager may start adding policy routes (if there are peers that
have a default-route). This is a change in behavior, as the user already
clearly had this setup working and configured some working solution
already.
The new automatism picks the rule priority automatically and adds the
default-route to the routing table that has the same number as the fwmark.
If any of this is unsuitable, then the user is free to disable this
automatism. Note that since 1.18.0 NetworkManager supports policy routing (*).
That means, what this automatism does can be also achieved via explicit
configuration of the profile, which gives the user more flexibility to
adjust all parameters explicitly).
(*) but only since 1.20.0 NetworkManager supports the "suppress_prefixlength"
rule attribute, which makes it impossible to configure exactly this rule-based
solution with 1.18.0 NetworkManager.
[1] https://www.wireguard.com/netns/#improved-rule-based-routing
2019-04-30 17:48:46 +02:00
|
|
|
(const guint8 *) uuid,
|
|
|
|
|
uuid ? strlen(uuid) + 1u : 0u);
|
2020-09-28 16:03:33 +02:00
|
|
|
|
wireguard: use fixed fwmark/rule-priority for auto-default-route
With "wireguard.ip4-auto-default-route" and "wireguard.ip6-auto-default-route",
NetworkManager automatically adds policy routing rules for the default
route.
For that, it needs to pick (up to) two numbers:
- the fwmark. This is used both for WireGuard's fwmark setting and
is also the number of the routing table where the default-route is
added.
- the rule priority. NetworkManager adds two policy routing rules, and
we need to place them somewhere before the default rules (at 32766).
Previously, we looked at exiting platform configuration and picked
numbers that were not yet used. However, during restart of
NetworkManager, we leave the interface up and after restart we will
take over the previous configuration. At that point, we need to choose
the same fwmark/priority, otherwise the configuration changes.
But since we picked numbers that were not yet used, we would always choose
different numbers. For routing rules that meant that after restart a second
pair of rules was added.
We possibly could store this data in the device's state-file. But that
is complex. Instead, just pick numbers deterministically based on the
connection's UUID.
If the picked numbers are not suitable, then the user can still work
around that:
- for fwmark, the user can explicitly configure wireguard.fwmark
setting.
- for the policy routes, the user can explicitly add the rules with
the desired priorirites (arguably, currently the default-route cannot
be added like a regular route, so the table cannot be set. Possibly
the user would have to add two /1 routes instead with
suppress_prefixlength=1).
(cherry picked from commit cfb497e49936d36867ae3175537c7d4f77259655)
2019-07-31 10:01:04 +02:00
|
|
|
return RANGE_TOP - (((rnd_seed % (PRIO_WIDTH * AUTO_RANDOM_RANGE)) / PRIO_WIDTH) * PRIO_WIDTH);
|
wireguard: support configuring policy routing to avoid routing loops
For WireGuard (like for all IP-tunnels and IP-based VPNs), the IP addresses of
the peers must be reached outside the tunnel/VPN itself.
For VPN connections, NetworkManager usually adds a direct /32 route to
the external VPN gateway to the underlying device. For WireGuard that is
not done, because injecting a route to another device is ugly and error
prone. Worse: WireGuard with automatic roaming and multiple peers makes this
more complicated.
This is commonly a problem when setting the default-route via the VPN,
but there are also other subtle setups where special care must be taken
to prevent such routing loops.
WireGuard's wg-quick provides a simple, automatic solution by adding two policy
routing rules and relying on the WireGuard packets having a fwmark set (see [1]).
Let's also do that. Add new properties "wireguard.ip4-auto-default-route"
and "wireguard.ip6-auto-default-route" to enable/disable this. Note that
the default value lets NetworkManager automatically choose whether to
enable it (depending on whether there are any peers that have a default
route). This means, common scenarios should now work well without additional
configuration.
Note that this is also a change in behavior and upon package upgrade
NetworkManager may start adding policy routes (if there are peers that
have a default-route). This is a change in behavior, as the user already
clearly had this setup working and configured some working solution
already.
The new automatism picks the rule priority automatically and adds the
default-route to the routing table that has the same number as the fwmark.
If any of this is unsuitable, then the user is free to disable this
automatism. Note that since 1.18.0 NetworkManager supports policy routing (*).
That means, what this automatism does can be also achieved via explicit
configuration of the profile, which gives the user more flexibility to
adjust all parameters explicitly).
(*) but only since 1.20.0 NetworkManager supports the "suppress_prefixlength"
rule attribute, which makes it impossible to configure exactly this rule-based
solution with 1.18.0 NetworkManager.
[1] https://www.wireguard.com/netns/#improved-rule-based-routing
2019-04-30 17:48:46 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
_auto_default_route_init(NMDeviceWireGuard *self)
|
|
|
|
|
{
|
|
|
|
|
NMDeviceWireGuardPrivate *priv = NM_DEVICE_WIREGUARD_GET_PRIVATE(self);
|
2021-11-09 13:28:54 +01:00
|
|
|
NMConnection *connection;
|
wireguard: fix crash in _auto_default_route_init()
#3 0x00007fb0aa9e7d3d in g_return_if_fail_warning
(log_domain=log_domain@entry=0x562295fd5ee3 "libnm", pretty_function=pretty_function@entry=0x562295fd71d0 <__func__.35180> "_connection_get_setting_check", expression=expression@entry=0x562295f8edf7 "NM_IS_CONNECTION (connection)") at ../glib/gmessages.c:2767
#4 0x0000562295df151a in _connection_get_setting_check (connection=0x0, setting_type=0x562297b17050 [NMSettingWireGuard/NMSetting]) at libnm-core/nm-connection.c:207
#5 0x0000562295df151a in _connection_get_setting_check (connection=0x0, setting_type=0x562297b17050 [NMSettingWireGuard/NMSetting]) at libnm-core/nm-connection.c:205
#6 0x0000562295ef132a in _nm_connection_get_setting (type=<optimized out>, connection=0x0) at ./libnm-core/nm-core-internal.h:483
#7 0x0000562295ef132a in _auto_default_route_init (self=self@entry=0x562297bf82b0 [NMDeviceWireGuard]) at src/devices/nm-device-wireguard.c:443
#8 0x0000562295ef1b98 in coerce_route_table (device=0x562297bf82b0 [NMDeviceWireGuard], addr_family=2, route_table=0, is_user_config=<optimized out>)
at src/devices/nm-device-wireguard.c:565
#9 0x0000562295ea42ae in _get_route_table (self=self@entry=0x562297bf82b0 [NMDeviceWireGuard], addr_family=addr_family@entry=2) at src/devices/nm-device.c:2311
#10 0x0000562295ea4593 in nm_device_get_route_table (self=0x562297bf82b0 [NMDeviceWireGuard], addr_family=2) at src/devices/nm-device.c:2338
#11 0x0000562295eabde0 in ip_config_merge_and_apply (self=0x562297bf82b0 [NMDeviceWireGuard], addr_family=2, commit=1) at src/devices/nm-device.c:7590
#12 0x0000562295ed2f0b in device_link_changed (self=self@entry=0x562297bf82b0 [NMDeviceWireGuard]) at src/devices/nm-device.c:3939
#13 0x00007fb0aa9dc7db in g_idle_dispatch (source=source@entry=0x562297bf0b30, callback=0x562295ed2880 <device_link_changed>, user_data=0x562297bf82b0) at ../glib/gmain.c:5627
#14 0x00007fb0aa9dfedd in g_main_dispatch (context=0x562297a28090) at ../glib/gmain.c:3189
#15 0x00007fb0aa9dfedd in g_main_context_dispatch (context=context@entry=0x562297a28090) at ../glib/gmain.c:3854
#16 0x00007fb0aa9e0270 in g_main_context_iterate (context=0x562297a28090, block=block@entry=1, dispatch=dispatch@entry=1, self=<optimized out>) at ../glib/gmain.c:3927
#17 0x00007fb0aa9e05a3 in g_main_loop_run (loop=0x562297a0b380) at ../glib/gmain.c:4123
#18 0x0000562295d0b147 in main (argc=<optimized out>, argv=<optimized out>) at src/main.c:465
https://bugzilla.redhat.com/show_bug.cgi?id=1734383
(cherry picked from commit 47fc1a4293437a88adfd247734e32fa1b86ca7a9)
2019-07-30 18:38:48 +02:00
|
|
|
gboolean enabled_v4 = FALSE;
|
|
|
|
|
gboolean enabled_v6 = FALSE;
|
wireguard: support configuring policy routing to avoid routing loops
For WireGuard (like for all IP-tunnels and IP-based VPNs), the IP addresses of
the peers must be reached outside the tunnel/VPN itself.
For VPN connections, NetworkManager usually adds a direct /32 route to
the external VPN gateway to the underlying device. For WireGuard that is
not done, because injecting a route to another device is ugly and error
prone. Worse: WireGuard with automatic roaming and multiple peers makes this
more complicated.
This is commonly a problem when setting the default-route via the VPN,
but there are also other subtle setups where special care must be taken
to prevent such routing loops.
WireGuard's wg-quick provides a simple, automatic solution by adding two policy
routing rules and relying on the WireGuard packets having a fwmark set (see [1]).
Let's also do that. Add new properties "wireguard.ip4-auto-default-route"
and "wireguard.ip6-auto-default-route" to enable/disable this. Note that
the default value lets NetworkManager automatically choose whether to
enable it (depending on whether there are any peers that have a default
route). This means, common scenarios should now work well without additional
configuration.
Note that this is also a change in behavior and upon package upgrade
NetworkManager may start adding policy routes (if there are peers that
have a default-route). This is a change in behavior, as the user already
clearly had this setup working and configured some working solution
already.
The new automatism picks the rule priority automatically and adds the
default-route to the routing table that has the same number as the fwmark.
If any of this is unsuitable, then the user is free to disable this
automatism. Note that since 1.18.0 NetworkManager supports policy routing (*).
That means, what this automatism does can be also achieved via explicit
configuration of the profile, which gives the user more flexibility to
adjust all parameters explicitly).
(*) but only since 1.20.0 NetworkManager supports the "suppress_prefixlength"
rule attribute, which makes it impossible to configure exactly this rule-based
solution with 1.18.0 NetworkManager.
[1] https://www.wireguard.com/netns/#improved-rule-based-routing
2019-04-30 17:48:46 +02:00
|
|
|
gboolean refreshing_only;
|
wireguard: fix crash in _auto_default_route_init()
#3 0x00007fb0aa9e7d3d in g_return_if_fail_warning
(log_domain=log_domain@entry=0x562295fd5ee3 "libnm", pretty_function=pretty_function@entry=0x562295fd71d0 <__func__.35180> "_connection_get_setting_check", expression=expression@entry=0x562295f8edf7 "NM_IS_CONNECTION (connection)") at ../glib/gmessages.c:2767
#4 0x0000562295df151a in _connection_get_setting_check (connection=0x0, setting_type=0x562297b17050 [NMSettingWireGuard/NMSetting]) at libnm-core/nm-connection.c:207
#5 0x0000562295df151a in _connection_get_setting_check (connection=0x0, setting_type=0x562297b17050 [NMSettingWireGuard/NMSetting]) at libnm-core/nm-connection.c:205
#6 0x0000562295ef132a in _nm_connection_get_setting (type=<optimized out>, connection=0x0) at ./libnm-core/nm-core-internal.h:483
#7 0x0000562295ef132a in _auto_default_route_init (self=self@entry=0x562297bf82b0 [NMDeviceWireGuard]) at src/devices/nm-device-wireguard.c:443
#8 0x0000562295ef1b98 in coerce_route_table (device=0x562297bf82b0 [NMDeviceWireGuard], addr_family=2, route_table=0, is_user_config=<optimized out>)
at src/devices/nm-device-wireguard.c:565
#9 0x0000562295ea42ae in _get_route_table (self=self@entry=0x562297bf82b0 [NMDeviceWireGuard], addr_family=addr_family@entry=2) at src/devices/nm-device.c:2311
#10 0x0000562295ea4593 in nm_device_get_route_table (self=0x562297bf82b0 [NMDeviceWireGuard], addr_family=2) at src/devices/nm-device.c:2338
#11 0x0000562295eabde0 in ip_config_merge_and_apply (self=0x562297bf82b0 [NMDeviceWireGuard], addr_family=2, commit=1) at src/devices/nm-device.c:7590
#12 0x0000562295ed2f0b in device_link_changed (self=self@entry=0x562297bf82b0 [NMDeviceWireGuard]) at src/devices/nm-device.c:3939
#13 0x00007fb0aa9dc7db in g_idle_dispatch (source=source@entry=0x562297bf0b30, callback=0x562295ed2880 <device_link_changed>, user_data=0x562297bf82b0) at ../glib/gmain.c:5627
#14 0x00007fb0aa9dfedd in g_main_dispatch (context=0x562297a28090) at ../glib/gmain.c:3189
#15 0x00007fb0aa9dfedd in g_main_context_dispatch (context=context@entry=0x562297a28090) at ../glib/gmain.c:3854
#16 0x00007fb0aa9e0270 in g_main_context_iterate (context=0x562297a28090, block=block@entry=1, dispatch=dispatch@entry=1, self=<optimized out>) at ../glib/gmain.c:3927
#17 0x00007fb0aa9e05a3 in g_main_loop_run (loop=0x562297a0b380) at ../glib/gmain.c:4123
#18 0x0000562295d0b147 in main (argc=<optimized out>, argv=<optimized out>) at src/main.c:465
https://bugzilla.redhat.com/show_bug.cgi?id=1734383
(cherry picked from commit 47fc1a4293437a88adfd247734e32fa1b86ca7a9)
2019-07-30 18:38:48 +02:00
|
|
|
guint32 new_fwmark = 0;
|
wireguard: support configuring policy routing to avoid routing loops
For WireGuard (like for all IP-tunnels and IP-based VPNs), the IP addresses of
the peers must be reached outside the tunnel/VPN itself.
For VPN connections, NetworkManager usually adds a direct /32 route to
the external VPN gateway to the underlying device. For WireGuard that is
not done, because injecting a route to another device is ugly and error
prone. Worse: WireGuard with automatic roaming and multiple peers makes this
more complicated.
This is commonly a problem when setting the default-route via the VPN,
but there are also other subtle setups where special care must be taken
to prevent such routing loops.
WireGuard's wg-quick provides a simple, automatic solution by adding two policy
routing rules and relying on the WireGuard packets having a fwmark set (see [1]).
Let's also do that. Add new properties "wireguard.ip4-auto-default-route"
and "wireguard.ip6-auto-default-route" to enable/disable this. Note that
the default value lets NetworkManager automatically choose whether to
enable it (depending on whether there are any peers that have a default
route). This means, common scenarios should now work well without additional
configuration.
Note that this is also a change in behavior and upon package upgrade
NetworkManager may start adding policy routes (if there are peers that
have a default-route). This is a change in behavior, as the user already
clearly had this setup working and configured some working solution
already.
The new automatism picks the rule priority automatically and adds the
default-route to the routing table that has the same number as the fwmark.
If any of this is unsuitable, then the user is free to disable this
automatism. Note that since 1.18.0 NetworkManager supports policy routing (*).
That means, what this automatism does can be also achieved via explicit
configuration of the profile, which gives the user more flexibility to
adjust all parameters explicitly).
(*) but only since 1.20.0 NetworkManager supports the "suppress_prefixlength"
rule attribute, which makes it impossible to configure exactly this rule-based
solution with 1.18.0 NetworkManager.
[1] https://www.wireguard.com/netns/#improved-rule-based-routing
2019-04-30 17:48:46 +02:00
|
|
|
guint32 old_fwmark;
|
|
|
|
|
char sbuf1[100];
|
2020-09-28 16:03:33 +02:00
|
|
|
|
wireguard: support configuring policy routing to avoid routing loops
For WireGuard (like for all IP-tunnels and IP-based VPNs), the IP addresses of
the peers must be reached outside the tunnel/VPN itself.
For VPN connections, NetworkManager usually adds a direct /32 route to
the external VPN gateway to the underlying device. For WireGuard that is
not done, because injecting a route to another device is ugly and error
prone. Worse: WireGuard with automatic roaming and multiple peers makes this
more complicated.
This is commonly a problem when setting the default-route via the VPN,
but there are also other subtle setups where special care must be taken
to prevent such routing loops.
WireGuard's wg-quick provides a simple, automatic solution by adding two policy
routing rules and relying on the WireGuard packets having a fwmark set (see [1]).
Let's also do that. Add new properties "wireguard.ip4-auto-default-route"
and "wireguard.ip6-auto-default-route" to enable/disable this. Note that
the default value lets NetworkManager automatically choose whether to
enable it (depending on whether there are any peers that have a default
route). This means, common scenarios should now work well without additional
configuration.
Note that this is also a change in behavior and upon package upgrade
NetworkManager may start adding policy routes (if there are peers that
have a default-route). This is a change in behavior, as the user already
clearly had this setup working and configured some working solution
already.
The new automatism picks the rule priority automatically and adds the
default-route to the routing table that has the same number as the fwmark.
If any of this is unsuitable, then the user is free to disable this
automatism. Note that since 1.18.0 NetworkManager supports policy routing (*).
That means, what this automatism does can be also achieved via explicit
configuration of the profile, which gives the user more flexibility to
adjust all parameters explicitly).
(*) but only since 1.20.0 NetworkManager supports the "suppress_prefixlength"
rule attribute, which makes it impossible to configure exactly this rule-based
solution with 1.18.0 NetworkManager.
[1] https://www.wireguard.com/netns/#improved-rule-based-routing
2019-04-30 17:48:46 +02:00
|
|
|
if (G_LIKELY(priv->auto_default_route_initialized && !priv->auto_default_route_refresh))
|
|
|
|
|
return;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
wireguard: support configuring policy routing to avoid routing loops
For WireGuard (like for all IP-tunnels and IP-based VPNs), the IP addresses of
the peers must be reached outside the tunnel/VPN itself.
For VPN connections, NetworkManager usually adds a direct /32 route to
the external VPN gateway to the underlying device. For WireGuard that is
not done, because injecting a route to another device is ugly and error
prone. Worse: WireGuard with automatic roaming and multiple peers makes this
more complicated.
This is commonly a problem when setting the default-route via the VPN,
but there are also other subtle setups where special care must be taken
to prevent such routing loops.
WireGuard's wg-quick provides a simple, automatic solution by adding two policy
routing rules and relying on the WireGuard packets having a fwmark set (see [1]).
Let's also do that. Add new properties "wireguard.ip4-auto-default-route"
and "wireguard.ip6-auto-default-route" to enable/disable this. Note that
the default value lets NetworkManager automatically choose whether to
enable it (depending on whether there are any peers that have a default
route). This means, common scenarios should now work well without additional
configuration.
Note that this is also a change in behavior and upon package upgrade
NetworkManager may start adding policy routes (if there are peers that
have a default-route). This is a change in behavior, as the user already
clearly had this setup working and configured some working solution
already.
The new automatism picks the rule priority automatically and adds the
default-route to the routing table that has the same number as the fwmark.
If any of this is unsuitable, then the user is free to disable this
automatism. Note that since 1.18.0 NetworkManager supports policy routing (*).
That means, what this automatism does can be also achieved via explicit
configuration of the profile, which gives the user more flexibility to
adjust all parameters explicitly).
(*) but only since 1.20.0 NetworkManager supports the "suppress_prefixlength"
rule attribute, which makes it impossible to configure exactly this rule-based
solution with 1.18.0 NetworkManager.
[1] https://www.wireguard.com/netns/#improved-rule-based-routing
2019-04-30 17:48:46 +02:00
|
|
|
refreshing_only = priv->auto_default_route_initialized && priv->auto_default_route_refresh;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
wireguard: fix crash in _auto_default_route_init()
#3 0x00007fb0aa9e7d3d in g_return_if_fail_warning
(log_domain=log_domain@entry=0x562295fd5ee3 "libnm", pretty_function=pretty_function@entry=0x562295fd71d0 <__func__.35180> "_connection_get_setting_check", expression=expression@entry=0x562295f8edf7 "NM_IS_CONNECTION (connection)") at ../glib/gmessages.c:2767
#4 0x0000562295df151a in _connection_get_setting_check (connection=0x0, setting_type=0x562297b17050 [NMSettingWireGuard/NMSetting]) at libnm-core/nm-connection.c:207
#5 0x0000562295df151a in _connection_get_setting_check (connection=0x0, setting_type=0x562297b17050 [NMSettingWireGuard/NMSetting]) at libnm-core/nm-connection.c:205
#6 0x0000562295ef132a in _nm_connection_get_setting (type=<optimized out>, connection=0x0) at ./libnm-core/nm-core-internal.h:483
#7 0x0000562295ef132a in _auto_default_route_init (self=self@entry=0x562297bf82b0 [NMDeviceWireGuard]) at src/devices/nm-device-wireguard.c:443
#8 0x0000562295ef1b98 in coerce_route_table (device=0x562297bf82b0 [NMDeviceWireGuard], addr_family=2, route_table=0, is_user_config=<optimized out>)
at src/devices/nm-device-wireguard.c:565
#9 0x0000562295ea42ae in _get_route_table (self=self@entry=0x562297bf82b0 [NMDeviceWireGuard], addr_family=addr_family@entry=2) at src/devices/nm-device.c:2311
#10 0x0000562295ea4593 in nm_device_get_route_table (self=0x562297bf82b0 [NMDeviceWireGuard], addr_family=2) at src/devices/nm-device.c:2338
#11 0x0000562295eabde0 in ip_config_merge_and_apply (self=0x562297bf82b0 [NMDeviceWireGuard], addr_family=2, commit=1) at src/devices/nm-device.c:7590
#12 0x0000562295ed2f0b in device_link_changed (self=self@entry=0x562297bf82b0 [NMDeviceWireGuard]) at src/devices/nm-device.c:3939
#13 0x00007fb0aa9dc7db in g_idle_dispatch (source=source@entry=0x562297bf0b30, callback=0x562295ed2880 <device_link_changed>, user_data=0x562297bf82b0) at ../glib/gmain.c:5627
#14 0x00007fb0aa9dfedd in g_main_dispatch (context=0x562297a28090) at ../glib/gmain.c:3189
#15 0x00007fb0aa9dfedd in g_main_context_dispatch (context=context@entry=0x562297a28090) at ../glib/gmain.c:3854
#16 0x00007fb0aa9e0270 in g_main_context_iterate (context=0x562297a28090, block=block@entry=1, dispatch=dispatch@entry=1, self=<optimized out>) at ../glib/gmain.c:3927
#17 0x00007fb0aa9e05a3 in g_main_loop_run (loop=0x562297a0b380) at ../glib/gmain.c:4123
#18 0x0000562295d0b147 in main (argc=<optimized out>, argv=<optimized out>) at src/main.c:465
https://bugzilla.redhat.com/show_bug.cgi?id=1734383
(cherry picked from commit 47fc1a4293437a88adfd247734e32fa1b86ca7a9)
2019-07-30 18:38:48 +02:00
|
|
|
old_fwmark = priv->auto_default_route_fwmark;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
wireguard: support configuring policy routing to avoid routing loops
For WireGuard (like for all IP-tunnels and IP-based VPNs), the IP addresses of
the peers must be reached outside the tunnel/VPN itself.
For VPN connections, NetworkManager usually adds a direct /32 route to
the external VPN gateway to the underlying device. For WireGuard that is
not done, because injecting a route to another device is ugly and error
prone. Worse: WireGuard with automatic roaming and multiple peers makes this
more complicated.
This is commonly a problem when setting the default-route via the VPN,
but there are also other subtle setups where special care must be taken
to prevent such routing loops.
WireGuard's wg-quick provides a simple, automatic solution by adding two policy
routing rules and relying on the WireGuard packets having a fwmark set (see [1]).
Let's also do that. Add new properties "wireguard.ip4-auto-default-route"
and "wireguard.ip6-auto-default-route" to enable/disable this. Note that
the default value lets NetworkManager automatically choose whether to
enable it (depending on whether there are any peers that have a default
route). This means, common scenarios should now work well without additional
configuration.
Note that this is also a change in behavior and upon package upgrade
NetworkManager may start adding policy routes (if there are peers that
have a default-route). This is a change in behavior, as the user already
clearly had this setup working and configured some working solution
already.
The new automatism picks the rule priority automatically and adds the
default-route to the routing table that has the same number as the fwmark.
If any of this is unsuitable, then the user is free to disable this
automatism. Note that since 1.18.0 NetworkManager supports policy routing (*).
That means, what this automatism does can be also achieved via explicit
configuration of the profile, which gives the user more flexibility to
adjust all parameters explicitly).
(*) but only since 1.20.0 NetworkManager supports the "suppress_prefixlength"
rule attribute, which makes it impossible to configure exactly this rule-based
solution with 1.18.0 NetworkManager.
[1] https://www.wireguard.com/netns/#improved-rule-based-routing
2019-04-30 17:48:46 +02:00
|
|
|
connection = nm_device_get_applied_connection(NM_DEVICE(self));
|
wireguard: fix crash in _auto_default_route_init()
#3 0x00007fb0aa9e7d3d in g_return_if_fail_warning
(log_domain=log_domain@entry=0x562295fd5ee3 "libnm", pretty_function=pretty_function@entry=0x562295fd71d0 <__func__.35180> "_connection_get_setting_check", expression=expression@entry=0x562295f8edf7 "NM_IS_CONNECTION (connection)") at ../glib/gmessages.c:2767
#4 0x0000562295df151a in _connection_get_setting_check (connection=0x0, setting_type=0x562297b17050 [NMSettingWireGuard/NMSetting]) at libnm-core/nm-connection.c:207
#5 0x0000562295df151a in _connection_get_setting_check (connection=0x0, setting_type=0x562297b17050 [NMSettingWireGuard/NMSetting]) at libnm-core/nm-connection.c:205
#6 0x0000562295ef132a in _nm_connection_get_setting (type=<optimized out>, connection=0x0) at ./libnm-core/nm-core-internal.h:483
#7 0x0000562295ef132a in _auto_default_route_init (self=self@entry=0x562297bf82b0 [NMDeviceWireGuard]) at src/devices/nm-device-wireguard.c:443
#8 0x0000562295ef1b98 in coerce_route_table (device=0x562297bf82b0 [NMDeviceWireGuard], addr_family=2, route_table=0, is_user_config=<optimized out>)
at src/devices/nm-device-wireguard.c:565
#9 0x0000562295ea42ae in _get_route_table (self=self@entry=0x562297bf82b0 [NMDeviceWireGuard], addr_family=addr_family@entry=2) at src/devices/nm-device.c:2311
#10 0x0000562295ea4593 in nm_device_get_route_table (self=0x562297bf82b0 [NMDeviceWireGuard], addr_family=2) at src/devices/nm-device.c:2338
#11 0x0000562295eabde0 in ip_config_merge_and_apply (self=0x562297bf82b0 [NMDeviceWireGuard], addr_family=2, commit=1) at src/devices/nm-device.c:7590
#12 0x0000562295ed2f0b in device_link_changed (self=self@entry=0x562297bf82b0 [NMDeviceWireGuard]) at src/devices/nm-device.c:3939
#13 0x00007fb0aa9dc7db in g_idle_dispatch (source=source@entry=0x562297bf0b30, callback=0x562295ed2880 <device_link_changed>, user_data=0x562297bf82b0) at ../glib/gmain.c:5627
#14 0x00007fb0aa9dfedd in g_main_dispatch (context=0x562297a28090) at ../glib/gmain.c:3189
#15 0x00007fb0aa9dfedd in g_main_context_dispatch (context=context@entry=0x562297a28090) at ../glib/gmain.c:3854
#16 0x00007fb0aa9e0270 in g_main_context_iterate (context=0x562297a28090, block=block@entry=1, dispatch=dispatch@entry=1, self=<optimized out>) at ../glib/gmain.c:3927
#17 0x00007fb0aa9e05a3 in g_main_loop_run (loop=0x562297a0b380) at ../glib/gmain.c:4123
#18 0x0000562295d0b147 in main (argc=<optimized out>, argv=<optimized out>) at src/main.c:465
https://bugzilla.redhat.com/show_bug.cgi?id=1734383
(cherry picked from commit 47fc1a4293437a88adfd247734e32fa1b86ca7a9)
2019-07-30 18:38:48 +02:00
|
|
|
if (connection) {
|
|
|
|
|
NMSettingWireGuard *s_wg;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
wireguard: fix crash in _auto_default_route_init()
#3 0x00007fb0aa9e7d3d in g_return_if_fail_warning
(log_domain=log_domain@entry=0x562295fd5ee3 "libnm", pretty_function=pretty_function@entry=0x562295fd71d0 <__func__.35180> "_connection_get_setting_check", expression=expression@entry=0x562295f8edf7 "NM_IS_CONNECTION (connection)") at ../glib/gmessages.c:2767
#4 0x0000562295df151a in _connection_get_setting_check (connection=0x0, setting_type=0x562297b17050 [NMSettingWireGuard/NMSetting]) at libnm-core/nm-connection.c:207
#5 0x0000562295df151a in _connection_get_setting_check (connection=0x0, setting_type=0x562297b17050 [NMSettingWireGuard/NMSetting]) at libnm-core/nm-connection.c:205
#6 0x0000562295ef132a in _nm_connection_get_setting (type=<optimized out>, connection=0x0) at ./libnm-core/nm-core-internal.h:483
#7 0x0000562295ef132a in _auto_default_route_init (self=self@entry=0x562297bf82b0 [NMDeviceWireGuard]) at src/devices/nm-device-wireguard.c:443
#8 0x0000562295ef1b98 in coerce_route_table (device=0x562297bf82b0 [NMDeviceWireGuard], addr_family=2, route_table=0, is_user_config=<optimized out>)
at src/devices/nm-device-wireguard.c:565
#9 0x0000562295ea42ae in _get_route_table (self=self@entry=0x562297bf82b0 [NMDeviceWireGuard], addr_family=addr_family@entry=2) at src/devices/nm-device.c:2311
#10 0x0000562295ea4593 in nm_device_get_route_table (self=0x562297bf82b0 [NMDeviceWireGuard], addr_family=2) at src/devices/nm-device.c:2338
#11 0x0000562295eabde0 in ip_config_merge_and_apply (self=0x562297bf82b0 [NMDeviceWireGuard], addr_family=2, commit=1) at src/devices/nm-device.c:7590
#12 0x0000562295ed2f0b in device_link_changed (self=self@entry=0x562297bf82b0 [NMDeviceWireGuard]) at src/devices/nm-device.c:3939
#13 0x00007fb0aa9dc7db in g_idle_dispatch (source=source@entry=0x562297bf0b30, callback=0x562295ed2880 <device_link_changed>, user_data=0x562297bf82b0) at ../glib/gmain.c:5627
#14 0x00007fb0aa9dfedd in g_main_dispatch (context=0x562297a28090) at ../glib/gmain.c:3189
#15 0x00007fb0aa9dfedd in g_main_context_dispatch (context=context@entry=0x562297a28090) at ../glib/gmain.c:3854
#16 0x00007fb0aa9e0270 in g_main_context_iterate (context=0x562297a28090, block=block@entry=1, dispatch=dispatch@entry=1, self=<optimized out>) at ../glib/gmain.c:3927
#17 0x00007fb0aa9e05a3 in g_main_loop_run (loop=0x562297a0b380) at ../glib/gmain.c:4123
#18 0x0000562295d0b147 in main (argc=<optimized out>, argv=<optimized out>) at src/main.c:465
https://bugzilla.redhat.com/show_bug.cgi?id=1734383
(cherry picked from commit 47fc1a4293437a88adfd247734e32fa1b86ca7a9)
2019-07-30 18:38:48 +02:00
|
|
|
s_wg = _nm_connection_get_setting(connection, NM_TYPE_SETTING_WIREGUARD);
|
2020-09-28 16:03:33 +02:00
|
|
|
|
wireguard: fix crash in _auto_default_route_init()
#3 0x00007fb0aa9e7d3d in g_return_if_fail_warning
(log_domain=log_domain@entry=0x562295fd5ee3 "libnm", pretty_function=pretty_function@entry=0x562295fd71d0 <__func__.35180> "_connection_get_setting_check", expression=expression@entry=0x562295f8edf7 "NM_IS_CONNECTION (connection)") at ../glib/gmessages.c:2767
#4 0x0000562295df151a in _connection_get_setting_check (connection=0x0, setting_type=0x562297b17050 [NMSettingWireGuard/NMSetting]) at libnm-core/nm-connection.c:207
#5 0x0000562295df151a in _connection_get_setting_check (connection=0x0, setting_type=0x562297b17050 [NMSettingWireGuard/NMSetting]) at libnm-core/nm-connection.c:205
#6 0x0000562295ef132a in _nm_connection_get_setting (type=<optimized out>, connection=0x0) at ./libnm-core/nm-core-internal.h:483
#7 0x0000562295ef132a in _auto_default_route_init (self=self@entry=0x562297bf82b0 [NMDeviceWireGuard]) at src/devices/nm-device-wireguard.c:443
#8 0x0000562295ef1b98 in coerce_route_table (device=0x562297bf82b0 [NMDeviceWireGuard], addr_family=2, route_table=0, is_user_config=<optimized out>)
at src/devices/nm-device-wireguard.c:565
#9 0x0000562295ea42ae in _get_route_table (self=self@entry=0x562297bf82b0 [NMDeviceWireGuard], addr_family=addr_family@entry=2) at src/devices/nm-device.c:2311
#10 0x0000562295ea4593 in nm_device_get_route_table (self=0x562297bf82b0 [NMDeviceWireGuard], addr_family=2) at src/devices/nm-device.c:2338
#11 0x0000562295eabde0 in ip_config_merge_and_apply (self=0x562297bf82b0 [NMDeviceWireGuard], addr_family=2, commit=1) at src/devices/nm-device.c:7590
#12 0x0000562295ed2f0b in device_link_changed (self=self@entry=0x562297bf82b0 [NMDeviceWireGuard]) at src/devices/nm-device.c:3939
#13 0x00007fb0aa9dc7db in g_idle_dispatch (source=source@entry=0x562297bf0b30, callback=0x562295ed2880 <device_link_changed>, user_data=0x562297bf82b0) at ../glib/gmain.c:5627
#14 0x00007fb0aa9dfedd in g_main_dispatch (context=0x562297a28090) at ../glib/gmain.c:3189
#15 0x00007fb0aa9dfedd in g_main_context_dispatch (context=context@entry=0x562297a28090) at ../glib/gmain.c:3854
#16 0x00007fb0aa9e0270 in g_main_context_iterate (context=0x562297a28090, block=block@entry=1, dispatch=dispatch@entry=1, self=<optimized out>) at ../glib/gmain.c:3927
#17 0x00007fb0aa9e05a3 in g_main_loop_run (loop=0x562297a0b380) at ../glib/gmain.c:4123
#18 0x0000562295d0b147 in main (argc=<optimized out>, argv=<optimized out>) at src/main.c:465
https://bugzilla.redhat.com/show_bug.cgi?id=1734383
(cherry picked from commit 47fc1a4293437a88adfd247734e32fa1b86ca7a9)
2019-07-30 18:38:48 +02:00
|
|
|
new_fwmark = nm_setting_wireguard_get_fwmark(s_wg);
|
2020-09-28 16:03:33 +02:00
|
|
|
|
wireguard: fix crash in _auto_default_route_init()
#3 0x00007fb0aa9e7d3d in g_return_if_fail_warning
(log_domain=log_domain@entry=0x562295fd5ee3 "libnm", pretty_function=pretty_function@entry=0x562295fd71d0 <__func__.35180> "_connection_get_setting_check", expression=expression@entry=0x562295f8edf7 "NM_IS_CONNECTION (connection)") at ../glib/gmessages.c:2767
#4 0x0000562295df151a in _connection_get_setting_check (connection=0x0, setting_type=0x562297b17050 [NMSettingWireGuard/NMSetting]) at libnm-core/nm-connection.c:207
#5 0x0000562295df151a in _connection_get_setting_check (connection=0x0, setting_type=0x562297b17050 [NMSettingWireGuard/NMSetting]) at libnm-core/nm-connection.c:205
#6 0x0000562295ef132a in _nm_connection_get_setting (type=<optimized out>, connection=0x0) at ./libnm-core/nm-core-internal.h:483
#7 0x0000562295ef132a in _auto_default_route_init (self=self@entry=0x562297bf82b0 [NMDeviceWireGuard]) at src/devices/nm-device-wireguard.c:443
#8 0x0000562295ef1b98 in coerce_route_table (device=0x562297bf82b0 [NMDeviceWireGuard], addr_family=2, route_table=0, is_user_config=<optimized out>)
at src/devices/nm-device-wireguard.c:565
#9 0x0000562295ea42ae in _get_route_table (self=self@entry=0x562297bf82b0 [NMDeviceWireGuard], addr_family=addr_family@entry=2) at src/devices/nm-device.c:2311
#10 0x0000562295ea4593 in nm_device_get_route_table (self=0x562297bf82b0 [NMDeviceWireGuard], addr_family=2) at src/devices/nm-device.c:2338
#11 0x0000562295eabde0 in ip_config_merge_and_apply (self=0x562297bf82b0 [NMDeviceWireGuard], addr_family=2, commit=1) at src/devices/nm-device.c:7590
#12 0x0000562295ed2f0b in device_link_changed (self=self@entry=0x562297bf82b0 [NMDeviceWireGuard]) at src/devices/nm-device.c:3939
#13 0x00007fb0aa9dc7db in g_idle_dispatch (source=source@entry=0x562297bf0b30, callback=0x562295ed2880 <device_link_changed>, user_data=0x562297bf82b0) at ../glib/gmain.c:5627
#14 0x00007fb0aa9dfedd in g_main_dispatch (context=0x562297a28090) at ../glib/gmain.c:3189
#15 0x00007fb0aa9dfedd in g_main_context_dispatch (context=context@entry=0x562297a28090) at ../glib/gmain.c:3854
#16 0x00007fb0aa9e0270 in g_main_context_iterate (context=0x562297a28090, block=block@entry=1, dispatch=dispatch@entry=1, self=<optimized out>) at ../glib/gmain.c:3927
#17 0x00007fb0aa9e05a3 in g_main_loop_run (loop=0x562297a0b380) at ../glib/gmain.c:4123
#18 0x0000562295d0b147 in main (argc=<optimized out>, argv=<optimized out>) at src/main.c:465
https://bugzilla.redhat.com/show_bug.cgi?id=1734383
(cherry picked from commit 47fc1a4293437a88adfd247734e32fa1b86ca7a9)
2019-07-30 18:38:48 +02:00
|
|
|
_auto_default_route_get_enabled(s_wg, connection, &enabled_v4, &enabled_v6);
|
|
|
|
|
}
|
2020-09-28 16:03:33 +02:00
|
|
|
|
wireguard: fix crash in _auto_default_route_init()
#3 0x00007fb0aa9e7d3d in g_return_if_fail_warning
(log_domain=log_domain@entry=0x562295fd5ee3 "libnm", pretty_function=pretty_function@entry=0x562295fd71d0 <__func__.35180> "_connection_get_setting_check", expression=expression@entry=0x562295f8edf7 "NM_IS_CONNECTION (connection)") at ../glib/gmessages.c:2767
#4 0x0000562295df151a in _connection_get_setting_check (connection=0x0, setting_type=0x562297b17050 [NMSettingWireGuard/NMSetting]) at libnm-core/nm-connection.c:207
#5 0x0000562295df151a in _connection_get_setting_check (connection=0x0, setting_type=0x562297b17050 [NMSettingWireGuard/NMSetting]) at libnm-core/nm-connection.c:205
#6 0x0000562295ef132a in _nm_connection_get_setting (type=<optimized out>, connection=0x0) at ./libnm-core/nm-core-internal.h:483
#7 0x0000562295ef132a in _auto_default_route_init (self=self@entry=0x562297bf82b0 [NMDeviceWireGuard]) at src/devices/nm-device-wireguard.c:443
#8 0x0000562295ef1b98 in coerce_route_table (device=0x562297bf82b0 [NMDeviceWireGuard], addr_family=2, route_table=0, is_user_config=<optimized out>)
at src/devices/nm-device-wireguard.c:565
#9 0x0000562295ea42ae in _get_route_table (self=self@entry=0x562297bf82b0 [NMDeviceWireGuard], addr_family=addr_family@entry=2) at src/devices/nm-device.c:2311
#10 0x0000562295ea4593 in nm_device_get_route_table (self=0x562297bf82b0 [NMDeviceWireGuard], addr_family=2) at src/devices/nm-device.c:2338
#11 0x0000562295eabde0 in ip_config_merge_and_apply (self=0x562297bf82b0 [NMDeviceWireGuard], addr_family=2, commit=1) at src/devices/nm-device.c:7590
#12 0x0000562295ed2f0b in device_link_changed (self=self@entry=0x562297bf82b0 [NMDeviceWireGuard]) at src/devices/nm-device.c:3939
#13 0x00007fb0aa9dc7db in g_idle_dispatch (source=source@entry=0x562297bf0b30, callback=0x562295ed2880 <device_link_changed>, user_data=0x562297bf82b0) at ../glib/gmain.c:5627
#14 0x00007fb0aa9dfedd in g_main_dispatch (context=0x562297a28090) at ../glib/gmain.c:3189
#15 0x00007fb0aa9dfedd in g_main_context_dispatch (context=context@entry=0x562297a28090) at ../glib/gmain.c:3854
#16 0x00007fb0aa9e0270 in g_main_context_iterate (context=0x562297a28090, block=block@entry=1, dispatch=dispatch@entry=1, self=<optimized out>) at ../glib/gmain.c:3927
#17 0x00007fb0aa9e05a3 in g_main_loop_run (loop=0x562297a0b380) at ../glib/gmain.c:4123
#18 0x0000562295d0b147 in main (argc=<optimized out>, argv=<optimized out>) at src/main.c:465
https://bugzilla.redhat.com/show_bug.cgi?id=1734383
(cherry picked from commit 47fc1a4293437a88adfd247734e32fa1b86ca7a9)
2019-07-30 18:38:48 +02:00
|
|
|
if ((enabled_v4 || enabled_v6) && new_fwmark == 0u) {
|
|
|
|
|
if (refreshing_only)
|
|
|
|
|
new_fwmark = old_fwmark;
|
|
|
|
|
else
|
wireguard: use fixed fwmark/rule-priority for auto-default-route
With "wireguard.ip4-auto-default-route" and "wireguard.ip6-auto-default-route",
NetworkManager automatically adds policy routing rules for the default
route.
For that, it needs to pick (up to) two numbers:
- the fwmark. This is used both for WireGuard's fwmark setting and
is also the number of the routing table where the default-route is
added.
- the rule priority. NetworkManager adds two policy routing rules, and
we need to place them somewhere before the default rules (at 32766).
Previously, we looked at exiting platform configuration and picked
numbers that were not yet used. However, during restart of
NetworkManager, we leave the interface up and after restart we will
take over the previous configuration. At that point, we need to choose
the same fwmark/priority, otherwise the configuration changes.
But since we picked numbers that were not yet used, we would always choose
different numbers. For routing rules that meant that after restart a second
pair of rules was added.
We possibly could store this data in the device's state-file. But that
is complex. Instead, just pick numbers deterministically based on the
connection's UUID.
If the picked numbers are not suitable, then the user can still work
around that:
- for fwmark, the user can explicitly configure wireguard.fwmark
setting.
- for the policy routes, the user can explicitly add the rules with
the desired priorirites (arguably, currently the default-route cannot
be added like a regular route, so the table cannot be set. Possibly
the user would have to add two /1 routes instead with
suppress_prefixlength=1).
(cherry picked from commit cfb497e49936d36867ae3175537c7d4f77259655)
2019-07-31 10:01:04 +02:00
|
|
|
new_fwmark = _auto_default_route_get_auto_fwmark(nm_connection_get_uuid(connection));
|
wireguard: fix crash in _auto_default_route_init()
#3 0x00007fb0aa9e7d3d in g_return_if_fail_warning
(log_domain=log_domain@entry=0x562295fd5ee3 "libnm", pretty_function=pretty_function@entry=0x562295fd71d0 <__func__.35180> "_connection_get_setting_check", expression=expression@entry=0x562295f8edf7 "NM_IS_CONNECTION (connection)") at ../glib/gmessages.c:2767
#4 0x0000562295df151a in _connection_get_setting_check (connection=0x0, setting_type=0x562297b17050 [NMSettingWireGuard/NMSetting]) at libnm-core/nm-connection.c:207
#5 0x0000562295df151a in _connection_get_setting_check (connection=0x0, setting_type=0x562297b17050 [NMSettingWireGuard/NMSetting]) at libnm-core/nm-connection.c:205
#6 0x0000562295ef132a in _nm_connection_get_setting (type=<optimized out>, connection=0x0) at ./libnm-core/nm-core-internal.h:483
#7 0x0000562295ef132a in _auto_default_route_init (self=self@entry=0x562297bf82b0 [NMDeviceWireGuard]) at src/devices/nm-device-wireguard.c:443
#8 0x0000562295ef1b98 in coerce_route_table (device=0x562297bf82b0 [NMDeviceWireGuard], addr_family=2, route_table=0, is_user_config=<optimized out>)
at src/devices/nm-device-wireguard.c:565
#9 0x0000562295ea42ae in _get_route_table (self=self@entry=0x562297bf82b0 [NMDeviceWireGuard], addr_family=addr_family@entry=2) at src/devices/nm-device.c:2311
#10 0x0000562295ea4593 in nm_device_get_route_table (self=0x562297bf82b0 [NMDeviceWireGuard], addr_family=2) at src/devices/nm-device.c:2338
#11 0x0000562295eabde0 in ip_config_merge_and_apply (self=0x562297bf82b0 [NMDeviceWireGuard], addr_family=2, commit=1) at src/devices/nm-device.c:7590
#12 0x0000562295ed2f0b in device_link_changed (self=self@entry=0x562297bf82b0 [NMDeviceWireGuard]) at src/devices/nm-device.c:3939
#13 0x00007fb0aa9dc7db in g_idle_dispatch (source=source@entry=0x562297bf0b30, callback=0x562295ed2880 <device_link_changed>, user_data=0x562297bf82b0) at ../glib/gmain.c:5627
#14 0x00007fb0aa9dfedd in g_main_dispatch (context=0x562297a28090) at ../glib/gmain.c:3189
#15 0x00007fb0aa9dfedd in g_main_context_dispatch (context=context@entry=0x562297a28090) at ../glib/gmain.c:3854
#16 0x00007fb0aa9e0270 in g_main_context_iterate (context=0x562297a28090, block=block@entry=1, dispatch=dispatch@entry=1, self=<optimized out>) at ../glib/gmain.c:3927
#17 0x00007fb0aa9e05a3 in g_main_loop_run (loop=0x562297a0b380) at ../glib/gmain.c:4123
#18 0x0000562295d0b147 in main (argc=<optimized out>, argv=<optimized out>) at src/main.c:465
https://bugzilla.redhat.com/show_bug.cgi?id=1734383
(cherry picked from commit 47fc1a4293437a88adfd247734e32fa1b86ca7a9)
2019-07-30 18:38:48 +02:00
|
|
|
}
|
2020-09-28 16:03:33 +02:00
|
|
|
|
wireguard: fix crash in _auto_default_route_init()
#3 0x00007fb0aa9e7d3d in g_return_if_fail_warning
(log_domain=log_domain@entry=0x562295fd5ee3 "libnm", pretty_function=pretty_function@entry=0x562295fd71d0 <__func__.35180> "_connection_get_setting_check", expression=expression@entry=0x562295f8edf7 "NM_IS_CONNECTION (connection)") at ../glib/gmessages.c:2767
#4 0x0000562295df151a in _connection_get_setting_check (connection=0x0, setting_type=0x562297b17050 [NMSettingWireGuard/NMSetting]) at libnm-core/nm-connection.c:207
#5 0x0000562295df151a in _connection_get_setting_check (connection=0x0, setting_type=0x562297b17050 [NMSettingWireGuard/NMSetting]) at libnm-core/nm-connection.c:205
#6 0x0000562295ef132a in _nm_connection_get_setting (type=<optimized out>, connection=0x0) at ./libnm-core/nm-core-internal.h:483
#7 0x0000562295ef132a in _auto_default_route_init (self=self@entry=0x562297bf82b0 [NMDeviceWireGuard]) at src/devices/nm-device-wireguard.c:443
#8 0x0000562295ef1b98 in coerce_route_table (device=0x562297bf82b0 [NMDeviceWireGuard], addr_family=2, route_table=0, is_user_config=<optimized out>)
at src/devices/nm-device-wireguard.c:565
#9 0x0000562295ea42ae in _get_route_table (self=self@entry=0x562297bf82b0 [NMDeviceWireGuard], addr_family=addr_family@entry=2) at src/devices/nm-device.c:2311
#10 0x0000562295ea4593 in nm_device_get_route_table (self=0x562297bf82b0 [NMDeviceWireGuard], addr_family=2) at src/devices/nm-device.c:2338
#11 0x0000562295eabde0 in ip_config_merge_and_apply (self=0x562297bf82b0 [NMDeviceWireGuard], addr_family=2, commit=1) at src/devices/nm-device.c:7590
#12 0x0000562295ed2f0b in device_link_changed (self=self@entry=0x562297bf82b0 [NMDeviceWireGuard]) at src/devices/nm-device.c:3939
#13 0x00007fb0aa9dc7db in g_idle_dispatch (source=source@entry=0x562297bf0b30, callback=0x562295ed2880 <device_link_changed>, user_data=0x562297bf82b0) at ../glib/gmain.c:5627
#14 0x00007fb0aa9dfedd in g_main_dispatch (context=0x562297a28090) at ../glib/gmain.c:3189
#15 0x00007fb0aa9dfedd in g_main_context_dispatch (context=context@entry=0x562297a28090) at ../glib/gmain.c:3854
#16 0x00007fb0aa9e0270 in g_main_context_iterate (context=0x562297a28090, block=block@entry=1, dispatch=dispatch@entry=1, self=<optimized out>) at ../glib/gmain.c:3927
#17 0x00007fb0aa9e05a3 in g_main_loop_run (loop=0x562297a0b380) at ../glib/gmain.c:4123
#18 0x0000562295d0b147 in main (argc=<optimized out>, argv=<optimized out>) at src/main.c:465
https://bugzilla.redhat.com/show_bug.cgi?id=1734383
(cherry picked from commit 47fc1a4293437a88adfd247734e32fa1b86ca7a9)
2019-07-30 18:38:48 +02:00
|
|
|
priv->auto_default_route_refresh = FALSE;
|
|
|
|
|
priv->auto_default_route_fwmark = new_fwmark;
|
wireguard: support configuring policy routing to avoid routing loops
For WireGuard (like for all IP-tunnels and IP-based VPNs), the IP addresses of
the peers must be reached outside the tunnel/VPN itself.
For VPN connections, NetworkManager usually adds a direct /32 route to
the external VPN gateway to the underlying device. For WireGuard that is
not done, because injecting a route to another device is ugly and error
prone. Worse: WireGuard with automatic roaming and multiple peers makes this
more complicated.
This is commonly a problem when setting the default-route via the VPN,
but there are also other subtle setups where special care must be taken
to prevent such routing loops.
WireGuard's wg-quick provides a simple, automatic solution by adding two policy
routing rules and relying on the WireGuard packets having a fwmark set (see [1]).
Let's also do that. Add new properties "wireguard.ip4-auto-default-route"
and "wireguard.ip6-auto-default-route" to enable/disable this. Note that
the default value lets NetworkManager automatically choose whether to
enable it (depending on whether there are any peers that have a default
route). This means, common scenarios should now work well without additional
configuration.
Note that this is also a change in behavior and upon package upgrade
NetworkManager may start adding policy routes (if there are peers that
have a default-route). This is a change in behavior, as the user already
clearly had this setup working and configured some working solution
already.
The new automatism picks the rule priority automatically and adds the
default-route to the routing table that has the same number as the fwmark.
If any of this is unsuitable, then the user is free to disable this
automatism. Note that since 1.18.0 NetworkManager supports policy routing (*).
That means, what this automatism does can be also achieved via explicit
configuration of the profile, which gives the user more flexibility to
adjust all parameters explicitly).
(*) but only since 1.20.0 NetworkManager supports the "suppress_prefixlength"
rule attribute, which makes it impossible to configure exactly this rule-based
solution with 1.18.0 NetworkManager.
[1] https://www.wireguard.com/netns/#improved-rule-based-routing
2019-04-30 17:48:46 +02:00
|
|
|
priv->auto_default_route_enabled_4 = enabled_v4;
|
|
|
|
|
priv->auto_default_route_enabled_6 = enabled_v6;
|
|
|
|
|
priv->auto_default_route_initialized = TRUE;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
wireguard: fix crash in _auto_default_route_init()
#3 0x00007fb0aa9e7d3d in g_return_if_fail_warning
(log_domain=log_domain@entry=0x562295fd5ee3 "libnm", pretty_function=pretty_function@entry=0x562295fd71d0 <__func__.35180> "_connection_get_setting_check", expression=expression@entry=0x562295f8edf7 "NM_IS_CONNECTION (connection)") at ../glib/gmessages.c:2767
#4 0x0000562295df151a in _connection_get_setting_check (connection=0x0, setting_type=0x562297b17050 [NMSettingWireGuard/NMSetting]) at libnm-core/nm-connection.c:207
#5 0x0000562295df151a in _connection_get_setting_check (connection=0x0, setting_type=0x562297b17050 [NMSettingWireGuard/NMSetting]) at libnm-core/nm-connection.c:205
#6 0x0000562295ef132a in _nm_connection_get_setting (type=<optimized out>, connection=0x0) at ./libnm-core/nm-core-internal.h:483
#7 0x0000562295ef132a in _auto_default_route_init (self=self@entry=0x562297bf82b0 [NMDeviceWireGuard]) at src/devices/nm-device-wireguard.c:443
#8 0x0000562295ef1b98 in coerce_route_table (device=0x562297bf82b0 [NMDeviceWireGuard], addr_family=2, route_table=0, is_user_config=<optimized out>)
at src/devices/nm-device-wireguard.c:565
#9 0x0000562295ea42ae in _get_route_table (self=self@entry=0x562297bf82b0 [NMDeviceWireGuard], addr_family=addr_family@entry=2) at src/devices/nm-device.c:2311
#10 0x0000562295ea4593 in nm_device_get_route_table (self=0x562297bf82b0 [NMDeviceWireGuard], addr_family=2) at src/devices/nm-device.c:2338
#11 0x0000562295eabde0 in ip_config_merge_and_apply (self=0x562297bf82b0 [NMDeviceWireGuard], addr_family=2, commit=1) at src/devices/nm-device.c:7590
#12 0x0000562295ed2f0b in device_link_changed (self=self@entry=0x562297bf82b0 [NMDeviceWireGuard]) at src/devices/nm-device.c:3939
#13 0x00007fb0aa9dc7db in g_idle_dispatch (source=source@entry=0x562297bf0b30, callback=0x562295ed2880 <device_link_changed>, user_data=0x562297bf82b0) at ../glib/gmain.c:5627
#14 0x00007fb0aa9dfedd in g_main_dispatch (context=0x562297a28090) at ../glib/gmain.c:3189
#15 0x00007fb0aa9dfedd in g_main_context_dispatch (context=context@entry=0x562297a28090) at ../glib/gmain.c:3854
#16 0x00007fb0aa9e0270 in g_main_context_iterate (context=0x562297a28090, block=block@entry=1, dispatch=dispatch@entry=1, self=<optimized out>) at ../glib/gmain.c:3927
#17 0x00007fb0aa9e05a3 in g_main_loop_run (loop=0x562297a0b380) at ../glib/gmain.c:4123
#18 0x0000562295d0b147 in main (argc=<optimized out>, argv=<optimized out>) at src/main.c:465
https://bugzilla.redhat.com/show_bug.cgi?id=1734383
(cherry picked from commit 47fc1a4293437a88adfd247734e32fa1b86ca7a9)
2019-07-30 18:38:48 +02:00
|
|
|
if (connection) {
|
|
|
|
|
_LOGT(LOGD_DEVICE,
|
|
|
|
|
"auto-default-route is %s for IPv4 and %s for IPv6%s",
|
|
|
|
|
priv->auto_default_route_enabled_4 ? "enabled" : "disabled",
|
|
|
|
|
priv->auto_default_route_enabled_6 ? "enabled" : "disabled",
|
|
|
|
|
priv->auto_default_route_enabled_4 || priv->auto_default_route_enabled_6
|
|
|
|
|
? nm_sprintf_buf(sbuf1, " (fwmark 0x%x)", priv->auto_default_route_fwmark)
|
|
|
|
|
: "");
|
wireguard: support configuring policy routing to avoid routing loops
For WireGuard (like for all IP-tunnels and IP-based VPNs), the IP addresses of
the peers must be reached outside the tunnel/VPN itself.
For VPN connections, NetworkManager usually adds a direct /32 route to
the external VPN gateway to the underlying device. For WireGuard that is
not done, because injecting a route to another device is ugly and error
prone. Worse: WireGuard with automatic roaming and multiple peers makes this
more complicated.
This is commonly a problem when setting the default-route via the VPN,
but there are also other subtle setups where special care must be taken
to prevent such routing loops.
WireGuard's wg-quick provides a simple, automatic solution by adding two policy
routing rules and relying on the WireGuard packets having a fwmark set (see [1]).
Let's also do that. Add new properties "wireguard.ip4-auto-default-route"
and "wireguard.ip6-auto-default-route" to enable/disable this. Note that
the default value lets NetworkManager automatically choose whether to
enable it (depending on whether there are any peers that have a default
route). This means, common scenarios should now work well without additional
configuration.
Note that this is also a change in behavior and upon package upgrade
NetworkManager may start adding policy routes (if there are peers that
have a default-route). This is a change in behavior, as the user already
clearly had this setup working and configured some working solution
already.
The new automatism picks the rule priority automatically and adds the
default-route to the routing table that has the same number as the fwmark.
If any of this is unsuitable, then the user is free to disable this
automatism. Note that since 1.18.0 NetworkManager supports policy routing (*).
That means, what this automatism does can be also achieved via explicit
configuration of the profile, which gives the user more flexibility to
adjust all parameters explicitly).
(*) but only since 1.20.0 NetworkManager supports the "suppress_prefixlength"
rule attribute, which makes it impossible to configure exactly this rule-based
solution with 1.18.0 NetworkManager.
[1] https://www.wireguard.com/netns/#improved-rule-based-routing
2019-04-30 17:48:46 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static GPtrArray *
|
|
|
|
|
get_extra_rules(NMDevice *device)
|
|
|
|
|
{
|
2021-11-09 13:28:54 +01:00
|
|
|
NMDeviceWireGuard *self = NM_DEVICE_WIREGUARD(device);
|
|
|
|
|
NMDeviceWireGuardPrivate *priv = NM_DEVICE_WIREGUARD_GET_PRIVATE(self);
|
wireguard: support configuring policy routing to avoid routing loops
For WireGuard (like for all IP-tunnels and IP-based VPNs), the IP addresses of
the peers must be reached outside the tunnel/VPN itself.
For VPN connections, NetworkManager usually adds a direct /32 route to
the external VPN gateway to the underlying device. For WireGuard that is
not done, because injecting a route to another device is ugly and error
prone. Worse: WireGuard with automatic roaming and multiple peers makes this
more complicated.
This is commonly a problem when setting the default-route via the VPN,
but there are also other subtle setups where special care must be taken
to prevent such routing loops.
WireGuard's wg-quick provides a simple, automatic solution by adding two policy
routing rules and relying on the WireGuard packets having a fwmark set (see [1]).
Let's also do that. Add new properties "wireguard.ip4-auto-default-route"
and "wireguard.ip6-auto-default-route" to enable/disable this. Note that
the default value lets NetworkManager automatically choose whether to
enable it (depending on whether there are any peers that have a default
route). This means, common scenarios should now work well without additional
configuration.
Note that this is also a change in behavior and upon package upgrade
NetworkManager may start adding policy routes (if there are peers that
have a default-route). This is a change in behavior, as the user already
clearly had this setup working and configured some working solution
already.
The new automatism picks the rule priority automatically and adds the
default-route to the routing table that has the same number as the fwmark.
If any of this is unsuitable, then the user is free to disable this
automatism. Note that since 1.18.0 NetworkManager supports policy routing (*).
That means, what this automatism does can be also achieved via explicit
configuration of the profile, which gives the user more flexibility to
adjust all parameters explicitly).
(*) but only since 1.20.0 NetworkManager supports the "suppress_prefixlength"
rule attribute, which makes it impossible to configure exactly this rule-based
solution with 1.18.0 NetworkManager.
[1] https://www.wireguard.com/netns/#improved-rule-based-routing
2019-04-30 17:48:46 +02:00
|
|
|
gs_unref_ptrarray GPtrArray *extra_rules = NULL;
|
|
|
|
|
guint32 priority = 0;
|
|
|
|
|
int is_ipv4;
|
2021-11-09 13:28:54 +01:00
|
|
|
NMConnection *connection;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
wireguard: support configuring policy routing to avoid routing loops
For WireGuard (like for all IP-tunnels and IP-based VPNs), the IP addresses of
the peers must be reached outside the tunnel/VPN itself.
For VPN connections, NetworkManager usually adds a direct /32 route to
the external VPN gateway to the underlying device. For WireGuard that is
not done, because injecting a route to another device is ugly and error
prone. Worse: WireGuard with automatic roaming and multiple peers makes this
more complicated.
This is commonly a problem when setting the default-route via the VPN,
but there are also other subtle setups where special care must be taken
to prevent such routing loops.
WireGuard's wg-quick provides a simple, automatic solution by adding two policy
routing rules and relying on the WireGuard packets having a fwmark set (see [1]).
Let's also do that. Add new properties "wireguard.ip4-auto-default-route"
and "wireguard.ip6-auto-default-route" to enable/disable this. Note that
the default value lets NetworkManager automatically choose whether to
enable it (depending on whether there are any peers that have a default
route). This means, common scenarios should now work well without additional
configuration.
Note that this is also a change in behavior and upon package upgrade
NetworkManager may start adding policy routes (if there are peers that
have a default-route). This is a change in behavior, as the user already
clearly had this setup working and configured some working solution
already.
The new automatism picks the rule priority automatically and adds the
default-route to the routing table that has the same number as the fwmark.
If any of this is unsuitable, then the user is free to disable this
automatism. Note that since 1.18.0 NetworkManager supports policy routing (*).
That means, what this automatism does can be also achieved via explicit
configuration of the profile, which gives the user more flexibility to
adjust all parameters explicitly).
(*) but only since 1.20.0 NetworkManager supports the "suppress_prefixlength"
rule attribute, which makes it impossible to configure exactly this rule-based
solution with 1.18.0 NetworkManager.
[1] https://www.wireguard.com/netns/#improved-rule-based-routing
2019-04-30 17:48:46 +02:00
|
|
|
_auto_default_route_init(self);
|
2020-09-28 16:03:33 +02:00
|
|
|
|
wireguard: support configuring policy routing to avoid routing loops
For WireGuard (like for all IP-tunnels and IP-based VPNs), the IP addresses of
the peers must be reached outside the tunnel/VPN itself.
For VPN connections, NetworkManager usually adds a direct /32 route to
the external VPN gateway to the underlying device. For WireGuard that is
not done, because injecting a route to another device is ugly and error
prone. Worse: WireGuard with automatic roaming and multiple peers makes this
more complicated.
This is commonly a problem when setting the default-route via the VPN,
but there are also other subtle setups where special care must be taken
to prevent such routing loops.
WireGuard's wg-quick provides a simple, automatic solution by adding two policy
routing rules and relying on the WireGuard packets having a fwmark set (see [1]).
Let's also do that. Add new properties "wireguard.ip4-auto-default-route"
and "wireguard.ip6-auto-default-route" to enable/disable this. Note that
the default value lets NetworkManager automatically choose whether to
enable it (depending on whether there are any peers that have a default
route). This means, common scenarios should now work well without additional
configuration.
Note that this is also a change in behavior and upon package upgrade
NetworkManager may start adding policy routes (if there are peers that
have a default-route). This is a change in behavior, as the user already
clearly had this setup working and configured some working solution
already.
The new automatism picks the rule priority automatically and adds the
default-route to the routing table that has the same number as the fwmark.
If any of this is unsuitable, then the user is free to disable this
automatism. Note that since 1.18.0 NetworkManager supports policy routing (*).
That means, what this automatism does can be also achieved via explicit
configuration of the profile, which gives the user more flexibility to
adjust all parameters explicitly).
(*) but only since 1.20.0 NetworkManager supports the "suppress_prefixlength"
rule attribute, which makes it impossible to configure exactly this rule-based
solution with 1.18.0 NetworkManager.
[1] https://www.wireguard.com/netns/#improved-rule-based-routing
2019-04-30 17:48:46 +02:00
|
|
|
connection = nm_device_get_applied_connection(device);
|
|
|
|
|
if (!connection)
|
|
|
|
|
return NULL;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
wireguard: support configuring policy routing to avoid routing loops
For WireGuard (like for all IP-tunnels and IP-based VPNs), the IP addresses of
the peers must be reached outside the tunnel/VPN itself.
For VPN connections, NetworkManager usually adds a direct /32 route to
the external VPN gateway to the underlying device. For WireGuard that is
not done, because injecting a route to another device is ugly and error
prone. Worse: WireGuard with automatic roaming and multiple peers makes this
more complicated.
This is commonly a problem when setting the default-route via the VPN,
but there are also other subtle setups where special care must be taken
to prevent such routing loops.
WireGuard's wg-quick provides a simple, automatic solution by adding two policy
routing rules and relying on the WireGuard packets having a fwmark set (see [1]).
Let's also do that. Add new properties "wireguard.ip4-auto-default-route"
and "wireguard.ip6-auto-default-route" to enable/disable this. Note that
the default value lets NetworkManager automatically choose whether to
enable it (depending on whether there are any peers that have a default
route). This means, common scenarios should now work well without additional
configuration.
Note that this is also a change in behavior and upon package upgrade
NetworkManager may start adding policy routes (if there are peers that
have a default-route). This is a change in behavior, as the user already
clearly had this setup working and configured some working solution
already.
The new automatism picks the rule priority automatically and adds the
default-route to the routing table that has the same number as the fwmark.
If any of this is unsuitable, then the user is free to disable this
automatism. Note that since 1.18.0 NetworkManager supports policy routing (*).
That means, what this automatism does can be also achieved via explicit
configuration of the profile, which gives the user more flexibility to
adjust all parameters explicitly).
(*) but only since 1.20.0 NetworkManager supports the "suppress_prefixlength"
rule attribute, which makes it impossible to configure exactly this rule-based
solution with 1.18.0 NetworkManager.
[1] https://www.wireguard.com/netns/#improved-rule-based-routing
2019-04-30 17:48:46 +02:00
|
|
|
for (is_ipv4 = 0; is_ipv4 < 2; is_ipv4++) {
|
|
|
|
|
NMSettingIPConfig *s_ip;
|
|
|
|
|
int addr_family = is_ipv4 ? AF_INET : AF_INET6;
|
|
|
|
|
guint32 table_main;
|
|
|
|
|
guint32 fwmark;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
wireguard: support configuring policy routing to avoid routing loops
For WireGuard (like for all IP-tunnels and IP-based VPNs), the IP addresses of
the peers must be reached outside the tunnel/VPN itself.
For VPN connections, NetworkManager usually adds a direct /32 route to
the external VPN gateway to the underlying device. For WireGuard that is
not done, because injecting a route to another device is ugly and error
prone. Worse: WireGuard with automatic roaming and multiple peers makes this
more complicated.
This is commonly a problem when setting the default-route via the VPN,
but there are also other subtle setups where special care must be taken
to prevent such routing loops.
WireGuard's wg-quick provides a simple, automatic solution by adding two policy
routing rules and relying on the WireGuard packets having a fwmark set (see [1]).
Let's also do that. Add new properties "wireguard.ip4-auto-default-route"
and "wireguard.ip6-auto-default-route" to enable/disable this. Note that
the default value lets NetworkManager automatically choose whether to
enable it (depending on whether there are any peers that have a default
route). This means, common scenarios should now work well without additional
configuration.
Note that this is also a change in behavior and upon package upgrade
NetworkManager may start adding policy routes (if there are peers that
have a default-route). This is a change in behavior, as the user already
clearly had this setup working and configured some working solution
already.
The new automatism picks the rule priority automatically and adds the
default-route to the routing table that has the same number as the fwmark.
If any of this is unsuitable, then the user is free to disable this
automatism. Note that since 1.18.0 NetworkManager supports policy routing (*).
That means, what this automatism does can be also achieved via explicit
configuration of the profile, which gives the user more flexibility to
adjust all parameters explicitly).
(*) but only since 1.20.0 NetworkManager supports the "suppress_prefixlength"
rule attribute, which makes it impossible to configure exactly this rule-based
solution with 1.18.0 NetworkManager.
[1] https://www.wireguard.com/netns/#improved-rule-based-routing
2019-04-30 17:48:46 +02:00
|
|
|
if (is_ipv4) {
|
|
|
|
|
if (!priv->auto_default_route_enabled_4)
|
|
|
|
|
continue;
|
|
|
|
|
} else {
|
|
|
|
|
if (!priv->auto_default_route_enabled_6)
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2020-09-28 16:03:33 +02:00
|
|
|
|
wireguard: support configuring policy routing to avoid routing loops
For WireGuard (like for all IP-tunnels and IP-based VPNs), the IP addresses of
the peers must be reached outside the tunnel/VPN itself.
For VPN connections, NetworkManager usually adds a direct /32 route to
the external VPN gateway to the underlying device. For WireGuard that is
not done, because injecting a route to another device is ugly and error
prone. Worse: WireGuard with automatic roaming and multiple peers makes this
more complicated.
This is commonly a problem when setting the default-route via the VPN,
but there are also other subtle setups where special care must be taken
to prevent such routing loops.
WireGuard's wg-quick provides a simple, automatic solution by adding two policy
routing rules and relying on the WireGuard packets having a fwmark set (see [1]).
Let's also do that. Add new properties "wireguard.ip4-auto-default-route"
and "wireguard.ip6-auto-default-route" to enable/disable this. Note that
the default value lets NetworkManager automatically choose whether to
enable it (depending on whether there are any peers that have a default
route). This means, common scenarios should now work well without additional
configuration.
Note that this is also a change in behavior and upon package upgrade
NetworkManager may start adding policy routes (if there are peers that
have a default-route). This is a change in behavior, as the user already
clearly had this setup working and configured some working solution
already.
The new automatism picks the rule priority automatically and adds the
default-route to the routing table that has the same number as the fwmark.
If any of this is unsuitable, then the user is free to disable this
automatism. Note that since 1.18.0 NetworkManager supports policy routing (*).
That means, what this automatism does can be also achieved via explicit
configuration of the profile, which gives the user more flexibility to
adjust all parameters explicitly).
(*) but only since 1.20.0 NetworkManager supports the "suppress_prefixlength"
rule attribute, which makes it impossible to configure exactly this rule-based
solution with 1.18.0 NetworkManager.
[1] https://www.wireguard.com/netns/#improved-rule-based-routing
2019-04-30 17:48:46 +02:00
|
|
|
if (!extra_rules) {
|
|
|
|
|
if (priv->auto_default_route_priority_initialized)
|
|
|
|
|
priority = priv->auto_default_route_priority;
|
|
|
|
|
else {
|
wireguard: use fixed fwmark/rule-priority for auto-default-route
With "wireguard.ip4-auto-default-route" and "wireguard.ip6-auto-default-route",
NetworkManager automatically adds policy routing rules for the default
route.
For that, it needs to pick (up to) two numbers:
- the fwmark. This is used both for WireGuard's fwmark setting and
is also the number of the routing table where the default-route is
added.
- the rule priority. NetworkManager adds two policy routing rules, and
we need to place them somewhere before the default rules (at 32766).
Previously, we looked at exiting platform configuration and picked
numbers that were not yet used. However, during restart of
NetworkManager, we leave the interface up and after restart we will
take over the previous configuration. At that point, we need to choose
the same fwmark/priority, otherwise the configuration changes.
But since we picked numbers that were not yet used, we would always choose
different numbers. For routing rules that meant that after restart a second
pair of rules was added.
We possibly could store this data in the device's state-file. But that
is complex. Instead, just pick numbers deterministically based on the
connection's UUID.
If the picked numbers are not suitable, then the user can still work
around that:
- for fwmark, the user can explicitly configure wireguard.fwmark
setting.
- for the policy routes, the user can explicitly add the rules with
the desired priorirites (arguably, currently the default-route cannot
be added like a regular route, so the table cannot be set. Possibly
the user would have to add two /1 routes instead with
suppress_prefixlength=1).
(cherry picked from commit cfb497e49936d36867ae3175537c7d4f77259655)
2019-07-31 10:01:04 +02:00
|
|
|
priority =
|
|
|
|
|
_auto_default_route_get_auto_priority(nm_connection_get_uuid(connection));
|
wireguard: support configuring policy routing to avoid routing loops
For WireGuard (like for all IP-tunnels and IP-based VPNs), the IP addresses of
the peers must be reached outside the tunnel/VPN itself.
For VPN connections, NetworkManager usually adds a direct /32 route to
the external VPN gateway to the underlying device. For WireGuard that is
not done, because injecting a route to another device is ugly and error
prone. Worse: WireGuard with automatic roaming and multiple peers makes this
more complicated.
This is commonly a problem when setting the default-route via the VPN,
but there are also other subtle setups where special care must be taken
to prevent such routing loops.
WireGuard's wg-quick provides a simple, automatic solution by adding two policy
routing rules and relying on the WireGuard packets having a fwmark set (see [1]).
Let's also do that. Add new properties "wireguard.ip4-auto-default-route"
and "wireguard.ip6-auto-default-route" to enable/disable this. Note that
the default value lets NetworkManager automatically choose whether to
enable it (depending on whether there are any peers that have a default
route). This means, common scenarios should now work well without additional
configuration.
Note that this is also a change in behavior and upon package upgrade
NetworkManager may start adding policy routes (if there are peers that
have a default-route). This is a change in behavior, as the user already
clearly had this setup working and configured some working solution
already.
The new automatism picks the rule priority automatically and adds the
default-route to the routing table that has the same number as the fwmark.
If any of this is unsuitable, then the user is free to disable this
automatism. Note that since 1.18.0 NetworkManager supports policy routing (*).
That means, what this automatism does can be also achieved via explicit
configuration of the profile, which gives the user more flexibility to
adjust all parameters explicitly).
(*) but only since 1.20.0 NetworkManager supports the "suppress_prefixlength"
rule attribute, which makes it impossible to configure exactly this rule-based
solution with 1.18.0 NetworkManager.
[1] https://www.wireguard.com/netns/#improved-rule-based-routing
2019-04-30 17:48:46 +02:00
|
|
|
priv->auto_default_route_priority = priority;
|
|
|
|
|
priv->auto_default_route_priority_initialized = TRUE;
|
|
|
|
|
}
|
|
|
|
|
extra_rules = g_ptr_array_new_with_free_func((GDestroyNotify) nmp_object_unref);
|
|
|
|
|
}
|
2020-09-28 16:03:33 +02:00
|
|
|
|
wireguard: support configuring policy routing to avoid routing loops
For WireGuard (like for all IP-tunnels and IP-based VPNs), the IP addresses of
the peers must be reached outside the tunnel/VPN itself.
For VPN connections, NetworkManager usually adds a direct /32 route to
the external VPN gateway to the underlying device. For WireGuard that is
not done, because injecting a route to another device is ugly and error
prone. Worse: WireGuard with automatic roaming and multiple peers makes this
more complicated.
This is commonly a problem when setting the default-route via the VPN,
but there are also other subtle setups where special care must be taken
to prevent such routing loops.
WireGuard's wg-quick provides a simple, automatic solution by adding two policy
routing rules and relying on the WireGuard packets having a fwmark set (see [1]).
Let's also do that. Add new properties "wireguard.ip4-auto-default-route"
and "wireguard.ip6-auto-default-route" to enable/disable this. Note that
the default value lets NetworkManager automatically choose whether to
enable it (depending on whether there are any peers that have a default
route). This means, common scenarios should now work well without additional
configuration.
Note that this is also a change in behavior and upon package upgrade
NetworkManager may start adding policy routes (if there are peers that
have a default-route). This is a change in behavior, as the user already
clearly had this setup working and configured some working solution
already.
The new automatism picks the rule priority automatically and adds the
default-route to the routing table that has the same number as the fwmark.
If any of this is unsuitable, then the user is free to disable this
automatism. Note that since 1.18.0 NetworkManager supports policy routing (*).
That means, what this automatism does can be also achieved via explicit
configuration of the profile, which gives the user more flexibility to
adjust all parameters explicitly).
(*) but only since 1.20.0 NetworkManager supports the "suppress_prefixlength"
rule attribute, which makes it impossible to configure exactly this rule-based
solution with 1.18.0 NetworkManager.
[1] https://www.wireguard.com/netns/#improved-rule-based-routing
2019-04-30 17:48:46 +02:00
|
|
|
s_ip = nm_connection_get_setting_ip_config(connection, addr_family);
|
|
|
|
|
table_main = nm_setting_ip_config_get_route_table(s_ip);
|
|
|
|
|
if (table_main == 0)
|
|
|
|
|
table_main = RT_TABLE_MAIN;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
wireguard: support configuring policy routing to avoid routing loops
For WireGuard (like for all IP-tunnels and IP-based VPNs), the IP addresses of
the peers must be reached outside the tunnel/VPN itself.
For VPN connections, NetworkManager usually adds a direct /32 route to
the external VPN gateway to the underlying device. For WireGuard that is
not done, because injecting a route to another device is ugly and error
prone. Worse: WireGuard with automatic roaming and multiple peers makes this
more complicated.
This is commonly a problem when setting the default-route via the VPN,
but there are also other subtle setups where special care must be taken
to prevent such routing loops.
WireGuard's wg-quick provides a simple, automatic solution by adding two policy
routing rules and relying on the WireGuard packets having a fwmark set (see [1]).
Let's also do that. Add new properties "wireguard.ip4-auto-default-route"
and "wireguard.ip6-auto-default-route" to enable/disable this. Note that
the default value lets NetworkManager automatically choose whether to
enable it (depending on whether there are any peers that have a default
route). This means, common scenarios should now work well without additional
configuration.
Note that this is also a change in behavior and upon package upgrade
NetworkManager may start adding policy routes (if there are peers that
have a default-route). This is a change in behavior, as the user already
clearly had this setup working and configured some working solution
already.
The new automatism picks the rule priority automatically and adds the
default-route to the routing table that has the same number as the fwmark.
If any of this is unsuitable, then the user is free to disable this
automatism. Note that since 1.18.0 NetworkManager supports policy routing (*).
That means, what this automatism does can be also achieved via explicit
configuration of the profile, which gives the user more flexibility to
adjust all parameters explicitly).
(*) but only since 1.20.0 NetworkManager supports the "suppress_prefixlength"
rule attribute, which makes it impossible to configure exactly this rule-based
solution with 1.18.0 NetworkManager.
[1] https://www.wireguard.com/netns/#improved-rule-based-routing
2019-04-30 17:48:46 +02:00
|
|
|
fwmark = priv->auto_default_route_fwmark;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
wireguard: support configuring policy routing to avoid routing loops
For WireGuard (like for all IP-tunnels and IP-based VPNs), the IP addresses of
the peers must be reached outside the tunnel/VPN itself.
For VPN connections, NetworkManager usually adds a direct /32 route to
the external VPN gateway to the underlying device. For WireGuard that is
not done, because injecting a route to another device is ugly and error
prone. Worse: WireGuard with automatic roaming and multiple peers makes this
more complicated.
This is commonly a problem when setting the default-route via the VPN,
but there are also other subtle setups where special care must be taken
to prevent such routing loops.
WireGuard's wg-quick provides a simple, automatic solution by adding two policy
routing rules and relying on the WireGuard packets having a fwmark set (see [1]).
Let's also do that. Add new properties "wireguard.ip4-auto-default-route"
and "wireguard.ip6-auto-default-route" to enable/disable this. Note that
the default value lets NetworkManager automatically choose whether to
enable it (depending on whether there are any peers that have a default
route). This means, common scenarios should now work well without additional
configuration.
Note that this is also a change in behavior and upon package upgrade
NetworkManager may start adding policy routes (if there are peers that
have a default-route). This is a change in behavior, as the user already
clearly had this setup working and configured some working solution
already.
The new automatism picks the rule priority automatically and adds the
default-route to the routing table that has the same number as the fwmark.
If any of this is unsuitable, then the user is free to disable this
automatism. Note that since 1.18.0 NetworkManager supports policy routing (*).
That means, what this automatism does can be also achieved via explicit
configuration of the profile, which gives the user more flexibility to
adjust all parameters explicitly).
(*) but only since 1.20.0 NetworkManager supports the "suppress_prefixlength"
rule attribute, which makes it impossible to configure exactly this rule-based
solution with 1.18.0 NetworkManager.
[1] https://www.wireguard.com/netns/#improved-rule-based-routing
2019-04-30 17:48:46 +02:00
|
|
|
G_STATIC_ASSERT_EXPR(PRIO_WIDTH == 2);
|
2020-09-28 16:03:33 +02:00
|
|
|
|
wireguard: support configuring policy routing to avoid routing loops
For WireGuard (like for all IP-tunnels and IP-based VPNs), the IP addresses of
the peers must be reached outside the tunnel/VPN itself.
For VPN connections, NetworkManager usually adds a direct /32 route to
the external VPN gateway to the underlying device. For WireGuard that is
not done, because injecting a route to another device is ugly and error
prone. Worse: WireGuard with automatic roaming and multiple peers makes this
more complicated.
This is commonly a problem when setting the default-route via the VPN,
but there are also other subtle setups where special care must be taken
to prevent such routing loops.
WireGuard's wg-quick provides a simple, automatic solution by adding two policy
routing rules and relying on the WireGuard packets having a fwmark set (see [1]).
Let's also do that. Add new properties "wireguard.ip4-auto-default-route"
and "wireguard.ip6-auto-default-route" to enable/disable this. Note that
the default value lets NetworkManager automatically choose whether to
enable it (depending on whether there are any peers that have a default
route). This means, common scenarios should now work well without additional
configuration.
Note that this is also a change in behavior and upon package upgrade
NetworkManager may start adding policy routes (if there are peers that
have a default-route). This is a change in behavior, as the user already
clearly had this setup working and configured some working solution
already.
The new automatism picks the rule priority automatically and adds the
default-route to the routing table that has the same number as the fwmark.
If any of this is unsuitable, then the user is free to disable this
automatism. Note that since 1.18.0 NetworkManager supports policy routing (*).
That means, what this automatism does can be also achieved via explicit
configuration of the profile, which gives the user more flexibility to
adjust all parameters explicitly).
(*) but only since 1.20.0 NetworkManager supports the "suppress_prefixlength"
rule attribute, which makes it impossible to configure exactly this rule-based
solution with 1.18.0 NetworkManager.
[1] https://www.wireguard.com/netns/#improved-rule-based-routing
2019-04-30 17:48:46 +02:00
|
|
|
g_ptr_array_add(extra_rules,
|
|
|
|
|
nmp_object_new(NMP_OBJECT_TYPE_ROUTING_RULE,
|
|
|
|
|
&((const NMPlatformRoutingRule){
|
|
|
|
|
.priority = priority,
|
|
|
|
|
.addr_family = addr_family,
|
|
|
|
|
.action = FR_ACT_TO_TBL,
|
|
|
|
|
.table = table_main,
|
|
|
|
|
.suppress_prefixlen_inverse = ~((guint32) 0u),
|
|
|
|
|
})));
|
2020-09-28 16:03:33 +02:00
|
|
|
|
wireguard: support configuring policy routing to avoid routing loops
For WireGuard (like for all IP-tunnels and IP-based VPNs), the IP addresses of
the peers must be reached outside the tunnel/VPN itself.
For VPN connections, NetworkManager usually adds a direct /32 route to
the external VPN gateway to the underlying device. For WireGuard that is
not done, because injecting a route to another device is ugly and error
prone. Worse: WireGuard with automatic roaming and multiple peers makes this
more complicated.
This is commonly a problem when setting the default-route via the VPN,
but there are also other subtle setups where special care must be taken
to prevent such routing loops.
WireGuard's wg-quick provides a simple, automatic solution by adding two policy
routing rules and relying on the WireGuard packets having a fwmark set (see [1]).
Let's also do that. Add new properties "wireguard.ip4-auto-default-route"
and "wireguard.ip6-auto-default-route" to enable/disable this. Note that
the default value lets NetworkManager automatically choose whether to
enable it (depending on whether there are any peers that have a default
route). This means, common scenarios should now work well without additional
configuration.
Note that this is also a change in behavior and upon package upgrade
NetworkManager may start adding policy routes (if there are peers that
have a default-route). This is a change in behavior, as the user already
clearly had this setup working and configured some working solution
already.
The new automatism picks the rule priority automatically and adds the
default-route to the routing table that has the same number as the fwmark.
If any of this is unsuitable, then the user is free to disable this
automatism. Note that since 1.18.0 NetworkManager supports policy routing (*).
That means, what this automatism does can be also achieved via explicit
configuration of the profile, which gives the user more flexibility to
adjust all parameters explicitly).
(*) but only since 1.20.0 NetworkManager supports the "suppress_prefixlength"
rule attribute, which makes it impossible to configure exactly this rule-based
solution with 1.18.0 NetworkManager.
[1] https://www.wireguard.com/netns/#improved-rule-based-routing
2019-04-30 17:48:46 +02:00
|
|
|
g_ptr_array_add(extra_rules,
|
|
|
|
|
nmp_object_new(NMP_OBJECT_TYPE_ROUTING_RULE,
|
|
|
|
|
&((const NMPlatformRoutingRule){
|
|
|
|
|
.priority = priority + 1u,
|
|
|
|
|
.addr_family = addr_family,
|
|
|
|
|
.action = FR_ACT_TO_TBL,
|
|
|
|
|
.table = fwmark,
|
|
|
|
|
.flags = FIB_RULE_INVERT,
|
|
|
|
|
.fwmark = fwmark,
|
|
|
|
|
.fwmask = 0xFFFFFFFFu,
|
|
|
|
|
})));
|
|
|
|
|
}
|
2020-09-28 16:03:33 +02:00
|
|
|
|
wireguard: support configuring policy routing to avoid routing loops
For WireGuard (like for all IP-tunnels and IP-based VPNs), the IP addresses of
the peers must be reached outside the tunnel/VPN itself.
For VPN connections, NetworkManager usually adds a direct /32 route to
the external VPN gateway to the underlying device. For WireGuard that is
not done, because injecting a route to another device is ugly and error
prone. Worse: WireGuard with automatic roaming and multiple peers makes this
more complicated.
This is commonly a problem when setting the default-route via the VPN,
but there are also other subtle setups where special care must be taken
to prevent such routing loops.
WireGuard's wg-quick provides a simple, automatic solution by adding two policy
routing rules and relying on the WireGuard packets having a fwmark set (see [1]).
Let's also do that. Add new properties "wireguard.ip4-auto-default-route"
and "wireguard.ip6-auto-default-route" to enable/disable this. Note that
the default value lets NetworkManager automatically choose whether to
enable it (depending on whether there are any peers that have a default
route). This means, common scenarios should now work well without additional
configuration.
Note that this is also a change in behavior and upon package upgrade
NetworkManager may start adding policy routes (if there are peers that
have a default-route). This is a change in behavior, as the user already
clearly had this setup working and configured some working solution
already.
The new automatism picks the rule priority automatically and adds the
default-route to the routing table that has the same number as the fwmark.
If any of this is unsuitable, then the user is free to disable this
automatism. Note that since 1.18.0 NetworkManager supports policy routing (*).
That means, what this automatism does can be also achieved via explicit
configuration of the profile, which gives the user more flexibility to
adjust all parameters explicitly).
(*) but only since 1.20.0 NetworkManager supports the "suppress_prefixlength"
rule attribute, which makes it impossible to configure exactly this rule-based
solution with 1.18.0 NetworkManager.
[1] https://www.wireguard.com/netns/#improved-rule-based-routing
2019-04-30 17:48:46 +02:00
|
|
|
return g_steal_pointer(&extra_rules);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static guint32
|
|
|
|
|
coerce_route_table(NMDevice *device, int addr_family, guint32 route_table, gboolean is_user_config)
|
|
|
|
|
{
|
2021-11-09 13:28:54 +01:00
|
|
|
NMDeviceWireGuard *self = NM_DEVICE_WIREGUARD(device);
|
wireguard: support configuring policy routing to avoid routing loops
For WireGuard (like for all IP-tunnels and IP-based VPNs), the IP addresses of
the peers must be reached outside the tunnel/VPN itself.
For VPN connections, NetworkManager usually adds a direct /32 route to
the external VPN gateway to the underlying device. For WireGuard that is
not done, because injecting a route to another device is ugly and error
prone. Worse: WireGuard with automatic roaming and multiple peers makes this
more complicated.
This is commonly a problem when setting the default-route via the VPN,
but there are also other subtle setups where special care must be taken
to prevent such routing loops.
WireGuard's wg-quick provides a simple, automatic solution by adding two policy
routing rules and relying on the WireGuard packets having a fwmark set (see [1]).
Let's also do that. Add new properties "wireguard.ip4-auto-default-route"
and "wireguard.ip6-auto-default-route" to enable/disable this. Note that
the default value lets NetworkManager automatically choose whether to
enable it (depending on whether there are any peers that have a default
route). This means, common scenarios should now work well without additional
configuration.
Note that this is also a change in behavior and upon package upgrade
NetworkManager may start adding policy routes (if there are peers that
have a default-route). This is a change in behavior, as the user already
clearly had this setup working and configured some working solution
already.
The new automatism picks the rule priority automatically and adds the
default-route to the routing table that has the same number as the fwmark.
If any of this is unsuitable, then the user is free to disable this
automatism. Note that since 1.18.0 NetworkManager supports policy routing (*).
That means, what this automatism does can be also achieved via explicit
configuration of the profile, which gives the user more flexibility to
adjust all parameters explicitly).
(*) but only since 1.20.0 NetworkManager supports the "suppress_prefixlength"
rule attribute, which makes it impossible to configure exactly this rule-based
solution with 1.18.0 NetworkManager.
[1] https://www.wireguard.com/netns/#improved-rule-based-routing
2019-04-30 17:48:46 +02:00
|
|
|
NMDeviceWireGuardPrivate *priv = NM_DEVICE_WIREGUARD_GET_PRIVATE(self);
|
|
|
|
|
gboolean auto_default_route_enabled;
|
|
|
|
|
|
|
|
|
|
if (route_table != 0u)
|
|
|
|
|
return route_table;
|
|
|
|
|
|
|
|
|
|
_auto_default_route_init(self);
|
|
|
|
|
|
|
|
|
|
auto_default_route_enabled = (addr_family == AF_INET) ? priv->auto_default_route_enabled_4
|
|
|
|
|
: priv->auto_default_route_enabled_6;
|
|
|
|
|
|
|
|
|
|
if (auto_default_route_enabled) {
|
|
|
|
|
/* we need to enable full-sync mode of all routing tables. */
|
|
|
|
|
_LOGT(LOGD_DEVICE,
|
|
|
|
|
"coerce ipv%c.route-table setting to \"main\" (table 254) as we enable "
|
|
|
|
|
"auto-default-route handling",
|
|
|
|
|
nm_utils_addr_family_to_char(addr_family));
|
|
|
|
|
return RT_TABLE_MAIN;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2019-01-12 12:33:35 +01:00
|
|
|
static gboolean
|
|
|
|
|
_peer_data_equal(gconstpointer ptr_a, gconstpointer ptr_b)
|
|
|
|
|
{
|
|
|
|
|
const PeerData *peer_data_a = ptr_a;
|
|
|
|
|
const PeerData *peer_data_b = ptr_b;
|
|
|
|
|
|
|
|
|
|
return nm_streq(nm_wireguard_peer_get_public_key(peer_data_a->peer),
|
|
|
|
|
nm_wireguard_peer_get_public_key(peer_data_b->peer));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static guint
|
|
|
|
|
_peer_data_hash(gconstpointer ptr)
|
|
|
|
|
{
|
|
|
|
|
const PeerData *peer_data = ptr;
|
|
|
|
|
|
|
|
|
|
return nm_hash_str(nm_wireguard_peer_get_public_key(peer_data->peer));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static PeerData *
|
|
|
|
|
_peers_find(NMDeviceWireGuardPrivate *priv, NMWireGuardPeer *peer)
|
|
|
|
|
{
|
|
|
|
|
nm_assert(peer);
|
|
|
|
|
|
|
|
|
|
G_STATIC_ASSERT_EXPR(G_STRUCT_OFFSET(PeerData, peer) == 0);
|
|
|
|
|
|
|
|
|
|
return g_hash_table_lookup(priv->peers, &peer);
|
|
|
|
|
}
|
|
|
|
|
|
wireguard: delay activation while resolving DNS names for WireGuard peers to avoid race
The endpoints of WireGuard peers can be configured as DNS name, which
NetworkManager will resolve.
Since activating a profile might affect now names get resolved, we must
first resolve names before completing the activation of the WireGuard
device (and before reconfiguring DNS accordingly).
For example, if you configure exclusive DNS resolution via the WireGuard
device, and if the peer needs to be resolved via DNS, then resolving the
peer name must happen before the reconfiguration of DNS. Otherwise the
new DNS configuration will be broken due to being unable to reach the
WireGuard peer.
Fix that by waiting.
There is still an unfixed problem. If resolving any peers fails,
activation silently proceeds -- again possibly breaking the network
setup. Of course, NetworkManager will repeatedly try to re-resolve
the name, but that may never succeed if DNS would be resolved via
the VPN itself.
That is different from `wg set` which resolves hostnames and fails.
Consequently `wg-quick up` would also fail. But these are both one shot
applications, they are not around and basically let the user handle the
error (by reading the log and invoking the command again). NetworkManager
can do something different and proceed activation (as it will also
periodically re-resolve the hostnames again). Note that it's also valid
to activate a WireGuard device without any peers (and to modify the
activated device later with Reapply()). As such, having no peers (or
being unable to resolve a hostname) may be a valid configuration.
I think we should add an option/flag that when enabled will cause
the activation to fail of names cannot be resolved.
https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/issues/535
https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/issues/616
https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/721
2021-01-08 11:09:03 +01:00
|
|
|
static guint
|
|
|
|
|
_peers_resolving_cnt(NMDeviceWireGuardPrivate *priv)
|
|
|
|
|
{
|
|
|
|
|
nm_assert(priv);
|
|
|
|
|
#if NM_MORE_ASSERTS > 3
|
|
|
|
|
{
|
|
|
|
|
PeerData *peer_data;
|
|
|
|
|
guint cnt = 0;
|
|
|
|
|
|
|
|
|
|
c_list_for_each_entry (peer_data, &priv->lst_peers_head, lst_peers) {
|
|
|
|
|
if (peer_data->ep_resolv.cancellable)
|
|
|
|
|
cnt++;
|
|
|
|
|
}
|
|
|
|
|
nm_assert(cnt == priv->peers_resolving_cnt);
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
return priv->peers_resolving_cnt;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
_peers_resolving_cnt_decrement(NMDeviceWireGuard *self)
|
|
|
|
|
{
|
|
|
|
|
NMDeviceWireGuardPrivate *priv = NM_DEVICE_WIREGUARD_GET_PRIVATE(self);
|
|
|
|
|
|
|
|
|
|
nm_assert(priv);
|
|
|
|
|
nm_assert(priv->peers_resolving_cnt > 0);
|
|
|
|
|
|
|
|
|
|
priv->peers_resolving_cnt--;
|
|
|
|
|
|
|
|
|
|
nm_assert(_peers_resolving_cnt(priv) == priv->peers_resolving_cnt);
|
|
|
|
|
|
|
|
|
|
if (priv->peers_resolving_cnt == 0) {
|
|
|
|
|
if (nm_device_get_state(NM_DEVICE(self)) == NM_DEVICE_STATE_CONFIG) {
|
|
|
|
|
_LOGT(LOGD_DEVICE,
|
|
|
|
|
"activation delayed to resolve DNS names of peers: completed, proceed now");
|
|
|
|
|
nm_device_activate_schedule_stage2_device_config(NM_DEVICE(self), FALSE);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-01-12 12:33:35 +01:00
|
|
|
static void
|
wireguard: delay activation while resolving DNS names for WireGuard peers to avoid race
The endpoints of WireGuard peers can be configured as DNS name, which
NetworkManager will resolve.
Since activating a profile might affect now names get resolved, we must
first resolve names before completing the activation of the WireGuard
device (and before reconfiguring DNS accordingly).
For example, if you configure exclusive DNS resolution via the WireGuard
device, and if the peer needs to be resolved via DNS, then resolving the
peer name must happen before the reconfiguration of DNS. Otherwise the
new DNS configuration will be broken due to being unable to reach the
WireGuard peer.
Fix that by waiting.
There is still an unfixed problem. If resolving any peers fails,
activation silently proceeds -- again possibly breaking the network
setup. Of course, NetworkManager will repeatedly try to re-resolve
the name, but that may never succeed if DNS would be resolved via
the VPN itself.
That is different from `wg set` which resolves hostnames and fails.
Consequently `wg-quick up` would also fail. But these are both one shot
applications, they are not around and basically let the user handle the
error (by reading the log and invoking the command again). NetworkManager
can do something different and proceed activation (as it will also
periodically re-resolve the hostnames again). Note that it's also valid
to activate a WireGuard device without any peers (and to modify the
activated device later with Reapply()). As such, having no peers (or
being unable to resolve a hostname) may be a valid configuration.
I think we should add an option/flag that when enabled will cause
the activation to fail of names cannot be resolved.
https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/issues/535
https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/issues/616
https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/721
2021-01-08 11:09:03 +01:00
|
|
|
_peers_remove(NMDeviceWireGuard *self, PeerData *peer_data)
|
2019-01-12 12:33:35 +01:00
|
|
|
{
|
wireguard: delay activation while resolving DNS names for WireGuard peers to avoid race
The endpoints of WireGuard peers can be configured as DNS name, which
NetworkManager will resolve.
Since activating a profile might affect now names get resolved, we must
first resolve names before completing the activation of the WireGuard
device (and before reconfiguring DNS accordingly).
For example, if you configure exclusive DNS resolution via the WireGuard
device, and if the peer needs to be resolved via DNS, then resolving the
peer name must happen before the reconfiguration of DNS. Otherwise the
new DNS configuration will be broken due to being unable to reach the
WireGuard peer.
Fix that by waiting.
There is still an unfixed problem. If resolving any peers fails,
activation silently proceeds -- again possibly breaking the network
setup. Of course, NetworkManager will repeatedly try to re-resolve
the name, but that may never succeed if DNS would be resolved via
the VPN itself.
That is different from `wg set` which resolves hostnames and fails.
Consequently `wg-quick up` would also fail. But these are both one shot
applications, they are not around and basically let the user handle the
error (by reading the log and invoking the command again). NetworkManager
can do something different and proceed activation (as it will also
periodically re-resolve the hostnames again). Note that it's also valid
to activate a WireGuard device without any peers (and to modify the
activated device later with Reapply()). As such, having no peers (or
being unable to resolve a hostname) may be a valid configuration.
I think we should add an option/flag that when enabled will cause
the activation to fail of names cannot be resolved.
https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/issues/535
https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/issues/616
https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/721
2021-01-08 11:09:03 +01:00
|
|
|
NMDeviceWireGuardPrivate *priv = NM_DEVICE_WIREGUARD_GET_PRIVATE(self);
|
|
|
|
|
|
2019-01-12 12:33:35 +01:00
|
|
|
nm_assert(peer_data);
|
|
|
|
|
nm_assert(g_hash_table_lookup(priv->peers, peer_data) == peer_data);
|
|
|
|
|
|
|
|
|
|
if (!g_hash_table_remove(priv->peers, peer_data))
|
|
|
|
|
nm_assert_not_reached();
|
|
|
|
|
|
|
|
|
|
c_list_unlink_stale(&peer_data->lst_peers);
|
|
|
|
|
nm_wireguard_peer_unref(peer_data->peer);
|
wireguard: delay activation while resolving DNS names for WireGuard peers to avoid race
The endpoints of WireGuard peers can be configured as DNS name, which
NetworkManager will resolve.
Since activating a profile might affect now names get resolved, we must
first resolve names before completing the activation of the WireGuard
device (and before reconfiguring DNS accordingly).
For example, if you configure exclusive DNS resolution via the WireGuard
device, and if the peer needs to be resolved via DNS, then resolving the
peer name must happen before the reconfiguration of DNS. Otherwise the
new DNS configuration will be broken due to being unable to reach the
WireGuard peer.
Fix that by waiting.
There is still an unfixed problem. If resolving any peers fails,
activation silently proceeds -- again possibly breaking the network
setup. Of course, NetworkManager will repeatedly try to re-resolve
the name, but that may never succeed if DNS would be resolved via
the VPN itself.
That is different from `wg set` which resolves hostnames and fails.
Consequently `wg-quick up` would also fail. But these are both one shot
applications, they are not around and basically let the user handle the
error (by reading the log and invoking the command again). NetworkManager
can do something different and proceed activation (as it will also
periodically re-resolve the hostnames again). Note that it's also valid
to activate a WireGuard device without any peers (and to modify the
activated device later with Reapply()). As such, having no peers (or
being unable to resolve a hostname) may be a valid configuration.
I think we should add an option/flag that when enabled will cause
the activation to fail of names cannot be resolved.
https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/issues/535
https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/issues/616
https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/721
2021-01-08 11:09:03 +01:00
|
|
|
if (nm_clear_g_cancellable(&peer_data->ep_resolv.cancellable))
|
|
|
|
|
_peers_resolving_cnt_decrement(self);
|
2019-01-12 12:33:35 +01:00
|
|
|
g_slice_free(PeerData, peer_data);
|
|
|
|
|
|
2019-08-03 12:27:57 +02:00
|
|
|
if (c_list_is_empty(&priv->lst_peers_head)) {
|
2019-01-12 12:33:35 +01:00
|
|
|
nm_clear_g_source(&priv->resolve_next_try_id);
|
|
|
|
|
nm_clear_g_source(&priv->link_config_delayed_id);
|
|
|
|
|
}
|
wireguard: delay activation while resolving DNS names for WireGuard peers to avoid race
The endpoints of WireGuard peers can be configured as DNS name, which
NetworkManager will resolve.
Since activating a profile might affect now names get resolved, we must
first resolve names before completing the activation of the WireGuard
device (and before reconfiguring DNS accordingly).
For example, if you configure exclusive DNS resolution via the WireGuard
device, and if the peer needs to be resolved via DNS, then resolving the
peer name must happen before the reconfiguration of DNS. Otherwise the
new DNS configuration will be broken due to being unable to reach the
WireGuard peer.
Fix that by waiting.
There is still an unfixed problem. If resolving any peers fails,
activation silently proceeds -- again possibly breaking the network
setup. Of course, NetworkManager will repeatedly try to re-resolve
the name, but that may never succeed if DNS would be resolved via
the VPN itself.
That is different from `wg set` which resolves hostnames and fails.
Consequently `wg-quick up` would also fail. But these are both one shot
applications, they are not around and basically let the user handle the
error (by reading the log and invoking the command again). NetworkManager
can do something different and proceed activation (as it will also
periodically re-resolve the hostnames again). Note that it's also valid
to activate a WireGuard device without any peers (and to modify the
activated device later with Reapply()). As such, having no peers (or
being unable to resolve a hostname) may be a valid configuration.
I think we should add an option/flag that when enabled will cause
the activation to fail of names cannot be resolved.
https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/issues/535
https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/issues/616
https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/721
2021-01-08 11:09:03 +01:00
|
|
|
|
|
|
|
|
nm_assert(_peers_resolving_cnt(priv) == priv->peers_resolving_cnt);
|
2019-01-12 12:33:35 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static PeerData *
|
|
|
|
|
_peers_add(NMDeviceWireGuard *self, NMWireGuardPeer *peer)
|
|
|
|
|
{
|
|
|
|
|
NMDeviceWireGuardPrivate *priv = NM_DEVICE_WIREGUARD_GET_PRIVATE(self);
|
2021-11-09 13:28:54 +01:00
|
|
|
PeerData *peer_data;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2019-01-12 12:33:35 +01:00
|
|
|
nm_assert(peer);
|
|
|
|
|
nm_assert(nm_wireguard_peer_is_sealed(peer));
|
|
|
|
|
nm_assert(!_peers_find(priv, peer));
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2019-01-12 12:33:35 +01:00
|
|
|
peer_data = g_slice_new(PeerData);
|
|
|
|
|
*peer_data = (PeerData){
|
|
|
|
|
.self = self,
|
|
|
|
|
.peer = nm_wireguard_peer_ref(peer),
|
|
|
|
|
.ep_resolv =
|
|
|
|
|
{
|
|
|
|
|
.sockaddr = NM_SOCK_ADDR_UNION_INIT_UNSPEC,
|
|
|
|
|
},
|
|
|
|
|
};
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2019-01-12 12:33:35 +01:00
|
|
|
c_list_link_tail(&priv->lst_peers_head, &peer_data->lst_peers);
|
|
|
|
|
if (!nm_g_hash_table_add(priv->peers, peer_data))
|
|
|
|
|
nm_assert_not_reached();
|
|
|
|
|
return peer_data;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
|
_peers_resolve_retry_timeout(gpointer user_data)
|
|
|
|
|
{
|
2021-11-09 13:28:54 +01:00
|
|
|
NMDeviceWireGuard *self = user_data;
|
2019-01-12 12:33:35 +01:00
|
|
|
NMDeviceWireGuardPrivate *priv = NM_DEVICE_WIREGUARD_GET_PRIVATE(self);
|
2021-11-09 13:28:54 +01:00
|
|
|
PeerData *peer_data;
|
2019-01-12 12:33:35 +01:00
|
|
|
gint64 now;
|
|
|
|
|
gint64 next;
|
|
|
|
|
|
|
|
|
|
priv->resolve_next_try_id = 0;
|
|
|
|
|
|
|
|
|
|
_LOGT(LOGD_DEVICE, "wireguard-peers: rechecking peer endpoints...");
|
|
|
|
|
|
2019-12-13 16:54:30 +01:00
|
|
|
now = nm_utils_get_monotonic_timestamp_nsec();
|
2019-01-12 12:33:35 +01:00
|
|
|
next = G_MAXINT64;
|
|
|
|
|
c_list_for_each_entry (peer_data, &priv->lst_peers_head, lst_peers) {
|
|
|
|
|
if (peer_data->ep_resolv.next_try_at_nsec <= 0)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
if (peer_data->ep_resolv.cancellable) {
|
|
|
|
|
/* we are currently resolving a name. We don't need the global
|
|
|
|
|
* watchdog to guard this peer. No need to adjust @next for
|
|
|
|
|
* this one, when the currently ongoing resolving completes, we
|
|
|
|
|
* may reschedule. Skip. */
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (peer_data->ep_resolv.next_try_at_nsec == NEXT_TRY_AT_NSEC_ASAP
|
|
|
|
|
|| now >= peer_data->ep_resolv.next_try_at_nsec) {
|
|
|
|
|
_peers_resolve_start(self, peer_data);
|
|
|
|
|
/* same here. Now we are resolving. We don't need the global
|
|
|
|
|
* watchdog. Skip w.r.t. finding @next. */
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (next > peer_data->ep_resolv.next_try_at_nsec)
|
|
|
|
|
next = peer_data->ep_resolv.next_try_at_nsec;
|
|
|
|
|
}
|
|
|
|
|
if (next < G_MAXINT64)
|
|
|
|
|
_peers_resolve_retry_reschedule(self, next);
|
|
|
|
|
|
|
|
|
|
return G_SOURCE_REMOVE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
_peers_resolve_retry_reschedule(NMDeviceWireGuard *self, gint64 new_next_try_at_nsec)
|
|
|
|
|
{
|
|
|
|
|
NMDeviceWireGuardPrivate *priv = NM_DEVICE_WIREGUARD_GET_PRIVATE(self);
|
|
|
|
|
guint32 interval_ms;
|
|
|
|
|
gint64 now;
|
|
|
|
|
|
|
|
|
|
nm_assert(new_next_try_at_nsec > 0);
|
|
|
|
|
nm_assert(new_next_try_at_nsec != NEXT_TRY_AT_NSEC_ASAP);
|
|
|
|
|
|
|
|
|
|
if (priv->resolve_next_try_id && priv->resolve_next_try_at <= new_next_try_at_nsec) {
|
|
|
|
|
/* we already have an earlier timeout scheduled (possibly for
|
|
|
|
|
* another peer that expires sooner). Don't reschedule now.
|
|
|
|
|
* Even if the scheduled timeout expires too early, we will
|
|
|
|
|
* compute the right next-timeout and reschedule then. */
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2019-12-13 16:54:30 +01:00
|
|
|
now = nm_utils_get_monotonic_timestamp_nsec();
|
2019-01-12 12:33:35 +01:00
|
|
|
|
|
|
|
|
/* schedule at most one day ahead. No problem if we expire earlier
|
|
|
|
|
* than expected. Also, rate-limit to 500 msec. */
|
2019-12-13 16:54:30 +01:00
|
|
|
interval_ms = NM_CLAMP((new_next_try_at_nsec - now) / NM_UTILS_NSEC_PER_MSEC,
|
2019-01-12 12:33:35 +01:00
|
|
|
(gint64) 500,
|
2021-05-03 21:48:58 +02:00
|
|
|
(gint64) (24 * 60 * 60 * 1000));
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2019-01-12 12:33:35 +01:00
|
|
|
_LOGT(LOGD_DEVICE,
|
|
|
|
|
"wireguard-peers: schedule rechecking peer endpoints in %u msec",
|
|
|
|
|
interval_ms);
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2019-01-12 12:33:35 +01:00
|
|
|
nm_clear_g_source(&priv->resolve_next_try_id);
|
|
|
|
|
priv->resolve_next_try_at = new_next_try_at_nsec;
|
|
|
|
|
priv->resolve_next_try_id = g_timeout_add(interval_ms, _peers_resolve_retry_timeout, self);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
_peers_resolve_retry_reschedule_for_peer(NMDeviceWireGuard *self,
|
2021-11-09 13:28:54 +01:00
|
|
|
PeerData *peer_data,
|
2019-01-12 12:33:35 +01:00
|
|
|
gint64 retry_in_msec)
|
|
|
|
|
{
|
|
|
|
|
nm_assert(retry_in_msec >= 0);
|
|
|
|
|
|
|
|
|
|
if (retry_in_msec == RETRY_IN_MSEC_ASAP) {
|
|
|
|
|
_peers_resolve_start(self, peer_data);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2019-12-13 16:54:30 +01:00
|
|
|
peer_data->ep_resolv.next_try_at_nsec =
|
|
|
|
|
nm_utils_get_monotonic_timestamp_nsec() + (retry_in_msec * NM_UTILS_NSEC_PER_MSEC);
|
2019-01-12 12:33:35 +01:00
|
|
|
_peers_resolve_retry_reschedule(self, peer_data->ep_resolv.next_try_at_nsec);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static gint64
|
|
|
|
|
_peers_retry_in_msec(PeerData *peer_data, gboolean after_failure)
|
|
|
|
|
{
|
|
|
|
|
if (peer_data->ep_resolv.next_try_at_nsec == NEXT_TRY_AT_NSEC_ASAP) {
|
|
|
|
|
peer_data->ep_resolv.resolv_fail_count = 0;
|
|
|
|
|
return RETRY_IN_MSEC_ASAP;
|
|
|
|
|
}
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2019-01-12 12:33:35 +01:00
|
|
|
if (after_failure) {
|
|
|
|
|
if (peer_data->ep_resolv.resolv_fail_count < G_MAXUINT)
|
|
|
|
|
peer_data->ep_resolv.resolv_fail_count++;
|
|
|
|
|
} else
|
|
|
|
|
peer_data->ep_resolv.resolv_fail_count = 0;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2019-01-12 12:33:35 +01:00
|
|
|
if (!after_failure)
|
|
|
|
|
return RETRY_IN_MSEC_MAX;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2019-01-12 12:33:35 +01:00
|
|
|
if (peer_data->ep_resolv.resolv_fail_count > 20)
|
|
|
|
|
return RETRY_IN_MSEC_MAX;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2019-01-12 12:33:35 +01:00
|
|
|
/* double the retry-time, starting with one second. */
|
|
|
|
|
return NM_MIN(RETRY_IN_MSEC_MAX, (1u << peer_data->ep_resolv.resolv_fail_count) * 500);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
_peers_resolve_cb(GObject *source_object, GAsyncResult *res, gpointer user_data)
|
|
|
|
|
{
|
2021-11-09 13:28:54 +01:00
|
|
|
NMDeviceWireGuard *self;
|
wireguard: delay activation while resolving DNS names for WireGuard peers to avoid race
The endpoints of WireGuard peers can be configured as DNS name, which
NetworkManager will resolve.
Since activating a profile might affect now names get resolved, we must
first resolve names before completing the activation of the WireGuard
device (and before reconfiguring DNS accordingly).
For example, if you configure exclusive DNS resolution via the WireGuard
device, and if the peer needs to be resolved via DNS, then resolving the
peer name must happen before the reconfiguration of DNS. Otherwise the
new DNS configuration will be broken due to being unable to reach the
WireGuard peer.
Fix that by waiting.
There is still an unfixed problem. If resolving any peers fails,
activation silently proceeds -- again possibly breaking the network
setup. Of course, NetworkManager will repeatedly try to re-resolve
the name, but that may never succeed if DNS would be resolved via
the VPN itself.
That is different from `wg set` which resolves hostnames and fails.
Consequently `wg-quick up` would also fail. But these are both one shot
applications, they are not around and basically let the user handle the
error (by reading the log and invoking the command again). NetworkManager
can do something different and proceed activation (as it will also
periodically re-resolve the hostnames again). Note that it's also valid
to activate a WireGuard device without any peers (and to modify the
activated device later with Reapply()). As such, having no peers (or
being unable to resolve a hostname) may be a valid configuration.
I think we should add an option/flag that when enabled will cause
the activation to fail of names cannot be resolved.
https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/issues/535
https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/issues/616
https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/721
2021-01-08 11:09:03 +01:00
|
|
|
NMDeviceWireGuardPrivate *priv;
|
2021-11-09 13:28:54 +01:00
|
|
|
PeerData *peer_data;
|
|
|
|
|
gs_free_error GError *resolv_error = NULL;
|
|
|
|
|
GList *list;
|
|
|
|
|
gboolean changed;
|
|
|
|
|
NMSockAddrUnion sockaddr;
|
|
|
|
|
gint64 retry_in_msec;
|
|
|
|
|
char s_sockaddr[100];
|
|
|
|
|
char s_retry[100];
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2019-01-12 12:33:35 +01:00
|
|
|
list = g_resolver_lookup_by_name_finish(G_RESOLVER(source_object), res, &resolv_error);
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2020-01-23 10:36:24 +01:00
|
|
|
if (nm_utils_error_is_cancelled(resolv_error))
|
2019-01-12 12:33:35 +01:00
|
|
|
return;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2019-01-12 12:33:35 +01:00
|
|
|
peer_data = user_data;
|
|
|
|
|
self = peer_data->self;
|
wireguard: delay activation while resolving DNS names for WireGuard peers to avoid race
The endpoints of WireGuard peers can be configured as DNS name, which
NetworkManager will resolve.
Since activating a profile might affect now names get resolved, we must
first resolve names before completing the activation of the WireGuard
device (and before reconfiguring DNS accordingly).
For example, if you configure exclusive DNS resolution via the WireGuard
device, and if the peer needs to be resolved via DNS, then resolving the
peer name must happen before the reconfiguration of DNS. Otherwise the
new DNS configuration will be broken due to being unable to reach the
WireGuard peer.
Fix that by waiting.
There is still an unfixed problem. If resolving any peers fails,
activation silently proceeds -- again possibly breaking the network
setup. Of course, NetworkManager will repeatedly try to re-resolve
the name, but that may never succeed if DNS would be resolved via
the VPN itself.
That is different from `wg set` which resolves hostnames and fails.
Consequently `wg-quick up` would also fail. But these are both one shot
applications, they are not around and basically let the user handle the
error (by reading the log and invoking the command again). NetworkManager
can do something different and proceed activation (as it will also
periodically re-resolve the hostnames again). Note that it's also valid
to activate a WireGuard device without any peers (and to modify the
activated device later with Reapply()). As such, having no peers (or
being unable to resolve a hostname) may be a valid configuration.
I think we should add an option/flag that when enabled will cause
the activation to fail of names cannot be resolved.
https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/issues/535
https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/issues/616
https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/721
2021-01-08 11:09:03 +01:00
|
|
|
priv = NM_DEVICE_WIREGUARD_GET_PRIVATE(self);
|
2020-09-28 16:03:33 +02:00
|
|
|
|
wireguard: delay activation while resolving DNS names for WireGuard peers to avoid race
The endpoints of WireGuard peers can be configured as DNS name, which
NetworkManager will resolve.
Since activating a profile might affect now names get resolved, we must
first resolve names before completing the activation of the WireGuard
device (and before reconfiguring DNS accordingly).
For example, if you configure exclusive DNS resolution via the WireGuard
device, and if the peer needs to be resolved via DNS, then resolving the
peer name must happen before the reconfiguration of DNS. Otherwise the
new DNS configuration will be broken due to being unable to reach the
WireGuard peer.
Fix that by waiting.
There is still an unfixed problem. If resolving any peers fails,
activation silently proceeds -- again possibly breaking the network
setup. Of course, NetworkManager will repeatedly try to re-resolve
the name, but that may never succeed if DNS would be resolved via
the VPN itself.
That is different from `wg set` which resolves hostnames and fails.
Consequently `wg-quick up` would also fail. But these are both one shot
applications, they are not around and basically let the user handle the
error (by reading the log and invoking the command again). NetworkManager
can do something different and proceed activation (as it will also
periodically re-resolve the hostnames again). Note that it's also valid
to activate a WireGuard device without any peers (and to modify the
activated device later with Reapply()). As such, having no peers (or
being unable to resolve a hostname) may be a valid configuration.
I think we should add an option/flag that when enabled will cause
the activation to fail of names cannot be resolved.
https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/issues/535
https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/issues/616
https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/721
2021-01-08 11:09:03 +01:00
|
|
|
if (nm_clear_g_object(&peer_data->ep_resolv.cancellable))
|
|
|
|
|
_peers_resolving_cnt_decrement(self);
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2019-01-12 12:33:35 +01:00
|
|
|
nm_assert((!resolv_error) != (!list));
|
wireguard: delay activation while resolving DNS names for WireGuard peers to avoid race
The endpoints of WireGuard peers can be configured as DNS name, which
NetworkManager will resolve.
Since activating a profile might affect now names get resolved, we must
first resolve names before completing the activation of the WireGuard
device (and before reconfiguring DNS accordingly).
For example, if you configure exclusive DNS resolution via the WireGuard
device, and if the peer needs to be resolved via DNS, then resolving the
peer name must happen before the reconfiguration of DNS. Otherwise the
new DNS configuration will be broken due to being unable to reach the
WireGuard peer.
Fix that by waiting.
There is still an unfixed problem. If resolving any peers fails,
activation silently proceeds -- again possibly breaking the network
setup. Of course, NetworkManager will repeatedly try to re-resolve
the name, but that may never succeed if DNS would be resolved via
the VPN itself.
That is different from `wg set` which resolves hostnames and fails.
Consequently `wg-quick up` would also fail. But these are both one shot
applications, they are not around and basically let the user handle the
error (by reading the log and invoking the command again). NetworkManager
can do something different and proceed activation (as it will also
periodically re-resolve the hostnames again). Note that it's also valid
to activate a WireGuard device without any peers (and to modify the
activated device later with Reapply()). As such, having no peers (or
being unable to resolve a hostname) may be a valid configuration.
I think we should add an option/flag that when enabled will cause
the activation to fail of names cannot be resolved.
https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/issues/535
https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/issues/616
https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/721
2021-01-08 11:09:03 +01:00
|
|
|
nm_assert(_peers_resolving_cnt(priv) == priv->peers_resolving_cnt);
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2019-01-12 12:33:35 +01:00
|
|
|
#define _retry_in_msec_to_string(retry_in_msec, s_retry) \
|
|
|
|
|
({ \
|
|
|
|
|
gint64 _retry_in_msec = (retry_in_msec); \
|
|
|
|
|
\
|
|
|
|
|
_retry_in_msec == RETRY_IN_MSEC_ASAP \
|
|
|
|
|
? "right away" \
|
|
|
|
|
: nm_sprintf_buf(s_retry, "in %" G_GINT64_FORMAT " msec", _retry_in_msec); \
|
|
|
|
|
})
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2019-01-12 12:33:35 +01:00
|
|
|
if (resolv_error
|
|
|
|
|
&& !g_error_matches(resolv_error, G_RESOLVER_ERROR, G_RESOLVER_ERROR_NOT_FOUND)) {
|
|
|
|
|
retry_in_msec = _peers_retry_in_msec(peer_data, TRUE);
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2019-01-12 12:33:35 +01:00
|
|
|
_LOGT(LOGD_DEVICE,
|
|
|
|
|
"wireguard-peer[%s]: failure to resolve endpoint \"%s\": %s (retry %s)",
|
|
|
|
|
nm_wireguard_peer_get_public_key(peer_data->peer),
|
|
|
|
|
nm_wireguard_peer_get_endpoint(peer_data->peer),
|
|
|
|
|
resolv_error->message,
|
|
|
|
|
_retry_in_msec_to_string(retry_in_msec, s_retry));
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2019-01-12 12:33:35 +01:00
|
|
|
_peers_resolve_retry_reschedule_for_peer(self, peer_data, retry_in_msec);
|
|
|
|
|
return;
|
|
|
|
|
}
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2019-01-12 12:33:35 +01:00
|
|
|
sockaddr = (NMSockAddrUnion) NM_SOCK_ADDR_UNION_INIT_UNSPEC;
|
2021-02-13 15:47:31 +01:00
|
|
|
changed = FALSE;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2019-01-12 12:33:35 +01:00
|
|
|
if (!resolv_error) {
|
|
|
|
|
GList *iter;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2019-01-12 12:33:35 +01:00
|
|
|
for (iter = list; iter; iter = iter->next) {
|
2021-11-09 13:28:54 +01:00
|
|
|
GInetAddress *a = iter->data;
|
2021-02-13 15:47:31 +01:00
|
|
|
NMSockAddrUnion sockaddr_tmp;
|
|
|
|
|
NMSockAddrUnion *s;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2021-02-13 15:47:31 +01:00
|
|
|
s = sockaddr.sa.sa_family == AF_UNSPEC ? &sockaddr : &sockaddr_tmp;
|
|
|
|
|
|
|
|
|
|
switch (g_inet_address_get_family(a)) {
|
|
|
|
|
case G_SOCKET_FAMILY_IPV4:
|
2019-01-12 12:33:35 +01:00
|
|
|
nm_assert(g_inet_address_get_native_size(a) == sizeof(struct in_addr));
|
2021-02-13 15:47:31 +01:00
|
|
|
s->in = (struct sockaddr_in){
|
2019-01-12 12:33:35 +01:00
|
|
|
.sin_family = AF_INET,
|
|
|
|
|
.sin_port = htons(nm_sock_addr_endpoint_get_port(
|
|
|
|
|
_nm_wireguard_peer_get_endpoint(peer_data->peer))),
|
|
|
|
|
};
|
2021-02-13 15:47:31 +01:00
|
|
|
memcpy(&s->in.sin_addr, g_inet_address_to_bytes(a), sizeof(struct in_addr));
|
2019-01-12 12:33:35 +01:00
|
|
|
break;
|
2021-02-13 15:47:31 +01:00
|
|
|
case G_SOCKET_FAMILY_IPV6:
|
2019-01-12 12:33:35 +01:00
|
|
|
nm_assert(g_inet_address_get_native_size(a) == sizeof(struct in6_addr));
|
2021-02-13 15:47:31 +01:00
|
|
|
s->in6 = (struct sockaddr_in6){
|
2019-01-12 12:33:35 +01:00
|
|
|
.sin6_family = AF_INET6,
|
|
|
|
|
.sin6_port = htons(nm_sock_addr_endpoint_get_port(
|
|
|
|
|
_nm_wireguard_peer_get_endpoint(peer_data->peer))),
|
|
|
|
|
.sin6_scope_id = 0,
|
|
|
|
|
.sin6_flowinfo = 0,
|
|
|
|
|
};
|
2021-02-13 15:47:31 +01:00
|
|
|
memcpy(&s->in6.sin6_addr, g_inet_address_to_bytes(a), sizeof(struct in6_addr));
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
changed = TRUE;
|
|
|
|
|
if (peer_data->ep_resolv.sockaddr.sa.sa_family == AF_UNSPEC)
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
if (nm_sock_addr_union_cmp(&peer_data->ep_resolv.sockaddr, &sockaddr) == 0) {
|
|
|
|
|
changed = FALSE;
|
2019-01-12 12:33:35 +01:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2019-01-12 12:33:35 +01:00
|
|
|
g_list_free_full(list, g_object_unref);
|
|
|
|
|
}
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2019-01-12 12:33:35 +01:00
|
|
|
if (sockaddr.sa.sa_family == AF_UNSPEC) {
|
|
|
|
|
/* we failed to resolve the name. There is no need to reset the previous
|
|
|
|
|
* sockaddr. Either it was already AF_UNSPEC, or we had a good name
|
|
|
|
|
* from resolving before. In that case, we don't want to throw away
|
|
|
|
|
* a possibly good IP address, since WireGuard supports automatic roaming
|
|
|
|
|
* anyway. Either the IP address is still good (and we would wrongly
|
|
|
|
|
* reject it), or it isn't -- in which case it does not hurt much. */
|
2021-02-13 15:47:31 +01:00
|
|
|
} else if (changed)
|
2019-01-12 12:33:35 +01:00
|
|
|
peer_data->ep_resolv.sockaddr = sockaddr;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2019-01-12 12:33:35 +01:00
|
|
|
if (resolv_error || peer_data->ep_resolv.sockaddr.sa.sa_family == AF_UNSPEC) {
|
|
|
|
|
/* while it technically did not fail, something is probably odd. Retry frequently to
|
|
|
|
|
* resolve the name, like we would do for normal failures. */
|
|
|
|
|
retry_in_msec = _peers_retry_in_msec(peer_data, TRUE);
|
|
|
|
|
_LOGT(LOGD_DEVICE,
|
|
|
|
|
"wireguard-peer[%s]: no %sresults for endpoint \"%s\" (retry %s)",
|
|
|
|
|
nm_wireguard_peer_get_public_key(peer_data->peer),
|
|
|
|
|
resolv_error ? "" : "suitable ",
|
|
|
|
|
nm_wireguard_peer_get_endpoint(peer_data->peer),
|
|
|
|
|
_retry_in_msec_to_string(retry_in_msec, s_retry));
|
|
|
|
|
} else {
|
|
|
|
|
retry_in_msec = _peers_retry_in_msec(peer_data, FALSE);
|
|
|
|
|
_LOGT(LOGD_DEVICE,
|
|
|
|
|
"wireguard-peer[%s]: endpoint \"%s\" resolved to %s (retry %s)",
|
|
|
|
|
nm_wireguard_peer_get_public_key(peer_data->peer),
|
|
|
|
|
nm_wireguard_peer_get_endpoint(peer_data->peer),
|
|
|
|
|
nm_sock_addr_union_to_string(&peer_data->ep_resolv.sockaddr,
|
|
|
|
|
s_sockaddr,
|
|
|
|
|
sizeof(s_sockaddr)),
|
|
|
|
|
_retry_in_msec_to_string(retry_in_msec, s_retry));
|
|
|
|
|
}
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2019-01-12 12:33:35 +01:00
|
|
|
_peers_resolve_retry_reschedule_for_peer(self, peer_data, retry_in_msec);
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2019-01-12 12:33:35 +01:00
|
|
|
if (changed) {
|
|
|
|
|
/* schedule the job in the background, to give multiple resolve events time
|
|
|
|
|
* to complete. */
|
|
|
|
|
nm_clear_g_source(&priv->link_config_delayed_id);
|
|
|
|
|
priv->link_config_delayed_id = g_idle_add_full(G_PRIORITY_DEFAULT_IDLE + 1,
|
|
|
|
|
link_config_delayed_resolver_cb,
|
|
|
|
|
self,
|
|
|
|
|
NULL);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
_peers_resolve_start(NMDeviceWireGuard *self, PeerData *peer_data)
|
|
|
|
|
{
|
2021-11-09 13:28:54 +01:00
|
|
|
NMDeviceWireGuardPrivate *priv = NM_DEVICE_WIREGUARD_GET_PRIVATE(self);
|
2019-01-12 12:33:35 +01:00
|
|
|
gs_unref_object GResolver *resolver = NULL;
|
2021-11-09 13:28:54 +01:00
|
|
|
const char *host;
|
2019-01-12 12:33:35 +01:00
|
|
|
|
|
|
|
|
resolver = g_resolver_get_default();
|
|
|
|
|
|
|
|
|
|
nm_assert(!peer_data->ep_resolv.cancellable);
|
|
|
|
|
|
|
|
|
|
peer_data->ep_resolv.cancellable = g_cancellable_new();
|
wireguard: delay activation while resolving DNS names for WireGuard peers to avoid race
The endpoints of WireGuard peers can be configured as DNS name, which
NetworkManager will resolve.
Since activating a profile might affect now names get resolved, we must
first resolve names before completing the activation of the WireGuard
device (and before reconfiguring DNS accordingly).
For example, if you configure exclusive DNS resolution via the WireGuard
device, and if the peer needs to be resolved via DNS, then resolving the
peer name must happen before the reconfiguration of DNS. Otherwise the
new DNS configuration will be broken due to being unable to reach the
WireGuard peer.
Fix that by waiting.
There is still an unfixed problem. If resolving any peers fails,
activation silently proceeds -- again possibly breaking the network
setup. Of course, NetworkManager will repeatedly try to re-resolve
the name, but that may never succeed if DNS would be resolved via
the VPN itself.
That is different from `wg set` which resolves hostnames and fails.
Consequently `wg-quick up` would also fail. But these are both one shot
applications, they are not around and basically let the user handle the
error (by reading the log and invoking the command again). NetworkManager
can do something different and proceed activation (as it will also
periodically re-resolve the hostnames again). Note that it's also valid
to activate a WireGuard device without any peers (and to modify the
activated device later with Reapply()). As such, having no peers (or
being unable to resolve a hostname) may be a valid configuration.
I think we should add an option/flag that when enabled will cause
the activation to fail of names cannot be resolved.
https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/issues/535
https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/issues/616
https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/721
2021-01-08 11:09:03 +01:00
|
|
|
priv->peers_resolving_cnt++;
|
2019-01-12 12:33:35 +01:00
|
|
|
|
|
|
|
|
/* set a special next-try timestamp. It is positive, and indicates
|
|
|
|
|
* that we are in the process of trying.
|
|
|
|
|
* This timestamp however already lies in the past, but that is correct,
|
|
|
|
|
* because we are currently in the process of trying. We will determine
|
|
|
|
|
* a next-try timestamp once the try completes. */
|
|
|
|
|
peer_data->ep_resolv.next_try_at_nsec = NEXT_TRY_AT_NSEC_PAST;
|
|
|
|
|
|
|
|
|
|
host = nm_sock_addr_endpoint_get_host(_nm_wireguard_peer_get_endpoint(peer_data->peer));
|
|
|
|
|
|
|
|
|
|
g_resolver_lookup_by_name_async(resolver,
|
|
|
|
|
host,
|
|
|
|
|
peer_data->ep_resolv.cancellable,
|
|
|
|
|
_peers_resolve_cb,
|
|
|
|
|
peer_data);
|
|
|
|
|
|
|
|
|
|
_LOGT(LOGD_DEVICE,
|
|
|
|
|
"wireguard-peer[%s]: resolving name \"%s\" for endpoint \"%s\"...",
|
|
|
|
|
nm_wireguard_peer_get_public_key(peer_data->peer),
|
|
|
|
|
host,
|
|
|
|
|
nm_wireguard_peer_get_endpoint(peer_data->peer));
|
wireguard: delay activation while resolving DNS names for WireGuard peers to avoid race
The endpoints of WireGuard peers can be configured as DNS name, which
NetworkManager will resolve.
Since activating a profile might affect now names get resolved, we must
first resolve names before completing the activation of the WireGuard
device (and before reconfiguring DNS accordingly).
For example, if you configure exclusive DNS resolution via the WireGuard
device, and if the peer needs to be resolved via DNS, then resolving the
peer name must happen before the reconfiguration of DNS. Otherwise the
new DNS configuration will be broken due to being unable to reach the
WireGuard peer.
Fix that by waiting.
There is still an unfixed problem. If resolving any peers fails,
activation silently proceeds -- again possibly breaking the network
setup. Of course, NetworkManager will repeatedly try to re-resolve
the name, but that may never succeed if DNS would be resolved via
the VPN itself.
That is different from `wg set` which resolves hostnames and fails.
Consequently `wg-quick up` would also fail. But these are both one shot
applications, they are not around and basically let the user handle the
error (by reading the log and invoking the command again). NetworkManager
can do something different and proceed activation (as it will also
periodically re-resolve the hostnames again). Note that it's also valid
to activate a WireGuard device without any peers (and to modify the
activated device later with Reapply()). As such, having no peers (or
being unable to resolve a hostname) may be a valid configuration.
I think we should add an option/flag that when enabled will cause
the activation to fail of names cannot be resolved.
https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/issues/535
https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/issues/616
https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/721
2021-01-08 11:09:03 +01:00
|
|
|
|
|
|
|
|
nm_assert(_peers_resolving_cnt(priv) == priv->peers_resolving_cnt);
|
2019-01-12 12:33:35 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
_peers_resolve_reresolve_all(NMDeviceWireGuard *self)
|
|
|
|
|
{
|
|
|
|
|
NMDeviceWireGuardPrivate *priv = NM_DEVICE_WIREGUARD_GET_PRIVATE(self);
|
2021-11-09 13:28:54 +01:00
|
|
|
PeerData *peer_data;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2019-01-12 12:33:35 +01:00
|
|
|
c_list_for_each_entry (peer_data, &priv->lst_peers_head, lst_peers) {
|
|
|
|
|
if (peer_data->ep_resolv.cancellable) {
|
|
|
|
|
/* remember to retry when the currently ongoing request completes. */
|
|
|
|
|
peer_data->ep_resolv.next_try_at_nsec = NEXT_TRY_AT_NSEC_ASAP;
|
|
|
|
|
} else if (peer_data->ep_resolv.next_try_at_nsec <= 0) {
|
|
|
|
|
/* this peer does not require resolving the name. Skip it. */
|
|
|
|
|
} else {
|
|
|
|
|
/* we have a next-try scheduled. Restart right away. */
|
|
|
|
|
peer_data->ep_resolv.resolv_fail_count = 0;
|
|
|
|
|
_peers_resolve_start(self, peer_data);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
|
_peers_update(NMDeviceWireGuard *self,
|
2021-11-09 13:28:54 +01:00
|
|
|
PeerData *peer_data,
|
|
|
|
|
NMWireGuardPeer *peer,
|
2019-01-12 12:33:35 +01:00
|
|
|
gboolean force_update)
|
|
|
|
|
{
|
|
|
|
|
nm_auto_unref_wgpeer NMWireGuardPeer *old_peer = NULL;
|
2021-11-09 13:28:54 +01:00
|
|
|
NMSockAddrEndpoint *old_endpoint;
|
|
|
|
|
NMSockAddrEndpoint *endpoint;
|
2019-01-12 12:33:35 +01:00
|
|
|
gboolean endpoint_changed = FALSE;
|
|
|
|
|
gboolean changed;
|
|
|
|
|
NMSockAddrUnion sockaddr;
|
|
|
|
|
gboolean sockaddr_fixed;
|
|
|
|
|
char sockaddr_sbuf[100];
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2019-01-12 12:33:35 +01:00
|
|
|
nm_assert(peer);
|
|
|
|
|
nm_assert(nm_wireguard_peer_is_sealed(peer));
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2019-01-12 12:33:35 +01:00
|
|
|
if (peer == peer_data->peer && !force_update)
|
|
|
|
|
return FALSE;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2019-01-12 12:33:35 +01:00
|
|
|
changed = (nm_wireguard_peer_cmp(peer, peer_data->peer, NM_SETTING_COMPARE_FLAG_EXACT) != 0);
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2019-01-12 12:33:35 +01:00
|
|
|
old_peer = peer_data->peer;
|
|
|
|
|
peer_data->peer = nm_wireguard_peer_ref(peer);
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2019-01-12 12:33:35 +01:00
|
|
|
old_endpoint = old_peer ? _nm_wireguard_peer_get_endpoint(old_peer) : NULL;
|
|
|
|
|
endpoint = peer ? _nm_wireguard_peer_get_endpoint(peer) : NULL;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2019-01-12 12:33:35 +01:00
|
|
|
endpoint_changed = (endpoint != old_endpoint
|
|
|
|
|
&& (!old_endpoint || !endpoint
|
|
|
|
|
|| !nm_streq(nm_sock_addr_endpoint_get_endpoint(old_endpoint),
|
|
|
|
|
nm_sock_addr_endpoint_get_endpoint(endpoint))));
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2019-01-12 12:33:35 +01:00
|
|
|
if (!force_update && !endpoint_changed) {
|
|
|
|
|
/* nothing to do. */
|
|
|
|
|
return changed;
|
|
|
|
|
}
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2019-01-12 12:33:35 +01:00
|
|
|
sockaddr = (NMSockAddrUnion) NM_SOCK_ADDR_UNION_INIT_UNSPEC;
|
|
|
|
|
sockaddr_fixed = TRUE;
|
|
|
|
|
if (endpoint && nm_sock_addr_endpoint_get_host(endpoint)) {
|
|
|
|
|
if (!nm_sock_addr_endpoint_get_fixed_sockaddr(endpoint, &sockaddr)) {
|
|
|
|
|
/* we have an endpoint, but it's not a static IP address. We need to resolve
|
|
|
|
|
* the names. */
|
|
|
|
|
sockaddr_fixed = FALSE;
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2019-01-12 12:33:35 +01:00
|
|
|
if (nm_sock_addr_union_cmp(&peer_data->ep_resolv.sockaddr, &sockaddr) != 0)
|
|
|
|
|
changed = TRUE;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
wireguard: delay activation while resolving DNS names for WireGuard peers to avoid race
The endpoints of WireGuard peers can be configured as DNS name, which
NetworkManager will resolve.
Since activating a profile might affect now names get resolved, we must
first resolve names before completing the activation of the WireGuard
device (and before reconfiguring DNS accordingly).
For example, if you configure exclusive DNS resolution via the WireGuard
device, and if the peer needs to be resolved via DNS, then resolving the
peer name must happen before the reconfiguration of DNS. Otherwise the
new DNS configuration will be broken due to being unable to reach the
WireGuard peer.
Fix that by waiting.
There is still an unfixed problem. If resolving any peers fails,
activation silently proceeds -- again possibly breaking the network
setup. Of course, NetworkManager will repeatedly try to re-resolve
the name, but that may never succeed if DNS would be resolved via
the VPN itself.
That is different from `wg set` which resolves hostnames and fails.
Consequently `wg-quick up` would also fail. But these are both one shot
applications, they are not around and basically let the user handle the
error (by reading the log and invoking the command again). NetworkManager
can do something different and proceed activation (as it will also
periodically re-resolve the hostnames again). Note that it's also valid
to activate a WireGuard device without any peers (and to modify the
activated device later with Reapply()). As such, having no peers (or
being unable to resolve a hostname) may be a valid configuration.
I think we should add an option/flag that when enabled will cause
the activation to fail of names cannot be resolved.
https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/issues/535
https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/issues/616
https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/721
2021-01-08 11:09:03 +01:00
|
|
|
if (nm_clear_g_cancellable(&peer_data->ep_resolv.cancellable))
|
|
|
|
|
_peers_resolving_cnt_decrement(self);
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2019-01-12 12:33:35 +01:00
|
|
|
peer_data->ep_resolv = (PeerEndpointResolveData){
|
|
|
|
|
.sockaddr = sockaddr,
|
|
|
|
|
.resolv_fail_count = 0,
|
|
|
|
|
.cancellable = NULL,
|
|
|
|
|
.next_try_at_nsec = 0,
|
|
|
|
|
};
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2019-01-12 12:33:35 +01:00
|
|
|
if (!endpoint) {
|
|
|
|
|
_LOGT(LOGD_DEVICE,
|
|
|
|
|
"wireguard-peer[%s]: no endpoint configured",
|
|
|
|
|
nm_wireguard_peer_get_public_key(peer_data->peer));
|
|
|
|
|
} else if (!nm_sock_addr_endpoint_get_host(endpoint)) {
|
|
|
|
|
_LOGT(LOGD_DEVICE,
|
|
|
|
|
"wireguard-peer[%s]: invalid endpoint \"%s\"",
|
|
|
|
|
nm_wireguard_peer_get_public_key(peer_data->peer),
|
|
|
|
|
nm_sock_addr_endpoint_get_endpoint(endpoint));
|
|
|
|
|
} else if (sockaddr_fixed) {
|
|
|
|
|
_LOGT(LOGD_DEVICE,
|
|
|
|
|
"wireguard-peer[%s]: fixed endpoint \"%s\" (%s)",
|
|
|
|
|
nm_wireguard_peer_get_public_key(peer_data->peer),
|
|
|
|
|
nm_sock_addr_endpoint_get_endpoint(endpoint),
|
|
|
|
|
nm_sock_addr_union_to_string(&peer_data->ep_resolv.sockaddr,
|
|
|
|
|
sockaddr_sbuf,
|
|
|
|
|
sizeof(sockaddr_sbuf)));
|
|
|
|
|
} else
|
|
|
|
|
_peers_resolve_start(self, peer_data);
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2019-01-12 12:33:35 +01:00
|
|
|
return changed;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
wireguard: delay activation while resolving DNS names for WireGuard peers to avoid race
The endpoints of WireGuard peers can be configured as DNS name, which
NetworkManager will resolve.
Since activating a profile might affect now names get resolved, we must
first resolve names before completing the activation of the WireGuard
device (and before reconfiguring DNS accordingly).
For example, if you configure exclusive DNS resolution via the WireGuard
device, and if the peer needs to be resolved via DNS, then resolving the
peer name must happen before the reconfiguration of DNS. Otherwise the
new DNS configuration will be broken due to being unable to reach the
WireGuard peer.
Fix that by waiting.
There is still an unfixed problem. If resolving any peers fails,
activation silently proceeds -- again possibly breaking the network
setup. Of course, NetworkManager will repeatedly try to re-resolve
the name, but that may never succeed if DNS would be resolved via
the VPN itself.
That is different from `wg set` which resolves hostnames and fails.
Consequently `wg-quick up` would also fail. But these are both one shot
applications, they are not around and basically let the user handle the
error (by reading the log and invoking the command again). NetworkManager
can do something different and proceed activation (as it will also
periodically re-resolve the hostnames again). Note that it's also valid
to activate a WireGuard device without any peers (and to modify the
activated device later with Reapply()). As such, having no peers (or
being unable to resolve a hostname) may be a valid configuration.
I think we should add an option/flag that when enabled will cause
the activation to fail of names cannot be resolved.
https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/issues/535
https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/issues/616
https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/721
2021-01-08 11:09:03 +01:00
|
|
|
_peers_remove_all(NMDeviceWireGuard *self)
|
2019-01-12 12:33:35 +01:00
|
|
|
{
|
wireguard: delay activation while resolving DNS names for WireGuard peers to avoid race
The endpoints of WireGuard peers can be configured as DNS name, which
NetworkManager will resolve.
Since activating a profile might affect now names get resolved, we must
first resolve names before completing the activation of the WireGuard
device (and before reconfiguring DNS accordingly).
For example, if you configure exclusive DNS resolution via the WireGuard
device, and if the peer needs to be resolved via DNS, then resolving the
peer name must happen before the reconfiguration of DNS. Otherwise the
new DNS configuration will be broken due to being unable to reach the
WireGuard peer.
Fix that by waiting.
There is still an unfixed problem. If resolving any peers fails,
activation silently proceeds -- again possibly breaking the network
setup. Of course, NetworkManager will repeatedly try to re-resolve
the name, but that may never succeed if DNS would be resolved via
the VPN itself.
That is different from `wg set` which resolves hostnames and fails.
Consequently `wg-quick up` would also fail. But these are both one shot
applications, they are not around and basically let the user handle the
error (by reading the log and invoking the command again). NetworkManager
can do something different and proceed activation (as it will also
periodically re-resolve the hostnames again). Note that it's also valid
to activate a WireGuard device without any peers (and to modify the
activated device later with Reapply()). As such, having no peers (or
being unable to resolve a hostname) may be a valid configuration.
I think we should add an option/flag that when enabled will cause
the activation to fail of names cannot be resolved.
https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/issues/535
https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/issues/616
https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/721
2021-01-08 11:09:03 +01:00
|
|
|
NMDeviceWireGuardPrivate *priv = NM_DEVICE_WIREGUARD_GET_PRIVATE(self);
|
2021-11-09 13:28:54 +01:00
|
|
|
PeerData *peer_data;
|
2019-01-12 12:33:35 +01:00
|
|
|
|
|
|
|
|
while ((peer_data = c_list_first_entry(&priv->lst_peers_head, PeerData, lst_peers)))
|
wireguard: delay activation while resolving DNS names for WireGuard peers to avoid race
The endpoints of WireGuard peers can be configured as DNS name, which
NetworkManager will resolve.
Since activating a profile might affect now names get resolved, we must
first resolve names before completing the activation of the WireGuard
device (and before reconfiguring DNS accordingly).
For example, if you configure exclusive DNS resolution via the WireGuard
device, and if the peer needs to be resolved via DNS, then resolving the
peer name must happen before the reconfiguration of DNS. Otherwise the
new DNS configuration will be broken due to being unable to reach the
WireGuard peer.
Fix that by waiting.
There is still an unfixed problem. If resolving any peers fails,
activation silently proceeds -- again possibly breaking the network
setup. Of course, NetworkManager will repeatedly try to re-resolve
the name, but that may never succeed if DNS would be resolved via
the VPN itself.
That is different from `wg set` which resolves hostnames and fails.
Consequently `wg-quick up` would also fail. But these are both one shot
applications, they are not around and basically let the user handle the
error (by reading the log and invoking the command again). NetworkManager
can do something different and proceed activation (as it will also
periodically re-resolve the hostnames again). Note that it's also valid
to activate a WireGuard device without any peers (and to modify the
activated device later with Reapply()). As such, having no peers (or
being unable to resolve a hostname) may be a valid configuration.
I think we should add an option/flag that when enabled will cause
the activation to fail of names cannot be resolved.
https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/issues/535
https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/issues/616
https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/721
2021-01-08 11:09:03 +01:00
|
|
|
_peers_remove(self, peer_data);
|
2019-01-12 12:33:35 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
_peers_update_all(NMDeviceWireGuard *self, NMSettingWireGuard *s_wg, gboolean *out_peers_removed)
|
|
|
|
|
{
|
|
|
|
|
NMDeviceWireGuardPrivate *priv = NM_DEVICE_WIREGUARD_GET_PRIVATE(self);
|
2021-11-09 13:28:54 +01:00
|
|
|
PeerData *peer_data_safe;
|
|
|
|
|
PeerData *peer_data;
|
2019-01-12 12:33:35 +01:00
|
|
|
guint i, n;
|
|
|
|
|
gboolean peers_removed = FALSE;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2019-01-12 12:33:35 +01:00
|
|
|
c_list_for_each_entry (peer_data, &priv->lst_peers_head, lst_peers)
|
|
|
|
|
peer_data->dirty_update_all = TRUE;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2019-01-12 12:33:35 +01:00
|
|
|
n = nm_setting_wireguard_get_peers_len(s_wg);
|
|
|
|
|
for (i = 0; i < n; i++) {
|
|
|
|
|
NMWireGuardPeer *peer = nm_setting_wireguard_get_peer(s_wg, i);
|
|
|
|
|
gboolean added = FALSE;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2019-01-12 12:33:35 +01:00
|
|
|
peer_data = _peers_find(priv, peer);
|
|
|
|
|
if (!peer_data) {
|
|
|
|
|
peer_data = _peers_add(self, peer);
|
|
|
|
|
added = TRUE;
|
|
|
|
|
}
|
|
|
|
|
_peers_update(self, peer_data, peer, added);
|
|
|
|
|
peer_data->dirty_update_all = FALSE;
|
|
|
|
|
}
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2019-01-12 12:33:35 +01:00
|
|
|
c_list_for_each_entry_safe (peer_data, peer_data_safe, &priv->lst_peers_head, lst_peers) {
|
|
|
|
|
if (peer_data->dirty_update_all) {
|
wireguard: delay activation while resolving DNS names for WireGuard peers to avoid race
The endpoints of WireGuard peers can be configured as DNS name, which
NetworkManager will resolve.
Since activating a profile might affect now names get resolved, we must
first resolve names before completing the activation of the WireGuard
device (and before reconfiguring DNS accordingly).
For example, if you configure exclusive DNS resolution via the WireGuard
device, and if the peer needs to be resolved via DNS, then resolving the
peer name must happen before the reconfiguration of DNS. Otherwise the
new DNS configuration will be broken due to being unable to reach the
WireGuard peer.
Fix that by waiting.
There is still an unfixed problem. If resolving any peers fails,
activation silently proceeds -- again possibly breaking the network
setup. Of course, NetworkManager will repeatedly try to re-resolve
the name, but that may never succeed if DNS would be resolved via
the VPN itself.
That is different from `wg set` which resolves hostnames and fails.
Consequently `wg-quick up` would also fail. But these are both one shot
applications, they are not around and basically let the user handle the
error (by reading the log and invoking the command again). NetworkManager
can do something different and proceed activation (as it will also
periodically re-resolve the hostnames again). Note that it's also valid
to activate a WireGuard device without any peers (and to modify the
activated device later with Reapply()). As such, having no peers (or
being unable to resolve a hostname) may be a valid configuration.
I think we should add an option/flag that when enabled will cause
the activation to fail of names cannot be resolved.
https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/issues/535
https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/issues/616
https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/721
2021-01-08 11:09:03 +01:00
|
|
|
_peers_remove(self, peer_data);
|
2019-01-12 12:33:35 +01:00
|
|
|
peers_removed = TRUE;
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2019-01-12 12:33:35 +01:00
|
|
|
NM_SET_OUT(out_peers_removed, peers_removed);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2021-11-09 13:28:54 +01:00
|
|
|
_peers_get_platform_list(NMDeviceWireGuardPrivate *priv,
|
2019-01-12 12:33:35 +01:00
|
|
|
LinkConfigMode config_mode,
|
2021-11-09 13:28:54 +01:00
|
|
|
NMPWireGuardPeer **out_peers,
|
2019-01-12 12:33:35 +01:00
|
|
|
NMPlatformWireGuardChangePeerFlags **out_peer_flags,
|
2021-11-09 13:28:54 +01:00
|
|
|
guint *out_len,
|
|
|
|
|
GArray **out_allowed_ips_data)
|
2019-01-12 12:33:35 +01:00
|
|
|
{
|
2021-11-09 13:28:54 +01:00
|
|
|
gs_free NMPWireGuardPeer *plpeers = NULL;
|
2019-01-12 12:33:35 +01:00
|
|
|
gs_free NMPlatformWireGuardChangePeerFlags *plpeer_flags = NULL;
|
2021-11-09 13:28:54 +01:00
|
|
|
gs_unref_array GArray *allowed_ips = NULL;
|
|
|
|
|
PeerData *peer_data;
|
|
|
|
|
guint i_good;
|
|
|
|
|
guint n_aip;
|
|
|
|
|
guint i_aip;
|
|
|
|
|
guint len;
|
|
|
|
|
guint i;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2019-01-12 12:33:35 +01:00
|
|
|
nm_assert(out_peers && !*out_peers);
|
|
|
|
|
nm_assert(out_peer_flags && !*out_peer_flags);
|
|
|
|
|
nm_assert(out_len && *out_len == 0);
|
|
|
|
|
nm_assert(out_allowed_ips_data && !*out_allowed_ips_data);
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2019-01-12 12:33:35 +01:00
|
|
|
len = g_hash_table_size(priv->peers);
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2019-01-12 12:33:35 +01:00
|
|
|
nm_assert(len == c_list_length(&priv->lst_peers_head));
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2019-01-12 12:33:35 +01:00
|
|
|
if (len == 0)
|
|
|
|
|
return;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2019-01-12 12:33:35 +01:00
|
|
|
plpeers = g_new0(NMPWireGuardPeer, len);
|
|
|
|
|
plpeer_flags = g_new0(NMPlatformWireGuardChangePeerFlags, len);
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2019-01-12 12:33:35 +01:00
|
|
|
i_good = 0;
|
|
|
|
|
c_list_for_each_entry (peer_data, &priv->lst_peers_head, lst_peers) {
|
|
|
|
|
NMPlatformWireGuardChangePeerFlags *plf = &plpeer_flags[i_good];
|
2021-11-09 13:28:54 +01:00
|
|
|
NMPWireGuardPeer *plp = &plpeers[i_good];
|
2019-01-12 12:33:35 +01:00
|
|
|
NMSettingSecretFlags psk_secret_flags;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2019-03-02 17:10:25 +01:00
|
|
|
if (!nm_utils_base64secret_decode(nm_wireguard_peer_get_public_key(peer_data->peer),
|
|
|
|
|
sizeof(plp->public_key),
|
|
|
|
|
plp->public_key))
|
2019-01-12 12:33:35 +01:00
|
|
|
continue;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2019-01-12 12:33:35 +01:00
|
|
|
*plf = NM_PLATFORM_WIREGUARD_CHANGE_PEER_FLAG_NONE;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2019-01-12 12:33:35 +01:00
|
|
|
plp->persistent_keepalive_interval =
|
|
|
|
|
nm_wireguard_peer_get_persistent_keepalive(peer_data->peer);
|
|
|
|
|
if (NM_IN_SET(config_mode, LINK_CONFIG_MODE_FULL, LINK_CONFIG_MODE_REAPPLY))
|
|
|
|
|
*plf |= NM_PLATFORM_WIREGUARD_CHANGE_PEER_FLAG_HAS_KEEPALIVE_INTERVAL;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2019-01-12 12:33:35 +01:00
|
|
|
/* if the peer has an endpoint but it is not yet resolved (not ready),
|
|
|
|
|
* we still configure it and leave the endpoint unspecified. Later,
|
|
|
|
|
* when we can resolve the endpoint, we will update. */
|
|
|
|
|
plp->endpoint = peer_data->ep_resolv.sockaddr;
|
|
|
|
|
if (plp->endpoint.sa.sa_family == AF_UNSPEC) {
|
|
|
|
|
/* we don't actually ever clear endpoints, if we don't have better information. */
|
|
|
|
|
} else
|
|
|
|
|
*plf |= NM_PLATFORM_WIREGUARD_CHANGE_PEER_FLAG_HAS_ENDPOINT;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2019-01-12 12:33:35 +01:00
|
|
|
if (NM_IN_SET(config_mode, LINK_CONFIG_MODE_FULL, LINK_CONFIG_MODE_REAPPLY)) {
|
|
|
|
|
psk_secret_flags = nm_wireguard_peer_get_preshared_key_flags(peer_data->peer);
|
|
|
|
|
if (!NM_FLAGS_HAS(psk_secret_flags, NM_SETTING_SECRET_FLAG_NOT_REQUIRED)) {
|
2019-03-02 17:10:25 +01:00
|
|
|
if (!nm_utils_base64secret_decode(
|
|
|
|
|
nm_wireguard_peer_get_preshared_key(peer_data->peer),
|
|
|
|
|
sizeof(plp->preshared_key),
|
|
|
|
|
plp->preshared_key)
|
2019-01-12 12:33:35 +01:00
|
|
|
&& config_mode == LINK_CONFIG_MODE_FULL)
|
|
|
|
|
goto skip;
|
|
|
|
|
}
|
|
|
|
|
*plf |= NM_PLATFORM_WIREGUARD_CHANGE_PEER_FLAG_HAS_PRESHARED_KEY;
|
|
|
|
|
}
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2019-01-12 12:33:35 +01:00
|
|
|
if (NM_IN_SET(config_mode, LINK_CONFIG_MODE_FULL, LINK_CONFIG_MODE_REAPPLY)
|
|
|
|
|
&& ((n_aip = nm_wireguard_peer_get_allowed_ips_len(peer_data->peer)) > 0)) {
|
|
|
|
|
if (!allowed_ips)
|
|
|
|
|
allowed_ips = g_array_new(FALSE, FALSE, sizeof(NMPWireGuardAllowedIP));
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2019-01-12 12:33:35 +01:00
|
|
|
*plf |= NM_PLATFORM_WIREGUARD_CHANGE_PEER_FLAG_HAS_ALLOWEDIPS
|
|
|
|
|
| NM_PLATFORM_WIREGUARD_CHANGE_PEER_FLAG_REPLACE_ALLOWEDIPS;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2019-01-12 12:33:35 +01:00
|
|
|
plp->_construct_idx_start = allowed_ips->len;
|
|
|
|
|
for (i_aip = 0; i_aip < n_aip; i_aip++) {
|
|
|
|
|
const char *aip;
|
|
|
|
|
NMIPAddr addrbin = {};
|
|
|
|
|
int addr_family;
|
|
|
|
|
gboolean valid;
|
|
|
|
|
int prefix;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2019-01-12 12:33:35 +01:00
|
|
|
aip = nm_wireguard_peer_get_allowed_ip(peer_data->peer, i_aip, &valid);
|
|
|
|
|
if (!valid
|
|
|
|
|
|| !nm_utils_parse_inaddr_prefix_bin(AF_UNSPEC,
|
|
|
|
|
aip,
|
|
|
|
|
&addr_family,
|
|
|
|
|
&addrbin,
|
|
|
|
|
&prefix)) {
|
|
|
|
|
/* the address is really not expected to be invalid, because then
|
|
|
|
|
* the connection would not verify. Anyway, silently skip it. */
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (prefix == -1)
|
|
|
|
|
prefix = addr_family == AF_INET ? 32 : 128;
|
|
|
|
|
|
|
|
|
|
g_array_append_val(allowed_ips,
|
|
|
|
|
((NMPWireGuardAllowedIP){
|
|
|
|
|
.family = addr_family,
|
|
|
|
|
.mask = prefix,
|
|
|
|
|
.addr = addrbin,
|
|
|
|
|
}));
|
|
|
|
|
}
|
|
|
|
|
plp->_construct_idx_end = allowed_ips->len;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
i_good++;
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
skip:
|
|
|
|
|
memset(plp, 0, sizeof(*plp));
|
|
|
|
|
}
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2019-01-12 12:33:35 +01:00
|
|
|
if (i_good == 0)
|
|
|
|
|
return;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2019-01-12 12:33:35 +01:00
|
|
|
for (i = 0; i < i_good; i++) {
|
|
|
|
|
NMPWireGuardPeer *plp = &plpeers[i];
|
|
|
|
|
guint l;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2019-01-12 12:33:35 +01:00
|
|
|
if (plp->_construct_idx_end == 0) {
|
|
|
|
|
nm_assert(plp->_construct_idx_start == 0);
|
|
|
|
|
plp->allowed_ips = NULL;
|
|
|
|
|
plp->allowed_ips_len = 0;
|
|
|
|
|
} else {
|
|
|
|
|
nm_assert(plp->_construct_idx_start < plp->_construct_idx_end);
|
|
|
|
|
l = plp->_construct_idx_end - plp->_construct_idx_start;
|
|
|
|
|
plp->allowed_ips =
|
|
|
|
|
&g_array_index(allowed_ips, NMPWireGuardAllowedIP, plp->_construct_idx_start);
|
|
|
|
|
plp->allowed_ips_len = l;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
*out_peers = g_steal_pointer(&plpeers);
|
2020-09-23 12:43:47 +02:00
|
|
|
*out_peer_flags = g_steal_pointer(&plpeer_flags);
|
2019-01-12 12:33:35 +01:00
|
|
|
*out_len = i_good;
|
|
|
|
|
*out_allowed_ips_data = g_steal_pointer(&allowed_ips);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2018-03-13 13:42:38 +00:00
|
|
|
static void
|
|
|
|
|
update_properties(NMDevice *device)
|
|
|
|
|
{
|
2021-11-09 13:28:54 +01:00
|
|
|
NMDeviceWireGuard *self;
|
|
|
|
|
NMDeviceWireGuardPrivate *priv;
|
|
|
|
|
const NMPlatformLink *plink;
|
2018-03-13 13:42:38 +00:00
|
|
|
const NMPlatformLnkWireGuard *props = NULL;
|
|
|
|
|
int ifindex;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2018-03-13 13:42:38 +00:00
|
|
|
g_return_if_fail(NM_IS_DEVICE_WIREGUARD(device));
|
|
|
|
|
self = NM_DEVICE_WIREGUARD(device);
|
2018-12-29 00:04:20 +01:00
|
|
|
priv = NM_DEVICE_WIREGUARD_GET_PRIVATE(self);
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2018-03-13 13:42:38 +00:00
|
|
|
ifindex = nm_device_get_ifindex(device);
|
|
|
|
|
props = nm_platform_link_get_lnk_wireguard(nm_device_get_platform(device), ifindex, &plink);
|
|
|
|
|
if (!props) {
|
|
|
|
|
_LOGW(LOGD_PLATFORM, "could not get wireguard properties");
|
|
|
|
|
return;
|
|
|
|
|
}
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2018-03-13 13:42:38 +00:00
|
|
|
g_object_freeze_notify(G_OBJECT(device));
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2018-03-13 13:42:38 +00:00
|
|
|
#define CHECK_PROPERTY_CHANGED(field, prop) \
|
|
|
|
|
G_STMT_START \
|
|
|
|
|
{ \
|
2018-12-29 00:04:20 +01:00
|
|
|
if (priv->lnk_curr.field != props->field) { \
|
|
|
|
|
priv->lnk_curr.field = props->field; \
|
2018-03-13 13:42:38 +00:00
|
|
|
_notify(self, prop); \
|
2020-09-28 16:03:33 +02:00
|
|
|
} \
|
2018-03-13 13:42:38 +00:00
|
|
|
} \
|
|
|
|
|
G_STMT_END
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2018-03-13 13:42:38 +00:00
|
|
|
#define CHECK_PROPERTY_CHANGED_ARRAY(field, prop) \
|
|
|
|
|
G_STMT_START \
|
|
|
|
|
{ \
|
2018-12-29 00:04:20 +01:00
|
|
|
if (memcmp(&priv->lnk_curr.field, &props->field, sizeof(priv->lnk_curr.field)) != 0) { \
|
|
|
|
|
memcpy(&priv->lnk_curr.field, &props->field, sizeof(priv->lnk_curr.field)); \
|
2018-03-13 13:42:38 +00:00
|
|
|
_notify(self, prop); \
|
|
|
|
|
} \
|
|
|
|
|
} \
|
|
|
|
|
G_STMT_END
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2018-03-13 13:42:38 +00:00
|
|
|
CHECK_PROPERTY_CHANGED_ARRAY(public_key, PROP_PUBLIC_KEY);
|
|
|
|
|
CHECK_PROPERTY_CHANGED(listen_port, PROP_LISTEN_PORT);
|
|
|
|
|
CHECK_PROPERTY_CHANGED(fwmark, PROP_FWMARK);
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2018-03-13 13:42:38 +00:00
|
|
|
g_object_thaw_notify(G_OBJECT(device));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
link_changed(NMDevice *device, const NMPlatformLink *pllink)
|
|
|
|
|
{
|
|
|
|
|
NM_DEVICE_CLASS(nm_device_wireguard_parent_class)->link_changed(device, pllink);
|
|
|
|
|
update_properties(device);
|
|
|
|
|
}
|
|
|
|
|
|
2018-12-29 00:04:20 +01:00
|
|
|
static NMDeviceCapabilities
|
|
|
|
|
get_generic_capabilities(NMDevice *dev)
|
|
|
|
|
{
|
|
|
|
|
return NM_DEVICE_CAP_IS_SOFTWARE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
static gboolean
|
2021-11-09 13:28:54 +01:00
|
|
|
create_and_realize(NMDevice *device,
|
|
|
|
|
NMConnection *connection,
|
|
|
|
|
NMDevice *parent,
|
2018-12-29 00:04:20 +01:00
|
|
|
const NMPlatformLink **out_plink,
|
2021-11-09 13:28:54 +01:00
|
|
|
GError **error)
|
2018-12-29 00:04:20 +01:00
|
|
|
{
|
|
|
|
|
const char *iface = nm_device_get_iface(device);
|
|
|
|
|
int r;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2018-12-29 00:04:20 +01:00
|
|
|
g_return_val_if_fail(iface, FALSE);
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2018-12-29 00:04:20 +01:00
|
|
|
r = nm_platform_link_wireguard_add(nm_device_get_platform(device), iface, out_plink);
|
|
|
|
|
if (r < 0) {
|
|
|
|
|
g_set_error(error,
|
|
|
|
|
NM_DEVICE_ERROR,
|
|
|
|
|
NM_DEVICE_ERROR_CREATION_FAILED,
|
|
|
|
|
"Failed to create WireGuard interface '%s' for '%s': %s",
|
|
|
|
|
iface,
|
|
|
|
|
nm_connection_get_id(connection),
|
|
|
|
|
nm_strerror(r));
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2018-12-29 00:04:20 +01:00
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
_secrets_cancel(NMDeviceWireGuard *self)
|
|
|
|
|
{
|
|
|
|
|
NMDeviceWireGuardPrivate *priv = NM_DEVICE_WIREGUARD_GET_PRIVATE(self);
|
|
|
|
|
|
|
|
|
|
if (priv->secrets_call_id)
|
|
|
|
|
nm_act_request_cancel_secrets(NULL, priv->secrets_call_id);
|
|
|
|
|
nm_assert(!priv->secrets_call_id);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2021-11-09 13:28:54 +01:00
|
|
|
_secrets_cb(NMActRequest *req,
|
2018-12-29 00:04:20 +01:00
|
|
|
NMActRequestGetSecretsCallId *call_id,
|
2021-11-09 13:28:54 +01:00
|
|
|
NMSettingsConnection *connection,
|
|
|
|
|
GError *error,
|
2018-12-29 00:04:20 +01:00
|
|
|
gpointer user_data)
|
|
|
|
|
{
|
2021-11-09 13:28:54 +01:00
|
|
|
NMDeviceWireGuard *self = NM_DEVICE_WIREGUARD(user_data);
|
|
|
|
|
NMDevice *device = NM_DEVICE(self);
|
2018-12-29 00:04:20 +01:00
|
|
|
NMDeviceWireGuardPrivate *priv;
|
|
|
|
|
|
|
|
|
|
g_return_if_fail(NM_IS_DEVICE_WIREGUARD(self));
|
|
|
|
|
g_return_if_fail(NM_IS_ACT_REQUEST(req));
|
|
|
|
|
|
|
|
|
|
priv = NM_DEVICE_WIREGUARD_GET_PRIVATE(self);
|
|
|
|
|
|
|
|
|
|
g_return_if_fail(priv->secrets_call_id == call_id);
|
|
|
|
|
|
|
|
|
|
priv->secrets_call_id = NULL;
|
|
|
|
|
|
|
|
|
|
if (g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
|
|
|
|
|
return;
|
2018-03-13 13:42:38 +00:00
|
|
|
|
2018-12-29 00:04:20 +01:00
|
|
|
g_return_if_fail(req == nm_device_get_act_request(device));
|
|
|
|
|
g_return_if_fail(nm_device_get_state(device) == NM_DEVICE_STATE_NEED_AUTH);
|
|
|
|
|
g_return_if_fail(nm_act_request_get_settings_connection(req) == connection);
|
|
|
|
|
|
|
|
|
|
if (error) {
|
|
|
|
|
_LOGW(LOGD_ETHER, "%s", error->message);
|
|
|
|
|
nm_device_state_changed(device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_NO_SECRETS);
|
2019-08-22 10:43:22 +02:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2020-02-28 13:55:40 +01:00
|
|
|
nm_device_activate_schedule_stage1_device_prepare(device, FALSE);
|
2018-12-29 00:04:20 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2021-11-09 13:28:54 +01:00
|
|
|
_secrets_get_secrets(NMDeviceWireGuard *self,
|
|
|
|
|
const char *setting_name,
|
2018-12-29 00:04:20 +01:00
|
|
|
NMSecretAgentGetSecretsFlags flags,
|
2021-11-09 13:28:54 +01:00
|
|
|
const char *const *hints)
|
2018-12-29 00:04:20 +01:00
|
|
|
{
|
|
|
|
|
NMDeviceWireGuardPrivate *priv = NM_DEVICE_WIREGUARD_GET_PRIVATE(self);
|
2021-11-09 13:28:54 +01:00
|
|
|
NMActRequest *req;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2018-12-29 00:04:20 +01:00
|
|
|
_secrets_cancel(self);
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2018-12-29 00:04:20 +01:00
|
|
|
req = nm_device_get_act_request(NM_DEVICE(self));
|
|
|
|
|
g_return_if_fail(NM_IS_ACT_REQUEST(req));
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2018-12-29 00:04:20 +01:00
|
|
|
priv->secrets_call_id =
|
|
|
|
|
nm_act_request_get_secrets(req, TRUE, setting_name, flags, hints, _secrets_cb, self);
|
|
|
|
|
g_return_if_fail(priv->secrets_call_id);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static NMActStageReturn
|
|
|
|
|
_secrets_handle_auth_or_fail(NMDeviceWireGuard *self, NMActRequest *req, gboolean new_secrets)
|
|
|
|
|
{
|
2021-11-09 13:28:54 +01:00
|
|
|
NMConnection *applied_connection;
|
|
|
|
|
const char *setting_name;
|
2018-12-29 00:04:20 +01:00
|
|
|
gs_unref_ptrarray GPtrArray *hints = NULL;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2018-12-29 00:04:20 +01:00
|
|
|
if (!nm_device_auth_retries_try_next(NM_DEVICE(self)))
|
|
|
|
|
return NM_ACT_STAGE_RETURN_FAILURE;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2018-12-29 00:04:20 +01:00
|
|
|
nm_device_state_changed(NM_DEVICE(self),
|
|
|
|
|
NM_DEVICE_STATE_NEED_AUTH,
|
|
|
|
|
NM_DEVICE_STATE_REASON_NONE);
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2018-12-29 00:04:20 +01:00
|
|
|
nm_active_connection_clear_secrets(NM_ACTIVE_CONNECTION(req));
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2018-12-29 00:04:20 +01:00
|
|
|
applied_connection = nm_act_request_get_applied_connection(req);
|
|
|
|
|
setting_name = nm_connection_need_secrets(applied_connection, &hints);
|
|
|
|
|
if (!setting_name) {
|
|
|
|
|
_LOGI(LOGD_DEVICE, "Cleared secrets, but setting didn't need any secrets.");
|
|
|
|
|
return NM_ACT_STAGE_RETURN_FAILURE;
|
|
|
|
|
}
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2018-12-29 00:04:20 +01:00
|
|
|
if (hints)
|
|
|
|
|
g_ptr_array_add(hints, NULL);
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2018-12-29 00:04:20 +01:00
|
|
|
_secrets_get_secrets(self,
|
|
|
|
|
setting_name,
|
|
|
|
|
NM_SECRET_AGENT_GET_SECRETS_FLAG_ALLOW_INTERACTION
|
|
|
|
|
| (new_secrets ? NM_SECRET_AGENT_GET_SECRETS_FLAG_REQUEST_NEW : 0),
|
|
|
|
|
(hints ? (const char *const *) hints->pdata : NULL));
|
|
|
|
|
return NM_ACT_STAGE_RETURN_POSTPONE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2019-01-12 12:33:35 +01:00
|
|
|
static void
|
|
|
|
|
_dns_config_changed(NMDnsManager *dns_manager, NMDeviceWireGuard *self)
|
|
|
|
|
{
|
|
|
|
|
/* when the DNS configuration changes, we re-resolve the peer addresses.
|
|
|
|
|
*
|
|
|
|
|
* Possibly, we should also do that when the default-route changes, but it's
|
|
|
|
|
* hard to figure out when that happens. */
|
|
|
|
|
_peers_resolve_reresolve_all(self);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2018-12-29 00:04:20 +01:00
|
|
|
static NMActStageReturn
|
2021-11-09 13:28:54 +01:00
|
|
|
link_config(NMDeviceWireGuard *self,
|
|
|
|
|
const char *reason,
|
2019-01-12 12:33:35 +01:00
|
|
|
LinkConfigMode config_mode,
|
2018-12-29 00:04:20 +01:00
|
|
|
NMDeviceStateReason *out_failure_reason)
|
|
|
|
|
{
|
2021-11-09 13:28:54 +01:00
|
|
|
NMDeviceWireGuardPrivate *priv = NM_DEVICE_WIREGUARD_GET_PRIVATE(self);
|
|
|
|
|
nm_auto_bzero_secret_ptr NMSecretPtr wg_lnk_clear_private_key = NM_SECRET_PTR_INIT();
|
|
|
|
|
NMSettingWireGuard *s_wg;
|
|
|
|
|
NMConnection *connection;
|
|
|
|
|
NMActStageReturn ret;
|
|
|
|
|
gs_unref_array GArray *allowed_ips_data = NULL;
|
|
|
|
|
NMPlatformLnkWireGuard wg_lnk;
|
|
|
|
|
gs_free NMPWireGuardPeer *plpeers = NULL;
|
2019-01-12 12:33:35 +01:00
|
|
|
gs_free NMPlatformWireGuardChangePeerFlags *plpeer_flags = NULL;
|
|
|
|
|
guint plpeers_len = 0;
|
2021-11-09 13:28:54 +01:00
|
|
|
const char *setting_name;
|
2019-01-12 12:33:35 +01:00
|
|
|
gboolean peers_removed;
|
|
|
|
|
NMPlatformWireGuardChangeFlags wg_change_flags;
|
2018-12-29 00:04:20 +01:00
|
|
|
int ifindex;
|
|
|
|
|
int r;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2019-01-12 12:33:35 +01:00
|
|
|
NM_SET_OUT(out_failure_reason, NM_DEVICE_STATE_REASON_NONE);
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2018-12-29 00:04:20 +01:00
|
|
|
connection = nm_device_get_applied_connection(NM_DEVICE(self));
|
|
|
|
|
s_wg = NM_SETTING_WIREGUARD(nm_connection_get_setting(connection, NM_TYPE_SETTING_WIREGUARD));
|
|
|
|
|
g_return_val_if_fail(s_wg, NM_ACT_STAGE_RETURN_FAILURE);
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2019-12-13 16:54:30 +01:00
|
|
|
priv->link_config_last_at = nm_utils_get_monotonic_timestamp_nsec();
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2019-01-12 12:33:35 +01:00
|
|
|
_LOGT(LOGD_DEVICE,
|
|
|
|
|
"wireguard link config (%s, %s)...",
|
|
|
|
|
reason,
|
|
|
|
|
_link_config_mode_to_string(config_mode));
|
2020-09-28 16:03:33 +02:00
|
|
|
|
wireguard: support configuring policy routing to avoid routing loops
For WireGuard (like for all IP-tunnels and IP-based VPNs), the IP addresses of
the peers must be reached outside the tunnel/VPN itself.
For VPN connections, NetworkManager usually adds a direct /32 route to
the external VPN gateway to the underlying device. For WireGuard that is
not done, because injecting a route to another device is ugly and error
prone. Worse: WireGuard with automatic roaming and multiple peers makes this
more complicated.
This is commonly a problem when setting the default-route via the VPN,
but there are also other subtle setups where special care must be taken
to prevent such routing loops.
WireGuard's wg-quick provides a simple, automatic solution by adding two policy
routing rules and relying on the WireGuard packets having a fwmark set (see [1]).
Let's also do that. Add new properties "wireguard.ip4-auto-default-route"
and "wireguard.ip6-auto-default-route" to enable/disable this. Note that
the default value lets NetworkManager automatically choose whether to
enable it (depending on whether there are any peers that have a default
route). This means, common scenarios should now work well without additional
configuration.
Note that this is also a change in behavior and upon package upgrade
NetworkManager may start adding policy routes (if there are peers that
have a default-route). This is a change in behavior, as the user already
clearly had this setup working and configured some working solution
already.
The new automatism picks the rule priority automatically and adds the
default-route to the routing table that has the same number as the fwmark.
If any of this is unsuitable, then the user is free to disable this
automatism. Note that since 1.18.0 NetworkManager supports policy routing (*).
That means, what this automatism does can be also achieved via explicit
configuration of the profile, which gives the user more flexibility to
adjust all parameters explicitly).
(*) but only since 1.20.0 NetworkManager supports the "suppress_prefixlength"
rule attribute, which makes it impossible to configure exactly this rule-based
solution with 1.18.0 NetworkManager.
[1] https://www.wireguard.com/netns/#improved-rule-based-routing
2019-04-30 17:48:46 +02:00
|
|
|
_auto_default_route_init(self);
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2019-01-12 12:33:35 +01:00
|
|
|
if (!priv->dns_manager) {
|
|
|
|
|
priv->dns_manager = g_object_ref(nm_dns_manager_get());
|
|
|
|
|
g_signal_connect(priv->dns_manager,
|
|
|
|
|
NM_DNS_MANAGER_CONFIG_CHANGED,
|
|
|
|
|
G_CALLBACK(_dns_config_changed),
|
|
|
|
|
self);
|
|
|
|
|
}
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2019-01-12 12:33:35 +01:00
|
|
|
if (NM_IN_SET(config_mode, LINK_CONFIG_MODE_FULL)
|
|
|
|
|
&& (setting_name = nm_connection_need_secrets(connection, NULL))) {
|
2018-12-29 00:04:20 +01:00
|
|
|
NMActRequest *req = nm_device_get_act_request(NM_DEVICE(self));
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2018-12-29 00:04:20 +01:00
|
|
|
_LOGD(LOGD_DEVICE,
|
|
|
|
|
"Activation: connection '%s' has security, but secrets are required.",
|
|
|
|
|
nm_connection_get_id(connection));
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2018-12-29 00:04:20 +01:00
|
|
|
ret = _secrets_handle_auth_or_fail(self, req, FALSE);
|
|
|
|
|
if (ret != NM_ACT_STAGE_RETURN_SUCCESS) {
|
2019-01-12 12:33:35 +01:00
|
|
|
if (ret != NM_ACT_STAGE_RETURN_POSTPONE) {
|
|
|
|
|
nm_assert(ret == NM_ACT_STAGE_RETURN_FAILURE);
|
|
|
|
|
NM_SET_OUT(out_failure_reason, NM_DEVICE_STATE_REASON_NO_SECRETS);
|
|
|
|
|
}
|
|
|
|
|
return ret;
|
2018-12-29 00:04:20 +01:00
|
|
|
}
|
2020-09-28 16:03:33 +02:00
|
|
|
}
|
|
|
|
|
|
2018-12-29 00:04:20 +01:00
|
|
|
ifindex = nm_device_get_ip_ifindex(NM_DEVICE(self));
|
|
|
|
|
if (ifindex <= 0) {
|
2019-01-12 12:33:35 +01:00
|
|
|
NM_SET_OUT(out_failure_reason, NM_DEVICE_STATE_REASON_CONFIG_FAILED);
|
|
|
|
|
return NM_ACT_STAGE_RETURN_FAILURE;
|
2018-12-29 00:04:20 +01:00
|
|
|
}
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2019-01-12 12:33:35 +01:00
|
|
|
_peers_update_all(self, s_wg, &peers_removed);
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2019-01-12 12:33:35 +01:00
|
|
|
wg_lnk = (NMPlatformLnkWireGuard){};
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2019-01-12 12:33:35 +01:00
|
|
|
wg_change_flags = NM_PLATFORM_WIREGUARD_CHANGE_FLAG_NONE;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2019-01-12 12:33:35 +01:00
|
|
|
if (NM_IN_SET(config_mode, LINK_CONFIG_MODE_FULL)
|
|
|
|
|
|| (NM_IN_SET(config_mode, LINK_CONFIG_MODE_REAPPLY) && peers_removed))
|
|
|
|
|
wg_change_flags |= NM_PLATFORM_WIREGUARD_CHANGE_FLAG_REPLACE_PEERS;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2019-01-12 12:33:35 +01:00
|
|
|
if (NM_IN_SET(config_mode, LINK_CONFIG_MODE_FULL, LINK_CONFIG_MODE_REAPPLY)) {
|
2019-07-15 11:00:01 +02:00
|
|
|
wg_lnk.listen_port = nm_setting_wireguard_get_listen_port(s_wg);
|
2019-01-12 12:33:35 +01:00
|
|
|
wg_change_flags |= NM_PLATFORM_WIREGUARD_CHANGE_FLAG_HAS_LISTEN_PORT;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
wireguard: support configuring policy routing to avoid routing loops
For WireGuard (like for all IP-tunnels and IP-based VPNs), the IP addresses of
the peers must be reached outside the tunnel/VPN itself.
For VPN connections, NetworkManager usually adds a direct /32 route to
the external VPN gateway to the underlying device. For WireGuard that is
not done, because injecting a route to another device is ugly and error
prone. Worse: WireGuard with automatic roaming and multiple peers makes this
more complicated.
This is commonly a problem when setting the default-route via the VPN,
but there are also other subtle setups where special care must be taken
to prevent such routing loops.
WireGuard's wg-quick provides a simple, automatic solution by adding two policy
routing rules and relying on the WireGuard packets having a fwmark set (see [1]).
Let's also do that. Add new properties "wireguard.ip4-auto-default-route"
and "wireguard.ip6-auto-default-route" to enable/disable this. Note that
the default value lets NetworkManager automatically choose whether to
enable it (depending on whether there are any peers that have a default
route). This means, common scenarios should now work well without additional
configuration.
Note that this is also a change in behavior and upon package upgrade
NetworkManager may start adding policy routes (if there are peers that
have a default-route). This is a change in behavior, as the user already
clearly had this setup working and configured some working solution
already.
The new automatism picks the rule priority automatically and adds the
default-route to the routing table that has the same number as the fwmark.
If any of this is unsuitable, then the user is free to disable this
automatism. Note that since 1.18.0 NetworkManager supports policy routing (*).
That means, what this automatism does can be also achieved via explicit
configuration of the profile, which gives the user more flexibility to
adjust all parameters explicitly).
(*) but only since 1.20.0 NetworkManager supports the "suppress_prefixlength"
rule attribute, which makes it impossible to configure exactly this rule-based
solution with 1.18.0 NetworkManager.
[1] https://www.wireguard.com/netns/#improved-rule-based-routing
2019-04-30 17:48:46 +02:00
|
|
|
wg_lnk.fwmark = priv->auto_default_route_fwmark;
|
2019-01-12 12:33:35 +01:00
|
|
|
wg_change_flags |= NM_PLATFORM_WIREGUARD_CHANGE_FLAG_HAS_FWMARK;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2019-03-02 17:10:25 +01:00
|
|
|
if (nm_utils_base64secret_decode(nm_setting_wireguard_get_private_key(s_wg),
|
|
|
|
|
sizeof(wg_lnk.private_key),
|
|
|
|
|
wg_lnk.private_key)) {
|
2019-01-12 12:33:35 +01:00
|
|
|
wg_lnk_clear_private_key = NM_SECRET_PTR_ARRAY(wg_lnk.private_key);
|
|
|
|
|
wg_change_flags |= NM_PLATFORM_WIREGUARD_CHANGE_FLAG_HAS_PRIVATE_KEY;
|
|
|
|
|
} else {
|
|
|
|
|
if (NM_IN_SET(config_mode, LINK_CONFIG_MODE_FULL)) {
|
|
|
|
|
_LOGD(LOGD_DEVICE, "the provided private-key is invalid");
|
|
|
|
|
NM_SET_OUT(out_failure_reason, NM_DEVICE_STATE_REASON_NO_SECRETS);
|
|
|
|
|
return NM_ACT_STAGE_RETURN_FAILURE;
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-12-29 00:04:20 +01:00
|
|
|
}
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2019-01-12 12:33:35 +01:00
|
|
|
_peers_get_platform_list(priv,
|
|
|
|
|
config_mode,
|
|
|
|
|
&plpeers,
|
|
|
|
|
&plpeer_flags,
|
|
|
|
|
&plpeers_len,
|
|
|
|
|
&allowed_ips_data);
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2018-12-29 00:04:20 +01:00
|
|
|
r = nm_platform_link_wireguard_change(nm_device_get_platform(NM_DEVICE(self)),
|
|
|
|
|
ifindex,
|
2019-01-12 12:33:35 +01:00
|
|
|
&wg_lnk,
|
|
|
|
|
plpeers,
|
|
|
|
|
plpeer_flags,
|
|
|
|
|
plpeers_len,
|
|
|
|
|
wg_change_flags);
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2019-08-02 09:16:34 +02:00
|
|
|
nm_explicit_bzero(plpeers, sizeof(plpeers[0]) * plpeers_len);
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2018-12-29 00:04:20 +01:00
|
|
|
if (r < 0) {
|
2019-01-12 12:33:35 +01:00
|
|
|
NM_SET_OUT(out_failure_reason, NM_DEVICE_STATE_REASON_CONFIG_FAILED);
|
|
|
|
|
return NM_ACT_STAGE_RETURN_FAILURE;
|
2018-12-29 00:04:20 +01:00
|
|
|
}
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2018-12-29 00:04:20 +01:00
|
|
|
return NM_ACT_STAGE_RETURN_SUCCESS;
|
2019-01-12 12:33:35 +01:00
|
|
|
}
|
2018-12-29 00:04:20 +01:00
|
|
|
|
2019-01-12 12:33:35 +01:00
|
|
|
static void
|
|
|
|
|
link_config_delayed(NMDeviceWireGuard *self, const char *reason)
|
|
|
|
|
{
|
|
|
|
|
NMDeviceWireGuardPrivate *priv = NM_DEVICE_WIREGUARD_GET_PRIVATE(self);
|
|
|
|
|
gint64 now;
|
|
|
|
|
|
|
|
|
|
priv->link_config_delayed_id = 0;
|
|
|
|
|
|
|
|
|
|
if (priv->link_config_last_at != 0) {
|
2019-12-13 16:54:30 +01:00
|
|
|
now = nm_utils_get_monotonic_timestamp_nsec();
|
2019-01-12 12:33:35 +01:00
|
|
|
if (now < priv->link_config_last_at + LINK_CONFIG_RATE_LIMIT_NSEC) {
|
2019-03-11 12:00:32 +01:00
|
|
|
/* we ratelimit calls to link_config(), because we call this whenever a resolver
|
2019-01-12 12:33:35 +01:00
|
|
|
* completes. */
|
|
|
|
|
_LOGT(LOGD_DEVICE, "wireguard link config (%s) (postponed)", reason);
|
2019-12-13 16:54:30 +01:00
|
|
|
priv->link_config_delayed_id =
|
|
|
|
|
g_timeout_add(NM_MAX((priv->link_config_last_at + LINK_CONFIG_RATE_LIMIT_NSEC - now)
|
|
|
|
|
/ NM_UTILS_NSEC_PER_MSEC,
|
2019-01-12 12:33:35 +01:00
|
|
|
(gint64) 1),
|
|
|
|
|
link_config_delayed_ratelimit_cb,
|
|
|
|
|
self);
|
|
|
|
|
return;
|
2018-12-29 00:04:20 +01:00
|
|
|
}
|
2020-09-28 16:03:33 +02:00
|
|
|
}
|
|
|
|
|
|
2019-01-12 12:33:35 +01:00
|
|
|
link_config(self, reason, LINK_CONFIG_MODE_ENDPOINTS, NULL);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
|
link_config_delayed_ratelimit_cb(gpointer user_data)
|
|
|
|
|
{
|
|
|
|
|
link_config_delayed(user_data, "after-ratelimiting");
|
|
|
|
|
return G_SOURCE_REMOVE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
|
link_config_delayed_resolver_cb(gpointer user_data)
|
|
|
|
|
{
|
|
|
|
|
link_config_delayed(user_data, "resolver-update");
|
|
|
|
|
return G_SOURCE_REMOVE;
|
2018-12-29 00:04:20 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static NMActStageReturn
|
2019-01-12 12:33:35 +01:00
|
|
|
act_stage2_config(NMDevice *device, NMDeviceStateReason *out_failure_reason)
|
2018-12-29 00:04:20 +01:00
|
|
|
{
|
2021-11-09 13:28:54 +01:00
|
|
|
NMDeviceWireGuard *self = NM_DEVICE_WIREGUARD(device);
|
wireguard: delay activation while resolving DNS names for WireGuard peers to avoid race
The endpoints of WireGuard peers can be configured as DNS name, which
NetworkManager will resolve.
Since activating a profile might affect now names get resolved, we must
first resolve names before completing the activation of the WireGuard
device (and before reconfiguring DNS accordingly).
For example, if you configure exclusive DNS resolution via the WireGuard
device, and if the peer needs to be resolved via DNS, then resolving the
peer name must happen before the reconfiguration of DNS. Otherwise the
new DNS configuration will be broken due to being unable to reach the
WireGuard peer.
Fix that by waiting.
There is still an unfixed problem. If resolving any peers fails,
activation silently proceeds -- again possibly breaking the network
setup. Of course, NetworkManager will repeatedly try to re-resolve
the name, but that may never succeed if DNS would be resolved via
the VPN itself.
That is different from `wg set` which resolves hostnames and fails.
Consequently `wg-quick up` would also fail. But these are both one shot
applications, they are not around and basically let the user handle the
error (by reading the log and invoking the command again). NetworkManager
can do something different and proceed activation (as it will also
periodically re-resolve the hostnames again). Note that it's also valid
to activate a WireGuard device without any peers (and to modify the
activated device later with Reapply()). As such, having no peers (or
being unable to resolve a hostname) may be a valid configuration.
I think we should add an option/flag that when enabled will cause
the activation to fail of names cannot be resolved.
https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/issues/535
https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/issues/616
https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/721
2021-01-08 11:09:03 +01:00
|
|
|
NMDeviceWireGuardPrivate *priv = NM_DEVICE_WIREGUARD_GET_PRIVATE(self);
|
|
|
|
|
NMDeviceSysIfaceState sys_iface_state;
|
|
|
|
|
NMDeviceStateReason failure_reason;
|
|
|
|
|
NMActStageReturn ret;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2019-01-12 12:33:35 +01:00
|
|
|
sys_iface_state = nm_device_sys_iface_state_get(device);
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2019-01-12 12:33:35 +01:00
|
|
|
if (sys_iface_state == NM_DEVICE_SYS_IFACE_STATE_EXTERNAL) {
|
|
|
|
|
NM_SET_OUT(out_failure_reason, NM_DEVICE_STATE_REASON_NONE);
|
|
|
|
|
return NM_ACT_STAGE_RETURN_SUCCESS;
|
|
|
|
|
}
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2019-01-12 12:33:35 +01:00
|
|
|
ret =
|
|
|
|
|
link_config(NM_DEVICE_WIREGUARD(device),
|
2020-02-27 16:45:16 +01:00
|
|
|
"configure",
|
|
|
|
|
(sys_iface_state == NM_DEVICE_SYS_IFACE_STATE_ASSUME) ? LINK_CONFIG_MODE_ASSUME
|
|
|
|
|
: LINK_CONFIG_MODE_FULL,
|
|
|
|
|
&failure_reason);
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2020-02-27 16:45:16 +01:00
|
|
|
if (ret == NM_ACT_STAGE_RETURN_FAILURE) {
|
wireguard: delay activation while resolving DNS names for WireGuard peers to avoid race
The endpoints of WireGuard peers can be configured as DNS name, which
NetworkManager will resolve.
Since activating a profile might affect now names get resolved, we must
first resolve names before completing the activation of the WireGuard
device (and before reconfiguring DNS accordingly).
For example, if you configure exclusive DNS resolution via the WireGuard
device, and if the peer needs to be resolved via DNS, then resolving the
peer name must happen before the reconfiguration of DNS. Otherwise the
new DNS configuration will be broken due to being unable to reach the
WireGuard peer.
Fix that by waiting.
There is still an unfixed problem. If resolving any peers fails,
activation silently proceeds -- again possibly breaking the network
setup. Of course, NetworkManager will repeatedly try to re-resolve
the name, but that may never succeed if DNS would be resolved via
the VPN itself.
That is different from `wg set` which resolves hostnames and fails.
Consequently `wg-quick up` would also fail. But these are both one shot
applications, they are not around and basically let the user handle the
error (by reading the log and invoking the command again). NetworkManager
can do something different and proceed activation (as it will also
periodically re-resolve the hostnames again). Note that it's also valid
to activate a WireGuard device without any peers (and to modify the
activated device later with Reapply()). As such, having no peers (or
being unable to resolve a hostname) may be a valid configuration.
I think we should add an option/flag that when enabled will cause
the activation to fail of names cannot be resolved.
https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/issues/535
https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/issues/616
https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/721
2021-01-08 11:09:03 +01:00
|
|
|
if (sys_iface_state == NM_DEVICE_SYS_IFACE_STATE_ASSUME) {
|
|
|
|
|
/* this never fails. */
|
|
|
|
|
return NM_ACT_STAGE_RETURN_SUCCESS;
|
|
|
|
|
}
|
|
|
|
|
|
2020-02-27 16:45:16 +01:00
|
|
|
nm_device_state_changed(device, NM_DEVICE_STATE_FAILED, failure_reason);
|
|
|
|
|
NM_SET_OUT(out_failure_reason, failure_reason);
|
|
|
|
|
return NM_ACT_STAGE_RETURN_FAILURE;
|
2019-01-12 12:33:35 +01:00
|
|
|
}
|
2020-09-28 16:03:33 +02:00
|
|
|
|
wireguard: delay activation while resolving DNS names for WireGuard peers to avoid race
The endpoints of WireGuard peers can be configured as DNS name, which
NetworkManager will resolve.
Since activating a profile might affect now names get resolved, we must
first resolve names before completing the activation of the WireGuard
device (and before reconfiguring DNS accordingly).
For example, if you configure exclusive DNS resolution via the WireGuard
device, and if the peer needs to be resolved via DNS, then resolving the
peer name must happen before the reconfiguration of DNS. Otherwise the
new DNS configuration will be broken due to being unable to reach the
WireGuard peer.
Fix that by waiting.
There is still an unfixed problem. If resolving any peers fails,
activation silently proceeds -- again possibly breaking the network
setup. Of course, NetworkManager will repeatedly try to re-resolve
the name, but that may never succeed if DNS would be resolved via
the VPN itself.
That is different from `wg set` which resolves hostnames and fails.
Consequently `wg-quick up` would also fail. But these are both one shot
applications, they are not around and basically let the user handle the
error (by reading the log and invoking the command again). NetworkManager
can do something different and proceed activation (as it will also
periodically re-resolve the hostnames again). Note that it's also valid
to activate a WireGuard device without any peers (and to modify the
activated device later with Reapply()). As such, having no peers (or
being unable to resolve a hostname) may be a valid configuration.
I think we should add an option/flag that when enabled will cause
the activation to fail of names cannot be resolved.
https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/issues/535
https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/issues/616
https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/721
2021-01-08 11:09:03 +01:00
|
|
|
nm_assert(NM_IN_SET(ret, NM_ACT_STAGE_RETURN_SUCCESS, NM_ACT_STAGE_RETURN_POSTPONE));
|
|
|
|
|
|
|
|
|
|
if (ret == NM_ACT_STAGE_RETURN_SUCCESS && _peers_resolving_cnt(priv) > 0u) {
|
|
|
|
|
_LOGT(LOGD_DEVICE,
|
|
|
|
|
"activation delayed to resolve DNS names of peers: resolving and waiting...");
|
|
|
|
|
return NM_ACT_STAGE_RETURN_POSTPONE;
|
|
|
|
|
}
|
|
|
|
|
|
2020-02-27 16:45:16 +01:00
|
|
|
return ret;
|
2018-12-29 00:04:20 +01:00
|
|
|
}
|
|
|
|
|
|
2021-08-06 15:17:05 +02:00
|
|
|
static const NML3ConfigData *
|
2019-03-04 09:31:28 +01:00
|
|
|
_get_dev2_ip_config(NMDeviceWireGuard *self, int addr_family)
|
|
|
|
|
{
|
2021-11-09 13:28:54 +01:00
|
|
|
NMDeviceWireGuardPrivate *priv = NM_DEVICE_WIREGUARD_GET_PRIVATE(self);
|
2021-08-06 15:17:05 +02:00
|
|
|
nm_auto_unref_l3cd_init NML3ConfigData *l3cd = NULL;
|
2021-11-09 13:28:54 +01:00
|
|
|
NMConnection *connection;
|
|
|
|
|
NMSettingWireGuard *s_wg;
|
2021-08-06 15:17:05 +02:00
|
|
|
guint n_peers;
|
|
|
|
|
guint i;
|
|
|
|
|
int ip_ifindex;
|
|
|
|
|
guint32 route_metric;
|
|
|
|
|
guint32 route_table_coerced;
|
|
|
|
|
gboolean auto_default_route_enabled;
|
wireguard: support configuring policy routing to avoid routing loops
For WireGuard (like for all IP-tunnels and IP-based VPNs), the IP addresses of
the peers must be reached outside the tunnel/VPN itself.
For VPN connections, NetworkManager usually adds a direct /32 route to
the external VPN gateway to the underlying device. For WireGuard that is
not done, because injecting a route to another device is ugly and error
prone. Worse: WireGuard with automatic roaming and multiple peers makes this
more complicated.
This is commonly a problem when setting the default-route via the VPN,
but there are also other subtle setups where special care must be taken
to prevent such routing loops.
WireGuard's wg-quick provides a simple, automatic solution by adding two policy
routing rules and relying on the WireGuard packets having a fwmark set (see [1]).
Let's also do that. Add new properties "wireguard.ip4-auto-default-route"
and "wireguard.ip6-auto-default-route" to enable/disable this. Note that
the default value lets NetworkManager automatically choose whether to
enable it (depending on whether there are any peers that have a default
route). This means, common scenarios should now work well without additional
configuration.
Note that this is also a change in behavior and upon package upgrade
NetworkManager may start adding policy routes (if there are peers that
have a default-route). This is a change in behavior, as the user already
clearly had this setup working and configured some working solution
already.
The new automatism picks the rule priority automatically and adds the
default-route to the routing table that has the same number as the fwmark.
If any of this is unsuitable, then the user is free to disable this
automatism. Note that since 1.18.0 NetworkManager supports policy routing (*).
That means, what this automatism does can be also achieved via explicit
configuration of the profile, which gives the user more flexibility to
adjust all parameters explicitly).
(*) but only since 1.20.0 NetworkManager supports the "suppress_prefixlength"
rule attribute, which makes it impossible to configure exactly this rule-based
solution with 1.18.0 NetworkManager.
[1] https://www.wireguard.com/netns/#improved-rule-based-routing
2019-04-30 17:48:46 +02:00
|
|
|
|
|
|
|
|
_auto_default_route_init(self);
|
2019-03-04 09:31:28 +01:00
|
|
|
|
|
|
|
|
connection = nm_device_get_applied_connection(NM_DEVICE(self));
|
|
|
|
|
|
|
|
|
|
s_wg = NM_SETTING_WIREGUARD(nm_connection_get_setting(connection, NM_TYPE_SETTING_WIREGUARD));
|
|
|
|
|
|
|
|
|
|
/* Differences to `wg-quick`.
|
|
|
|
|
*
|
|
|
|
|
* `wg-quick` supports the "Table" setting with 3 modes:
|
|
|
|
|
*
|
|
|
|
|
* a1) "off": this is what we do with "peer-routes" disabled.
|
|
|
|
|
*
|
|
|
|
|
* a2) an explicit routing table. This is our behavior with "peer-routes" on. In this case
|
|
|
|
|
* we honor the "ipv4.route-table" and "ipv6.route-table" settings. One difference is that
|
|
|
|
|
* `wg-quick` would resolve table names from /etc/iproute2/rt_tables. Our connection profiles
|
2019-03-11 12:00:32 +01:00
|
|
|
* only contain table numbers, so that conversion from name to table must have happened
|
2019-03-04 09:31:28 +01:00
|
|
|
* before already.
|
|
|
|
|
*
|
|
|
|
|
* a3) "auto" (the default). In this case, `wg-quick` would only add the route to the
|
|
|
|
|
* main table, if the AllowedIP range is not yet reachable on the link. With "peer-routes"
|
|
|
|
|
* enabled, we don't check for that and always add the routes to the main-table
|
|
|
|
|
* (with 'ipv4.route-table' and 'ipv6.route-table' set to zero or RT_TABLE_MAIN (254)).
|
|
|
|
|
*
|
|
|
|
|
* Also, in "auto" mode, `wg-quick` would add special handling for /0 routes and pick
|
|
|
|
|
* an empty table to configure policy routing to avoid routing loops. This handling
|
|
|
|
|
* of routing-loops via policy routing is not yet done, and requires a separate solution
|
|
|
|
|
* from constructing the peer-routes here.
|
|
|
|
|
*/
|
|
|
|
|
if (!nm_setting_wireguard_get_peer_routes(s_wg))
|
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
|
|
ip_ifindex = nm_device_get_ip_ifindex(NM_DEVICE(self));
|
|
|
|
|
|
|
|
|
|
if (ip_ifindex <= 0)
|
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
|
|
route_metric = nm_device_get_route_metric(NM_DEVICE(self), addr_family);
|
|
|
|
|
|
2019-04-30 14:01:10 +02:00
|
|
|
route_table_coerced =
|
|
|
|
|
nm_platform_route_table_coerce(nm_device_get_route_table(NM_DEVICE(self), addr_family));
|
2019-03-04 09:31:28 +01:00
|
|
|
|
wireguard: support configuring policy routing to avoid routing loops
For WireGuard (like for all IP-tunnels and IP-based VPNs), the IP addresses of
the peers must be reached outside the tunnel/VPN itself.
For VPN connections, NetworkManager usually adds a direct /32 route to
the external VPN gateway to the underlying device. For WireGuard that is
not done, because injecting a route to another device is ugly and error
prone. Worse: WireGuard with automatic roaming and multiple peers makes this
more complicated.
This is commonly a problem when setting the default-route via the VPN,
but there are also other subtle setups where special care must be taken
to prevent such routing loops.
WireGuard's wg-quick provides a simple, automatic solution by adding two policy
routing rules and relying on the WireGuard packets having a fwmark set (see [1]).
Let's also do that. Add new properties "wireguard.ip4-auto-default-route"
and "wireguard.ip6-auto-default-route" to enable/disable this. Note that
the default value lets NetworkManager automatically choose whether to
enable it (depending on whether there are any peers that have a default
route). This means, common scenarios should now work well without additional
configuration.
Note that this is also a change in behavior and upon package upgrade
NetworkManager may start adding policy routes (if there are peers that
have a default-route). This is a change in behavior, as the user already
clearly had this setup working and configured some working solution
already.
The new automatism picks the rule priority automatically and adds the
default-route to the routing table that has the same number as the fwmark.
If any of this is unsuitable, then the user is free to disable this
automatism. Note that since 1.18.0 NetworkManager supports policy routing (*).
That means, what this automatism does can be also achieved via explicit
configuration of the profile, which gives the user more flexibility to
adjust all parameters explicitly).
(*) but only since 1.20.0 NetworkManager supports the "suppress_prefixlength"
rule attribute, which makes it impossible to configure exactly this rule-based
solution with 1.18.0 NetworkManager.
[1] https://www.wireguard.com/netns/#improved-rule-based-routing
2019-04-30 17:48:46 +02:00
|
|
|
auto_default_route_enabled = (addr_family == AF_INET) ? priv->auto_default_route_enabled_4
|
|
|
|
|
: priv->auto_default_route_enabled_6;
|
|
|
|
|
|
2019-03-04 09:31:28 +01:00
|
|
|
n_peers = nm_setting_wireguard_get_peers_len(s_wg);
|
|
|
|
|
for (i = 0; i < n_peers; i++) {
|
|
|
|
|
NMWireGuardPeer *peer = nm_setting_wireguard_get_peer(s_wg, i);
|
|
|
|
|
guint n_aips;
|
|
|
|
|
guint j;
|
|
|
|
|
|
|
|
|
|
n_aips = nm_wireguard_peer_get_allowed_ips_len(peer);
|
|
|
|
|
for (j = 0; j < n_aips; j++) {
|
|
|
|
|
NMPlatformIPXRoute rt;
|
|
|
|
|
NMIPAddr addrbin;
|
2021-11-09 13:28:54 +01:00
|
|
|
const char *aip;
|
2019-03-04 09:31:28 +01:00
|
|
|
gboolean valid;
|
|
|
|
|
int prefix;
|
wireguard: support configuring policy routing to avoid routing loops
For WireGuard (like for all IP-tunnels and IP-based VPNs), the IP addresses of
the peers must be reached outside the tunnel/VPN itself.
For VPN connections, NetworkManager usually adds a direct /32 route to
the external VPN gateway to the underlying device. For WireGuard that is
not done, because injecting a route to another device is ugly and error
prone. Worse: WireGuard with automatic roaming and multiple peers makes this
more complicated.
This is commonly a problem when setting the default-route via the VPN,
but there are also other subtle setups where special care must be taken
to prevent such routing loops.
WireGuard's wg-quick provides a simple, automatic solution by adding two policy
routing rules and relying on the WireGuard packets having a fwmark set (see [1]).
Let's also do that. Add new properties "wireguard.ip4-auto-default-route"
and "wireguard.ip6-auto-default-route" to enable/disable this. Note that
the default value lets NetworkManager automatically choose whether to
enable it (depending on whether there are any peers that have a default
route). This means, common scenarios should now work well without additional
configuration.
Note that this is also a change in behavior and upon package upgrade
NetworkManager may start adding policy routes (if there are peers that
have a default-route). This is a change in behavior, as the user already
clearly had this setup working and configured some working solution
already.
The new automatism picks the rule priority automatically and adds the
default-route to the routing table that has the same number as the fwmark.
If any of this is unsuitable, then the user is free to disable this
automatism. Note that since 1.18.0 NetworkManager supports policy routing (*).
That means, what this automatism does can be also achieved via explicit
configuration of the profile, which gives the user more flexibility to
adjust all parameters explicitly).
(*) but only since 1.20.0 NetworkManager supports the "suppress_prefixlength"
rule attribute, which makes it impossible to configure exactly this rule-based
solution with 1.18.0 NetworkManager.
[1] https://www.wireguard.com/netns/#improved-rule-based-routing
2019-04-30 17:48:46 +02:00
|
|
|
guint32 rtable_coerced;
|
2019-03-04 09:31:28 +01:00
|
|
|
|
|
|
|
|
aip = nm_wireguard_peer_get_allowed_ip(peer, j, &valid);
|
|
|
|
|
|
|
|
|
|
if (!valid
|
|
|
|
|
|| !nm_utils_parse_inaddr_prefix_bin(addr_family, aip, NULL, &addrbin, &prefix))
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
if (prefix < 0)
|
|
|
|
|
prefix = (addr_family == AF_INET) ? 32 : 128;
|
|
|
|
|
|
2020-04-22 10:57:27 +02:00
|
|
|
if (prefix == 0) {
|
|
|
|
|
NMSettingIPConfig *s_ip;
|
|
|
|
|
|
|
|
|
|
s_ip = nm_connection_get_setting_ip_config(connection, addr_family);
|
|
|
|
|
if (nm_setting_ip_config_get_never_default(s_ip))
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2021-08-06 15:17:05 +02:00
|
|
|
if (!l3cd) {
|
|
|
|
|
l3cd = nm_device_create_l3_config_data(NM_DEVICE(self), NM_IP_CONFIG_SOURCE_USER);
|
|
|
|
|
nm_l3_config_data_set_flags(l3cd,
|
|
|
|
|
NM_L3_CONFIG_DAT_FLAGS_IGNORE_MERGE_NO_DEFAULT_ROUTES);
|
wireguard: don't let explicit gateway override WireGuard's peer route
The profile's "ipv4.gateway" and "ipv6.gateway" has only one real
purpose: to define the next hop of a static default route.
Usually, when specifying a gateway in this way, the default route from
other addressing methods (like DHCPv4 or IPv6 autoconf) gets ignored.
If you have a WireGuard peer with "AllowedIPs=0.0.0.0/0" and
"wireguard.peer-routes" enabled, NetworkManager would automatically add
a route to the peer. Previously, if the user also set a gateway, that
route was suppressed.
That doesn't feel right. Note that configuring a gateway on a WireGuard
profile is likely to be wrong to begin with. At least, unless you take
otherwise care to avoid routing loops. If you take care, setting a
gateway may work, but it would feel clearer to instead just add an
explicit /0 manual route instead.
Also, note that usually you don't need a gateway anyway. WireGuard is a
Layer 3 (IP) tunnel, where the next hop is alway just the other side of
the tunnel. The next hop has little effect on the routes that you
configure on a WireGuard interface. What however matters is whether a
default route is present or not.
Also, an explicit gateway probably works badly with "ipv[46].ip4-auto-default-route",
because in that case the automatism should add a /0 peer-route route in a
separate routing table. The explicit gateway interferes with that too.
Nonetheless, without this patch it's not obvious why the /0 peer
route gets suppressed when a gateway is set. Don't allow for that, and
always add the peer-route.
Probably the profile's gateway setting is still wrong and causes the
profile not to work. But at least, you see all routes configured, and
it's clearer where the (wrong) default route to the gateway comes from.
(cherry picked from commit 115291a46f52ee4adfe85264b9566d216bcb25e8)
2020-04-22 10:46:17 +02:00
|
|
|
}
|
2019-03-04 09:31:28 +01:00
|
|
|
|
|
|
|
|
nm_utils_ipx_address_clear_host_address(addr_family, &addrbin, NULL, prefix);
|
|
|
|
|
|
wireguard: support configuring policy routing to avoid routing loops
For WireGuard (like for all IP-tunnels and IP-based VPNs), the IP addresses of
the peers must be reached outside the tunnel/VPN itself.
For VPN connections, NetworkManager usually adds a direct /32 route to
the external VPN gateway to the underlying device. For WireGuard that is
not done, because injecting a route to another device is ugly and error
prone. Worse: WireGuard with automatic roaming and multiple peers makes this
more complicated.
This is commonly a problem when setting the default-route via the VPN,
but there are also other subtle setups where special care must be taken
to prevent such routing loops.
WireGuard's wg-quick provides a simple, automatic solution by adding two policy
routing rules and relying on the WireGuard packets having a fwmark set (see [1]).
Let's also do that. Add new properties "wireguard.ip4-auto-default-route"
and "wireguard.ip6-auto-default-route" to enable/disable this. Note that
the default value lets NetworkManager automatically choose whether to
enable it (depending on whether there are any peers that have a default
route). This means, common scenarios should now work well without additional
configuration.
Note that this is also a change in behavior and upon package upgrade
NetworkManager may start adding policy routes (if there are peers that
have a default-route). This is a change in behavior, as the user already
clearly had this setup working and configured some working solution
already.
The new automatism picks the rule priority automatically and adds the
default-route to the routing table that has the same number as the fwmark.
If any of this is unsuitable, then the user is free to disable this
automatism. Note that since 1.18.0 NetworkManager supports policy routing (*).
That means, what this automatism does can be also achieved via explicit
configuration of the profile, which gives the user more flexibility to
adjust all parameters explicitly).
(*) but only since 1.20.0 NetworkManager supports the "suppress_prefixlength"
rule attribute, which makes it impossible to configure exactly this rule-based
solution with 1.18.0 NetworkManager.
[1] https://www.wireguard.com/netns/#improved-rule-based-routing
2019-04-30 17:48:46 +02:00
|
|
|
rtable_coerced = route_table_coerced;
|
|
|
|
|
|
|
|
|
|
if (prefix == 0 && auto_default_route_enabled) {
|
|
|
|
|
/* In auto-default-route mode, we place the default route in a table that
|
|
|
|
|
* has the same number as the fwmark. wg-quick does that too. If you don't
|
|
|
|
|
* like that, configure the rules and the default-route explicitly in the
|
|
|
|
|
* connection profile. */
|
|
|
|
|
rtable_coerced = nm_platform_route_table_coerce(priv->auto_default_route_fwmark);
|
|
|
|
|
}
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2019-03-04 09:31:28 +01:00
|
|
|
if (addr_family == AF_INET) {
|
|
|
|
|
rt.r4 = (NMPlatformIP4Route){
|
|
|
|
|
.network = addrbin.addr4,
|
|
|
|
|
.plen = prefix,
|
|
|
|
|
.ifindex = ip_ifindex,
|
|
|
|
|
.rt_source = NM_IP_CONFIG_SOURCE_USER,
|
wireguard: support configuring policy routing to avoid routing loops
For WireGuard (like for all IP-tunnels and IP-based VPNs), the IP addresses of
the peers must be reached outside the tunnel/VPN itself.
For VPN connections, NetworkManager usually adds a direct /32 route to
the external VPN gateway to the underlying device. For WireGuard that is
not done, because injecting a route to another device is ugly and error
prone. Worse: WireGuard with automatic roaming and multiple peers makes this
more complicated.
This is commonly a problem when setting the default-route via the VPN,
but there are also other subtle setups where special care must be taken
to prevent such routing loops.
WireGuard's wg-quick provides a simple, automatic solution by adding two policy
routing rules and relying on the WireGuard packets having a fwmark set (see [1]).
Let's also do that. Add new properties "wireguard.ip4-auto-default-route"
and "wireguard.ip6-auto-default-route" to enable/disable this. Note that
the default value lets NetworkManager automatically choose whether to
enable it (depending on whether there are any peers that have a default
route). This means, common scenarios should now work well without additional
configuration.
Note that this is also a change in behavior and upon package upgrade
NetworkManager may start adding policy routes (if there are peers that
have a default-route). This is a change in behavior, as the user already
clearly had this setup working and configured some working solution
already.
The new automatism picks the rule priority automatically and adds the
default-route to the routing table that has the same number as the fwmark.
If any of this is unsuitable, then the user is free to disable this
automatism. Note that since 1.18.0 NetworkManager supports policy routing (*).
That means, what this automatism does can be also achieved via explicit
configuration of the profile, which gives the user more flexibility to
adjust all parameters explicitly).
(*) but only since 1.20.0 NetworkManager supports the "suppress_prefixlength"
rule attribute, which makes it impossible to configure exactly this rule-based
solution with 1.18.0 NetworkManager.
[1] https://www.wireguard.com/netns/#improved-rule-based-routing
2019-04-30 17:48:46 +02:00
|
|
|
.table_coerced = rtable_coerced,
|
2019-03-04 09:31:28 +01:00
|
|
|
.metric = route_metric,
|
|
|
|
|
};
|
|
|
|
|
} else {
|
|
|
|
|
rt.r6 = (NMPlatformIP6Route){
|
|
|
|
|
.network = addrbin.addr6,
|
|
|
|
|
.plen = prefix,
|
|
|
|
|
.ifindex = ip_ifindex,
|
|
|
|
|
.rt_source = NM_IP_CONFIG_SOURCE_USER,
|
wireguard: support configuring policy routing to avoid routing loops
For WireGuard (like for all IP-tunnels and IP-based VPNs), the IP addresses of
the peers must be reached outside the tunnel/VPN itself.
For VPN connections, NetworkManager usually adds a direct /32 route to
the external VPN gateway to the underlying device. For WireGuard that is
not done, because injecting a route to another device is ugly and error
prone. Worse: WireGuard with automatic roaming and multiple peers makes this
more complicated.
This is commonly a problem when setting the default-route via the VPN,
but there are also other subtle setups where special care must be taken
to prevent such routing loops.
WireGuard's wg-quick provides a simple, automatic solution by adding two policy
routing rules and relying on the WireGuard packets having a fwmark set (see [1]).
Let's also do that. Add new properties "wireguard.ip4-auto-default-route"
and "wireguard.ip6-auto-default-route" to enable/disable this. Note that
the default value lets NetworkManager automatically choose whether to
enable it (depending on whether there are any peers that have a default
route). This means, common scenarios should now work well without additional
configuration.
Note that this is also a change in behavior and upon package upgrade
NetworkManager may start adding policy routes (if there are peers that
have a default-route). This is a change in behavior, as the user already
clearly had this setup working and configured some working solution
already.
The new automatism picks the rule priority automatically and adds the
default-route to the routing table that has the same number as the fwmark.
If any of this is unsuitable, then the user is free to disable this
automatism. Note that since 1.18.0 NetworkManager supports policy routing (*).
That means, what this automatism does can be also achieved via explicit
configuration of the profile, which gives the user more flexibility to
adjust all parameters explicitly).
(*) but only since 1.20.0 NetworkManager supports the "suppress_prefixlength"
rule attribute, which makes it impossible to configure exactly this rule-based
solution with 1.18.0 NetworkManager.
[1] https://www.wireguard.com/netns/#improved-rule-based-routing
2019-04-30 17:48:46 +02:00
|
|
|
.table_coerced = rtable_coerced,
|
2019-03-04 09:31:28 +01:00
|
|
|
.metric = route_metric,
|
|
|
|
|
};
|
|
|
|
|
}
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2021-08-06 15:17:05 +02:00
|
|
|
nm_l3_config_data_add_route(l3cd, addr_family, NULL, &rt.rx);
|
2019-03-04 09:31:28 +01:00
|
|
|
}
|
|
|
|
|
}
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2021-08-06 15:17:05 +02:00
|
|
|
if (!l3cd)
|
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
|
|
return nm_l3_config_data_seal(g_steal_pointer(&l3cd));
|
2019-03-04 09:31:28 +01:00
|
|
|
}
|
|
|
|
|
|
2021-08-06 15:17:05 +02:00
|
|
|
static void
|
|
|
|
|
act_stage3_ip_config(NMDevice *device, int addr_family)
|
2019-03-04 09:31:28 +01:00
|
|
|
{
|
2021-08-06 15:17:05 +02:00
|
|
|
nm_auto_unref_l3cd const NML3ConfigData *l3cd = NULL;
|
2019-03-04 09:31:28 +01:00
|
|
|
|
2021-08-06 15:17:05 +02:00
|
|
|
l3cd = _get_dev2_ip_config(NM_DEVICE_WIREGUARD(device), addr_family);
|
|
|
|
|
nm_device_devip_set_state(device, addr_family, NM_DEVICE_IP_STATE_READY, l3cd);
|
2019-03-04 09:31:28 +01:00
|
|
|
}
|
|
|
|
|
|
2019-03-02 23:33:15 +01:00
|
|
|
static guint32
|
2019-09-11 10:57:07 +02:00
|
|
|
get_configured_mtu(NMDevice *device, NMDeviceMtuSource *out_source, gboolean *out_force)
|
2019-03-02 23:33:15 +01:00
|
|
|
{
|
|
|
|
|
/* When "MTU" for `wg-quick up` is unset, it calls `ip route get` for
|
|
|
|
|
* each configured endpoint, to determine the suitable MTU how to reach
|
|
|
|
|
* each endpoint.
|
|
|
|
|
* For `wg-quick` this works very well, because whenever the script runs it
|
|
|
|
|
* determines the best setting at that point in time. It's simply not concerned
|
|
|
|
|
* with what happens later (and it's not around anyway).
|
|
|
|
|
*
|
|
|
|
|
* NetworkManager sticks around, so the right MTU would need to be re-determined
|
|
|
|
|
* whenever anything relevant changes. Which basically means, to re-evaluate whenever
|
|
|
|
|
* something related to addresses or routing changes (which happens all the time).
|
|
|
|
|
*
|
|
|
|
|
* The correct MTU indeed depends on the MTU setting of other interfaces (or routes).
|
|
|
|
|
* But it's still odd, that activating/deactivating a seemingly unrelated interface
|
|
|
|
|
* would trigger an MTU change. It's odd to explain/document and odd to implemented
|
|
|
|
|
* -- despite this being the reality.
|
|
|
|
|
*
|
|
|
|
|
* For now, only support configuring an explicit MTU, or leave the setting untouched.
|
2019-03-11 12:00:32 +01:00
|
|
|
* The same limitation also applies to other "ip-tunnel" types, where we could use
|
2019-03-02 23:33:15 +01:00
|
|
|
* similar smarts for autodetecting the MTU.
|
|
|
|
|
*/
|
|
|
|
|
return nm_device_get_configured_mtu_from_connection(device,
|
|
|
|
|
NM_TYPE_SETTING_WIREGUARD,
|
|
|
|
|
out_source);
|
|
|
|
|
}
|
|
|
|
|
|
2019-07-29 14:37:29 +02:00
|
|
|
static void
|
|
|
|
|
_device_cleanup(NMDeviceWireGuard *self)
|
|
|
|
|
{
|
|
|
|
|
NMDeviceWireGuardPrivate *priv = NM_DEVICE_WIREGUARD_GET_PRIVATE(self);
|
|
|
|
|
|
wireguard: delay activation while resolving DNS names for WireGuard peers to avoid race
The endpoints of WireGuard peers can be configured as DNS name, which
NetworkManager will resolve.
Since activating a profile might affect now names get resolved, we must
first resolve names before completing the activation of the WireGuard
device (and before reconfiguring DNS accordingly).
For example, if you configure exclusive DNS resolution via the WireGuard
device, and if the peer needs to be resolved via DNS, then resolving the
peer name must happen before the reconfiguration of DNS. Otherwise the
new DNS configuration will be broken due to being unable to reach the
WireGuard peer.
Fix that by waiting.
There is still an unfixed problem. If resolving any peers fails,
activation silently proceeds -- again possibly breaking the network
setup. Of course, NetworkManager will repeatedly try to re-resolve
the name, but that may never succeed if DNS would be resolved via
the VPN itself.
That is different from `wg set` which resolves hostnames and fails.
Consequently `wg-quick up` would also fail. But these are both one shot
applications, they are not around and basically let the user handle the
error (by reading the log and invoking the command again). NetworkManager
can do something different and proceed activation (as it will also
periodically re-resolve the hostnames again). Note that it's also valid
to activate a WireGuard device without any peers (and to modify the
activated device later with Reapply()). As such, having no peers (or
being unable to resolve a hostname) may be a valid configuration.
I think we should add an option/flag that when enabled will cause
the activation to fail of names cannot be resolved.
https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/issues/535
https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/issues/616
https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/721
2021-01-08 11:09:03 +01:00
|
|
|
_peers_remove_all(self);
|
wireguard: support configuring policy routing to avoid routing loops
For WireGuard (like for all IP-tunnels and IP-based VPNs), the IP addresses of
the peers must be reached outside the tunnel/VPN itself.
For VPN connections, NetworkManager usually adds a direct /32 route to
the external VPN gateway to the underlying device. For WireGuard that is
not done, because injecting a route to another device is ugly and error
prone. Worse: WireGuard with automatic roaming and multiple peers makes this
more complicated.
This is commonly a problem when setting the default-route via the VPN,
but there are also other subtle setups where special care must be taken
to prevent such routing loops.
WireGuard's wg-quick provides a simple, automatic solution by adding two policy
routing rules and relying on the WireGuard packets having a fwmark set (see [1]).
Let's also do that. Add new properties "wireguard.ip4-auto-default-route"
and "wireguard.ip6-auto-default-route" to enable/disable this. Note that
the default value lets NetworkManager automatically choose whether to
enable it (depending on whether there are any peers that have a default
route). This means, common scenarios should now work well without additional
configuration.
Note that this is also a change in behavior and upon package upgrade
NetworkManager may start adding policy routes (if there are peers that
have a default-route). This is a change in behavior, as the user already
clearly had this setup working and configured some working solution
already.
The new automatism picks the rule priority automatically and adds the
default-route to the routing table that has the same number as the fwmark.
If any of this is unsuitable, then the user is free to disable this
automatism. Note that since 1.18.0 NetworkManager supports policy routing (*).
That means, what this automatism does can be also achieved via explicit
configuration of the profile, which gives the user more flexibility to
adjust all parameters explicitly).
(*) but only since 1.20.0 NetworkManager supports the "suppress_prefixlength"
rule attribute, which makes it impossible to configure exactly this rule-based
solution with 1.18.0 NetworkManager.
[1] https://www.wireguard.com/netns/#improved-rule-based-routing
2019-04-30 17:48:46 +02:00
|
|
|
|
2019-07-29 14:37:29 +02:00
|
|
|
_secrets_cancel(self);
|
wireguard: support configuring policy routing to avoid routing loops
For WireGuard (like for all IP-tunnels and IP-based VPNs), the IP addresses of
the peers must be reached outside the tunnel/VPN itself.
For VPN connections, NetworkManager usually adds a direct /32 route to
the external VPN gateway to the underlying device. For WireGuard that is
not done, because injecting a route to another device is ugly and error
prone. Worse: WireGuard with automatic roaming and multiple peers makes this
more complicated.
This is commonly a problem when setting the default-route via the VPN,
but there are also other subtle setups where special care must be taken
to prevent such routing loops.
WireGuard's wg-quick provides a simple, automatic solution by adding two policy
routing rules and relying on the WireGuard packets having a fwmark set (see [1]).
Let's also do that. Add new properties "wireguard.ip4-auto-default-route"
and "wireguard.ip6-auto-default-route" to enable/disable this. Note that
the default value lets NetworkManager automatically choose whether to
enable it (depending on whether there are any peers that have a default
route). This means, common scenarios should now work well without additional
configuration.
Note that this is also a change in behavior and upon package upgrade
NetworkManager may start adding policy routes (if there are peers that
have a default-route). This is a change in behavior, as the user already
clearly had this setup working and configured some working solution
already.
The new automatism picks the rule priority automatically and adds the
default-route to the routing table that has the same number as the fwmark.
If any of this is unsuitable, then the user is free to disable this
automatism. Note that since 1.18.0 NetworkManager supports policy routing (*).
That means, what this automatism does can be also achieved via explicit
configuration of the profile, which gives the user more flexibility to
adjust all parameters explicitly).
(*) but only since 1.20.0 NetworkManager supports the "suppress_prefixlength"
rule attribute, which makes it impossible to configure exactly this rule-based
solution with 1.18.0 NetworkManager.
[1] https://www.wireguard.com/netns/#improved-rule-based-routing
2019-04-30 17:48:46 +02:00
|
|
|
|
|
|
|
|
priv->auto_default_route_initialized = FALSE;
|
|
|
|
|
priv->auto_default_route_priority_initialized = FALSE;
|
2019-07-29 14:37:29 +02:00
|
|
|
}
|
|
|
|
|
|
2018-12-29 00:04:20 +01:00
|
|
|
static void
|
2021-11-09 13:28:54 +01:00
|
|
|
device_state_changed(NMDevice *device,
|
2018-12-29 00:04:20 +01:00
|
|
|
NMDeviceState new_state,
|
|
|
|
|
NMDeviceState old_state,
|
|
|
|
|
NMDeviceStateReason reason)
|
|
|
|
|
{
|
|
|
|
|
if (new_state <= NM_DEVICE_STATE_ACTIVATED)
|
|
|
|
|
return;
|
|
|
|
|
|
2019-07-29 14:37:29 +02:00
|
|
|
_device_cleanup(NM_DEVICE_WIREGUARD(device));
|
2018-12-29 00:04:20 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2019-01-12 12:33:35 +01:00
|
|
|
static gboolean
|
2021-11-09 13:28:54 +01:00
|
|
|
can_reapply_change(NMDevice *device,
|
2019-01-12 12:33:35 +01:00
|
|
|
const char *setting_name,
|
2021-11-09 13:28:54 +01:00
|
|
|
NMSetting *s_old,
|
|
|
|
|
NMSetting *s_new,
|
2019-01-12 12:33:35 +01:00
|
|
|
GHashTable *diffs,
|
2021-11-09 13:28:54 +01:00
|
|
|
GError **error)
|
2019-01-12 12:33:35 +01:00
|
|
|
{
|
|
|
|
|
if (nm_streq(setting_name, NM_SETTING_WIREGUARD_SETTING_NAME)) {
|
2019-03-02 23:33:15 +01:00
|
|
|
/* Most, but not all WireGuard settings can be reapplied. Whitelist.
|
|
|
|
|
*
|
|
|
|
|
* MTU cannot be reapplied. */
|
|
|
|
|
return nm_device_hash_check_invalid_keys(diffs,
|
|
|
|
|
NM_SETTING_WIREGUARD_SETTING_NAME,
|
|
|
|
|
error,
|
|
|
|
|
NM_SETTING_WIREGUARD_FWMARK,
|
wireguard: support configuring policy routing to avoid routing loops
For WireGuard (like for all IP-tunnels and IP-based VPNs), the IP addresses of
the peers must be reached outside the tunnel/VPN itself.
For VPN connections, NetworkManager usually adds a direct /32 route to
the external VPN gateway to the underlying device. For WireGuard that is
not done, because injecting a route to another device is ugly and error
prone. Worse: WireGuard with automatic roaming and multiple peers makes this
more complicated.
This is commonly a problem when setting the default-route via the VPN,
but there are also other subtle setups where special care must be taken
to prevent such routing loops.
WireGuard's wg-quick provides a simple, automatic solution by adding two policy
routing rules and relying on the WireGuard packets having a fwmark set (see [1]).
Let's also do that. Add new properties "wireguard.ip4-auto-default-route"
and "wireguard.ip6-auto-default-route" to enable/disable this. Note that
the default value lets NetworkManager automatically choose whether to
enable it (depending on whether there are any peers that have a default
route). This means, common scenarios should now work well without additional
configuration.
Note that this is also a change in behavior and upon package upgrade
NetworkManager may start adding policy routes (if there are peers that
have a default-route). This is a change in behavior, as the user already
clearly had this setup working and configured some working solution
already.
The new automatism picks the rule priority automatically and adds the
default-route to the routing table that has the same number as the fwmark.
If any of this is unsuitable, then the user is free to disable this
automatism. Note that since 1.18.0 NetworkManager supports policy routing (*).
That means, what this automatism does can be also achieved via explicit
configuration of the profile, which gives the user more flexibility to
adjust all parameters explicitly).
(*) but only since 1.20.0 NetworkManager supports the "suppress_prefixlength"
rule attribute, which makes it impossible to configure exactly this rule-based
solution with 1.18.0 NetworkManager.
[1] https://www.wireguard.com/netns/#improved-rule-based-routing
2019-04-30 17:48:46 +02:00
|
|
|
NM_SETTING_WIREGUARD_IP4_AUTO_DEFAULT_ROUTE,
|
|
|
|
|
NM_SETTING_WIREGUARD_IP6_AUTO_DEFAULT_ROUTE,
|
2019-03-02 23:33:15 +01:00
|
|
|
NM_SETTING_WIREGUARD_LISTEN_PORT,
|
|
|
|
|
NM_SETTING_WIREGUARD_PEERS,
|
2019-03-04 09:31:28 +01:00
|
|
|
NM_SETTING_WIREGUARD_PEER_ROUTES,
|
2019-03-02 23:33:15 +01:00
|
|
|
NM_SETTING_WIREGUARD_PRIVATE_KEY,
|
|
|
|
|
NM_SETTING_WIREGUARD_PRIVATE_KEY_FLAGS);
|
2019-01-12 12:33:35 +01:00
|
|
|
}
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2019-01-12 12:33:35 +01:00
|
|
|
return NM_DEVICE_CLASS(nm_device_wireguard_parent_class)
|
|
|
|
|
->can_reapply_change(device, setting_name, s_old, s_new, diffs, error);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
reapply_connection(NMDevice *device, NMConnection *con_old, NMConnection *con_new)
|
|
|
|
|
{
|
2021-11-09 13:28:54 +01:00
|
|
|
NMDeviceWireGuard *self = NM_DEVICE_WIREGUARD(device);
|
2021-08-06 15:17:05 +02:00
|
|
|
NMDeviceWireGuardPrivate *priv = NM_DEVICE_WIREGUARD_GET_PRIVATE(self);
|
|
|
|
|
NMDeviceState state = nm_device_get_state(device);
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2019-01-12 12:33:35 +01:00
|
|
|
NM_DEVICE_CLASS(nm_device_wireguard_parent_class)->reapply_connection(device, con_old, con_new);
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2019-10-22 16:55:55 +02:00
|
|
|
if (state >= NM_DEVICE_STATE_CONFIG) {
|
|
|
|
|
priv->auto_default_route_refresh = TRUE;
|
|
|
|
|
link_config(NM_DEVICE_WIREGUARD(device), "reapply", LINK_CONFIG_MODE_REAPPLY, NULL);
|
|
|
|
|
}
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2019-10-22 16:55:55 +02:00
|
|
|
if (state >= NM_DEVICE_STATE_IP_CONFIG) {
|
2021-08-06 15:17:05 +02:00
|
|
|
nm_auto_unref_l3cd const NML3ConfigData *l3cd_4 = NULL;
|
|
|
|
|
nm_auto_unref_l3cd const NML3ConfigData *l3cd_6 = NULL;
|
|
|
|
|
|
|
|
|
|
l3cd_4 = _get_dev2_ip_config(self, AF_INET);
|
|
|
|
|
l3cd_6 = _get_dev2_ip_config(self, AF_INET6);
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2021-08-06 15:17:05 +02:00
|
|
|
nm_device_devip_set_state(device, AF_INET, NM_DEVICE_IP_STATE_READY, l3cd_4);
|
|
|
|
|
nm_device_devip_set_state(device, AF_INET6, NM_DEVICE_IP_STATE_READY, l3cd_6);
|
2019-10-22 16:55:55 +02:00
|
|
|
}
|
2019-01-12 12:33:35 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2018-12-29 00:04:20 +01:00
|
|
|
static void
|
|
|
|
|
update_connection(NMDevice *device, NMConnection *connection)
|
|
|
|
|
{
|
|
|
|
|
NMDeviceWireGuardPrivate *priv = NM_DEVICE_WIREGUARD_GET_PRIVATE(device);
|
2021-08-20 18:40:21 +08:00
|
|
|
NMSettingWireGuard *s_wg = _nm_connection_ensure_setting(connection, NM_TYPE_SETTING_WIREGUARD);
|
2021-11-09 13:28:54 +01:00
|
|
|
const NMPObject *obj_wg;
|
2019-01-12 12:33:35 +01:00
|
|
|
const NMPObjectLnkWireGuard *olnk_wg;
|
|
|
|
|
guint i;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2018-12-29 00:04:20 +01:00
|
|
|
g_object_set(s_wg,
|
|
|
|
|
NM_SETTING_WIREGUARD_FWMARK,
|
|
|
|
|
(guint) priv->lnk_curr.fwmark,
|
|
|
|
|
NM_SETTING_WIREGUARD_LISTEN_PORT,
|
|
|
|
|
(guint) priv->lnk_curr.listen_port,
|
|
|
|
|
NULL);
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2019-01-12 12:33:35 +01:00
|
|
|
obj_wg = NMP_OBJECT_UP_CAST(nm_platform_link_get_lnk_wireguard(nm_device_get_platform(device),
|
|
|
|
|
nm_device_get_ip_ifindex(device),
|
|
|
|
|
NULL));
|
|
|
|
|
if (!obj_wg)
|
|
|
|
|
return;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2019-01-12 12:33:35 +01:00
|
|
|
olnk_wg = &obj_wg->_lnk_wireguard;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2019-01-12 12:33:35 +01:00
|
|
|
for (i = 0; i < olnk_wg->peers_len; i++) {
|
|
|
|
|
nm_auto_unref_wgpeer NMWireGuardPeer *peer = NULL;
|
2021-11-09 13:28:54 +01:00
|
|
|
const NMPWireGuardPeer *ppeer = &olnk_wg->peers[i];
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2019-01-12 12:33:35 +01:00
|
|
|
peer = nm_wireguard_peer_new();
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2019-01-12 12:33:35 +01:00
|
|
|
_nm_wireguard_peer_set_public_key_bin(peer, ppeer->public_key);
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2019-01-12 12:33:35 +01:00
|
|
|
nm_setting_wireguard_append_peer(s_wg, peer);
|
|
|
|
|
}
|
2018-12-29 00:04:20 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
2018-03-13 13:42:38 +00:00
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
|
|
|
|
|
{
|
2021-11-09 13:28:54 +01:00
|
|
|
NMDeviceWireGuard *self = NM_DEVICE_WIREGUARD(object);
|
2018-12-29 00:04:20 +01:00
|
|
|
NMDeviceWireGuardPrivate *priv = NM_DEVICE_WIREGUARD_GET_PRIVATE(self);
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2018-03-13 13:42:38 +00:00
|
|
|
switch (prop_id) {
|
|
|
|
|
case PROP_PUBLIC_KEY:
|
2021-04-15 09:17:47 +02:00
|
|
|
g_value_take_variant(
|
|
|
|
|
value,
|
|
|
|
|
nm_g_variant_new_ay(priv->lnk_curr.public_key, sizeof(priv->lnk_curr.public_key)));
|
2018-03-13 13:42:38 +00:00
|
|
|
break;
|
|
|
|
|
case PROP_LISTEN_PORT:
|
2018-12-29 00:04:20 +01:00
|
|
|
g_value_set_uint(value, priv->lnk_curr.listen_port);
|
2018-03-13 13:42:38 +00:00
|
|
|
break;
|
|
|
|
|
case PROP_FWMARK:
|
2018-12-29 00:04:20 +01:00
|
|
|
g_value_set_uint(value, priv->lnk_curr.fwmark);
|
2018-03-13 13:42:38 +00:00
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-12-29 00:04:20 +01:00
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2018-03-13 13:42:38 +00:00
|
|
|
static void
|
|
|
|
|
nm_device_wireguard_init(NMDeviceWireGuard *self)
|
|
|
|
|
{
|
2019-01-12 12:33:35 +01:00
|
|
|
NMDeviceWireGuardPrivate *priv = NM_DEVICE_WIREGUARD_GET_PRIVATE(self);
|
|
|
|
|
|
|
|
|
|
c_list_init(&priv->lst_peers_head);
|
|
|
|
|
priv->peers = g_hash_table_new(_peer_data_hash, _peer_data_equal);
|
2018-03-13 13:42:38 +00:00
|
|
|
}
|
|
|
|
|
|
2018-12-29 00:04:20 +01:00
|
|
|
static void
|
|
|
|
|
dispose(GObject *object)
|
|
|
|
|
{
|
|
|
|
|
NMDeviceWireGuard *self = NM_DEVICE_WIREGUARD(object);
|
|
|
|
|
|
2019-07-29 14:37:29 +02:00
|
|
|
_device_cleanup(self);
|
2019-01-12 12:33:35 +01:00
|
|
|
|
2018-12-29 00:04:20 +01:00
|
|
|
G_OBJECT_CLASS(nm_device_wireguard_parent_class)->dispose(object);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
finalize(GObject *object)
|
|
|
|
|
{
|
2021-11-09 13:28:54 +01:00
|
|
|
NMDeviceWireGuard *self = NM_DEVICE_WIREGUARD(object);
|
2018-12-29 00:04:20 +01:00
|
|
|
NMDeviceWireGuardPrivate *priv = NM_DEVICE_WIREGUARD_GET_PRIVATE(self);
|
|
|
|
|
|
|
|
|
|
nm_explicit_bzero(priv->lnk_curr.private_key, sizeof(priv->lnk_curr.private_key));
|
|
|
|
|
|
2019-01-12 12:33:35 +01:00
|
|
|
if (priv->dns_manager) {
|
|
|
|
|
g_signal_handlers_disconnect_by_func(priv->dns_manager, _dns_config_changed, self);
|
|
|
|
|
g_object_unref(priv->dns_manager);
|
|
|
|
|
}
|
|
|
|
|
|
2019-05-03 11:03:01 +02:00
|
|
|
g_hash_table_destroy(priv->peers);
|
|
|
|
|
|
2018-12-29 00:04:20 +01:00
|
|
|
G_OBJECT_CLASS(nm_device_wireguard_parent_class)->finalize(object);
|
|
|
|
|
}
|
|
|
|
|
|
2018-03-13 13:42:38 +00:00
|
|
|
static const NMDBusInterfaceInfoExtended interface_info_device_wireguard = {
|
|
|
|
|
.parent = NM_DEFINE_GDBUS_INTERFACE_INFO_INIT(
|
|
|
|
|
NM_DBUS_INTERFACE_DEVICE_WIREGUARD,
|
|
|
|
|
.properties = NM_DEFINE_GDBUS_PROPERTY_INFOS(
|
|
|
|
|
NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("PublicKey",
|
|
|
|
|
"ay",
|
|
|
|
|
NM_DEVICE_WIREGUARD_PUBLIC_KEY),
|
|
|
|
|
NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("ListenPort",
|
|
|
|
|
"q",
|
|
|
|
|
NM_DEVICE_WIREGUARD_LISTEN_PORT),
|
|
|
|
|
NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("FwMark",
|
|
|
|
|
"u",
|
|
|
|
|
NM_DEVICE_WIREGUARD_FWMARK), ), ),
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
nm_device_wireguard_class_init(NMDeviceWireGuardClass *klass)
|
|
|
|
|
{
|
2021-11-09 13:28:54 +01:00
|
|
|
GObjectClass *object_class = G_OBJECT_CLASS(klass);
|
2018-03-13 13:42:38 +00:00
|
|
|
NMDBusObjectClass *dbus_object_class = NM_DBUS_OBJECT_CLASS(klass);
|
2021-11-09 13:28:54 +01:00
|
|
|
NMDeviceClass *device_class = NM_DEVICE_CLASS(klass);
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2018-03-13 13:42:38 +00:00
|
|
|
object_class->get_property = get_property;
|
2018-12-29 00:04:20 +01:00
|
|
|
object_class->dispose = dispose;
|
|
|
|
|
object_class->finalize = finalize;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2018-03-13 13:42:38 +00:00
|
|
|
dbus_object_class->interface_infos = NM_DBUS_INTERFACE_INFOS(&interface_info_device_wireguard);
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2018-12-29 00:04:20 +01:00
|
|
|
device_class->connection_type_supported = NM_SETTING_WIREGUARD_SETTING_NAME;
|
|
|
|
|
device_class->connection_type_check_compatible = NM_SETTING_WIREGUARD_SETTING_NAME;
|
2018-03-13 13:42:38 +00:00
|
|
|
device_class->link_types = NM_DEVICE_DEFINE_LINK_TYPES(NM_LINK_TYPE_WIREGUARD);
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2018-12-29 00:04:20 +01:00
|
|
|
device_class->state_changed = device_state_changed;
|
|
|
|
|
device_class->create_and_realize = create_and_realize;
|
|
|
|
|
device_class->act_stage2_config = act_stage2_config;
|
2019-01-12 12:33:35 +01:00
|
|
|
device_class->act_stage2_config_also_for_external_or_assume = TRUE;
|
2021-08-06 15:17:05 +02:00
|
|
|
device_class->act_stage3_ip_config = act_stage3_ip_config;
|
2018-12-29 00:04:20 +01:00
|
|
|
device_class->get_generic_capabilities = get_generic_capabilities;
|
2018-03-13 13:42:38 +00:00
|
|
|
device_class->link_changed = link_changed;
|
2018-12-29 00:04:20 +01:00
|
|
|
device_class->update_connection = update_connection;
|
2019-01-12 12:33:35 +01:00
|
|
|
device_class->can_reapply_change = can_reapply_change;
|
|
|
|
|
device_class->reapply_connection = reapply_connection;
|
2019-03-02 23:33:15 +01:00
|
|
|
device_class->get_configured_mtu = get_configured_mtu;
|
wireguard: support configuring policy routing to avoid routing loops
For WireGuard (like for all IP-tunnels and IP-based VPNs), the IP addresses of
the peers must be reached outside the tunnel/VPN itself.
For VPN connections, NetworkManager usually adds a direct /32 route to
the external VPN gateway to the underlying device. For WireGuard that is
not done, because injecting a route to another device is ugly and error
prone. Worse: WireGuard with automatic roaming and multiple peers makes this
more complicated.
This is commonly a problem when setting the default-route via the VPN,
but there are also other subtle setups where special care must be taken
to prevent such routing loops.
WireGuard's wg-quick provides a simple, automatic solution by adding two policy
routing rules and relying on the WireGuard packets having a fwmark set (see [1]).
Let's also do that. Add new properties "wireguard.ip4-auto-default-route"
and "wireguard.ip6-auto-default-route" to enable/disable this. Note that
the default value lets NetworkManager automatically choose whether to
enable it (depending on whether there are any peers that have a default
route). This means, common scenarios should now work well without additional
configuration.
Note that this is also a change in behavior and upon package upgrade
NetworkManager may start adding policy routes (if there are peers that
have a default-route). This is a change in behavior, as the user already
clearly had this setup working and configured some working solution
already.
The new automatism picks the rule priority automatically and adds the
default-route to the routing table that has the same number as the fwmark.
If any of this is unsuitable, then the user is free to disable this
automatism. Note that since 1.18.0 NetworkManager supports policy routing (*).
That means, what this automatism does can be also achieved via explicit
configuration of the profile, which gives the user more flexibility to
adjust all parameters explicitly).
(*) but only since 1.20.0 NetworkManager supports the "suppress_prefixlength"
rule attribute, which makes it impossible to configure exactly this rule-based
solution with 1.18.0 NetworkManager.
[1] https://www.wireguard.com/netns/#improved-rule-based-routing
2019-04-30 17:48:46 +02:00
|
|
|
device_class->get_extra_rules = get_extra_rules;
|
|
|
|
|
device_class->coerce_route_table = coerce_route_table;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2018-03-13 13:42:38 +00:00
|
|
|
obj_properties[PROP_PUBLIC_KEY] =
|
|
|
|
|
g_param_spec_variant(NM_DEVICE_WIREGUARD_PUBLIC_KEY,
|
|
|
|
|
"",
|
|
|
|
|
"",
|
|
|
|
|
G_VARIANT_TYPE("ay"),
|
|
|
|
|
NULL,
|
|
|
|
|
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2018-03-13 13:42:38 +00:00
|
|
|
obj_properties[PROP_LISTEN_PORT] = g_param_spec_uint(NM_DEVICE_WIREGUARD_LISTEN_PORT,
|
|
|
|
|
"",
|
|
|
|
|
"",
|
|
|
|
|
0,
|
|
|
|
|
G_MAXUINT16,
|
|
|
|
|
0,
|
|
|
|
|
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2018-03-13 13:42:38 +00:00
|
|
|
obj_properties[PROP_FWMARK] = g_param_spec_uint(NM_DEVICE_WIREGUARD_FWMARK,
|
|
|
|
|
"",
|
|
|
|
|
"",
|
|
|
|
|
0,
|
|
|
|
|
G_MAXUINT32,
|
|
|
|
|
0,
|
|
|
|
|
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2018-03-13 13:42:38 +00:00
|
|
|
g_object_class_install_properties(object_class, _PROPERTY_ENUMS_LAST, obj_properties);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*************************************************************/
|
|
|
|
|
|
|
|
|
|
#define NM_TYPE_WIREGUARD_DEVICE_FACTORY (nm_wireguard_device_factory_get_type())
|
|
|
|
|
#define NM_WIREGUARD_DEVICE_FACTORY(obj) \
|
|
|
|
|
(G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_WIREGUARD_DEVICE_FACTORY, NMWireGuardDeviceFactory))
|
|
|
|
|
|
|
|
|
|
static NMDevice *
|
2021-11-09 13:28:54 +01:00
|
|
|
create_device(NMDeviceFactory *factory,
|
|
|
|
|
const char *iface,
|
2018-03-13 13:42:38 +00:00
|
|
|
const NMPlatformLink *plink,
|
2021-11-09 13:28:54 +01:00
|
|
|
NMConnection *connection,
|
|
|
|
|
gboolean *out_ignore)
|
2018-03-13 13:42:38 +00:00
|
|
|
{
|
2020-11-12 15:57:06 +01:00
|
|
|
return g_object_new(NM_TYPE_DEVICE_WIREGUARD,
|
|
|
|
|
NM_DEVICE_IFACE,
|
|
|
|
|
iface,
|
|
|
|
|
NM_DEVICE_TYPE_DESC,
|
|
|
|
|
"WireGuard",
|
|
|
|
|
NM_DEVICE_DEVICE_TYPE,
|
|
|
|
|
NM_DEVICE_TYPE_WIREGUARD,
|
|
|
|
|
NM_DEVICE_LINK_TYPE,
|
|
|
|
|
NM_LINK_TYPE_WIREGUARD,
|
|
|
|
|
NULL);
|
2018-03-13 13:42:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
NM_DEVICE_FACTORY_DEFINE_INTERNAL(
|
|
|
|
|
WIREGUARD,
|
|
|
|
|
WireGuard,
|
|
|
|
|
wireguard,
|
2018-12-29 00:04:20 +01:00
|
|
|
NM_DEVICE_FACTORY_DECLARE_LINK_TYPES(NM_LINK_TYPE_WIREGUARD)
|
|
|
|
|
NM_DEVICE_FACTORY_DECLARE_SETTING_TYPES(NM_SETTING_WIREGUARD_SETTING_NAME),
|
2018-03-13 13:42:38 +00:00
|
|
|
factory_class->create_device = create_device;)
|