From 7bc5a1fc267987d48627d7f33b6fc200fe6f524a Mon Sep 17 00:00:00 2001 From: Alexander Lochmann Date: Sun, 24 Dec 2023 14:24:04 +0100 Subject: [PATCH] Allow any kind of VPN as secondaries (Fixed #1395) As of now, NM allows only connections derived from the VPN plugin to be used as secondaries. This excludes non-VPN-plugin connections still providing VPN functionality, such as Wireguard, to be used as secondary. This commit extends NetworkManager to allow any kind of VPN connection to be used as secondary connection. (Code rework done by Martin Sucha) --- src/core/nm-manager.c | 22 +++++++++++++++ src/core/nm-policy.c | 5 ++-- src/libnm-client-impl/libnm.ver | 2 ++ src/libnm-core-impl/nm-connection.c | 40 +++++++++++++++++++++++++++ src/libnm-core-public/nm-connection.h | 4 +++ src/nmcli/settings.c | 17 ++++++------ 6 files changed, 78 insertions(+), 12 deletions(-) diff --git a/src/core/nm-manager.c b/src/core/nm-manager.c index 87dde2c3af..5bc6c80038 100644 --- a/src/core/nm-manager.c +++ b/src/core/nm-manager.c @@ -6196,6 +6196,28 @@ _new_active_connection(NMManager *self, activation_reason, initial_state_flags, subject); + } else if (nm_connection_is_valid_secondary(sett_conn ? nm_settings_connection_get_connection(sett_conn) + : incompl_conn)) { + /** + * For non-plugin VPN connections, re-validate the corresponding device. + * Wireguard, for example, needs its own (virtual) device. + * If it, however, gets activated as secondary device, + * 'device' points to the primary device that is currently starting. + * Bringing up a WG tunnel this way will fail. + */ + NMDevice *_device = NULL; + gs_free_error GError *local = NULL; + _device = + nm_manager_get_best_device_for_connection(self, sett_conn, applied, TRUE, NULL, &local); + if (!_device) { + g_set_error(error, + NM_MANAGER_ERROR, + NM_MANAGER_ERROR_UNKNOWN_DEVICE, + "No suitable device found for non-plugin VPN connection (%s).", + local->message); + return NULL; + } + device = _device; } return (NMActiveConnection *) nm_act_request_new(sett_conn, diff --git a/src/core/nm-policy.c b/src/core/nm-policy.c index f7be1a9f87..be6e1c19bc 100644 --- a/src/core/nm-policy.c +++ b/src/core/nm-policy.c @@ -2163,11 +2163,10 @@ activate_secondary_connections(NMPolicy *self, NMConnection *connection, NMDevic break; } - if (!nm_connection_is_type(nm_settings_connection_get_connection(sett_conn), - NM_SETTING_VPN_SETTING_NAME)) { + if (!nm_connection_is_valid_secondary(nm_settings_connection_get_connection(sett_conn))) { _LOGW(LOGD_DEVICE, "secondary connection '%s (%s)' auto-activation failed: The connection is not a " - "VPN.", + "valid secondary.", nm_settings_connection_get_id(sett_conn), sec_uuid); success = FALSE; diff --git a/src/libnm-client-impl/libnm.ver b/src/libnm-client-impl/libnm.ver index 64e2155d60..7d9735bb50 100644 --- a/src/libnm-client-impl/libnm.ver +++ b/src/libnm-client-impl/libnm.ver @@ -141,6 +141,7 @@ global: nm_connection_get_setting_vpn; nm_connection_get_setting_wimax; nm_connection_get_setting_wired; + nm_connection_get_setting_wireguard; nm_connection_get_setting_wireless; nm_connection_get_setting_wireless_security; nm_connection_get_type; @@ -2026,6 +2027,7 @@ libnm_1_50_4 { libnm_1_52_0 { global: + nm_connection_is_valid_secondary; nm_device_ipvlan_get_mode; nm_device_ipvlan_get_parent; nm_device_ipvlan_get_private; diff --git a/src/libnm-core-impl/nm-connection.c b/src/libnm-core-impl/nm-connection.c index 0fbadfb85c..81df36572e 100644 --- a/src/libnm-core-impl/nm-connection.c +++ b/src/libnm-core-impl/nm-connection.c @@ -15,6 +15,7 @@ #include "nm-connection-private.h" #include "nm-utils.h" #include "nm-setting-private.h" +#include "nm-setting-wireguard.h" #include "libnm-core-intern/nm-core-internal.h" /** @@ -2876,6 +2877,31 @@ nm_connection_is_type(NMConnection *connection, const char *type) return nm_streq0(type, nm_connection_get_connection_type(connection)); } +/** + * nm_connection_is_valid_secondary: + * @connection: the #NMConnection + * + * Checks whether the given connection can be activated as a secondary. + * + * Returns: %TRUE if the connection is a valid secondary. + * + * Since: 1.52 +*/ +gboolean +nm_connection_is_valid_secondary(NMConnection *connection) +{ + const char *type = nm_connection_get_connection_type(connection); + if (type) + return nm_streq(type, NM_SETTING_VPN_SETTING_NAME) + || nm_streq(type, NM_SETTING_WIREGUARD_SETTING_NAME); + + /* we have an incomplete (invalid) connection at hand. That can only + * happen during AddAndActivate. Determine whether it's VPN type based + * on the existence of a [vpn] section. */ + return !!nm_connection_get_setting_vpn(connection) + || nm_connection_get_setting_wireguard(connection); +} + int _nm_setting_sort_for_nm_assert(NMSetting *a, NMSetting *b) { @@ -3822,6 +3848,20 @@ nm_connection_get_setting_vpn(NMConnection *connection) return _nm_connection_get_setting_by_metatype(connection, NM_META_SETTING_TYPE_VPN); } +/** + * nm_connection_get_setting_wireguard: + * @connection: the #NMConnection + * + * A shortcut to return any #NMSettingVpn the connection might contain. + * + * Returns: (transfer none): an #NMSettingVpn if the connection contains one, otherwise %NULL + **/ +NMSettingVpn * +nm_connection_get_setting_wireguard(NMConnection *connection) +{ + return _nm_connection_get_setting_by_metatype(connection, NM_META_SETTING_TYPE_WIREGUARD); +} + /** * nm_connection_get_setting_vxlan: * @connection: the #NMConnection diff --git a/src/libnm-core-public/nm-connection.h b/src/libnm-core-public/nm-connection.h index ff3e0f924a..3b8c60b3e6 100644 --- a/src/libnm-core-public/nm-connection.h +++ b/src/libnm-core-public/nm-connection.h @@ -240,12 +240,16 @@ NMSettingVpn *nm_connection_get_setting_vpn(NMConnection *connectio NMSettingWimax *nm_connection_get_setting_wimax(NMConnection *connection); NMSettingAdsl *nm_connection_get_setting_adsl(NMConnection *connection); NMSettingWired *nm_connection_get_setting_wired(NMConnection *connection); +NMSettingVpn *nm_connection_get_setting_wireguard(NMConnection *connection); NMSettingWireless *nm_connection_get_setting_wireless(NMConnection *connection); NMSettingWirelessSecurity *nm_connection_get_setting_wireless_security(NMConnection *connection); NMSettingVlan *nm_connection_get_setting_vlan(NMConnection *connection); NM_AVAILABLE_IN_1_2 NMSettingVxlan *nm_connection_get_setting_vxlan(NMConnection *connection); +NM_AVAILABLE_IN_1_52 +gboolean nm_connection_is_valid_secondary(NMConnection *connection); + G_END_DECLS #endif /* __NM_CONNECTION_H__ */ diff --git a/src/nmcli/settings.c b/src/nmcli/settings.c index 458b03e58d..bf497240bb 100644 --- a/src/nmcli/settings.c +++ b/src/nmcli/settings.c @@ -378,13 +378,13 @@ _set_fcn_precheck_connection_secondaries(NMClient *client, con = nmc_find_connection(connections, "uuid", *iter, NULL, FALSE); if (!con) { nmc_printerr(_("Warning: %s is not an UUID of any existing connection profile\n"), - *iter); - } else { + *iter); + } else { /* Currently, NM only supports VPN connections as secondaries */ - if (!nm_connection_is_type(con, NM_SETTING_VPN_SETTING_NAME)) { - g_set_error(error, 1, 0, _("'%s' is not a VPN connection profile"), *iter); + if (!nm_connection_is_valid_secondary(con)) { + g_set_error(error, 1, 0, _("'%s' is not a valid secondary profile"), *iter); return FALSE; - } + } } } else { con = nmc_find_connection(connections, "id", *iter, NULL, FALSE); @@ -392,10 +392,9 @@ _set_fcn_precheck_connection_secondaries(NMClient *client, g_set_error(error, 1, 0, _("'%s' is not a name of any existing profile"), *iter); return FALSE; } - - /* Currently, NM only supports VPN connections as secondaries */ - if (!nm_connection_is_type(con, NM_SETTING_VPN_SETTING_NAME)) { - g_set_error(error, 1, 0, _("'%s' is not a VPN connection profile"), *iter); + /* Currently, NM only supports VPN connections, including Wireguard, as secondaries */ + if (!nm_connection_is_valid_secondary(con)) { + g_set_error(error, 1, 0, _("'%s' is not a valid secondary profile"), *iter); return FALSE; }