summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDan Williams <dcbw@redhat.com>2015-03-03 09:28:45 -0600
committerDan Williams <dcbw@redhat.com>2015-03-03 09:28:45 -0600
commitaf83bafcac7ccc0ba162fb75e14acaf5332d49ef (patch)
treea3943b6ce09f62dd12bf0b6319e7f7d4ac10e0e9
parent75e04d31c539a82be5d095781fa3432f0ff0b1c0 (diff)
parent2022a5adf154c88a28f8ef7e3b3ef957bb132071 (diff)
downloadNetworkManager-dcbw/nm-0-9-10-slaves.tar.gz
merge: assume connections for slave interfaces (rh #1141266) (rh #1176319)dcbw/nm-0-9-10-slaves
This is a merge of a rebase of 'lr/bridge-rh1141266' onto nm-0-9-10, plus a couple cleanup patches committed after that branch was merged to master. It fixes the crash from (rh #1176319) becuase slave connections are now assumed and thus auto-connect connections are not activated.
-rw-r--r--src/devices/nm-device.c96
-rw-r--r--src/devices/nm-device.h2
-rw-r--r--src/nm-manager.c1
-rw-r--r--src/platform/nm-linux-platform.c243
4 files changed, 257 insertions, 85 deletions
diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c
index a5c13f6d88..997284878a 100644
--- a/src/devices/nm-device.c
+++ b/src/devices/nm-device.c
@@ -854,6 +854,22 @@ nm_device_release_one_slave (NMDevice *dev, NMDevice *slave, gboolean configure,
return success;
}
+/**
+ * nm_device_finish_init:
+ * @self: the master device
+ *
+ * Whatever needs to be done post-initialization, when the device has a DBus
+ * object name.
+ */
+void
+nm_device_finish_init (NMDevice *self)
+{
+ NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+
+ if (priv->master)
+ nm_device_enslave_slave (priv->master, self, NULL);
+}
+
static void
carrier_changed (NMDevice *device, gboolean carrier)
{
@@ -1018,6 +1034,27 @@ update_for_ip_ifname_change (NMDevice *device)
}
static void
+device_set_master (NMDevice *self, int ifindex)
+{
+ NMDevice *master;
+ NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+
+ master = nm_manager_get_device_by_ifindex (nm_manager_get (), ifindex);
+ if (master && NM_DEVICE_GET_CLASS (master)->enslave_slave) {
+ g_clear_object (&priv->master);
+ priv->master = g_object_ref (master);
+ nm_device_master_add_slave (master, self, FALSE);
+ } else if (master) {
+ nm_log_info (LOGD_DEVICE, "(%s): enslaved to non-master-type device %s; ignoring",
+ nm_device_get_iface (self),
+ nm_device_get_iface (master));
+ } else {
+ nm_log_warn (LOGD_DEVICE, "(%s): enslaved to unknown device",
+ nm_device_get_iface (self));
+ }
+}
+
+static void
device_link_changed (NMDevice *device, NMPlatformLink *info)
{
NMDeviceClass *klass = NM_DEVICE_GET_CLASS (device);
@@ -1060,27 +1097,13 @@ device_link_changed (NMDevice *device, NMPlatformLink *info)
}
/* Update slave status for external changes */
- if (info->master && !priv->enslaved) {
- NMDevice *master;
-
- master = nm_manager_get_device_by_ifindex (nm_manager_get (), info->master);
- if (master && NM_DEVICE_GET_CLASS (master)->enslave_slave) {
- g_clear_object (&priv->master);
- priv->master = g_object_ref (master);
- nm_device_master_add_slave (master, device, FALSE);
- nm_device_enslave_slave (master, device, NULL);
- } else if (master) {
- nm_log_info (LOGD_DEVICE, "(%s): enslaved to non-master-type device %s; ignoring",
- nm_device_get_iface (device),
- nm_device_get_iface (master));
- } else {
- nm_log_warn (LOGD_DEVICE, "(%s): enslaved to unknown device %d %s",
- nm_device_get_iface (device),
- info->master,
- nm_platform_link_get_name (info->master));
- }
- } else if (priv->enslaved && !info->master)
+ if (priv->enslaved && info->master != nm_device_get_ifindex (priv->master))
nm_device_release_one_slave (priv->master, device, FALSE, NM_DEVICE_STATE_REASON_NONE);
+ if (info->master && !priv->enslaved) {
+ device_set_master (device, info->master);
+ if (priv->master)
+ nm_device_enslave_slave (priv->master, device, NULL);
+ }
if (klass->link_changed)
klass->link_changed (device, info);
@@ -1252,6 +1275,7 @@ nm_device_master_add_slave (NMDevice *dev, NMDevice *slave, gboolean configure)
G_CALLBACK (slave_state_changed), dev);
priv->slaves = g_slist_append (priv->slaves, info);
}
+ nm_device_queue_recheck_assume (dev);
return TRUE;
}
@@ -1471,11 +1495,16 @@ nm_device_slave_notify_release (NMDevice *dev, NMDeviceStateReason reason)
master_status);
nm_device_queue_state (dev, new_state, reason);
- } else {
+ } else if (priv->master) {
nm_log_info (LOGD_DEVICE,
"(%s): released from master %s",
nm_device_get_iface (dev),
nm_device_get_iface (priv->master));
+ } else {
+ nm_log_info (LOGD_DEVICE,
+ "(%s): released from master%s",
+ nm_device_get_iface (dev),
+ priv->enslaved ? "" : " (was not enslaved)");
}
if (priv->enslaved) {
@@ -1760,8 +1789,9 @@ nm_device_generate_connection (NMDevice *device)
ip6_method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP6_CONFIG);
if ( g_strcmp0 (ip4_method, NM_SETTING_IP4_CONFIG_METHOD_DISABLED) == 0
&& g_strcmp0 (ip6_method, NM_SETTING_IP6_CONFIG_METHOD_IGNORE) == 0
- && !nm_setting_connection_get_master (NM_SETTING_CONNECTION (s_con))) {
- nm_log_dbg (LOGD_DEVICE, "(%s): ignoring generated connection (no IP and not slave)", ifname);
+ && !nm_setting_connection_get_master (NM_SETTING_CONNECTION (s_con))
+ && !priv->slaves) {
+ nm_log_dbg (LOGD_DEVICE, "(%s): ignoring generated connection (no IP and not in master-slave relationship)", ifname);
g_object_unref (connection);
connection = NULL;
}
@@ -1971,7 +2001,7 @@ nm_device_emit_recheck_assume (gpointer self)
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
priv->recheck_assume_id = 0;
- if (!nm_device_get_act_request (self) && (priv->ip4_config || priv->ip6_config))
+ if (!nm_device_get_act_request (self))
g_signal_emit (self, signals[RECHECK_ASSUME], 0);
return G_SOURCE_REMOVE;
}
@@ -6879,7 +6909,10 @@ _set_state_full (NMDevice *device,
nm_act_request_get_connection (req),
device);
} else {
- priv->dispatcher.post_state = NM_DEVICE_STATE_DISCONNECTED;
+ if (nm_device_is_available (device))
+ priv->dispatcher.post_state = NM_DEVICE_STATE_DISCONNECTED;
+ else
+ priv->dispatcher.post_state = NM_DEVICE_STATE_UNAVAILABLE;
priv->dispatcher.post_state_reason = reason;
if (!nm_dispatcher_call (DISPATCHER_ACTION_PRE_DOWN,
nm_act_request_get_connection (req),
@@ -6915,6 +6948,13 @@ _set_state_full (NMDevice *device,
"Activation (%s) failed for connection '%s'",
nm_device_get_iface (device),
connection ? nm_connection_get_id (connection) : "<unknown>");
+ if (req && nm_active_connection_get_assumed (NM_ACTIVE_CONNECTION (req))) {
+ /* Avoid tearing down assumed connection, assume it's connected */
+ nm_device_queue_state (device,
+ NM_DEVICE_STATE_ACTIVATED,
+ NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED);
+ break;
+ }
/* Notify any slaves of the unexpected failure */
nm_device_master_release_slaves (device);
@@ -7409,6 +7449,7 @@ constructed (GObject *object)
{
NMDevice *dev = NM_DEVICE (object);
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (dev);
+ int master;
nm_device_update_hw_address (dev);
@@ -7441,6 +7482,11 @@ constructed (GObject *object)
if (priv->ifindex > 0)
priv->mtu = nm_platform_link_get_mtu (priv->ifindex);
+ /* Enslave ourselves */
+ master = nm_platform_link_get_master (priv->ifindex);
+ if (master)
+ device_set_master (dev, master);
+
priv->con_provider = nm_connection_provider_get ();
g_assert (priv->con_provider);
g_signal_connect (priv->con_provider,
diff --git a/src/devices/nm-device.h b/src/devices/nm-device.h
index a2f276934e..f6ceff7116 100644
--- a/src/devices/nm-device.h
+++ b/src/devices/nm-device.h
@@ -220,6 +220,8 @@ GType nm_device_get_type (void);
const char * nm_device_get_path (NMDevice *dev);
void nm_device_dbus_export (NMDevice *device);
+void nm_device_finish_init (NMDevice *device);
+
const char * nm_device_get_udi (NMDevice *dev);
const char * nm_device_get_iface (NMDevice *dev);
int nm_device_get_ifindex (NMDevice *dev);
diff --git a/src/nm-manager.c b/src/nm-manager.c
index 6be83380eb..0214d47f36 100644
--- a/src/nm-manager.c
+++ b/src/nm-manager.c
@@ -1790,6 +1790,7 @@ add_device (NMManager *self, NMDevice *device, gboolean generate_con)
nm_device_set_initial_unmanaged_flag (device, NM_UNMANAGED_INTERNAL, sleeping);
nm_device_dbus_export (device);
+ nm_device_finish_init (device);
/* Don't generate a connection e.g. for devices NM just created, or
* for the loopback, or when we're sleeping. */
diff --git a/src/platform/nm-linux-platform.c b/src/platform/nm-linux-platform.c
index 1f925a65a3..cf2164d748 100644
--- a/src/platform/nm-linux-platform.c
+++ b/src/platform/nm-linux-platform.c
@@ -1818,6 +1818,33 @@ _rtnl_addr_timestamps_equal_fuzzy (guint32 ts1, guint32 ts2)
return diff <= 2;
}
+static gboolean
+nm_nl_object_diff (struct nl_object *_a, struct nl_object *_b, ObjectType type)
+{
+ if (nl_object_diff (_a, _b)) {
+ /* libnl thinks objects are different*/
+ return TRUE;
+ }
+
+ if (type == OBJECT_TYPE_IP4_ADDRESS || type == OBJECT_TYPE_IP6_ADDRESS) {
+ struct rtnl_addr *a = (struct rtnl_addr *) _a;
+ struct rtnl_addr *b = (struct rtnl_addr *) _b;
+
+ /* libnl nl_object_diff() ignores differences in timestamp. Let's care about
+ * them (if they are large enough).
+ *
+ * Note that these valid and preferred timestamps are absolute, after
+ * _rtnl_addr_hack_lifetimes_rel_to_abs(). */
+ if ( !_rtnl_addr_timestamps_equal_fuzzy (rtnl_addr_get_preferred_lifetime (a),
+ rtnl_addr_get_preferred_lifetime (b))
+ || !_rtnl_addr_timestamps_equal_fuzzy (rtnl_addr_get_valid_lifetime (a),
+ rtnl_addr_get_valid_lifetime (b)))
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
/* This function does all the magic to avoid race conditions caused
* by concurrent usage of synchronous commands and an asynchronous cache. This
* might be a nice future addition to libnl but it requires to do all operations
@@ -1928,24 +1955,9 @@ event_notification (struct nl_msg *msg, gpointer user_data)
* This also catches notifications for internal addition or change, unless
* another action occured very soon after it.
*/
- if (!nl_object_diff (kernel_object, cached_object)) {
- if (type == OBJECT_TYPE_IP4_ADDRESS || type == OBJECT_TYPE_IP6_ADDRESS) {
- struct rtnl_addr *c = (struct rtnl_addr *) cached_object;
- struct rtnl_addr *k = (struct rtnl_addr *) kernel_object;
-
- /* libnl nl_object_diff() ignores differences in timestamp. Let's care about
- * them (if they are large enough).
- *
- * Note that these valid and preferred timestamps are absolute, after
- * _rtnl_addr_hack_lifetimes_rel_to_abs(). */
- if ( _rtnl_addr_timestamps_equal_fuzzy (rtnl_addr_get_preferred_lifetime (c),
- rtnl_addr_get_preferred_lifetime (k))
- && _rtnl_addr_timestamps_equal_fuzzy (rtnl_addr_get_valid_lifetime (c),
- rtnl_addr_get_valid_lifetime (k)))
- return NL_OK;
- } else
- return NL_OK;
- }
+ if (!nm_nl_object_diff (kernel_object, cached_object, type))
+ return NL_OK;
+
/* Handle external change */
nl_cache_remove (cached_object);
nle = nl_cache_add (cache, kernel_object);
@@ -3818,6 +3830,152 @@ ip6_route_exists (NMPlatform *platform, int ifindex, struct in6_addr network, in
/******************************************************************/
+/* Initialize the link cache while ensuring all links are of AF_UNSPEC,
+ * family (even though the kernel might set AF_BRIDGE for bridges).
+ * See also: _nl_link_family_unset() */
+static void
+init_link_cache (NMPlatform *platform)
+{
+ NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform);
+ struct nl_object *object = NULL;
+
+ rtnl_link_alloc_cache (priv->nlh, AF_UNSPEC, &priv->link_cache);
+
+ do {
+ for (object = nl_cache_get_first (priv->link_cache); object; object = nl_cache_get_next (object)) {
+ if (rtnl_link_get_family ((struct rtnl_link *)object) != AF_UNSPEC)
+ break;
+ }
+
+ if (object) {
+ /* A non-AF_UNSPEC object encoutnered */
+ struct nl_object *existing;
+
+ nl_object_get (object);
+ nl_cache_remove (object);
+ rtnl_link_set_family ((struct rtnl_link *)object, AF_UNSPEC);
+ existing = nl_cache_search (priv->link_cache, object);
+ if (existing) {
+ nl_object_put (existing);
+ } else {
+ nl_cache_add (priv->link_cache, object);
+ }
+ nl_object_put (object);
+ }
+ } while (object);
+}
+
+/* Calls announce_object with appropriate arguments for all objects
+ * which are not coherent between old and new caches and deallocates
+ * the old cache. */
+static gboolean
+sync_cache (NMPlatform *platform, struct nl_cache *new, struct nl_cache *old)
+{
+ struct nl_object *object;
+ gboolean changed = FALSE;
+
+ if (!old)
+ return changed;
+
+ for (object = nl_cache_get_first (new); object; object = nl_cache_get_next (object)) {
+ struct nl_object *cached_object = nm_nl_cache_search (old, object);
+
+ if (cached_object) {
+ ObjectType type = object_type_from_nl_object (object);
+ if (nm_nl_object_diff (object, cached_object, type)) {
+ announce_object (platform, object, NM_PLATFORM_SIGNAL_CHANGED, NM_PLATFORM_REASON_EXTERNAL);
+ changed = TRUE;
+ }
+ nl_object_put (cached_object);
+ } else {
+ announce_object (platform, object, NM_PLATFORM_SIGNAL_ADDED, NM_PLATFORM_REASON_EXTERNAL);
+ changed = TRUE;
+ }
+ }
+ for (object = nl_cache_get_first (old); object; object = nl_cache_get_next (object)) {
+ struct nl_object *cached_object = nm_nl_cache_search (new, object);
+ if (cached_object) {
+ nl_object_put (cached_object);
+ } else {
+ announce_object (platform, object, NM_PLATFORM_SIGNAL_REMOVED, NM_PLATFORM_REASON_EXTERNAL);
+ changed = TRUE;
+ }
+ }
+
+ nl_cache_free (old);
+ return changed;
+}
+
+/* The cache should always avoid containing objects not handled by NM, like
+ * e.g. addresses of the AF_PHONET family. */
+static void
+cache_remove_unknown (struct nl_cache *cache)
+{
+ GPtrArray *objects_to_remove = NULL;
+ struct nl_object *object;
+
+ for (object = nl_cache_get_first (cache); object; object = nl_cache_get_next (object)) {
+ if (object_type_from_nl_object (object) == OBJECT_TYPE_UNKNOWN) {
+ if (!objects_to_remove)
+ objects_to_remove = g_ptr_array_new_with_free_func ((GDestroyNotify) nl_object_put);
+ nl_object_get (object);
+ g_ptr_array_add (objects_to_remove, object);
+ }
+ }
+
+ if (objects_to_remove) {
+ guint i;
+
+ for (i = 0; i < objects_to_remove->len; i++)
+ nl_cache_remove (g_ptr_array_index (objects_to_remove, i));
+
+ g_ptr_array_free (objects_to_remove, TRUE);
+ }
+}
+
+/* Creates and populates the netlink object caches. Called upon platform init
+ * and when we run out of sync (out of buffer space, netlink congestion control).
+ * Returns TRUE if there were changes,
+ * Does not guarrantee the caches are coherent with kernel -- we could have missed
+ * events while synchronizing. Should be re-called when there were changes. */
+static gboolean
+sync_platform (NMPlatform *platform)
+{
+ NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform);
+ struct nl_cache *old_link_cache = priv->link_cache;
+ struct nl_cache *old_address_cache = priv->address_cache;
+ struct nl_cache *old_route_cache = priv->route_cache;
+ struct nl_object *object;
+ gboolean changed = FALSE;
+
+ /* Allocate new netlink caches */
+ init_link_cache (platform);
+ rtnl_addr_alloc_cache (priv->nlh, &priv->address_cache);
+ rtnl_route_alloc_cache (priv->nlh, AF_UNSPEC, 0, &priv->route_cache);
+ g_assert (priv->link_cache && priv->address_cache && priv->route_cache);
+
+ /* Remove all unknown objects from the caches */
+ cache_remove_unknown (priv->link_cache);
+ cache_remove_unknown (priv->address_cache);
+ cache_remove_unknown (priv->route_cache);
+
+ for (object = nl_cache_get_first (priv->address_cache); object; object = nl_cache_get_next (object)) {
+ _rtnl_addr_hack_lifetimes_rel_to_abs ((struct rtnl_addr *) object);
+ }
+
+ /* Make sure all changes we've missed are announced. */
+ if (sync_cache (platform, priv->link_cache, old_link_cache))
+ changed = TRUE;
+ if (sync_cache (platform, priv->address_cache, old_address_cache))
+ changed = TRUE;
+ if (sync_cache (platform, priv->route_cache, old_route_cache))
+ changed = TRUE;
+
+ return changed;
+}
+
+/******************************************************************/
+
#define EVENT_CONDITIONS ((GIOCondition) (G_IO_IN | G_IO_PRI))
#define ERROR_CONDITIONS ((GIOCondition) (G_IO_ERR | G_IO_NVAL))
#define DISCONNECT_CONDITIONS ((GIOCondition) (G_IO_HUP))
@@ -3844,7 +4002,8 @@ event_handler (GIOChannel *channel,
GIOCondition io_condition,
gpointer user_data)
{
- NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (user_data);
+ NMPlatform *platform = NM_PLATFORM (user_data);
+ NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform);
int nle;
nle = nl_recvmsgs_default (priv->nlh_event);
@@ -3856,6 +4015,10 @@ event_handler (GIOChannel *channel,
* and can happen easily. */
debug ("Uncritical failure to retrieve incoming events: %s (%d)", nl_geterror (nle), nle);
break;
+ case -NLE_NOMEM:
+ warning ("Too many netlink events. Trying to resynchronize: %s (%d)", nl_geterror (nle), nle);
+ while (sync_platform (platform));
+ break;
default:
error ("Failed to retrieve incoming events: %s (%d)", nl_geterror (nle), nle);
break;
@@ -4015,33 +4178,6 @@ nm_linux_platform_init (NMLinuxPlatform *platform)
{
}
-/* The cache should always avoid containing objects not handled by NM, like
- * e.g. addresses of the AF_PHONET family. */
-static void
-cache_remove_unknown (struct nl_cache *cache)
-{
- GPtrArray *objects_to_remove = NULL;
- struct nl_object *object;
-
- for (object = nl_cache_get_first (cache); object; object = nl_cache_get_next (object)) {
- if (object_type_from_nl_object (object) == OBJECT_TYPE_UNKNOWN) {
- if (!objects_to_remove)
- objects_to_remove = g_ptr_array_new_with_free_func ((GDestroyNotify) nl_object_put);
- nl_object_get (object);
- g_ptr_array_add (objects_to_remove, object);
- }
- }
-
- if (objects_to_remove) {
- guint i;
-
- for (i = 0; i < objects_to_remove->len; i++)
- nl_cache_remove (g_ptr_array_index (objects_to_remove, i));
-
- g_ptr_array_free (objects_to_remove, TRUE);
- }
-}
-
static gboolean
setup (NMPlatform *platform)
{
@@ -4052,7 +4188,6 @@ setup (NMPlatform *platform)
int channel_flags;
gboolean status;
int nle;
- struct nl_object *object;
/* Initialize netlink socket for requests */
priv->nlh = setup_socket (FALSE, platform);
@@ -4088,19 +4223,7 @@ setup (NMPlatform *platform)
(EVENT_CONDITIONS | ERROR_CONDITIONS | DISCONNECT_CONDITIONS),
event_handler, platform);
- /* Allocate netlink caches */
- rtnl_link_alloc_cache (priv->nlh, AF_UNSPEC, &priv->link_cache);
- rtnl_addr_alloc_cache (priv->nlh, &priv->address_cache);
- rtnl_route_alloc_cache (priv->nlh, AF_UNSPEC, 0, &priv->route_cache);
- g_assert (priv->link_cache && priv->address_cache && priv->route_cache);
-
- /* Remove all unknown objects from the caches */
- cache_remove_unknown (priv->link_cache);
- cache_remove_unknown (priv->address_cache);
- cache_remove_unknown (priv->route_cache);
-
- for (object = nl_cache_get_first (priv->address_cache); object; object = nl_cache_get_next (object))
- _rtnl_addr_hack_lifetimes_rel_to_abs ((struct rtnl_addr *) object);
+ sync_platform (platform);
/* Set up udev monitoring */
priv->udev_client = g_udev_client_new (udev_subsys);