diff options
Diffstat (limited to 'src/core/devices/ovs/nm-ovs-factory.c')
| -rw-r--r-- | src/core/devices/ovs/nm-ovs-factory.c | 324 |
1 files changed, 324 insertions, 0 deletions
diff --git a/src/core/devices/ovs/nm-ovs-factory.c b/src/core/devices/ovs/nm-ovs-factory.c new file mode 100644 index 00000000..e7af38d8 --- /dev/null +++ b/src/core/devices/ovs/nm-ovs-factory.c @@ -0,0 +1,324 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2017 Red Hat, Inc. + */ + +#include "src/core/nm-default-daemon.h" + +#include "nm-manager.h" +#include "nm-ovsdb.h" +#include "nm-device-ovs-interface.h" +#include "nm-device-ovs-port.h" +#include "nm-device-ovs-bridge.h" +#include "platform/nm-platform.h" +#include "nm-core-internal.h" +#include "settings/nm-settings.h" +#include "devices/nm-device-factory.h" +#include "devices/nm-device-private.h" + +/*****************************************************************************/ + +typedef struct { + NMDeviceFactory parent; +} NMOvsFactory; + +typedef struct { + NMDeviceFactoryClass parent; +} NMOvsFactoryClass; + +#define NM_TYPE_OVS_FACTORY (nm_ovs_factory_get_type()) +#define NM_OVS_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_OVS_FACTORY, NMOvsFactory)) +#define NM_OVS_FACTORY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_OVS_FACTORY, NMOvsFactoryClass)) +#define NM_IS_OVS_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_OVS_FACTORY)) +#define NM_IS_OVS_FACTORY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), NM_TYPE_OVS_FACTORY)) +#define NM_OVS_FACTORY_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS((obj), NM_TYPE_OVS_FACTORY, NMOvsFactoryClass)) + +static GType nm_ovs_factory_get_type(void); +G_DEFINE_TYPE(NMOvsFactory, nm_ovs_factory, NM_TYPE_DEVICE_FACTORY) + +/*****************************************************************************/ + +#define _NMLOG_DOMAIN LOGD_DEVICE +#define _NMLOG(level, ifname, con_uuid, ...) \ + G_STMT_START \ + { \ + nm_log((level), \ + _NMLOG_DOMAIN, \ + (ifname), \ + (con_uuid), \ + "ovs: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__) _NM_UTILS_MACRO_REST(__VA_ARGS__)); \ + } \ + G_STMT_END + +/*****************************************************************************/ + +NM_DEVICE_FACTORY_DECLARE_TYPES( + NM_DEVICE_FACTORY_DECLARE_LINK_TYPES(NM_LINK_TYPE_OPENVSWITCH) + NM_DEVICE_FACTORY_DECLARE_SETTING_TYPES(NM_SETTING_OVS_BRIDGE_SETTING_NAME, + NM_SETTING_OVS_INTERFACE_SETTING_NAME, + NM_SETTING_OVS_PORT_SETTING_NAME)) + +G_MODULE_EXPORT NMDeviceFactory * + nm_device_factory_create(GError **error) +{ + nm_manager_set_capability(NM_MANAGER_GET, NM_CAPABILITY_OVS); + return g_object_new(NM_TYPE_OVS_FACTORY, NULL); +} + +static NMDevice * +new_device_from_type(const char *name, NMDeviceType device_type) +{ + GType type; + const char *type_desc; + NMLinkType link_type = NM_LINK_TYPE_NONE; + + if (nm_manager_get_device(NM_MANAGER_GET, name, device_type)) + return NULL; + + if (device_type == NM_DEVICE_TYPE_OVS_INTERFACE) { + type = NM_TYPE_DEVICE_OVS_INTERFACE; + type_desc = "Open vSwitch Interface"; + link_type = NM_LINK_TYPE_OPENVSWITCH; + } else if (device_type == NM_DEVICE_TYPE_OVS_PORT) { + type = NM_TYPE_DEVICE_OVS_PORT; + type_desc = "Open vSwitch Port"; + } else if (device_type == NM_DEVICE_TYPE_OVS_BRIDGE) { + type = NM_TYPE_DEVICE_OVS_BRIDGE; + type_desc = "Open vSwitch Bridge"; + } else { + return NULL; + } + + return g_object_new(type, + NM_DEVICE_IFACE, + name, + NM_DEVICE_DRIVER, + "openvswitch", + NM_DEVICE_DEVICE_TYPE, + device_type, + NM_DEVICE_TYPE_DESC, + type_desc, + NM_DEVICE_LINK_TYPE, + link_type, + NULL); +} + +static void +ovsdb_device_added(NMOvsdb * ovsdb, + const char * name, + guint device_type_i, + const char * subtype, + NMDeviceFactory *self) +{ + const NMDeviceType device_type = device_type_i; + NMDevice * device; + + if (device_type == NM_DEVICE_TYPE_OVS_INTERFACE + && !NM_IN_STRSET(subtype, "internal", "patch")) { + /* system interfaces refer to kernel devices and + * don't need to be created by this factory. Ignore + * anything that is not an internal or patch + * interface. */ + return; + } + + device = new_device_from_type(name, device_type); + if (!device) + return; + + g_signal_emit_by_name(self, NM_DEVICE_FACTORY_DEVICE_ADDED, device); + g_object_unref(device); +} + +static void +ovsdb_device_removed(NMOvsdb * ovsdb, + const char * name, + guint device_type_i, + const char * subtype, + NMDeviceFactory *self) +{ + const NMDeviceType device_type = device_type_i; + NMDevice * device = NULL; + NMDeviceState device_state; + gboolean is_system_interface = FALSE; + + if (device_type == NM_DEVICE_TYPE_OVS_INTERFACE + && !NM_IN_STRSET(subtype, "internal", "patch", "system")) + return; + + if (device_type == NM_DEVICE_TYPE_OVS_INTERFACE && nm_streq0(subtype, "system")) { + NMDevice * d; + const CList * list; + NMSettingOvsInterface *s_ovs_int; + + /* The device associated to an OVS system interface can be of + * any kind. Find an interface with the same name and which has + * the OVS-interface setting. */ + is_system_interface = TRUE; + nm_manager_for_each_device (NM_MANAGER_GET, d, list) { + if (!nm_streq0(nm_device_get_iface(d), name)) + continue; + s_ovs_int = nm_device_get_applied_setting(d, NM_TYPE_SETTING_OVS_INTERFACE); + if (!s_ovs_int) + continue; + if (!nm_streq0(nm_setting_ovs_interface_get_interface_type(s_ovs_int), "system")) + continue; + /* Failing the system interface device is almost always the right + * thing to do when the ovsdb entry is removed. However, to avoid + * that a late device-removed signal tears down a different, + * newly-activated connection, let's also check that we have a master. + * Or in alternative, that the device is assumed/external: in such + * case it's always fine to fail the device. + */ + if (!nm_device_get_master(d) && !nm_device_sys_iface_state_is_external_or_assume(d)) + continue; + + device = d; + } + } else { + device = nm_manager_get_device(NM_MANAGER_GET, name, device_type); + } + + if (!device) + return; + + device_state = nm_device_get_state(device); + + if (device_type == NM_DEVICE_TYPE_OVS_INTERFACE && nm_device_get_act_request(device) + && device_state < NM_DEVICE_STATE_DEACTIVATING) { + nm_device_state_changed(device, + NM_DEVICE_STATE_DEACTIVATING, + NM_DEVICE_STATE_REASON_REMOVED); + return; + } + + /* OVS system interfaces still exist even without the ovsdb entry */ + if (!is_system_interface && device_state == NM_DEVICE_STATE_UNMANAGED) { + nm_device_unrealize(device, TRUE, NULL); + } +} + +static void +ovsdb_interface_failed(NMOvsdb * ovsdb, + const char * name, + const char * connection_uuid, + const char * error, + NMDeviceFactory *self) +{ + NMDevice * device = NULL; + NMSettingsConnection * connection = NULL; + NMConnection * c; + const char * type; + NMSettingOvsInterface *s_ovs_int; + gboolean is_patch = FALSE; + gboolean ignore; + + device = nm_manager_get_device(NM_MANAGER_GET, name, NM_DEVICE_TYPE_OVS_INTERFACE); + if (device && connection_uuid) { + connection = + nm_settings_get_connection_by_uuid(nm_device_get_settings(device), connection_uuid); + } + + /* The patch interface which gets created first is expected to + * fail because the second patch doesn't exist yet. Ignore all + * failures of patch interfaces. */ + if (connection && (c = nm_settings_connection_get_connection(connection)) + && (type = nm_connection_get_connection_type(c)) + && nm_streq0(type, NM_SETTING_OVS_INTERFACE_SETTING_NAME) + && (s_ovs_int = nm_connection_get_setting_ovs_interface(c)) + && nm_streq0(nm_setting_ovs_interface_get_interface_type(s_ovs_int), "patch")) + is_patch = TRUE; + + ignore = !device || is_patch; + + _NMLOG(ignore ? LOGL_DEBUG : LOGL_INFO, + name, + connection_uuid, + "ovs interface \"%s\" (%s) failed%s: %s", + name, + connection_uuid, + ignore ? " (ignored)" : "", + error); + + if (ignore) + return; + + if (connection) { + nm_settings_connection_autoconnect_blocked_reason_set( + connection, + NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_FAILED, + TRUE); + } + + nm_device_state_changed(device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_OVSDB_FAILED); +} + +static void +start(NMDeviceFactory *self) +{ + NMOvsdb *ovsdb; + + ovsdb = nm_ovsdb_get(); + + g_signal_connect_object(ovsdb, + NM_OVSDB_DEVICE_ADDED, + G_CALLBACK(ovsdb_device_added), + self, + (GConnectFlags) 0); + g_signal_connect_object(ovsdb, + NM_OVSDB_DEVICE_REMOVED, + G_CALLBACK(ovsdb_device_removed), + self, + (GConnectFlags) 0); + g_signal_connect_object(ovsdb, + NM_OVSDB_INTERFACE_FAILED, + G_CALLBACK(ovsdb_interface_failed), + self, + (GConnectFlags) 0); +} + +static NMDevice * +create_device(NMDeviceFactory * self, + const char * iface, + const NMPlatformLink *plink, + NMConnection * connection, + gboolean * out_ignore) +{ + NMDeviceType device_type = NM_DEVICE_TYPE_UNKNOWN; + const char * connection_type = NULL; + + if (g_strcmp0(iface, "ovs-system") == 0) { + *out_ignore = TRUE; + return NULL; + } + + if (connection) + connection_type = nm_connection_get_connection_type(connection); + + if (plink) + device_type = NM_DEVICE_TYPE_OVS_INTERFACE; + else if (g_strcmp0(connection_type, NM_SETTING_OVS_INTERFACE_SETTING_NAME) == 0) + device_type = NM_DEVICE_TYPE_OVS_INTERFACE; + else if (g_strcmp0(connection_type, NM_SETTING_OVS_PORT_SETTING_NAME) == 0) + device_type = NM_DEVICE_TYPE_OVS_PORT; + else if (g_strcmp0(connection_type, NM_SETTING_OVS_BRIDGE_SETTING_NAME) == 0) + device_type = NM_DEVICE_TYPE_OVS_BRIDGE; + + return new_device_from_type(iface, device_type); +} + +static void +nm_ovs_factory_init(NMOvsFactory *self) +{} + +static void +nm_ovs_factory_class_init(NMOvsFactoryClass *klass) +{ + NMDeviceFactoryClass *factory_class = NM_DEVICE_FACTORY_CLASS(klass); + + factory_class->get_supported_types = get_supported_types; + factory_class->start = start; + factory_class->create_device = create_device; +} |