diff options
author | Beniamino Galvani <bgalvani@redhat.com> | 2023-09-27 09:07:13 +0200 |
---|---|---|
committer | Íñigo Huguet <ihuguet@redhat.com> | 2024-02-21 11:49:19 +0100 |
commit | f8e020c29eb7f166b2a7aad54f49e9ea4172a2a0 (patch) | |
tree | 3b1ed38a99355bbb5350a19e5103bf9509cdd005 | |
parent | a5d194f8a6617d055151f18c5191345e5408eadc (diff) |
device: support creating generic devices via device-handler
If the device-handler of the generic connection is set, the connection
is virtual and the device is created by invoking the device-handler
via NetworkManager-dispatcher service.
With this change, a generic device now represents two different device
classes:
- existing interfaces that are not natively supported or recognized
by NetworkManager. Those devices have the `has_device_handler`
property set to FALSE;
- interfaces that are created by NM by invoking the device-handler;
they have `has_device_handler` set to TRUE.
(cherry picked from commit df6c35ec7553fd39f9ea224d0bd5779ad1a846b2)
-rw-r--r-- | src/core/devices/nm-device-factory.c | 1 | ||||
-rw-r--r-- | src/core/devices/nm-device-generic.c | 343 | ||||
-rw-r--r-- | src/core/devices/nm-device-generic.h | 3 | ||||
-rw-r--r-- | src/core/devices/nm-device-utils.c | 4 | ||||
-rw-r--r-- | src/core/nm-dispatcher.c | 2 | ||||
-rw-r--r-- | src/libnm-core-impl/nm-connection.c | 7 | ||||
-rw-r--r-- | src/libnm-core-public/nm-dbus-interface.h | 3 | ||||
-rw-r--r-- | src/libnmc-base/nm-client-utils.c | 4 |
8 files changed, 351 insertions, 16 deletions
diff --git a/src/core/devices/nm-device-factory.c b/src/core/devices/nm-device-factory.c index c97fbb57d1..381e0d88e7 100644 --- a/src/core/devices/nm-device-factory.c +++ b/src/core/devices/nm-device-factory.c @@ -396,6 +396,7 @@ nm_device_factory_manager_load_factories(NMDeviceFactoryManagerFactoryFunc callb _ADD_INTERNAL(nm_bridge_device_factory_get_type); _ADD_INTERNAL(nm_dummy_device_factory_get_type); _ADD_INTERNAL(nm_ethernet_device_factory_get_type); + _ADD_INTERNAL(nm_generic_device_factory_get_type); _ADD_INTERNAL(nm_hsr_device_factory_get_type); _ADD_INTERNAL(nm_infiniband_device_factory_get_type); _ADD_INTERNAL(nm_ip_tunnel_device_factory_get_type); diff --git a/src/core/devices/nm-device-generic.c b/src/core/devices/nm-device-generic.c index ead671d4d7..85f6524695 100644 --- a/src/core/devices/nm-device-generic.c +++ b/src/core/devices/nm-device-generic.c @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ /* - * Copyright (C) 2013 Red Hat, Inc. + * Copyright (C) 2013-2023 Red Hat, Inc. */ #include "src/core/nm-default-daemon.h" @@ -10,13 +10,27 @@ #include "nm-device-private.h" #include "libnm-platform/nm-platform.h" #include "libnm-core-intern/nm-core-internal.h" +#include "nm-dispatcher.h" +#include "nm-device-factory.h" + +#define _NMLOG_DEVICE_TYPE NMDeviceGeneric +#include "devices/nm-device-logging.h" /*****************************************************************************/ -NM_GOBJECT_PROPERTIES_DEFINE_BASE(PROP_TYPE_DESCRIPTION, ); +NM_GOBJECT_PROPERTIES_DEFINE(NMDeviceGeneric, PROP_TYPE_DESCRIPTION, PROP_HAS_DEVICE_HANDLER, ); typedef struct { - const char *type_description; + const char *type_description; + bool prepare_done : 1; + bool has_device_handler : 1; + NMDispatcherCallId *dispatcher_call_id; + struct { + NMDeviceDeactivateCallback callback; + gpointer callback_data; + GCancellable *cancellable; + gulong cancellable_id; + } deactivate; } NMDeviceGenericPrivate; struct _NMDeviceGeneric { @@ -38,13 +52,151 @@ G_DEFINE_TYPE(NMDeviceGeneric, nm_device_generic, NM_TYPE_DEVICE) static NMDeviceCapabilities get_generic_capabilities(NMDevice *device) { - int ifindex = nm_device_get_ifindex(device); + NMDeviceGenericPrivate *priv = NM_DEVICE_GENERIC_GET_PRIVATE(device); + int ifindex = nm_device_get_ifindex(device); + NMDeviceCapabilities cap = NM_DEVICE_CAP_NONE; + + if (priv->has_device_handler) + cap |= NM_DEVICE_CAP_IS_SOFTWARE; if (ifindex > 0 && nm_platform_link_supports_carrier_detect(nm_device_get_platform(device), ifindex)) - return NM_DEVICE_CAP_CARRIER_DETECT; - else - return NM_DEVICE_CAP_NONE; + cap |= NM_DEVICE_CAP_CARRIER_DETECT; + + return cap; +} + +static void +device_add_dispatcher_cb(NMDispatcherCallId *call_id, + gpointer user_data, + gboolean success, + const char *error, + GHashTable *dict) +{ + nm_auto_unref_object NMDeviceGeneric *self = NM_DEVICE_GENERIC(user_data); + NMDeviceGenericPrivate *priv = NM_DEVICE_GENERIC_GET_PRIVATE(self); + NMDevice *device = NM_DEVICE(self); + NMPlatform *platform = nm_device_get_platform(device); + const NMPlatformLink *link; + int ifindex = -1; + const char *ifindex_str; + NMSettingConnection *s_con; + + nm_assert(call_id == priv->dispatcher_call_id); + priv->dispatcher_call_id = NULL; + + if (!success) { + _LOGW(LOGD_CORE, "device handler 'device-add' failed: %s", error); + nm_device_state_changed(device, + NM_DEVICE_STATE_FAILED, + NM_DEVICE_STATE_REASON_DEVICE_HANDLER_FAILED); + return; + } + + ifindex_str = g_hash_table_lookup(dict, "IFINDEX"); + if (!ifindex_str) { + _LOGW(LOGD_CORE, "device handler 'device-add' didn't return a IFINDEX key"); + nm_device_state_changed(device, + NM_DEVICE_STATE_FAILED, + NM_DEVICE_STATE_REASON_DEVICE_HANDLER_FAILED); + return; + } + + ifindex = _nm_utils_ascii_str_to_int64(ifindex_str, 10, 1, G_MAXINT32, -1); + if (ifindex < 0) { + _LOGW(LOGD_CORE, "device handler 'device-add' returned invalid ifindex '%s'", ifindex_str); + nm_device_state_changed(device, + NM_DEVICE_STATE_FAILED, + NM_DEVICE_STATE_REASON_DEVICE_HANDLER_FAILED); + return; + } + + _LOGD(LOGD_DEVICE, "device handler 'device-add' returned ifindex %d", ifindex); + + /* Check that the ifindex is valid and matches the interface name. */ + nm_platform_process_events(platform); + link = nm_platform_link_get(platform, ifindex); + if (!link) { + _LOGW(LOGD_DEVICE, + "device handler 'device-add' didn't create link with ifindex %d", + ifindex); + nm_device_state_changed(device, + NM_DEVICE_STATE_FAILED, + NM_DEVICE_STATE_REASON_DEVICE_HANDLER_FAILED); + return; + } + + s_con = nm_device_get_applied_setting(device, NM_TYPE_SETTING_CONNECTION); + nm_assert(s_con); + + if (!nm_streq(link->name, nm_setting_connection_get_interface_name(s_con))) { + _LOGW(LOGD_DEVICE, + "device handler 'device-add' created a kernel link with name '%s' instead of '%s'", + link->name, + nm_setting_connection_get_interface_name(s_con)); + nm_device_state_changed(device, + NM_DEVICE_STATE_FAILED, + NM_DEVICE_STATE_REASON_DEVICE_HANDLER_FAILED); + return; + } + + priv->prepare_done = TRUE; + nm_device_activate_schedule_stage1_device_prepare(device, FALSE); +} + +static NMActStageReturn +act_stage1_prepare(NMDevice *self, NMDeviceStateReason *out_failure_reason) +{ + NMDevice *device = NM_DEVICE(self); + NMDeviceGenericPrivate *priv = NM_DEVICE_GENERIC_GET_PRIVATE(device); + NMSettingGeneric *s_generic; + const char *type_desc; + int ifindex; + + s_generic = nm_device_get_applied_setting(device, NM_TYPE_SETTING_GENERIC); + g_return_val_if_fail(s_generic, NM_ACT_STAGE_RETURN_FAILURE); + + if (!nm_setting_generic_get_device_handler(s_generic)) + return NM_ACT_STAGE_RETURN_SUCCESS; + + if (priv->prepare_done) { + /* after we create a new interface via a device-handler, update the + * type description */ + ifindex = nm_device_get_ip_ifindex(NM_DEVICE(self)); + if (ifindex > 0) { + type_desc = nm_platform_link_get_type_name(nm_device_get_platform(device), ifindex); + if (!nm_streq0(priv->type_description, type_desc)) { + priv->type_description = type_desc; + _notify(NM_DEVICE_GENERIC(self), PROP_TYPE_DESCRIPTION); + } + } + return NM_ACT_STAGE_RETURN_SUCCESS; + } + + if (priv->dispatcher_call_id) { + nm_dispatcher_call_cancel(priv->dispatcher_call_id); + priv->dispatcher_call_id = NULL; + } + + _LOGD(LOGD_CORE, "calling device handler 'device-add'"); + if (!nm_dispatcher_call_device_handler(NM_DISPATCHER_ACTION_DEVICE_ADD, + device, + NULL, + device_add_dispatcher_cb, + g_object_ref(self), + &priv->dispatcher_call_id)) { + _LOGW(LOGD_DEVICE, "failed to call device handler 'device-add'"); + NM_SET_OUT(out_failure_reason, NM_DEVICE_STATE_REASON_DEVICE_HANDLER_FAILED); + return NM_ACT_STAGE_RETURN_FAILURE; + } + + return NM_ACT_STAGE_RETURN_POSTPONE; +} + +static void +act_stage3_ip_config(NMDevice *device, int addr_family) +{ + nm_device_devip_set_state(device, addr_family, NM_DEVICE_IP_STATE_READY, NULL); } static const char * @@ -110,6 +262,111 @@ update_connection(NMDevice *device, NMConnection *connection) NULL); } +static gboolean +create_and_realize(NMDevice *device, + NMConnection *connection, + NMDevice *parent, + const NMPlatformLink **out_plink, + GError **error) +{ + /* The actual interface is created during stage1 once the device + * starts activating, as we need to call the dispatcher service + * which returns asynchronously */ + return TRUE; +} + +static void +deactivate_clear_data(NMDeviceGeneric *self) +{ + NMDeviceGenericPrivate *priv = NM_DEVICE_GENERIC_GET_PRIVATE(self); + + if (priv->dispatcher_call_id) { + nm_dispatcher_call_cancel(priv->dispatcher_call_id); + priv->dispatcher_call_id = NULL; + } + + priv->deactivate.callback = NULL; + priv->deactivate.callback_data = NULL; + g_clear_object(&priv->deactivate.cancellable); +} + +static void +device_delete_dispatcher_cb(NMDispatcherCallId *call_id, + gpointer user_data, + gboolean success, + const char *error, + GHashTable *dict) +{ + NMDeviceGeneric *self = user_data; + NMDeviceGenericPrivate *priv = NM_DEVICE_GENERIC_GET_PRIVATE(self); + gs_free_error GError *local = NULL; + + nm_assert(call_id == priv->dispatcher_call_id); + priv->dispatcher_call_id = NULL; + + if (success) + _LOGT(LOGD_DEVICE, "deactivate: async callback"); + else { + local = g_error_new(NM_DEVICE_ERROR, + NM_DEVICE_ERROR_FAILED, + "device handler 'device-delete' failed with error: %s", + error); + } + + priv->deactivate.callback(NM_DEVICE(self), local, priv->deactivate.callback_data); + nm_clear_g_cancellable_disconnect(priv->deactivate.cancellable, + &priv->deactivate.cancellable_id); + deactivate_clear_data(self); +} + +static void +deactivate_cancellable_cancelled(GCancellable *cancellable, NMDeviceGeneric *self) +{ + NMDeviceGenericPrivate *priv = NM_DEVICE_GENERIC_GET_PRIVATE(self); + gs_free_error GError *error = NULL; + + error = nm_utils_error_new_cancelled(FALSE, NULL); + priv->deactivate.callback(NM_DEVICE(self), error, priv->deactivate.callback_data); + + deactivate_clear_data(self); +} + +static void +deactivate_async(NMDevice *device, + GCancellable *cancellable, + NMDeviceDeactivateCallback callback, + gpointer callback_user_data) +{ + NMDeviceGeneric *self = NM_DEVICE_GENERIC(device); + NMDeviceGenericPrivate *priv = NM_DEVICE_GENERIC_GET_PRIVATE(self); + + _LOGT(LOGD_CORE, "deactivate: start async"); + + priv->prepare_done = FALSE; + + if (priv->dispatcher_call_id) { + nm_dispatcher_call_cancel(priv->dispatcher_call_id); + priv->dispatcher_call_id = NULL; + } + + g_object_ref(self); + priv->deactivate.callback = callback; + priv->deactivate.callback_data = callback_user_data; + priv->deactivate.cancellable = g_object_ref(cancellable); + priv->deactivate.cancellable_id = + g_cancellable_connect(cancellable, + G_CALLBACK(deactivate_cancellable_cancelled), + self, + NULL); + + nm_dispatcher_call_device_handler(NM_DISPATCHER_ACTION_DEVICE_DELETE, + device, + NULL, + device_delete_dispatcher_cb, + self, + &priv->dispatcher_call_id); +} + /*****************************************************************************/ static void @@ -122,6 +379,26 @@ get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) case PROP_TYPE_DESCRIPTION: g_value_set_string(value, priv->type_description); break; + case PROP_HAS_DEVICE_HANDLER: + g_value_set_boolean(value, priv->has_device_handler); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +static void +set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) +{ + NMDeviceGeneric *self = (NMDeviceGeneric *) object; + NMDeviceGenericPrivate *priv = NM_DEVICE_GENERIC_GET_PRIVATE(self); + + switch (prop_id) { + case PROP_HAS_DEVICE_HANDLER: + /* construct-only */ + priv->has_device_handler = g_value_get_boolean(value); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); break; @@ -137,16 +414,41 @@ nm_device_generic_init(NMDeviceGeneric *self) static GObject * constructor(GType type, guint n_construct_params, GObjectConstructParam *construct_params) { - GObject *object; + GObject *object; + NMDeviceGenericPrivate *priv; object = G_OBJECT_CLASS(nm_device_generic_parent_class) ->constructor(type, n_construct_params, construct_params); - nm_device_set_unmanaged_flags((NMDevice *) object, NM_UNMANAGED_BY_DEFAULT, TRUE); + priv = NM_DEVICE_GENERIC_GET_PRIVATE(object); + /* If the device is software (has a device-handler), don't set + * unmanaged-by-default so that the device can autoconnect if + * necessary. */ + if (!priv->has_device_handler) + nm_device_set_unmanaged_flags((NMDevice *) object, NM_UNMANAGED_BY_DEFAULT, TRUE); return object; } +static NMDevice * +create_device(NMDeviceFactory *factory, + const char *iface, + const NMPlatformLink *plink, + NMConnection *connection, + gboolean *out_ignore) +{ + return g_object_new(NM_TYPE_DEVICE_GENERIC, + NM_DEVICE_IFACE, + iface, + NM_DEVICE_TYPE_DESC, + "Generic", + NM_DEVICE_DEVICE_TYPE, + NM_DEVICE_TYPE_GENERIC, + NM_DEVICE_GENERIC_HAS_DEVICE_HANDLER, + TRUE, + NULL); +} + NMDevice * nm_device_generic_new(const NMPlatformLink *plink, gboolean nm_plugin_missing) { @@ -188,6 +490,7 @@ nm_device_generic_class_init(NMDeviceGenericClass *klass) object_class->constructor = constructor; object_class->get_property = get_property; + object_class->set_property = set_property; dbus_object_class->interface_infos = NM_DBUS_INTERFACE_INFOS(&interface_info_device_generic); @@ -195,10 +498,14 @@ nm_device_generic_class_init(NMDeviceGenericClass *klass) device_class->connection_type_check_compatible = NM_SETTING_GENERIC_SETTING_NAME; device_class->link_types = NM_DEVICE_DEFINE_LINK_TYPES(NM_LINK_TYPE_ANY); - device_class->realize_start_notify = realize_start_notify; + device_class->act_stage1_prepare = act_stage1_prepare; + device_class->act_stage3_ip_config = act_stage3_ip_config; + device_class->check_connection_compatible = check_connection_compatible; + device_class->create_and_realize = create_and_realize; + device_class->deactivate_async = deactivate_async; device_class->get_generic_capabilities = get_generic_capabilities; device_class->get_type_description = get_type_description; - device_class->check_connection_compatible = check_connection_compatible; + device_class->realize_start_notify = realize_start_notify; device_class->update_connection = update_connection; obj_properties[PROP_TYPE_DESCRIPTION] = @@ -207,6 +514,18 @@ nm_device_generic_class_init(NMDeviceGenericClass *klass) "", NULL, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); - + obj_properties[PROP_HAS_DEVICE_HANDLER] = g_param_spec_boolean( + NM_DEVICE_GENERIC_HAS_DEVICE_HANDLER, + "", + "", + FALSE, + G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); g_object_class_install_properties(object_class, _PROPERTY_ENUMS_LAST, obj_properties); } + +NM_DEVICE_FACTORY_DEFINE_INTERNAL( + GENERIC, + Generic, + generic, + NM_DEVICE_FACTORY_DECLARE_SETTING_TYPES(NM_SETTING_GENERIC_SETTING_NAME), + factory_class->create_device = create_device;); diff --git a/src/core/devices/nm-device-generic.h b/src/core/devices/nm-device-generic.h index f06a5bdc3f..07cb544754 100644 --- a/src/core/devices/nm-device-generic.h +++ b/src/core/devices/nm-device-generic.h @@ -18,7 +18,8 @@ #define NM_DEVICE_GENERIC_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS((obj), NM_TYPE_DEVICE_GENERIC, NMDeviceGenericClass)) -#define NM_DEVICE_GENERIC_TYPE_DESCRIPTION "type-description" +#define NM_DEVICE_GENERIC_TYPE_DESCRIPTION "type-description" +#define NM_DEVICE_GENERIC_HAS_DEVICE_HANDLER "has-device-handler" typedef struct _NMDeviceGeneric NMDeviceGeneric; typedef struct _NMDeviceGenericClass NMDeviceGenericClass; diff --git a/src/core/devices/nm-device-utils.c b/src/core/devices/nm-device-utils.c index 2bf24ae6da..ed0a27382a 100644 --- a/src/core/devices/nm-device-utils.c +++ b/src/core/devices/nm-device-utils.c @@ -127,7 +127,9 @@ NM_UTILS_LOOKUP_STR_DEFINE( NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_IP_METHOD_UNSUPPORTED, "ip-method-unsupported"), NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_SRIOV_CONFIGURATION_FAILED, "sriov-configuration-failed"), - NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_PEER_NOT_FOUND, "peer-not-found"), ); + NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_PEER_NOT_FOUND, "peer-not-found"), + NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_DEVICE_HANDLER_FAILED, + "device-handler-failed"), ); NM_UTILS_LOOKUP_STR_DEFINE(nm_device_mtu_source_to_string, NMDeviceMtuSource, diff --git a/src/core/nm-dispatcher.c b/src/core/nm-dispatcher.c index a5b351f3e8..4f442c685a 100644 --- a/src/core/nm-dispatcher.c +++ b/src/core/nm-dispatcher.c @@ -529,7 +529,7 @@ dispatcher_results_process(NMDispatcherAction action, NM_SET_OUT(out_success, FALSE); NM_SET_OUT(out_dict, NULL); NM_SET_OUT(out_error_msg, - err2 ? g_strdup_printf("%s: Error: %s", err, err2) : g_strdup(err)); + err2 ? g_strdup_printf("%s (Error: %s)", err, err2) : g_strdup(err)); } break; } diff --git a/src/libnm-core-impl/nm-connection.c b/src/libnm-core-impl/nm-connection.c index a23dc113c3..33360d04a1 100644 --- a/src/libnm-core-impl/nm-connection.c +++ b/src/libnm-core-impl/nm-connection.c @@ -3207,6 +3207,13 @@ nm_connection_is_virtual(NMConnection *connection) return !!nm_setting_pppoe_get_parent(s_pppoe); } + if (nm_streq(type, NM_SETTING_GENERIC_SETTING_NAME)) { + NMSettingGeneric *s_generic; + + s_generic = nm_connection_get_setting_generic(connection); + return !!nm_setting_generic_get_device_handler(s_generic); + } + return FALSE; } diff --git a/src/libnm-core-public/nm-dbus-interface.h b/src/libnm-core-public/nm-dbus-interface.h index cc87c74474..66cd590d6c 100644 --- a/src/libnm-core-public/nm-dbus-interface.h +++ b/src/libnm-core-public/nm-dbus-interface.h @@ -610,6 +610,8 @@ typedef enum { * @NM_DEVICE_STATE_REASON_IP_METHOD_UNSUPPORTED: The selected IP method is not supported * @NM_DEVICE_STATE_REASON_SRIOV_CONFIGURATION_FAILED: configuration of SR-IOV parameters failed * @NM_DEVICE_STATE_REASON_PEER_NOT_FOUND: The Wi-Fi P2P peer could not be found + * @NM_DEVICE_STATE_REASON_DEVICE_HANDLER_FAILED: The device handler dispatcher returned an + * error. Since: 1.46 * * Device state change reason codes */ @@ -682,6 +684,7 @@ typedef enum { NM_DEVICE_STATE_REASON_IP_METHOD_UNSUPPORTED = 65, NM_DEVICE_STATE_REASON_SRIOV_CONFIGURATION_FAILED = 66, NM_DEVICE_STATE_REASON_PEER_NOT_FOUND = 67, + NM_DEVICE_STATE_REASON_DEVICE_HANDLER_FAILED = 68, } NMDeviceStateReason; /** diff --git a/src/libnmc-base/nm-client-utils.c b/src/libnmc-base/nm-client-utils.c index b052a307fd..30213e41b5 100644 --- a/src/libnmc-base/nm-client-utils.c +++ b/src/libnmc-base/nm-client-utils.c @@ -464,7 +464,9 @@ NM_UTILS_LOOKUP_STR_DEFINE( NM_UTILS_LOOKUP_ITEM(NM_DEVICE_STATE_REASON_SRIOV_CONFIGURATION_FAILED, N_("Failed to configure SR-IOV parameters")), NM_UTILS_LOOKUP_ITEM(NM_DEVICE_STATE_REASON_PEER_NOT_FOUND, - N_("The Wi-Fi P2P peer could not be found")), ); + N_("The Wi-Fi P2P peer could not be found")), + NM_UTILS_LOOKUP_ITEM(NM_DEVICE_STATE_REASON_DEVICE_HANDLER_FAILED, + N_("The device handler dispatcher returned an error")), ); NM_UTILS_LOOKUP_STR_DEFINE( nm_active_connection_state_reason_to_string, |