Blame src/devices/ovs/nm-device-ovs-interface.c

Packit 5756e2
// SPDX-License-Identifier: GPL-2.0+
Packit 5756e2
/*
Packit 5756e2
 * Copyright (C) 2017 Red Hat, Inc.
Packit 5756e2
 */
Packit 5756e2
Packit 5756e2
#include "nm-default.h"
Packit 5756e2
Packit 5756e2
#include "nm-device-ovs-interface.h"
Packit 5756e2
#include "nm-ovsdb.h"
Packit 5756e2
Packit 5756e2
#include "devices/nm-device-private.h"
Packit 5756e2
#include "nm-active-connection.h"
Packit 5756e2
#include "nm-setting-connection.h"
Packit 5756e2
#include "nm-setting-ovs-interface.h"
Packit 5756e2
#include "nm-setting-ovs-port.h"
Packit 5756e2
Packit 5756e2
#include "devices/nm-device-logging.h"
Packit 5756e2
_LOG_DECLARE_SELF(NMDeviceOvsInterface);
Packit 5756e2
Packit 5756e2
/*****************************************************************************/
Packit 5756e2
Packit 5756e2
typedef struct {
Packit 5756e2
	bool waiting_for_interface:1;
Packit 5756e2
} NMDeviceOvsInterfacePrivate;
Packit 5756e2
Packit 5756e2
struct _NMDeviceOvsInterface {
Packit 5756e2
	NMDevice parent;
Packit 5756e2
	NMDeviceOvsInterfacePrivate _priv;
Packit 5756e2
};
Packit 5756e2
Packit 5756e2
struct _NMDeviceOvsInterfaceClass {
Packit 5756e2
	NMDeviceClass parent;
Packit 5756e2
};
Packit 5756e2
Packit 5756e2
G_DEFINE_TYPE (NMDeviceOvsInterface, nm_device_ovs_interface, NM_TYPE_DEVICE)
Packit 5756e2
Packit 5756e2
#define NM_DEVICE_OVS_INTERFACE_GET_PRIVATE(self) _NM_GET_PRIVATE (self, NMDeviceOvsInterface, NM_IS_DEVICE_OVS_INTERFACE, NMDevice)
Packit 5756e2
Packit 5756e2
/*****************************************************************************/
Packit 5756e2
Packit 5756e2
static const char *
Packit 5756e2
get_type_description (NMDevice *device)
Packit 5756e2
{
Packit 5756e2
	return "ovs-interface";
Packit 5756e2
}
Packit 5756e2
Packit 5756e2
static gboolean
Packit 5756e2
create_and_realize (NMDevice *device,
Packit 5756e2
                    NMConnection *connection,
Packit 5756e2
                    NMDevice *parent,
Packit 5756e2
                    const NMPlatformLink **out_plink,
Packit 5756e2
                    GError **error)
Packit 5756e2
{
Packit 5756e2
	/* The actual backing resources will be created once an interface is
Packit 5756e2
	 * added to a port of ours, since there can be neither an empty port nor
Packit 5756e2
	 * an empty bridge. */
Packit 5756e2
Packit 5756e2
	return TRUE;
Packit 5756e2
}
Packit 5756e2
Packit 5756e2
static NMDeviceCapabilities
Packit 5756e2
get_generic_capabilities (NMDevice *device)
Packit 5756e2
{
Packit 5756e2
	return NM_DEVICE_CAP_CARRIER_DETECT | NM_DEVICE_CAP_IS_SOFTWARE;
Packit 5756e2
}
Packit 5756e2
Packit 5756e2
static gboolean
Packit 5756e2
is_available (NMDevice *device, NMDeviceCheckDevAvailableFlags flags)
Packit 5756e2
{
Packit 5756e2
	return TRUE;
Packit 5756e2
}
Packit 5756e2
Packit 5756e2
static gboolean
Packit 5756e2
check_connection_compatible (NMDevice *device, NMConnection *connection, GError **error)
Packit 5756e2
{
Packit 5756e2
	NMSettingOvsInterface *s_ovs_iface;
Packit 5756e2
Packit 5756e2
	if (!NM_DEVICE_CLASS (nm_device_ovs_interface_parent_class)->check_connection_compatible (device, connection, error))
Packit 5756e2
		return FALSE;
Packit 5756e2
Packit 5756e2
	s_ovs_iface = nm_connection_get_setting_ovs_interface (connection);
Packit 5756e2
Packit 5756e2
	if (!NM_IN_STRSET (nm_setting_ovs_interface_get_interface_type (s_ovs_iface),
Packit 5756e2
	                   "dpdk",
Packit 5756e2
	                   "internal",
Packit 5756e2
	                   "patch")) {
Packit 5756e2
		nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
Packit 5756e2
		                            "unsupported OVS interface type in profile");
Packit 5756e2
		return FALSE;
Packit 5756e2
	}
Packit 5756e2
Packit 5756e2
	return TRUE;
Packit 5756e2
}
Packit 5756e2
Packit 5756e2
static void
Packit 5756e2
link_changed (NMDevice *device,
Packit 5756e2
              const NMPlatformLink *pllink)
Packit 5756e2
{
Packit 5756e2
	NMDeviceOvsInterfacePrivate *priv = NM_DEVICE_OVS_INTERFACE_GET_PRIVATE (device);
Packit 5756e2
Packit 5756e2
	if (!pllink || !priv->waiting_for_interface)
Packit 5756e2
		return;
Packit 5756e2
Packit 5756e2
	priv->waiting_for_interface = FALSE;
Packit 5756e2
Packit 5756e2
	if (nm_device_get_state (device) == NM_DEVICE_STATE_IP_CONFIG) {
Packit 5756e2
		if (!nm_device_hw_addr_set_cloned (device,
Packit 5756e2
		                                   nm_device_get_applied_connection (device),
Packit 5756e2
		                                   FALSE)) {
Packit 5756e2
			nm_device_state_changed (device,
Packit 5756e2
			                         NM_DEVICE_STATE_FAILED,
Packit 5756e2
			                         NM_DEVICE_STATE_REASON_CONFIG_FAILED);
Packit 5756e2
			return;
Packit 5756e2
		}
Packit 5756e2
		nm_device_bring_up (device, TRUE, NULL);
Packit 5756e2
		nm_device_activate_schedule_stage3_ip_config_start (device);
Packit 5756e2
	}
Packit 5756e2
}
Packit 5756e2
Packit 5756e2
static gboolean
Packit 5756e2
_is_internal_interface (NMDevice *device)
Packit 5756e2
{
Packit 5756e2
	NMSettingOvsInterface *s_ovs_iface;
Packit 5756e2
Packit 5756e2
	s_ovs_iface = nm_device_get_applied_setting (device, NM_TYPE_SETTING_OVS_INTERFACE);
Packit 5756e2
Packit 5756e2
	g_return_val_if_fail (s_ovs_iface, FALSE);
Packit 5756e2
Packit 5756e2
	return nm_streq (nm_setting_ovs_interface_get_interface_type (s_ovs_iface), "internal");
Packit 5756e2
}
Packit 5756e2
Packit 5756e2
static void
Packit 5756e2
set_platform_mtu_cb (GError *error, gpointer user_data)
Packit 5756e2
{
Packit 5756e2
	NMDevice *device = user_data;
Packit 5756e2
	NMDeviceOvsInterface *self = NM_DEVICE_OVS_INTERFACE (device);
Packit 5756e2
Packit 5756e2
	if (   error
Packit 5756e2
	    && !g_error_matches (error, NM_UTILS_ERROR, NM_UTILS_ERROR_CANCELLED_DISPOSING)) {
Packit 5756e2
		_LOGW (LOGD_DEVICE, "could not change mtu of '%s': %s",
Packit 5756e2
		       nm_device_get_iface (device), error->message);
Packit 5756e2
	}
Packit 5756e2
Packit 5756e2
	g_object_unref (device);
Packit 5756e2
}
Packit 5756e2
Packit 5756e2
static gboolean
Packit 5756e2
set_platform_mtu (NMDevice *device, guint32 mtu)
Packit 5756e2
{
Packit 5756e2
	/*
Packit 5756e2
	 * If the MTU is not set in ovsdb, Open vSwitch will change
Packit 5756e2
	 * the MTU of an internal interface to match the minimum of
Packit 5756e2
	 * the other interfaces in the bridge.
Packit 5756e2
	 */
Packit 5756e2
	/* FIXME(shutdown): the function should become cancellable so
Packit 5756e2
	 * that it doesn't need to hold a reference to the device, and
Packit 5756e2
	 * it can be stopped during shutdown.
Packit 5756e2
	 */
Packit 5756e2
	if (_is_internal_interface (device)) {
Packit 5756e2
		nm_ovsdb_set_interface_mtu (nm_ovsdb_get (),
Packit 5756e2
		                            nm_device_get_ip_iface (device),
Packit 5756e2
		                            mtu, set_platform_mtu_cb,
Packit 5756e2
		                            g_object_ref (device));
Packit 5756e2
	}
Packit 5756e2
Packit 5756e2
	return NM_DEVICE_CLASS (nm_device_ovs_interface_parent_class)->set_platform_mtu (device, mtu);
Packit 5756e2
}
Packit 5756e2
Packit 5756e2
static NMActStageReturn
Packit 5756e2
act_stage3_ip_config_start (NMDevice *device,
Packit 5756e2
                            int addr_family,
Packit 5756e2
                            gpointer *out_config,
Packit 5756e2
                            NMDeviceStateReason *out_failure_reason)
Packit 5756e2
{
Packit 5756e2
	NMDeviceOvsInterface *self = NM_DEVICE_OVS_INTERFACE (device);
Packit 5756e2
	NMDeviceOvsInterfacePrivate *priv = NM_DEVICE_OVS_INTERFACE_GET_PRIVATE (device);
Packit 5756e2
Packit 5756e2
	if (!_is_internal_interface (device))
Packit 5756e2
		return NM_ACT_STAGE_RETURN_IP_FAIL;
Packit 5756e2
Packit 5756e2
	if (nm_device_get_ip_ifindex (device) <= 0) {
Packit 5756e2
		_LOGT (LOGD_DEVICE, "waiting for link to appear");
Packit 5756e2
		priv->waiting_for_interface = TRUE;
Packit 5756e2
		return NM_ACT_STAGE_RETURN_POSTPONE;
Packit 5756e2
	}
Packit 5756e2
Packit 5756e2
	if (!nm_device_hw_addr_set_cloned (device,
Packit 5756e2
	                                   nm_device_get_applied_connection (device),
Packit 5756e2
	                                   FALSE)) {
Packit 5756e2
		*out_failure_reason = NM_DEVICE_STATE_REASON_CONFIG_FAILED;
Packit 5756e2
		return NM_ACT_STAGE_RETURN_FAILURE;
Packit 5756e2
	}
Packit 5756e2
Packit 5756e2
	return NM_DEVICE_CLASS (nm_device_ovs_interface_parent_class)->act_stage3_ip_config_start (device, addr_family, out_config, out_failure_reason);
Packit 5756e2
}
Packit 5756e2
Packit 5756e2
static gboolean
Packit 5756e2
can_unmanaged_external_down (NMDevice *self)
Packit 5756e2
{
Packit 5756e2
	return FALSE;
Packit 5756e2
}
Packit 5756e2
Packit 5756e2
static void
Packit 5756e2
deactivate (NMDevice *device)
Packit 5756e2
{
Packit 5756e2
	NMDeviceOvsInterface *self = NM_DEVICE_OVS_INTERFACE (device);
Packit 5756e2
	NMDeviceOvsInterfacePrivate *priv = NM_DEVICE_OVS_INTERFACE_GET_PRIVATE (self);
Packit 5756e2
Packit 5756e2
	priv->waiting_for_interface = FALSE;
Packit 5756e2
}
Packit 5756e2
Packit 5756e2
typedef struct {
Packit 5756e2
	NMDeviceOvsInterface *self;
Packit 5756e2
	GCancellable *cancellable;
Packit 5756e2
	NMDeviceDeactivateCallback callback;
Packit 5756e2
	gpointer callback_user_data;
Packit 5756e2
	gulong link_changed_id;
Packit 5756e2
	gulong cancelled_id;
Packit 5756e2
	guint link_timeout_id;
Packit 5756e2
} DeactivateData;
Packit 5756e2
Packit 5756e2
static void
Packit 5756e2
deactivate_invoke_cb (DeactivateData *data, GError *error)
Packit 5756e2
{
Packit 5756e2
	NMDeviceOvsInterface *self = data->self;
Packit 5756e2
Packit 5756e2
	_LOGT (LOGD_CORE,
Packit 5756e2
	       "deactivate: async callback (%s)",
Packit 5756e2
	       error ? error->message : "success");
Packit 5756e2
	data->callback (NM_DEVICE (data->self),
Packit 5756e2
	                error,
Packit 5756e2
	                data->callback_user_data);
Packit 5756e2
Packit 5756e2
	nm_clear_g_signal_handler (nm_device_get_platform (NM_DEVICE (data->self)),
Packit 5756e2
	                           &data->link_changed_id);
Packit 5756e2
	nm_clear_g_signal_handler (data->cancellable,
Packit 5756e2
	                           &data->cancelled_id);
Packit 5756e2
	nm_clear_g_source (&data->link_timeout_id);
Packit 5756e2
	g_object_unref (data->self);
Packit 5756e2
	g_object_unref (data->cancellable);
Packit 5756e2
	nm_g_slice_free (data);
Packit 5756e2
}
Packit 5756e2
Packit 5756e2
static void
Packit 5756e2
deactivate_link_changed_cb (NMPlatform *platform,
Packit 5756e2
                            int obj_type_i,
Packit 5756e2
                            int ifindex,
Packit 5756e2
                            NMPlatformLink *info,
Packit 5756e2
                            int change_type_i,
Packit 5756e2
                            DeactivateData *data)
Packit 5756e2
{
Packit 5756e2
	NMDeviceOvsInterface *self = data->self;
Packit 5756e2
	const NMPlatformSignalChangeType change_type = change_type_i;
Packit 5756e2
Packit 5756e2
	if (   change_type == NM_PLATFORM_SIGNAL_REMOVED
Packit 5756e2
	    && nm_streq0 (info->name, nm_device_get_iface (NM_DEVICE (self)))) {
Packit 5756e2
		_LOGT (LOGD_DEVICE, "deactivate: link removed, proceeding");
Packit 5756e2
		nm_device_update_from_platform_link (NM_DEVICE (self), NULL);
Packit 5756e2
		deactivate_invoke_cb (data, NULL);
Packit 5756e2
		return;
Packit 5756e2
	}
Packit 5756e2
}
Packit 5756e2
Packit 5756e2
static gboolean
Packit 5756e2
deactivate_link_timeout (gpointer user_data)
Packit 5756e2
{
Packit 5756e2
	DeactivateData *data = user_data;
Packit 5756e2
	NMDeviceOvsInterface *self = data->self;
Packit 5756e2
Packit 5756e2
	_LOGT (LOGD_DEVICE, "deactivate: timeout waiting link removal");
Packit 5756e2
	deactivate_invoke_cb (data, NULL);
Packit 5756e2
	return G_SOURCE_REMOVE;
Packit 5756e2
}
Packit 5756e2
Packit 5756e2
static void
Packit 5756e2
deactivate_cancelled_cb (GCancellable *cancellable,
Packit 5756e2
                         gpointer user_data)
Packit 5756e2
{
Packit 5756e2
	gs_free_error GError *error = NULL;
Packit 5756e2
Packit 5756e2
	nm_utils_error_set_cancelled (&error, FALSE, NULL);
Packit 5756e2
	deactivate_invoke_cb ((DeactivateData *) user_data, error);
Packit 5756e2
}
Packit 5756e2
Packit 5756e2
static void
Packit 5756e2
deactivate_cb_on_idle (gpointer user_data,
Packit 5756e2
                       GCancellable *cancellable)
Packit 5756e2
{
Packit 5756e2
	DeactivateData *data = user_data;
Packit 5756e2
	gs_free_error GError *cancelled_error = NULL;
Packit 5756e2
Packit 5756e2
	g_cancellable_set_error_if_cancelled (data->cancellable, &cancelled_error);
Packit 5756e2
	deactivate_invoke_cb (data, cancelled_error);
Packit 5756e2
}
Packit 5756e2
Packit 5756e2
static void
Packit 5756e2
deactivate_async (NMDevice *device,
Packit 5756e2
                  GCancellable *cancellable,
Packit 5756e2
                  NMDeviceDeactivateCallback callback,
Packit 5756e2
                  gpointer callback_user_data) {
Packit 5756e2
Packit 5756e2
	NMDeviceOvsInterface *self = NM_DEVICE_OVS_INTERFACE (device);
Packit 5756e2
	NMDeviceOvsInterfacePrivate *priv = NM_DEVICE_OVS_INTERFACE_GET_PRIVATE (self);
Packit 5756e2
	DeactivateData *data;
Packit 5756e2
Packit 5756e2
	_LOGT (LOGD_CORE, "deactivate: start async");
Packit 5756e2
Packit 5756e2
	/* We want to ensure that the kernel link for this device is
Packit 5756e2
	 * removed upon disconnection so that it will not interfere with
Packit 5756e2
	 * later activations of the same device. Unfortunately there is
Packit 5756e2
	 * no synchronization mechanism with vswitchd, we only update
Packit 5756e2
	 * ovsdb and wait that changes are picked up.
Packit 5756e2
	 */
Packit 5756e2
Packit 5756e2
	data = g_slice_new (DeactivateData);
Packit 5756e2
	*data = (DeactivateData) {
Packit 5756e2
		.self = g_object_ref (self),
Packit 5756e2
		.cancellable = g_object_ref (cancellable),
Packit 5756e2
		.callback = callback,
Packit 5756e2
		.callback_user_data = callback_user_data,
Packit 5756e2
	};
Packit 5756e2
Packit 5756e2
	if (   !priv->waiting_for_interface
Packit 5756e2
	    && !nm_platform_link_get_by_ifname (nm_device_get_platform (device),
Packit 5756e2
	                                        nm_device_get_iface (device))) {
Packit 5756e2
		_LOGT (LOGD_CORE, "deactivate: link not present, proceeding");
Packit 5756e2
		nm_device_update_from_platform_link (NM_DEVICE (self), NULL);
Packit 5756e2
		nm_utils_invoke_on_idle (cancellable, deactivate_cb_on_idle, data);
Packit 5756e2
		return;
Packit 5756e2
	}
Packit 5756e2
Packit 5756e2
	if (priv->waiting_for_interface) {
Packit 5756e2
		/* At this point we have issued an INSERT and a DELETE
Packit 5756e2
		 * command for the interface to ovsdb. We don't know if
Packit 5756e2
		 * vswitchd will see the two updates or only one. We
Packit 5756e2
		 * must add a timeout to avoid waiting forever in case
Packit 5756e2
		 * the link doesn't appear.
Packit 5756e2
		 */
Packit 5756e2
		data->link_timeout_id = g_timeout_add (6000, deactivate_link_timeout, data);
Packit 5756e2
		_LOGT (LOGD_DEVICE, "deactivate: waiting for link to disappear in 6 seconds");
Packit 5756e2
	} else
Packit 5756e2
		_LOGT (LOGD_DEVICE, "deactivate: waiting for link to disappear");
Packit 5756e2
Packit 5756e2
	data->cancelled_id = g_cancellable_connect (cancellable,
Packit 5756e2
	                                            G_CALLBACK (deactivate_cancelled_cb),
Packit 5756e2
	                                            data,
Packit 5756e2
	                                            NULL);
Packit 5756e2
	data->link_changed_id = g_signal_connect (nm_device_get_platform (device),
Packit 5756e2
	                                          NM_PLATFORM_SIGNAL_LINK_CHANGED,
Packit 5756e2
	                                          G_CALLBACK (deactivate_link_changed_cb),
Packit 5756e2
	                                          data);
Packit 5756e2
}
Packit 5756e2
Packit 5756e2
static gboolean
Packit 5756e2
can_update_from_platform_link (NMDevice *device, const NMPlatformLink *plink)
Packit 5756e2
{
Packit 5756e2
	/* If the device is deactivating, we already sent the
Packit 5756e2
	 * deletion command to ovsdb and we don't want to deal
Packit 5756e2
	 * with any new link appearing from the previous
Packit 5756e2
	 * activation.
Packit 5756e2
	 */
Packit 5756e2
	return    !plink
Packit 5756e2
	       || nm_device_get_state (device) != NM_DEVICE_STATE_DEACTIVATING;
Packit 5756e2
}
Packit 5756e2
Packit 5756e2
/*****************************************************************************/
Packit 5756e2
Packit 5756e2
static void
Packit 5756e2
nm_device_ovs_interface_init (NMDeviceOvsInterface *self)
Packit 5756e2
{
Packit 5756e2
}
Packit 5756e2
Packit 5756e2
static const NMDBusInterfaceInfoExtended interface_info_device_ovs_interface = {
Packit 5756e2
	.parent = NM_DEFINE_GDBUS_INTERFACE_INFO_INIT (
Packit 5756e2
		NM_DBUS_INTERFACE_DEVICE_OVS_INTERFACE,
Packit 5756e2
		.signals = NM_DEFINE_GDBUS_SIGNAL_INFOS (
Packit 5756e2
			&nm_signal_info_property_changed_legacy,
Packit 5756e2
		),
Packit 5756e2
	),
Packit 5756e2
	.legacy_property_changed = TRUE,
Packit 5756e2
};
Packit 5756e2
Packit 5756e2
static void
Packit 5756e2
nm_device_ovs_interface_class_init (NMDeviceOvsInterfaceClass *klass)
Packit 5756e2
{
Packit 5756e2
	NMDBusObjectClass *dbus_object_class = NM_DBUS_OBJECT_CLASS (klass);
Packit 5756e2
	NMDeviceClass *device_class = NM_DEVICE_CLASS (klass);
Packit 5756e2
Packit 5756e2
	dbus_object_class->interface_infos = NM_DBUS_INTERFACE_INFOS (&interface_info_device_ovs_interface);
Packit 5756e2
Packit 5756e2
	device_class->connection_type_supported = NM_SETTING_OVS_INTERFACE_SETTING_NAME;
Packit 5756e2
	device_class->connection_type_check_compatible = NM_SETTING_OVS_INTERFACE_SETTING_NAME;
Packit 5756e2
	device_class->link_types = NM_DEVICE_DEFINE_LINK_TYPES (NM_LINK_TYPE_OPENVSWITCH);
Packit 5756e2
Packit 5756e2
	device_class->can_update_from_platform_link = can_update_from_platform_link;
Packit 5756e2
	device_class->deactivate = deactivate;
Packit 5756e2
	device_class->deactivate_async = deactivate_async;
Packit 5756e2
	device_class->get_type_description = get_type_description;
Packit 5756e2
	device_class->create_and_realize = create_and_realize;
Packit 5756e2
	device_class->get_generic_capabilities = get_generic_capabilities;
Packit 5756e2
	device_class->is_available = is_available;
Packit 5756e2
	device_class->check_connection_compatible = check_connection_compatible;
Packit 5756e2
	device_class->link_changed = link_changed;
Packit 5756e2
	device_class->act_stage3_ip_config_start = act_stage3_ip_config_start;
Packit 5756e2
	device_class->can_unmanaged_external_down = can_unmanaged_external_down;
Packit 5756e2
	device_class->set_platform_mtu = set_platform_mtu;
Packit 5756e2
	device_class->get_configured_mtu = nm_device_get_configured_mtu_for_wired;
Packit 5756e2
}