Commit 10e05bf8 authored by Thomas Haller's avatar Thomas Haller

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
parent 79f6d4ad
......@@ -7137,6 +7137,12 @@ static const NMMetaPropertyInfo *const property_infos_WIREGUARD[] = {
PROPERTY_INFO_WITH_DESC (NM_SETTING_WIREGUARD_MTU,
.property_type = &_pt_gobject_mtu,
),
PROPERTY_INFO_WITH_DESC (NM_SETTING_WIREGUARD_IP4_AUTO_DEFAULT_ROUTE,
.property_type = &_pt_gobject_enum,
),
PROPERTY_INFO_WITH_DESC (NM_SETTING_WIREGUARD_IP6_AUTO_DEFAULT_ROUTE,
.property_type = &_pt_gobject_enum,
),
NULL
};
......
......@@ -369,7 +369,9 @@
#define DESCRIBE_DOC_NM_SETTING_WIFI_P2P_WPS_METHOD N_("Flags indicating which mode of WPS is to be used. There's little point in changing the default setting as NetworkManager will automatically determine the best method to use.")
#define DESCRIBE_DOC_NM_SETTING_WIMAX_MAC_ADDRESS N_("If specified, this connection will only apply to the WiMAX device whose MAC address matches. This property does not change the MAC address of the device (known as MAC spoofing). Deprecated: 1")
#define DESCRIBE_DOC_NM_SETTING_WIMAX_NETWORK_NAME N_("Network Service Provider (NSP) name of the WiMAX network this connection should use. Deprecated: 1")
#define DESCRIBE_DOC_NM_SETTING_WIREGUARD_FWMARK N_("The use of fwmark is optional and is by default off. Setting it to 0 disables it. Otherwise it is a 32-bit fwmark for outgoing packets.")
#define DESCRIBE_DOC_NM_SETTING_WIREGUARD_FWMARK N_("The use of fwmark is optional and is by default off. Setting it to 0 disables it. Otherwise it is a 32-bit fwmark for outgoing packets. Note that \"ip4-auto-default-route\" or \"ip6-auto-default-route\" enabled, implies to automatically choose a fwmark.")
#define DESCRIBE_DOC_NM_SETTING_WIREGUARD_IP4_AUTO_DEFAULT_ROUTE N_("Whether to enable special handling of the IPv4 default route. If enabled, the IPv4 default route will be placed to a dedicated routing-table and two policy routing rules will be added. The fwmark number is also used as routing-table for the default-route, and if fwmark is zero, a unused fwmark/table is chosen automatically. This corresponds to what wg-quick does with Table=auto. Leaving this at the default will enable this option automatically if ipv4.never-default is not set and there are any peers that use a default-route as allowed-ips.")
#define DESCRIBE_DOC_NM_SETTING_WIREGUARD_IP6_AUTO_DEFAULT_ROUTE N_("Like ip4-auto-default-route, but for the IPv6 default route.")
#define DESCRIBE_DOC_NM_SETTING_WIREGUARD_LISTEN_PORT N_("The listen-port. If listen-port is not specified, the port will be chosen randomly when the interface comes up.")
#define DESCRIBE_DOC_NM_SETTING_WIREGUARD_MTU N_("If non-zero, only transmit packets of the specified size or smaller, breaking larger packets up into multiple fragments. If zero a default MTU is used. Note that contrary to wg-quick's MTU setting, this does not take into account the current routes at the time of activation.")
#define DESCRIBE_DOC_NM_SETTING_WIREGUARD_PEER_ROUTES N_("Whether to automatically add routes for the AllowedIPs ranges of the peers. If TRUE (the default), NetworkManager will automatically add routes in the routing tables according to ipv4.route-table and ipv6.route-table. If FALSE, no such routes are added automatically. In this case, the user may want to configure static routes in ipv4.routes and ipv6.routes, respectively.")
......
......@@ -1370,7 +1370,7 @@ nm_setting_sriov_class_init (NMSettingSriovClass *klass)
*/
obj_properties[PROP_AUTOPROBE_DRIVERS] =
g_param_spec_enum (NM_SETTING_SRIOV_AUTOPROBE_DRIVERS, "", "",
nm_ternary_get_type (),
NM_TYPE_TERNARY,
NM_TERNARY_DEFAULT,
NM_SETTING_PARAM_FUZZY_IGNORE |
G_PARAM_READWRITE |
......
......@@ -907,6 +907,8 @@ typedef struct {
NM_GOBJECT_PROPERTIES_DEFINE_BASE (
PROP_FWMARK,
PROP_IP4_AUTO_DEFAULT_ROUTE,
PROP_IP6_AUTO_DEFAULT_ROUTE,
PROP_LISTEN_PORT,
PROP_MTU,
PROP_PEER_ROUTES,
......@@ -919,6 +921,8 @@ typedef struct {
GPtrArray *peers_arr;
GHashTable *peers_hash;
NMSettingSecretFlags private_key_flags;
NMTernary ip4_auto_default_route;
NMTernary ip6_auto_default_route;
guint32 fwmark;
guint32 mtu;
guint16 listen_port;
......@@ -1070,6 +1074,38 @@ nm_setting_wireguard_get_mtu (NMSettingWireGuard *self)
return NM_SETTING_WIREGUARD_GET_PRIVATE (self)->mtu;
}
/**
* nm_setting_wireguard_get_ip4_auto_default_route:
* @self: the #NMSettingWireGuard setting.
*
* Returns: the "ip4-auto-default-route" property of the setting.
*
* Since: 1.20
*/
NMTernary
nm_setting_wireguard_get_ip4_auto_default_route (NMSettingWireGuard *self)
{
g_return_val_if_fail (NM_IS_SETTING_WIREGUARD (self), NM_TERNARY_DEFAULT);
return NM_SETTING_WIREGUARD_GET_PRIVATE (self)->ip4_auto_default_route;
}
/**
* nm_setting_wireguard_get_ip6_auto_default_route:
* @self: the #NMSettingWireGuard setting.
*
* Returns: the "ip6-auto-default-route" property of the setting.
*
* Since: 1.20
*/
NMTernary
nm_setting_wireguard_get_ip6_auto_default_route (NMSettingWireGuard *self)
{
g_return_val_if_fail (NM_IS_SETTING_WIREGUARD (self), NM_TERNARY_DEFAULT);
return NM_SETTING_WIREGUARD_GET_PRIVATE (self)->ip6_auto_default_route;
}
/*****************************************************************************/
static void
......@@ -2260,6 +2296,12 @@ get_property (GObject *object, guint prop_id,
case PROP_FWMARK:
g_value_set_uint (value, priv->fwmark);
break;
case PROP_IP4_AUTO_DEFAULT_ROUTE:
g_value_set_enum (value, priv->ip4_auto_default_route);
break;
case PROP_IP6_AUTO_DEFAULT_ROUTE:
g_value_set_enum (value, priv->ip6_auto_default_route);
break;
case PROP_LISTEN_PORT:
g_value_set_uint (value, priv->listen_port);
break;
......@@ -2292,6 +2334,12 @@ set_property (GObject *object, guint prop_id,
case PROP_FWMARK:
priv->fwmark = g_value_get_uint (value);
break;
case PROP_IP4_AUTO_DEFAULT_ROUTE:
priv->ip4_auto_default_route = g_value_get_enum (value);
break;
case PROP_IP6_AUTO_DEFAULT_ROUTE:
priv->ip6_auto_default_route = g_value_get_enum (value);
break;
case PROP_LISTEN_PORT:
priv->listen_port = g_value_get_uint (value);
break;
......@@ -2334,6 +2382,8 @@ nm_setting_wireguard_init (NMSettingWireGuard *setting)
priv->peers_arr = g_ptr_array_new ();
priv->peers_hash = g_hash_table_new (nm_pstr_hash, nm_pstr_equal);
priv->peer_routes = TRUE;
priv->ip4_auto_default_route = NM_TERNARY_DEFAULT;
priv->ip6_auto_default_route = NM_TERNARY_DEFAULT;
}
/**
......@@ -2424,6 +2474,9 @@ nm_setting_wireguard_class_init (NMSettingWireGuardClass *klass)
* The use of fwmark is optional and is by default off. Setting it to 0
* disables it. Otherwise it is a 32-bit fwmark for outgoing packets.
*
* Note that "ip4-auto-default-route" or "ip6-auto-default-route" enabled,
* implies to automatically choose a fwmark.
*
* Since: 1.16
**/
obj_properties[PROP_FWMARK] =
......@@ -2487,6 +2540,45 @@ nm_setting_wireguard_class_init (NMSettingWireGuardClass *klass)
| NM_SETTING_PARAM_INFERRABLE
| G_PARAM_STATIC_STRINGS);
/**
* NMSettingWireGuard:ip4-auto-default-route:
*
* Whether to enable special handling of the IPv4 default route.
* If enabled, the IPv4 default route will be placed to a dedicated
* routing-table and two policy routing rules will be added.
* The fwmark number is also used as routing-table for the default-route,
* and if fwmark is zero, a unused fwmark/table is chosen automatically.
* This corresponds to what wg-quick does with Table=auto.
*
* Leaving this at the default will enable this option automatically
* if ipv4.never-default is not set and there are any peers that use
* a default-route as allowed-ips.
*
* Since: 1.20
**/
obj_properties[PROP_IP4_AUTO_DEFAULT_ROUTE] =
g_param_spec_enum (NM_SETTING_WIREGUARD_IP4_AUTO_DEFAULT_ROUTE, "", "",
NM_TYPE_TERNARY,
NM_TERNARY_DEFAULT,
NM_SETTING_PARAM_FUZZY_IGNORE |
G_PARAM_READWRITE |
G_PARAM_STATIC_STRINGS);
/**
* NMSettingWireGuard:ip6-auto-default-route:
*
* Like ip4-auto-default-route, but for the IPv6 default route.
*
* Since: 1.20
**/
obj_properties[PROP_IP6_AUTO_DEFAULT_ROUTE] =
g_param_spec_enum (NM_SETTING_WIREGUARD_IP6_AUTO_DEFAULT_ROUTE, "", "",
NM_TYPE_TERNARY,
NM_TERNARY_DEFAULT,
NM_SETTING_PARAM_FUZZY_IGNORE |
G_PARAM_READWRITE |
G_PARAM_STATIC_STRINGS);
/* ---dbus---
* property: peers
* format: array of 'a{sv}'
......
......@@ -138,6 +138,8 @@ int nm_wireguard_peer_cmp (const NMWireGuardPeer *a,
#define NM_SETTING_WIREGUARD_MTU "mtu"
#define NM_SETTING_WIREGUARD_PEER_ROUTES "peer-routes"
#define NM_SETTING_WIREGUARD_IP4_AUTO_DEFAULT_ROUTE "ip4-auto-default-route"
#define NM_SETTING_WIREGUARD_IP6_AUTO_DEFAULT_ROUTE "ip6-auto-default-route"
#define NM_WIREGUARD_PEER_ATTR_ALLOWED_IPS "allowed-ips"
#define NM_WIREGUARD_PEER_ATTR_ENDPOINT "endpoint"
......@@ -206,6 +208,12 @@ gboolean nm_setting_wireguard_get_peer_routes (NMSettingWireGuard *self);
NM_AVAILABLE_IN_1_16
guint32 nm_setting_wireguard_get_mtu (NMSettingWireGuard *self);
NM_AVAILABLE_IN_1_20
NMTernary nm_setting_wireguard_get_ip4_auto_default_route (NMSettingWireGuard *self);
NM_AVAILABLE_IN_1_20
NMTernary nm_setting_wireguard_get_ip6_auto_default_route (NMSettingWireGuard *self);
/*****************************************************************************/
G_END_DECLS
......
......@@ -1624,5 +1624,7 @@ global:
nm_setting_ovs_dpdk_get_devargs;
nm_setting_ovs_dpdk_get_type;
nm_setting_ovs_dpdk_new;
nm_setting_wireguard_get_ip4_auto_default_route;
nm_setting_wireguard_get_ip6_auto_default_route;
nm_settings_add_connection2_flags_get_type;
} libnm_1_18_0;
This diff is collapsed.
......@@ -11567,6 +11567,12 @@ check_and_reapply_connection (NMDevice *self,
/**************************************************************************
* Reapply changes
*
* Note that reapply_connection() is called as very first. This is for example
* important for NMDeviceWireGuard, which implements coerce_route_table()
* and get_extra_rules().
* That is because NMDeviceWireGuard caches settings, so during reapply that
* cache must be updated *first*.
*************************************************************************/
klass->reapply_connection (self, con_old, con_new);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment