summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLubomir Rintel <lkundrak@v3.sk>2014-12-11 11:49:48 +0100
committerLubomir Rintel <lkundrak@v3.sk>2014-12-11 11:49:48 +0100
commit1f1aebebea222be9b5b6b72dd61f88c09de2bd71 (patch)
tree35b28464520495a5589c180fce50cec4ef9dd6c1
parent8019a51a0e78d6b10311562e8b2b573832fa1b90 (diff)
parent62ad694421ae96de61436f87cdec1a01e3e2879a (diff)
downloadNetworkManager-1f1aebebea222be9b5b6b72dd61f88c09de2bd71.tar.gz
platform,device: merge branch 'lr/bridge-rh1141266'
Assume connections for bridges with slaves, and associated fixes for bridges. https://bugzilla.redhat.com/show_bug.cgi?id=1141266
-rw-r--r--src/devices/nm-device.c74
-rw-r--r--src/devices/nm-device.h2
-rw-r--r--src/nm-manager.c1
-rw-r--r--src/platform/nm-linux-platform.c185
4 files changed, 206 insertions, 56 deletions
diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c
index f834875cbf..50fc07562e 100644
--- a/src/devices/nm-device.c
+++ b/src/devices/nm-device.c
@@ -977,6 +977,22 @@ nm_device_release_one_slave (NMDevice *self, 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 *self, gboolean carrier)
{
@@ -1138,6 +1154,27 @@ update_for_ip_ifname_change (NMDevice *self)
}
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) {
+ _LOGI (LOGD_DEVICE, "enslaved to non-master-type device %s; ignoring",
+ nm_device_get_iface (master));
+ } else {
+ _LOGW (LOGD_DEVICE, "enslaved to unknown device %d %s",
+ ifindex,
+ nm_platform_link_get_name (ifindex));
+ }
+}
+
+static void
device_link_changed (NMDevice *self, NMPlatformLink *info)
{
NMDeviceClass *klass = NM_DEVICE_GET_CLASS (self);
@@ -1180,25 +1217,12 @@ device_link_changed (NMDevice *self, 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, self, FALSE);
- nm_device_enslave_slave (master, self, NULL);
- } else if (master) {
- _LOGI (LOGD_DEVICE, "enslaved to non-master-type device %s; ignoring",
- nm_device_get_iface (master));
- } else {
- _LOGW (LOGD_DEVICE, "enslaved to unknown device %d %s",
- 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, self, FALSE, NM_DEVICE_STATE_REASON_NONE);
+ if (info->master && !priv->enslaved)
+ device_set_master (self, info->master);
+ if (priv->master)
+ nm_device_enslave_slave (priv->master, self, NULL);
if (klass->link_changed)
klass->link_changed (self, info);
@@ -1380,6 +1404,7 @@ nm_device_master_add_slave (NMDevice *self, NMDevice *slave, gboolean configure)
G_CALLBACK (slave_state_changed), self);
priv->slaves = g_slist_append (priv->slaves, info);
}
+ nm_device_queue_recheck_assume (self);
return TRUE;
}
@@ -1965,8 +1990,9 @@ nm_device_generate_connection (NMDevice *self, NMDevice *master)
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))) {
- _LOGD (LOGD_DEVICE, "ignoring generated connection (no IP and not slave)");
+ && !nm_setting_connection_get_master (NM_SETTING_CONNECTION (s_con))
+ && !priv->slaves) {
+ _LOGD (LOGD_DEVICE, "ignoring generated connection (no IP and not in master-slave relationship)");
g_object_unref (connection);
connection = NULL;
}
@@ -2126,7 +2152,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)) {
_LOGD (LOGD_DEVICE, "emit RECHECK_ASSUME signal");
g_signal_emit (self, signals[RECHECK_ASSUME], 0);
}
@@ -7944,6 +7970,7 @@ constructed (GObject *object)
{
NMDevice *self = NM_DEVICE (object);
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+ int master;
nm_device_update_hw_address (self);
@@ -7976,6 +8003,11 @@ constructed (GObject *object)
if (priv->is_software)
priv->capabilities |= NM_DEVICE_CAP_IS_SOFTWARE;
+ /* Enslave ourselves */
+ master = nm_platform_link_get_master (priv->ifindex);
+ if (master)
+ device_set_master (self, 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 ab54262623..38eddaa8df 100644
--- a/src/devices/nm-device.h
+++ b/src/devices/nm-device.h
@@ -216,6 +216,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 1c946ec41a..06f24abdb8 100644
--- a/src/nm-manager.c
+++ b/src/nm-manager.c
@@ -1819,6 +1819,7 @@ add_device (NMManager *self, NMDevice *device, gboolean try_assume)
nm_device_set_initial_unmanaged_flag (device, NM_UNMANAGED_INTERNAL, sleeping);
nm_device_dbus_export (device);
+ nm_device_finish_init (device);
if (try_assume) {
connection_assumed = recheck_assume_connection (device, self);
diff --git a/src/platform/nm-linux-platform.c b/src/platform/nm-linux-platform.c
index 7ced0ba77b..cdf3ce24dd 100644
--- a/src/platform/nm-linux-platform.c
+++ b/src/platform/nm-linux-platform.c
@@ -1880,6 +1880,33 @@ _rtnl_addr_timestamps_equal_fuzzy (guint32 ts1, guint32 ts2)
return diff <= 2;
}
+static gboolean
+nm_nl_object_diff (ObjectType type, struct nl_object *_a, struct nl_object *_b)
+{
+ 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
@@ -1985,24 +2012,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 (type, kernel_object, cached_object))
+ return NL_OK;
+
/* Handle external change */
nl_cache_remove (cached_object);
nle = nl_cache_add (cache, kernel_object);
@@ -3982,6 +3994,106 @@ 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 void
+cache_announce_changes (NMPlatform *platform, struct nl_cache *new, struct nl_cache *old)
+{
+ struct nl_object *object;
+
+ if (!old)
+ return;
+
+ 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 (type, object, cached_object))
+ announce_object (platform, object, NM_PLATFORM_SIGNAL_CHANGED, NM_PLATFORM_REASON_EXTERNAL);
+ nl_object_put (cached_object);
+ } else
+ announce_object (platform, object, NM_PLATFORM_SIGNAL_ADDED, NM_PLATFORM_REASON_EXTERNAL);
+ }
+ 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);
+ }
+
+ nl_cache_free (old);
+}
+
+/* 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). In case
+ * the caches already exist, it finds changed, added and removed objects, announces
+ * them and destroys the old caches. */
+static void
+cache_repopulate_all (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;
+
+ debug ("platform: %spopulate platform cache", old_link_cache ? "re" : "");
+
+ /* 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);
+
+ 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. */
+ cache_announce_changes (platform, priv->link_cache, old_link_cache);
+ cache_announce_changes (platform, priv->address_cache, old_address_cache);
+ cache_announce_changes (platform, priv->route_cache, old_route_cache);
+}
+
+/******************************************************************/
+
#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))
@@ -4008,7 +4120,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);
@@ -4020,6 +4133,17 @@ 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. Need to resynchronize platform cache");
+ /* Drain the event queue, we've lost events and are out of sync anyway and we'd
+ * like to free up some space. We'll read in the status synchronously. */
+ nl_socket_modify_cb (priv->nlh_event, NL_CB_VALID, NL_CB_DEFAULT, NULL, NULL);
+ do {
+ nle = nl_recvmsgs_default (priv->nlh_event);
+ } while (nle != -NLE_AGAIN);
+ nl_socket_modify_cb (priv->nlh_event, NL_CB_VALID, NL_CB_CUSTOM, event_notification, user_data);
+ cache_repopulate_all (platform);
+ break;
default:
error ("Failed to retrieve incoming events: %s (%d)", nl_geterror (nle), nle);
break;
@@ -4051,6 +4175,12 @@ setup_socket (gboolean event, gpointer user_data)
nle = nl_socket_set_passcred (sock, 1);
g_assert (!nle);
+ /* No blocking for event socket, so that we can drain it safely. */
+ if (event) {
+ nle = nl_socket_set_nonblocking (sock);
+ g_assert (!nle);
+ }
+
return sock;
}
@@ -4065,7 +4195,6 @@ udev_device_added (NMPlatform *platform,
const char *ifname;
int ifindex;
gboolean was_announceable = FALSE;
- int nle;
ifname = g_udev_device_get_name (udev_device);
if (!ifname) {
@@ -4096,12 +4225,6 @@ udev_device_added (NMPlatform *platform,
g_hash_table_insert (priv->udev_devices, GINT_TO_POINTER (ifindex),
g_object_ref (udev_device));
- /* Grow the netlink socket buffer beyond 128k if we have more that 32 interfaces. */
- nle = nl_socket_set_buffer_size (priv->nlh_event,
- MAX (131072, 4096 * g_hash_table_size (priv->udev_devices)), 0);
- if (nle)
- warning ("udev-add: failed to adjust netlink socket buffer size");
-
/* Announce devices only if they also have been discovered via Netlink. */
if (rtnllink && link_is_announceable (platform, rtnllink))
announce_object (platform, (struct nl_object *) rtnllink, was_announceable ? NM_PLATFORM_SIGNAL_CHANGED : NM_PLATFORM_SIGNAL_ADDED, NM_PLATFORM_REASON_EXTERNAL);
@@ -4196,7 +4319,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);
@@ -4232,14 +4354,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);
-
- 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);
+ cache_repopulate_all (platform);
#if HAVE_LIBNL_INET6_ADDR_GEN_MODE
/* Initial check for user IPv6LL support once the link cache is allocated