From 17338069e332bb73e4d9e7332b67b7853fbe83b7 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Fri, 1 Feb 2013 18:03:11 -0600 Subject: [PATCH] core: only manage those bridges created by NetworkManager (rh #905035) Until we handle bridges non-destructively, only manage bridges created by NM. When quitting write out a file listing all bridges created by NM and a timestamp, and when starting read that file and if the timestamp is within 30 minutes, manage any bridge that was listed in that file. This scheme, while not foolproof (eg, if NM crashes), should ensure that NM can recognize bridges it created if it's restarted. The file is stored in /run or /var/run, which is cleaned each restart, ensuring that the state does not persist across reboots. If an automatic or user-initiated activation request for a bridge NM does not manage is received, that request is denied. Only if the bridge interface does not yet exist, or was present in the managed bridges file, will an NMDeviceBridge be created and activation be possible. --- src/nm-manager.c | 125 +++++++++++++++++++++++++++++++++++++++++++++-- src/nm-system.c | 8 ++- src/nm-system.h | 2 +- 3 files changed, 128 insertions(+), 7 deletions(-) diff --git a/src/nm-manager.c b/src/nm-manager.c index e1234ab0d5..fad14fcf91 100644 --- a/src/nm-manager.c +++ b/src/nm-manager.c @@ -244,6 +244,8 @@ typedef struct { guint timestamp_update_id; + GHashTable *nm_bridges; + gboolean disposed; } NMManagerPrivate; @@ -1130,6 +1132,92 @@ get_virtual_iface_placeholder_udi (void) return g_strdup_printf ("/virtual/device/placeholder/%d", id++); } +/***************************/ + +/* FIXME: remove when we handle bridges non-destructively */ + +#define NM_BRIDGE_FILE NMRUNDIR "/nm-bridges" + +static void +read_nm_created_bridges (NMManager *self) +{ + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); + char *contents; + char **lines, **iter; + GTimeVal tv; + glong ts; + + if (!g_file_get_contents (NM_BRIDGE_FILE, &contents, NULL, NULL)) + return; + + g_get_current_time (&tv); + + lines = g_strsplit_set (contents, "\n", 0); + g_free (contents); + + for (iter = lines; iter && *iter; iter++) { + if (g_str_has_prefix (*iter, "ts=")) { + errno = 0; + ts = strtol (*iter + 3, NULL, 10); + /* allow 30 minutes time difference before we ignore the file */ + if (errno || ABS (tv.tv_sec - ts) > 1800) + goto out; + } else if (g_str_has_prefix (*iter, "iface=")) + g_hash_table_insert (priv->nm_bridges, g_strdup (*iter + 6), GUINT_TO_POINTER (1)); + } + +out: + g_strfreev (lines); + unlink (NM_BRIDGE_FILE); +} + +static void +write_nm_created_bridges (NMManager *self) +{ + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); + GString *br_list; + GSList *iter; + GError *error = NULL; + GTimeVal tv; + gboolean found = FALSE; + + /* write out nm-created bridges list */ + br_list = g_string_sized_new (50); + + /* Timestamp is first line */ + g_get_current_time (&tv); + g_string_append_printf (br_list, "ts=%ld\n", tv.tv_sec); + + for (iter = priv->devices; iter; iter = g_slist_next (iter)) { + NMDevice *device = iter->data; + + if (nm_device_get_device_type (device) == NM_DEVICE_TYPE_BRIDGE) { + g_string_append_printf (br_list, "iface=%s\n", nm_device_get_iface (device)); + found = TRUE; + } + } + + if (found) { + if (!g_file_set_contents (NM_BRIDGE_FILE, br_list->str, -1, &error)) { + nm_log_warn (LOGD_BRIDGE, "Failed to write NetworkManager-created bridge list; " + "on restart bridges may not be recognized. (%s)", + error ? error->message : "unknown"); + g_clear_error (&error); + } + } + g_string_free (br_list, TRUE); +} + +static gboolean +bridge_created_by_nm (NMManager *self, const char *iface) +{ + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); + + return (priv->nm_bridges && g_hash_table_lookup (priv->nm_bridges, iface)); +} + +/***************************/ + /** * system_create_virtual_device: * @self: the #NMManager @@ -1179,12 +1267,21 @@ system_create_virtual_device (NMManager *self, NMConnection *connection) device = nm_device_bond_new (udi, iface); g_free (udi); } else if (nm_connection_is_type (connection, NM_SETTING_BRIDGE_SETTING_NAME)) { - if (!nm_system_create_bridge (iface)) { + gboolean exists = FALSE; + + if (!nm_system_create_bridge (iface, &exists)) { nm_log_warn (LOGD_DEVICE, "(%s): failed to add bridging interface for '%s'", iface, nm_connection_get_id (connection)); goto out; } + /* FIXME: remove when we handle bridges non-destructively */ + if (exists && !bridge_created_by_nm (self, iface)) { + nm_log_warn (LOGD_DEVICE, "(%s): cannot use existing bridge for '%s'", + iface, nm_connection_get_id (connection)); + goto out; + } + udi = get_virtual_iface_placeholder_udi (); device = nm_device_bridge_new (udi, iface); g_free (udi); @@ -2175,9 +2272,14 @@ udev_device_added_cb (NMUdevManager *udev_mgr, device = nm_device_infiniband_new (sysfs_path, iface, driver); else if (is_bond (ifindex)) device = nm_device_bond_new (sysfs_path, iface); - else if (is_bridge (ifindex)) - device = nm_device_bridge_new (sysfs_path, iface); - else if (is_vlan (ifindex)) { + else if (is_bridge (ifindex)) { + + /* FIXME: always create device when we handle bridges non-destructively */ + if (bridge_created_by_nm (self, iface)) + device = nm_device_bridge_new (sysfs_path, iface); + else + nm_log_info (LOGD_BRIDGE, "(%s): ignoring bridge not created by NetworkManager", iface); + } else if (is_vlan (ifindex)) { int parent_ifindex = -1; NMDevice *parent; @@ -3639,6 +3741,13 @@ nm_manager_start (NMManager *self) system_unmanaged_devices_changed_cb (priv->settings, NULL, self); system_hostname_changed_cb (priv->settings, NULL, self); + /* FIXME: remove when we handle bridges non-destructively */ + /* Read a list of bridges NM managed when it last quit, and only + * manage those bridges to avoid conflicts with external tools. + */ + priv->nm_bridges = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); + read_nm_created_bridges (self); + nm_udev_manager_query_devices (priv->udev_mgr); nm_bluez_manager_query_devices (priv->bluez_mgr); @@ -3655,6 +3764,10 @@ nm_manager_start (NMManager *self) * connection-added signals thus devices have to be created manually. */ system_create_virtual_devices (self); + + /* FIXME: remove when we handle bridges non-destructively */ + g_hash_table_unref (priv->nm_bridges); + priv->nm_bridges = NULL; } static gboolean @@ -4064,6 +4177,10 @@ dispose (GObject *object) nm_auth_changed_func_unregister (authority_changed_cb, manager); + /* FIXME: remove when we handle bridges non-destructively */ + write_nm_created_bridges (manager); + + /* Remove all devices */ while (g_slist_length (priv->devices)) { priv->devices = remove_one_device (manager, priv->devices, diff --git a/src/nm-system.c b/src/nm-system.c index 2f95076612..031daf8aaa 100644 --- a/src/nm-system.c +++ b/src/nm-system.c @@ -2363,14 +2363,16 @@ _bridge_create_compat (const char *iface) /** * nm_system_create_bridge: * @iface: Name bridging device to create + * @out_exists: on return, %TRUE if the bridge already exists * * Creates a new bridging device in the kernel. If a bridging device with - * the specified name already exists, it is being reused. + * the specified name already exists, it is reused and no error is returned, + * but @out_exists is set to %TRUE. * * Returns: %TRUE on success, %FALSE on error. */ gboolean -nm_system_create_bridge (const char *iface) +nm_system_create_bridge (const char *iface, gboolean *out_exists) { int err; @@ -2381,6 +2383,8 @@ nm_system_create_bridge (const char *iface) iface, strerror (-err)); return FALSE; } + if (out_exists && err == -EEXIST) + *out_exists = TRUE; return TRUE; } diff --git a/src/nm-system.h b/src/nm-system.h index ca07d0f25b..632786d3d2 100644 --- a/src/nm-system.h +++ b/src/nm-system.h @@ -128,7 +128,7 @@ gboolean nm_system_add_vlan_iface (NMConnection *connection, int parent_ifindex); gboolean nm_system_del_vlan_iface (const char *iface); -gboolean nm_system_create_bridge (const char *iface); +gboolean nm_system_create_bridge (const char *iface, gboolean *out_exists); gboolean nm_system_del_bridge (const char *iface); gboolean nm_system_bridge_attach (int master_ifindex,