From 7e22e5de78e91d14d3511098b522e9a42b26f0a2 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Sun, 28 Jul 2019 13:44:29 +0200 Subject: libnm/docs: fix section description for WireGuard settings --- libnm-core/nm-setting-wireguard.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libnm-core/nm-setting-wireguard.c b/libnm-core/nm-setting-wireguard.c index 9e67454d0d..2a8c1a87f2 100644 --- a/libnm-core/nm-setting-wireguard.c +++ b/libnm-core/nm-setting-wireguard.c @@ -929,7 +929,7 @@ typedef struct { /** * NMSettingWireGuard: * - * WireGuard Ethernet Settings + * WireGuard Settings * * Since: 1.16 */ -- cgit v1.2.1 From c4788e611edfa897860494398de394c4a6254d6e Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Sun, 28 Jul 2019 14:37:44 +0200 Subject: libnm: add internal _nm_connection_get_setting() accessor nm_connection_get_setting() returns a pointer of type NMSetting. That is very inconvenient, because most callers will need the the result pointer as a setting subtype (like NMSettingConnection). That would be like g_object_new() returning a "GObject *" pointer, which is technically correct but annoying. In the past that problem was avoided by having countless accessors like nm_connection_get_setting_ip4_config(), etc. But that just blows up the API and also is not generic. Meaning: the type is not a function argument but the function itself. That makes composing the code harder as the setting type cannot be treated generically (as a function argument). Anyway. Add an internal wrapper that returns a void pointer. --- libnm-core/nm-core-internal.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/libnm-core/nm-core-internal.h b/libnm-core/nm-core-internal.h index d263900d1a..868f731214 100644 --- a/libnm-core/nm-core-internal.h +++ b/libnm-core/nm-core-internal.h @@ -476,6 +476,13 @@ gboolean _nm_utils_generate_mac_address_mask_parse (const char *value, /*****************************************************************************/ +static inline gpointer +_nm_connection_get_setting (NMConnection *connection, + GType type) +{ + return (gpointer) nm_connection_get_setting (connection, type); +} + NMSettingIPConfig *nm_connection_get_setting_ip_config (NMConnection *connection, int addr_family); -- cgit v1.2.1 From 0e44c294e7a37c055ff067fce83da966c600ebd4 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Mon, 29 Jul 2019 11:06:18 +0200 Subject: platform: allow AF_UNSPEC for nmp_lookup_init_object_by_addr_family() --- src/platform/nmp-object.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/platform/nmp-object.c b/src/platform/nmp-object.c index fc5cffab2e..3c914f0c3e 100644 --- a/src/platform/nmp-object.c +++ b/src/platform/nmp-object.c @@ -2146,9 +2146,12 @@ nmp_lookup_init_object_by_addr_family (NMPLookup *lookup, NMPObject *o; nm_assert (lookup); - nm_assert_addr_family (addr_family); nm_assert (NM_IN_SET (obj_type, NMP_OBJECT_TYPE_ROUTING_RULE)); + if (addr_family == AF_UNSPEC) + return nmp_lookup_init_obj_type (lookup, obj_type); + + nm_assert_addr_family (addr_family); o = _nmp_object_stackinit_from_type (&lookup->selector_obj, obj_type); NMP_OBJECT_CAST_ROUTING_RULE (o)->addr_family = addr_family; lookup->cache_id_type = NMP_CACHE_ID_TYPE_OBJECT_BY_ADDR_FAMILY; -- cgit v1.2.1 From 25fd48948a23e5f3a9008e1f4211abcc92e1822a Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Sun, 28 Jul 2019 16:06:11 +0200 Subject: platform: make plobj argument of nmp_object_new() void pointer NMPlatformObject is a base-type of all actual platform structs. We very seldomly use this type directly. Most callers that pass the plobj to nmp_object_new() will need to cast it. Make the varible a void pointer to not require the cast. --- src/platform/nmp-object.c | 2 +- src/platform/nmp-object.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/platform/nmp-object.c b/src/platform/nmp-object.c index 3c914f0c3e..5cbf9428f8 100644 --- a/src/platform/nmp-object.c +++ b/src/platform/nmp-object.c @@ -786,7 +786,7 @@ _nmp_object_new_from_class (const NMPClass *klass) } NMPObject * -nmp_object_new (NMPObjectType obj_type, const NMPlatformObject *plobj) +nmp_object_new (NMPObjectType obj_type, gconstpointer plobj) { const NMPClass *klass = nmp_class_from_type (obj_type); NMPObject *obj; diff --git a/src/platform/nmp-object.h b/src/platform/nmp-object.h index fc844aaf86..95173130cd 100644 --- a/src/platform/nmp-object.h +++ b/src/platform/nmp-object.h @@ -673,7 +673,7 @@ nmp_object_unref (const NMPObject *obj) _changed; \ }) -NMPObject *nmp_object_new (NMPObjectType obj_type, const NMPlatformObject *plob); +NMPObject *nmp_object_new (NMPObjectType obj_type, gconstpointer plobj); NMPObject *nmp_object_new_link (int ifindex); const NMPObject *nmp_object_stackinit (NMPObject *obj, NMPObjectType obj_type, gconstpointer plobj); -- cgit v1.2.1 From 13718183f4cda7f36e66cc36e10e17a7e0a2d7f3 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Sun, 28 Jul 2019 14:16:37 +0200 Subject: platform: cleanup NMPObject cast macros --- src/platform/nmp-object.h | 105 +++++++--------------------------------------- 1 file changed, 15 insertions(+), 90 deletions(-) diff --git a/src/platform/nmp-object.h b/src/platform/nmp-object.h index 95173130cd..55c5ce2b76 100644 --- a/src/platform/nmp-object.h +++ b/src/platform/nmp-object.h @@ -534,101 +534,26 @@ _NMP_OBJECT_TYPE_IS_OBJ_WITH_IFINDEX (NMPObjectType obj_type) _obj ? &NM_CONSTCAST (NMPObject, _obj)->obj_with_ifindex : NULL; \ }) -#define NMP_OBJECT_CAST_LINK(obj) \ +#define _NMP_OBJECT_CAST(obj, field, ...) \ ({ \ typeof (obj) _obj = (obj); \ \ - nm_assert (!_obj || NMP_OBJECT_GET_TYPE (_obj) == NMP_OBJECT_TYPE_LINK); \ - _obj ? &NM_CONSTCAST (NMPObject, _obj)->link : NULL; \ + nm_assert (!_obj || NM_IN_SET (NMP_OBJECT_GET_TYPE (_obj), __VA_ARGS__)); \ + _obj ? &NM_CONSTCAST (NMPObject, _obj)->field : NULL; \ }) -#define NMP_OBJECT_CAST_IP_ADDRESS(obj) \ - ({ \ - typeof (obj) _obj = (obj); \ - \ - nm_assert (!_obj || NM_IN_SET (NMP_OBJECT_GET_TYPE (_obj), NMP_OBJECT_TYPE_IP4_ADDRESS, NMP_OBJECT_TYPE_IP6_ADDRESS)); \ - _obj ? &NM_CONSTCAST (NMPObject, _obj)->ip_address : NULL; \ - }) - -#define NMP_OBJECT_CAST_IPX_ADDRESS(obj) \ - ({ \ - typeof (obj) _obj = (obj); \ - \ - nm_assert (!_obj || NM_IN_SET (NMP_OBJECT_GET_TYPE (_obj), NMP_OBJECT_TYPE_IP4_ADDRESS, NMP_OBJECT_TYPE_IP6_ADDRESS)); \ - _obj ? &NM_CONSTCAST (NMPObject, _obj)->ipx_address : NULL; \ - }) - -#define NMP_OBJECT_CAST_IP4_ADDRESS(obj) \ - ({ \ - typeof (obj) _obj = (obj); \ - \ - nm_assert (!_obj || NMP_OBJECT_GET_TYPE (_obj) == NMP_OBJECT_TYPE_IP4_ADDRESS); \ - _obj ? &NM_CONSTCAST (NMPObject, _obj)->ip4_address : NULL; \ - }) - -#define NMP_OBJECT_CAST_IP6_ADDRESS(obj) \ - ({ \ - typeof (obj) _obj = (obj); \ - \ - nm_assert (!_obj || NMP_OBJECT_GET_TYPE (_obj) == NMP_OBJECT_TYPE_IP6_ADDRESS); \ - _obj ? &NM_CONSTCAST (NMPObject, _obj)->ip6_address : NULL; \ - }) - -#define NMP_OBJECT_CAST_IPX_ROUTE(obj) \ - ({ \ - typeof (obj) _obj = (obj); \ - \ - nm_assert (!_obj || NM_IN_SET (NMP_OBJECT_GET_TYPE (_obj), NMP_OBJECT_TYPE_IP4_ROUTE, NMP_OBJECT_TYPE_IP6_ROUTE)); \ - _obj ? &NM_CONSTCAST (NMPObject, _obj)->ipx_route : NULL; \ - }) - -#define NMP_OBJECT_CAST_IP_ROUTE(obj) \ - ({ \ - typeof (obj) _obj = (obj); \ - \ - nm_assert (!_obj || NM_IN_SET (NMP_OBJECT_GET_TYPE (_obj), NMP_OBJECT_TYPE_IP4_ROUTE, NMP_OBJECT_TYPE_IP6_ROUTE)); \ - _obj ? &NM_CONSTCAST (NMPObject, _obj)->ip_route : NULL; \ - }) - -#define NMP_OBJECT_CAST_IP4_ROUTE(obj) \ - ({ \ - typeof (obj) _obj = (obj); \ - \ - nm_assert (!_obj || NMP_OBJECT_GET_TYPE (_obj) == NMP_OBJECT_TYPE_IP4_ROUTE); \ - _obj ? &NM_CONSTCAST (NMPObject, _obj)->ip4_route : NULL; \ - }) - -#define NMP_OBJECT_CAST_IP6_ROUTE(obj) \ - ({ \ - typeof (obj) _obj = (obj); \ - \ - nm_assert (!_obj || NMP_OBJECT_GET_TYPE (_obj) == NMP_OBJECT_TYPE_IP6_ROUTE); \ - _obj ? &NM_CONSTCAST (NMPObject, _obj)->ip6_route : NULL; \ - }) - -#define NMP_OBJECT_CAST_ROUTING_RULE(obj) \ - ({ \ - typeof (obj) _obj = (obj); \ - \ - nm_assert (!_obj || NMP_OBJECT_GET_TYPE (_obj) == NMP_OBJECT_TYPE_ROUTING_RULE); \ - _obj ? &NM_CONSTCAST (NMPObject, _obj)->routing_rule : NULL; \ - }) - -#define NMP_OBJECT_CAST_QDISC(obj) \ - ({ \ - typeof (obj) _obj = (obj); \ - \ - nm_assert (!_obj || NMP_OBJECT_GET_TYPE (_obj) == NMP_OBJECT_TYPE_QDISC); \ - _obj ? &NM_CONSTCAST (NMPObject, _obj)->qdisc : NULL; \ - }) - -#define NMP_OBJECT_CAST_TFILTER(obj) \ - ({ \ - typeof (obj) _obj = (obj); \ - \ - nm_assert (!_obj || NMP_OBJECT_GET_TYPE (_obj) == NMP_OBJECT_TYPE_TFILTER); \ - _obj ? &NM_CONSTCAST (NMPObject, _obj)->tfilter : NULL; \ - }) +#define NMP_OBJECT_CAST_LINK(obj) _NMP_OBJECT_CAST (obj, link, NMP_OBJECT_TYPE_LINK) +#define NMP_OBJECT_CAST_IP_ADDRESS(obj) _NMP_OBJECT_CAST (obj, ip_address, NMP_OBJECT_TYPE_IP4_ADDRESS, NMP_OBJECT_TYPE_IP6_ADDRESS) +#define NMP_OBJECT_CAST_IPX_ADDRESS(obj) _NMP_OBJECT_CAST (obj, ipx_address, NMP_OBJECT_TYPE_IP4_ADDRESS, NMP_OBJECT_TYPE_IP6_ADDRESS) +#define NMP_OBJECT_CAST_IP4_ADDRESS(obj) _NMP_OBJECT_CAST (obj, ip4_address, NMP_OBJECT_TYPE_IP4_ADDRESS) +#define NMP_OBJECT_CAST_IP6_ADDRESS(obj) _NMP_OBJECT_CAST (obj, ip6_address, NMP_OBJECT_TYPE_IP6_ADDRESS) +#define NMP_OBJECT_CAST_IP_ROUTE(obj) _NMP_OBJECT_CAST (obj, ip_route, NMP_OBJECT_TYPE_IP4_ROUTE, NMP_OBJECT_TYPE_IP6_ROUTE) +#define NMP_OBJECT_CAST_IPX_ROUTE(obj) _NMP_OBJECT_CAST (obj, ipx_route, NMP_OBJECT_TYPE_IP4_ROUTE, NMP_OBJECT_TYPE_IP6_ROUTE) +#define NMP_OBJECT_CAST_IP4_ROUTE(obj) _NMP_OBJECT_CAST (obj, ip4_route, NMP_OBJECT_TYPE_IP4_ROUTE) +#define NMP_OBJECT_CAST_IP6_ROUTE(obj) _NMP_OBJECT_CAST (obj, ip6_route, NMP_OBJECT_TYPE_IP6_ROUTE) +#define NMP_OBJECT_CAST_ROUTING_RULE(obj) _NMP_OBJECT_CAST (obj, routing_rule, NMP_OBJECT_TYPE_ROUTING_RULE) +#define NMP_OBJECT_CAST_QDISC(obj) _NMP_OBJECT_CAST (obj, qdisc, NMP_OBJECT_TYPE_QDISC) +#define NMP_OBJECT_CAST_TFILTER(obj) _NMP_OBJECT_CAST (obj, tfilter, NMP_OBJECT_TYPE_TFILTER) static inline const NMPObject * nmp_object_ref (const NMPObject *obj) -- cgit v1.2.1 From 6b3783c77f8f0999405821d67e4797350cb891a9 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Sun, 28 Jul 2019 14:20:56 +0200 Subject: platform: add NMP_OBJECT_CAST_LNK_WIREGUARD() macro --- src/platform/nmp-object.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/platform/nmp-object.h b/src/platform/nmp-object.h index 55c5ce2b76..cea5f95810 100644 --- a/src/platform/nmp-object.h +++ b/src/platform/nmp-object.h @@ -554,6 +554,7 @@ _NMP_OBJECT_TYPE_IS_OBJ_WITH_IFINDEX (NMPObjectType obj_type) #define NMP_OBJECT_CAST_ROUTING_RULE(obj) _NMP_OBJECT_CAST (obj, routing_rule, NMP_OBJECT_TYPE_ROUTING_RULE) #define NMP_OBJECT_CAST_QDISC(obj) _NMP_OBJECT_CAST (obj, qdisc, NMP_OBJECT_TYPE_QDISC) #define NMP_OBJECT_CAST_TFILTER(obj) _NMP_OBJECT_CAST (obj, tfilter, NMP_OBJECT_TYPE_TFILTER) +#define NMP_OBJECT_CAST_LNK_WIREGUARD(obj) _NMP_OBJECT_CAST (obj, lnk_wireguard, NMP_OBJECT_TYPE_LNK_WIREGUARD) static inline const NMPObject * nmp_object_ref (const NMPObject *obj) -- cgit v1.2.1 From 310ea1bc6a694452c2a6f518c065e64402446948 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Mon, 29 Jul 2019 16:08:41 +0200 Subject: device: fix reapply for policy routing rules We need to re-sync the rules. --- src/devices/nm-device.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c index da34602427..450c4938f4 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -11550,6 +11550,8 @@ check_and_reapply_connection (NMDevice *self, nm_device_reactivate_ip4_config (self, s_ip4_old, s_ip4_new); nm_device_reactivate_ip6_config (self, s_ip6_old, s_ip6_new); + _routing_rules_sync (self, NM_TERNARY_TRUE); + reactivate_proxy_config (self); return TRUE; -- cgit v1.2.1 From 40ae1c8d7d2cea480ef26f32513937ec43d3f70a Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Sun, 28 Jul 2019 15:59:20 +0200 Subject: device: allow NMDevice implementations to inject policy routing rules --- src/devices/nm-device.c | 33 ++++++++++++++++++++++++++++----- src/devices/nm-device.h | 6 ++++++ 2 files changed, 34 insertions(+), 5 deletions(-) diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c index 450c4938f4..d4bee8780a 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -6587,11 +6587,15 @@ _routing_rules_sync (NMDevice *self, { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); NMPRulesManager *rules_manager = nm_netns_get_rules_manager (nm_device_get_netns (self)); + NMDeviceClass *klass = NM_DEVICE_GET_CLASS (self); gboolean untrack_only_dirty = FALSE; gboolean keep_deleted_rules; - gpointer user_tag; + gpointer user_tag_1; + gpointer user_tag_2; - user_tag = priv; + /* take two arbitrary user-tag pointers that belong to @self. */ + user_tag_1 = &priv->v4_route_table; + user_tag_2 = &priv->v6_route_table; if (set_mode == NM_TERNARY_TRUE) { NMConnection *applied_connection; @@ -6600,7 +6604,9 @@ _routing_rules_sync (NMDevice *self, int is_ipv4; untrack_only_dirty = TRUE; - nmp_rules_manager_set_dirty (rules_manager, user_tag); + nmp_rules_manager_set_dirty (rules_manager, user_tag_1); + if (klass->get_extra_rules) + nmp_rules_manager_set_dirty (rules_manager, user_tag_2); applied_connection = nm_device_get_applied_connection (self); @@ -6625,13 +6631,30 @@ _routing_rules_sync (NMDevice *self, nmp_rules_manager_track (rules_manager, &plrule, 10, - user_tag, + user_tag_1, NMP_RULES_MANAGER_EXTERN_WEAKLY_TRACKED_USER_TAG); } } + + if (klass->get_extra_rules) { + gs_unref_ptrarray GPtrArray *extra_rules = NULL; + + extra_rules = klass->get_extra_rules (self); + if (extra_rules) { + for (i = 0; i < extra_rules->len; i++) { + nmp_rules_manager_track (rules_manager, + NMP_OBJECT_CAST_ROUTING_RULE (extra_rules->pdata[i]), + 10, + user_tag_2, + NMP_RULES_MANAGER_EXTERN_WEAKLY_TRACKED_USER_TAG); + } + } + } } - nmp_rules_manager_untrack_all (rules_manager, user_tag, !untrack_only_dirty); + nmp_rules_manager_untrack_all (rules_manager, user_tag_1, !untrack_only_dirty); + if (klass->get_extra_rules) + nmp_rules_manager_untrack_all (rules_manager, user_tag_2, !untrack_only_dirty); keep_deleted_rules = FALSE; if (set_mode == NM_TERNARY_DEFAULT) { diff --git a/src/devices/nm-device.h b/src/devices/nm-device.h index 2108b134e9..40639987e4 100644 --- a/src/devices/nm-device.h +++ b/src/devices/nm-device.h @@ -320,6 +320,12 @@ typedef struct _NMDeviceClass { void (* set_enabled) (NMDevice *self, gboolean enabled); + /* let the subclass return additional NMPlatformRoutingRule (in form of NMPObject + * pointers) that shall be added to the rules provided by this device. + * The returned GPtrArray will be g_ptr_array_unref()'ed. The subclass may or + * may not keep an additional reference and return this array again and again. */ + GPtrArray *(*get_extra_rules) (NMDevice *self); + /* allow derived classes to override the result of nm_device_autoconnect_allowed(). * If the value changes, the class should call nm_device_emit_recheck_auto_activate(), * which emits NM_DEVICE_RECHECK_AUTO_ACTIVATE signal. */ -- cgit v1.2.1 From 9d88f0d73f8a0239f3023851b80a3c5db5800883 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Tue, 30 Apr 2019 15:49:07 +0200 Subject: device: allow device classes to overwrite the route-table --- src/devices/nm-device.c | 28 ++++++++++++++++++++-------- src/devices/nm-device.h | 7 +++++++ 2 files changed, 27 insertions(+), 8 deletions(-) diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c index d4bee8780a..01e00a7107 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -2265,9 +2265,11 @@ _get_route_table (NMDevice *self, int addr_family) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + NMDeviceClass *klass; NMConnection *connection; NMSettingIPConfig *s_ip; guint32 route_table = 0; + gboolean is_user_config = TRUE; nm_assert_addr_family (addr_family); @@ -2288,16 +2290,26 @@ _get_route_table (NMDevice *self, route_table = nm_setting_ip_config_get_route_table (s_ip); } if (route_table == 0u) { - route_table = nm_config_data_get_connection_default_int64 (NM_CONFIG_GET_DATA, - addr_family == AF_INET - ? NM_CON_DEFAULT ("ipv4.route-table") - : NM_CON_DEFAULT ("ipv6.route-table"), - self, - 0, - G_MAXUINT32, - 0); + gint64 v; + + v = nm_config_data_get_connection_default_int64 (NM_CONFIG_GET_DATA, + addr_family == AF_INET + ? NM_CON_DEFAULT ("ipv4.route-table") + : NM_CON_DEFAULT ("ipv6.route-table"), + self, + 0, + G_MAXUINT32, + -1); + if (v != -1) { + route_table = v; + is_user_config = FALSE; + } } + klass = NM_DEVICE_GET_CLASS (self); + if (klass->coerce_route_table) + route_table = klass->coerce_route_table (self, addr_family, route_table, is_user_config); + if (addr_family == AF_INET) { priv->v4_route_table_initialized = TRUE; priv->v4_route_table = route_table; diff --git a/src/devices/nm-device.h b/src/devices/nm-device.h index 40639987e4..ae6aab392a 100644 --- a/src/devices/nm-device.h +++ b/src/devices/nm-device.h @@ -337,6 +337,13 @@ typedef struct _NMDeviceClass { guint32 (*get_configured_mtu) (NMDevice *self, NMDeviceMtuSource *out_source); + /* allow the subclass to overwrite the routing table. This is mainly useful + * to change from partial mode (route-table=0) to full-sync mode (route-table=254). */ + guint32 (*coerce_route_table) (NMDevice *self, + int addr_family, + guint32 route_table, + gboolean is_user_config); + const char *(*get_auto_ip_config_method) (NMDevice *self, int addr_family); /* Checks whether the connection is compatible with the device using -- cgit v1.2.1 From 79f6d4ad181c3d7d18f5cd97d9123efa4ff68d6c Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Mon, 29 Jul 2019 14:37:29 +0200 Subject: wireguard: refactor cleanup of NMDeviceWireGuard on disconnect/dispose --- src/devices/nm-device-wireguard.c | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/src/devices/nm-device-wireguard.c b/src/devices/nm-device-wireguard.c index 169775146c..714dbd4740 100644 --- a/src/devices/nm-device-wireguard.c +++ b/src/devices/nm-device-wireguard.c @@ -1405,21 +1405,25 @@ get_configured_mtu (NMDevice *device, NMDeviceMtuSource *out_source) out_source); } +static void +_device_cleanup (NMDeviceWireGuard *self) +{ + NMDeviceWireGuardPrivate *priv = NM_DEVICE_WIREGUARD_GET_PRIVATE (self); + + _peers_remove_all (priv); + _secrets_cancel (self); +} + static void device_state_changed (NMDevice *device, NMDeviceState new_state, NMDeviceState old_state, NMDeviceStateReason reason) { - NMDeviceWireGuardPrivate *priv; - if (new_state <= NM_DEVICE_STATE_ACTIVATED) return; - priv = NM_DEVICE_WIREGUARD_GET_PRIVATE (device); - - _peers_remove_all (priv); - _secrets_cancel (NM_DEVICE_WIREGUARD (device)); + _device_cleanup (NM_DEVICE_WIREGUARD (device)); } /*****************************************************************************/ @@ -1567,11 +1571,8 @@ static void dispose (GObject *object) { NMDeviceWireGuard *self = NM_DEVICE_WIREGUARD (object); - NMDeviceWireGuardPrivate *priv = NM_DEVICE_WIREGUARD_GET_PRIVATE (self); - - _secrets_cancel (self); - _peers_remove_all (priv); + _device_cleanup (self); G_OBJECT_CLASS (nm_device_wireguard_parent_class)->dispose (object); } -- cgit v1.2.1 From 10e05bf8ab0460cef84cc7315533765384440007 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Tue, 30 Apr 2019 17:48:46 +0200 Subject: 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 --- clients/common/nm-meta-setting-desc.c | 6 + clients/common/settings-docs.h.in | 4 +- libnm-core/nm-setting-sriov.c | 2 +- libnm-core/nm-setting-wireguard.c | 92 +++++++ libnm-core/nm-setting-wireguard.h | 8 + libnm/libnm.ver | 2 + src/devices/nm-device-wireguard.c | 446 +++++++++++++++++++++++++++++++++- src/devices/nm-device.c | 6 + 8 files changed, 559 insertions(+), 7 deletions(-) diff --git a/clients/common/nm-meta-setting-desc.c b/clients/common/nm-meta-setting-desc.c index 0260fa828e..c1a5c2a7fe 100644 --- a/clients/common/nm-meta-setting-desc.c +++ b/clients/common/nm-meta-setting-desc.c @@ -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 }; diff --git a/clients/common/settings-docs.h.in b/clients/common/settings-docs.h.in index 8053b01973..789083059c 100644 --- a/clients/common/settings-docs.h.in +++ b/clients/common/settings-docs.h.in @@ -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.") diff --git a/libnm-core/nm-setting-sriov.c b/libnm-core/nm-setting-sriov.c index 94583b09fc..90ac44abb8 100644 --- a/libnm-core/nm-setting-sriov.c +++ b/libnm-core/nm-setting-sriov.c @@ -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 | diff --git a/libnm-core/nm-setting-wireguard.c b/libnm-core/nm-setting-wireguard.c index 2a8c1a87f2..07a841f4a7 100644 --- a/libnm-core/nm-setting-wireguard.c +++ b/libnm-core/nm-setting-wireguard.c @@ -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}' diff --git a/libnm-core/nm-setting-wireguard.h b/libnm-core/nm-setting-wireguard.h index 017eb1f6d3..1f81422fb1 100644 --- a/libnm-core/nm-setting-wireguard.h +++ b/libnm-core/nm-setting-wireguard.h @@ -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 diff --git a/libnm/libnm.ver b/libnm/libnm.ver index fb5c06b6e6..51e0d87241 100644 --- a/libnm/libnm.ver +++ b/libnm/libnm.ver @@ -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; diff --git a/src/devices/nm-device-wireguard.c b/src/devices/nm-device-wireguard.c index 714dbd4740..d36573cf25 100644 --- a/src/devices/nm-device-wireguard.c +++ b/src/devices/nm-device-wireguard.c @@ -21,12 +21,16 @@ #include "nm-device-wireguard.h" +#include +#include + #include "nm-setting-wireguard.h" #include "nm-core-internal.h" #include "nm-glib-aux/nm-secret-utils.h" #include "nm-device-private.h" #include "platform/nm-platform.h" #include "platform/nmp-object.h" +#include "platform/nmp-rules-manager.h" #include "nm-device-factory.h" #include "nm-active-connection.h" #include "nm-act-request.h" @@ -134,10 +138,21 @@ typedef struct { GHashTable *peers; gint64 resolve_next_try_at; - guint resolve_next_try_id; - gint64 link_config_last_at; + + guint resolve_next_try_id; guint link_config_delayed_id; + + guint32 auto_default_route_fwmark; + + 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; + } NMDeviceWireGuardPrivate; struct _NMDeviceWireGuard { @@ -177,6 +192,394 @@ NM_UTILS_LOOKUP_STR_DEFINE_STATIC (_link_config_mode_to_string, LinkConfigMode, /*****************************************************************************/ +static void +_auto_default_route_get_enabled (NMSettingWireGuard *s_wg, + NMConnection *connection, + gboolean *out_enabled_v4, + gboolean *out_enabled_v6) +{ + NMTernary enabled_v4; + NMTernary enabled_v6; + + enabled_v4 = nm_setting_wireguard_get_ip4_auto_default_route (s_wg); + enabled_v6 = nm_setting_wireguard_get_ip6_auto_default_route (s_wg); + + 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; + } + + if ( enabled_v4 == NM_TERNARY_DEFAULT + || enabled_v6 == NM_TERNARY_DEFAULT) { + guint i, n_peers; + + 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++) { + const char *aip; + gboolean valid; + int prefix; + int addr_family; + + 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; + + 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; + } + } + } + } +done: + ; + } + + *out_enabled_v4 = (enabled_v4 == TRUE); + *out_enabled_v6 = (enabled_v6 == TRUE); +} + +static guint32 +_auto_default_route_find_unused_table (NMPlatform *platform) +{ + guint32 table; + int is_ipv4; + + for (table = 51820; TRUE; table++) { + const NMDedupMultiHeadEntry *head_entry; + const guint32 table_coerced = nm_platform_route_table_coerce (table); + NMDedupMultiIter iter; + const NMPObject *plobj; + + /* find a table/fwmark that is not yet in use. */ + + for (is_ipv4 = 0; is_ipv4 < 2; is_ipv4++) { + head_entry = nm_platform_lookup_object (platform, + is_ipv4 + ? NMP_OBJECT_TYPE_IP4_ROUTE + : NMP_OBJECT_TYPE_IP6_ROUTE, + -1); + nmp_cache_iter_for_each (&iter, head_entry, &plobj) { + if (NMP_OBJECT_CAST_IP_ROUTE (plobj)->table_coerced == table_coerced) + goto try_next_table; + } + } + + head_entry = nm_platform_lookup_object_by_addr_family (platform, + NMP_OBJECT_TYPE_ROUTING_RULE, + AF_UNSPEC); + nmp_cache_iter_for_each (&iter, head_entry, &plobj) { + const NMPlatformRoutingRule *rr = NMP_OBJECT_CAST_ROUTING_RULE (plobj); + + if (rr->fwmark == table) + goto try_next_table; + } + + head_entry = nm_platform_lookup_obj_type (platform, NMP_OBJECT_TYPE_LINK); + nmp_cache_iter_for_each (&iter, head_entry, &plobj) { + const NMPObject *lnk_wg; + + if (plobj->link.type != NM_LINK_TYPE_WIREGUARD) + continue; + + lnk_wg = plobj->_link.netlink.lnk; + + if (!lnk_wg) + continue; + + if (NMP_OBJECT_GET_TYPE (lnk_wg) != NMP_OBJECT_TYPE_LNK_WIREGUARD) + continue; + + if (NMP_OBJECT_CAST_LNK_WIREGUARD (lnk_wg)->fwmark == table) + goto try_next_table; + } + + return table; +try_next_table: + ; + } +} + +#define PRIO_WIDTH ((guint32) 2) + +static gboolean +_auto_default_route_find_priority_exists (const NMDedupMultiHeadEntry *head_entry, + guint32 priority) +{ + NMDedupMultiIter iter; + const NMPObject *plobj; + + nmp_cache_iter_for_each (&iter, head_entry, &plobj) { + const NMPlatformRoutingRule *rr = NMP_OBJECT_CAST_ROUTING_RULE (plobj); + + /* we don't differenciate between IPv4 vs. IPv6. There should be no + * conflicting rules with the same priority. */ + if ( rr->priority >= priority + && rr->priority < priority + PRIO_WIDTH) + return TRUE; + } + + return FALSE; +} + +static guint32 +_auto_default_route_find_priority (NMPlatform *platform, + const char *uuid) +{ + const NMDedupMultiHeadEntry *head_entry; + guint64 rnd_seed; + const guint32 PRIME_NUMBER = 1111567573u; + const guint32 RANGE_TOP = ((32766u - 2u * PRIO_WIDTH) / PRIO_WIDTH); + const guint32 RANGE_LEN1 = 200u; + const guint32 RANGE_LEN2 = (RANGE_TOP - 100u) - RANGE_LEN1; + guint32 range_len; + guint32 range_top; + guint32 prio_candidate = 0; + guint32 i_step; + guint32 i; + + /* For the auto-default-route policy routing rule we add 4 rules (2 Ipv4 and 2 IPv6). + * Hence, we choose a priority for the first (of the two rules) and the second + * rule gets priority + 1. + * We want a priority that is + * - unused so far. + * - smaller than 32766u (which is the priority of the default rules for IPv4 and IPv6) + * - stable for each connection but different between connections (we hash the UUID + * as a "random" seed) + * - if possible, close to 32766u (RANGE_LEN1). Only otherwise fallback to the entire + * range (RANGE_LEN2). + */ + + rnd_seed = c_siphash_hash ((const guint8 [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); + + head_entry = nm_platform_lookup_object_by_addr_family (platform, + NMP_OBJECT_TYPE_ROUTING_RULE, + AF_UNSPEC); + + range_len = RANGE_LEN1; + range_top = RANGE_TOP; + +again: + i_step = ((guint32) rnd_seed) % range_len; + for (i = 0; i < range_len; i++) { + + /* we sample the range in a stable, but somewhat arbitrary order to + * find an unused priority. */ + i_step = (i_step + PRIME_NUMBER) % range_len; + + nm_assert (i_step < range_top); + + prio_candidate = (range_top - i_step) * PRIO_WIDTH; + + nm_assert (prio_candidate < 32766u); + + if (!_auto_default_route_find_priority_exists (head_entry, prio_candidate)) + return prio_candidate; + } + + if (range_len == RANGE_LEN1) { + /* within the narrow range close to RANGE_TOP we couldn't find any unused + * priority. Retry with the entire range... */ + range_len = RANGE_LEN2; + range_top -= RANGE_LEN1; + goto again; + } + + /* Couldn't find an unused one? Very odd, this really should not happen unless there + * are thousands of rules already. Just pick the last one we sampled. */ + return prio_candidate; +} + +static void +_auto_default_route_init (NMDeviceWireGuard *self) +{ + NMDeviceWireGuardPrivate *priv = NM_DEVICE_WIREGUARD_GET_PRIVATE (self); + NMConnection *connection; + NMSettingWireGuard *s_wg; + gboolean enabled_v4; + gboolean enabled_v6; + gboolean refreshing_only; + guint32 old_fwmark; + char sbuf1[100]; + + if (G_LIKELY ( priv->auto_default_route_initialized + && !priv->auto_default_route_refresh)) + return; + + refreshing_only = priv->auto_default_route_initialized + && priv->auto_default_route_refresh; + priv->auto_default_route_refresh = FALSE; + + connection = nm_device_get_applied_connection (NM_DEVICE (self)); + + s_wg = _nm_connection_get_setting (connection, NM_TYPE_SETTING_WIREGUARD); + + old_fwmark = priv->auto_default_route_fwmark; + + priv->auto_default_route_fwmark = nm_setting_wireguard_get_fwmark (s_wg); + + _auto_default_route_get_enabled (s_wg, + connection, + &enabled_v4, + &enabled_v6); + priv->auto_default_route_enabled_4 = enabled_v4; + priv->auto_default_route_enabled_6 = enabled_v6; + priv->auto_default_route_initialized = TRUE; + + if ( ( priv->auto_default_route_enabled_4 + || priv->auto_default_route_enabled_6) + && priv->auto_default_route_fwmark == 0u) { + if (refreshing_only) + priv->auto_default_route_fwmark = old_fwmark; + else + priv->auto_default_route_fwmark = _auto_default_route_find_unused_table (nm_device_get_platform (NM_DEVICE (self))); + } + + _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) + : ""); +} + +static GPtrArray * +get_extra_rules (NMDevice *device) +{ + NMDeviceWireGuard *self = NM_DEVICE_WIREGUARD (device); + NMDeviceWireGuardPrivate *priv = NM_DEVICE_WIREGUARD_GET_PRIVATE (self); + gs_unref_ptrarray GPtrArray *extra_rules = NULL; + guint32 priority = 0; + int is_ipv4; + NMConnection *connection; + + _auto_default_route_init (self); + + connection = nm_device_get_applied_connection (device); + if (!connection) + return NULL; + + 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; + + if (is_ipv4) { + if (!priv->auto_default_route_enabled_4) + continue; + } else { + if (!priv->auto_default_route_enabled_6) + continue; + } + + if (!extra_rules) { + if (priv->auto_default_route_priority_initialized) + priority = priv->auto_default_route_priority; + else { + priority = _auto_default_route_find_priority (nm_device_get_platform (device), + nm_connection_get_uuid (connection)); + 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); + } + + 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; + + fwmark = priv->auto_default_route_fwmark; + + G_STATIC_ASSERT_EXPR (PRIO_WIDTH == 2); + + 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), + }))); + + 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, + }))); + } + + return g_steal_pointer (&extra_rules); +} + +static guint32 +coerce_route_table (NMDevice *device, + int addr_family, + guint32 route_table, + gboolean is_user_config) +{ + NMDeviceWireGuard *self = NM_DEVICE_WIREGUARD (device); + 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; +} + +/*****************************************************************************/ + static gboolean _peer_data_equal (gconstpointer ptr_a, gconstpointer ptr_b) { @@ -1084,6 +1487,8 @@ link_config (NMDeviceWireGuard *self, _LOGT (LOGD_DEVICE, "wireguard link config (%s, %s)...", reason, _link_config_mode_to_string (config_mode)); + _auto_default_route_init (self); + 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); @@ -1130,7 +1535,7 @@ link_config (NMDeviceWireGuard *self, wg_lnk.listen_port = nm_setting_wireguard_get_listen_port (s_wg); wg_change_flags |= NM_PLATFORM_WIREGUARD_CHANGE_FLAG_HAS_LISTEN_PORT; - wg_lnk.fwmark = nm_setting_wireguard_get_fwmark (s_wg); + wg_lnk.fwmark = priv->auto_default_route_fwmark; wg_change_flags |= NM_PLATFORM_WIREGUARD_CHANGE_FLAG_HAS_FWMARK; if (nm_utils_base64secret_decode (nm_setting_wireguard_get_private_key (s_wg), @@ -1256,6 +1661,7 @@ static NMIPConfig * _get_dev2_ip_config (NMDeviceWireGuard *self, int addr_family) { + NMDeviceWireGuardPrivate *priv = NM_DEVICE_WIREGUARD_GET_PRIVATE (self); gs_unref_object NMIPConfig *ip_config = NULL; NMConnection *connection; NMSettingWireGuard *s_wg; @@ -1264,6 +1670,9 @@ _get_dev2_ip_config (NMDeviceWireGuard *self, int ip_ifindex; guint32 route_metric; guint32 route_table_coerced; + gboolean auto_default_route_enabled; + + _auto_default_route_init (self); connection = nm_device_get_applied_connection (NM_DEVICE (self)); @@ -1303,6 +1712,10 @@ _get_dev2_ip_config (NMDeviceWireGuard *self, route_table_coerced = nm_platform_route_table_coerce (nm_device_get_route_table (NM_DEVICE (self), addr_family)); + auto_default_route_enabled = (addr_family == AF_INET) + ? priv->auto_default_route_enabled_4 + : priv->auto_default_route_enabled_6; + 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); @@ -1316,6 +1729,7 @@ _get_dev2_ip_config (NMDeviceWireGuard *self, const char *aip; gboolean valid; int prefix; + guint32 rtable_coerced; aip = nm_wireguard_peer_get_allowed_ip (peer, j, &valid); @@ -1335,13 +1749,24 @@ _get_dev2_ip_config (NMDeviceWireGuard *self, nm_utils_ipx_address_clear_host_address (addr_family, &addrbin, NULL, prefix); + 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); + } + if (addr_family == AF_INET) { rt.r4 = (NMPlatformIP4Route) { .network = addrbin.addr4, .plen = prefix, .ifindex = ip_ifindex, .rt_source = NM_IP_CONFIG_SOURCE_USER, - .table_coerced = route_table_coerced, + .table_coerced = rtable_coerced, .metric = route_metric, }; } else { @@ -1350,7 +1775,7 @@ _get_dev2_ip_config (NMDeviceWireGuard *self, .plen = prefix, .ifindex = ip_ifindex, .rt_source = NM_IP_CONFIG_SOURCE_USER, - .table_coerced = route_table_coerced, + .table_coerced = rtable_coerced, .metric = route_metric, }; } @@ -1411,7 +1836,11 @@ _device_cleanup (NMDeviceWireGuard *self) NMDeviceWireGuardPrivate *priv = NM_DEVICE_WIREGUARD_GET_PRIVATE (self); _peers_remove_all (priv); + _secrets_cancel (self); + + priv->auto_default_route_initialized = FALSE; + priv->auto_default_route_priority_initialized = FALSE; } static void @@ -1444,6 +1873,8 @@ can_reapply_change (NMDevice *device, NM_SETTING_WIREGUARD_SETTING_NAME, error, NM_SETTING_WIREGUARD_FWMARK, + NM_SETTING_WIREGUARD_IP4_AUTO_DEFAULT_ROUTE, + NM_SETTING_WIREGUARD_IP6_AUTO_DEFAULT_ROUTE, NM_SETTING_WIREGUARD_LISTEN_PORT, NM_SETTING_WIREGUARD_PEERS, NM_SETTING_WIREGUARD_PEER_ROUTES, @@ -1465,9 +1896,12 @@ reapply_connection (NMDevice *device, NMConnection *con_new) { NMDeviceWireGuard *self = NM_DEVICE_WIREGUARD (device); + NMDeviceWireGuardPrivate *priv = NM_DEVICE_WIREGUARD_GET_PRIVATE (self); gs_unref_object NMIPConfig *ip4_config = NULL; gs_unref_object NMIPConfig *ip6_config = NULL; + priv->auto_default_route_refresh = TRUE; + ip4_config = _get_dev2_ip_config (self, AF_INET); ip6_config = _get_dev2_ip_config (self, AF_INET6); @@ -1634,6 +2068,8 @@ nm_device_wireguard_class_init (NMDeviceWireGuardClass *klass) device_class->can_reapply_change = can_reapply_change; device_class->reapply_connection = reapply_connection; device_class->get_configured_mtu = get_configured_mtu; + device_class->get_extra_rules = get_extra_rules; + device_class->coerce_route_table = coerce_route_table; obj_properties[PROP_PUBLIC_KEY] = g_param_spec_variant (NM_DEVICE_WIREGUARD_PUBLIC_KEY, diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c index 01e00a7107..feb5110d30 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -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); -- cgit v1.2.1 From 1a9f8a20a24c26732a659645367f9902e329ead5 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Mon, 29 Jul 2019 18:09:30 +0200 Subject: NEWS: update --- NEWS | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/NEWS b/NEWS index abda0292a1..cb2cbbb8db 100644 --- a/NEWS +++ b/NEWS @@ -26,6 +26,13 @@ USE AT YOUR OWN RISK. NOT RECOMMENDED FOR PRODUCTION USE! * Drop ibft settings plugin. This functionality is now covered by using nm-initrd-generator from initrd to pre-generate in-memory profiles. * Support "suppress_prefixlength" attribute for policy routing rules. + This is what wg-quick uses for the "Improved Rule-based Routing" solution, + and the user can now manually configure such policy routing rules. +* Support "wireguard.ip4-auto-default-route" and "wireguard.ip6-auto-default-route". + This automatically implements the "Improved Rule-based Routing" of wg-quick + to help with routing loops. Note that this is now enabled by default, so + there is a change in behavior if your WireGuard connection profiles had the + default-route (0.0.0.0/0 or ::/0) in allowed-ips. * Rework implementation of settings plugins and how profiles are presisted to disk. * In-memory profiles are now only handled by keyfile plugin and will also be -- cgit v1.2.1