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
This commit is contained in:
Lubomir Rintel 2014-12-11 11:49:48 +01:00
commit 1f1aebebea
4 changed files with 205 additions and 55 deletions

View file

@ -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)
{
@ -1137,6 +1153,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)
{
@ -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,

View file

@ -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);

View file

@ -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);

View file

@ -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;
if (!nm_nl_object_diff (type, kernel_object, cached_object))
return NL_OK;
/* 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;
}
/* 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