NetworkManager/src/nm-default-route-manager.c

1404 lines
46 KiB
C
Raw Normal View History

/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
/* NetworkManager -- Network link manager
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Copyright (C) 2014 Red Hat, Inc.
*/
#include "config.h"
#include "nm-default-route-manager.h"
#include "string.h"
#include "nm-logging.h"
#include "nm-device.h"
#include "nm-vpn-connection.h"
#include "nm-platform.h"
#include "nm-manager.h"
#include "nm-ip4-config.h"
#include "nm-ip6-config.h"
#include "nm-activation-request.h"
typedef struct {
GPtrArray *entries_ip4;
GPtrArray *entries_ip6;
struct {
guint guard;
guint backoff_wait_time_ms;
guint idle_handle;
gboolean has_v4_changes;
gboolean has_v6_changes;
} resync;
default-route-manager: avoid crash while disposing of NMDefaultRouteManager During dipose(), NMDefaultRouteManager unrefed all the source pointers in its list -- thereby having dangling pointers in the list of entries. The unrefing can cause the final destruction of the device (during shutdown), which would again call into NMDefaultRouteManager. Fix this by ensuring that after disposing starts, all external calls into NMDefaultRouteManager return early. #0 0x00007ffff4a2cc60 in g_logv (log_domain=0x535b51 "NetworkManager", log_level=G_LOG_LEVEL_CRITICAL, format=<optimized out>, args=args@entry=0x7fffffffd530) at gmessages.c:1046 #1 0x00007ffff4a2ce9f in g_log (log_domain=log_domain@entry=0x535b51 "NetworkManager", log_level=log_level@entry=G_LOG_LEVEL_CRITICAL, format=format@entry=0x528f68 "file %s: line %d (%s): should not be reached") at gmessages.c:1079 #2 0x000000000049b83b in _ipx_update_default_route (vtable=vtable@entry=0x7a49c0 <vtable_ip6>, self=0x7d1350 [NMDefaultRouteManager], source=source@entry=0x8b64e0) at nm-default-route-manager.c:659 #3 0x000000000049c652 in nm_default_route_manager_ip6_update_default_route (self=<optimized out>, source=source@entry=0x8b64e0) at nm-default-route-manager.c:819 #4 0x00000000004526a8 in _cleanup_generic_post (self=self@entry=0x8b64e0, deconfigure=deconfigure@entry=0) at devices/nm-device.c:7235 #5 0x0000000000452bc0 in dispose (object=0x8b64e0) at devices/nm-device.c:8324 #6 0x00007ffff4d29cbc in g_object_unref (_object=0x8b64e0) at gobject.c:3133 #7 0x0000000000499091 in _entry_free (entry=0x8bf140) at nm-default-route-manager.c:187 #8 0x00007ffff49fa82b in g_ptr_array_foreach (array=0x81d220, func=0x499080 <_entry_free>, user_data=0x0) at garray.c:1502 #9 0x00007ffff49fa8c0 in ptr_array_free (array=0x81d220, flags=FREE_SEGMENT) at garray.c:1088 #10 0x00007ffff49fa939 in g_ptr_array_free (array=<optimized out>, free_segment=free_segment@entry=1) at garray.c:1075 #11 0x000000000049abcf in dispose (object=0x7d1350 [NMDefaultRouteManager]) at nm-default-route-manager.c:1357 #12 0x00007ffff4d29cbc in g_object_unref (_object=0x7d1350) at gobject.c:3133 #13 0x00007ffff7deb507 in _dl_fini () at dl-fini.c:252 #14 0x00007ffff4658382 in __run_exit_handlers (status=status@entry=0, listp=0x7ffff49d66a0 <__exit_funcs>, run_list_atexit=run_list_atexit@entry=true) at exit.c:82 #15 0x00007ffff46583d5 in __GI_exit (status=status@entry=0) at exit.c:104 #16 0x0000000000432fd6 in main (argc=1, argv=0x7fffffffdeb8) at main.c:473
2015-02-17 07:53:24 +01:00
/* During disposing, we unref the sources of all entries. This happens usually
* during shutdown, which might call the final deletion of the object. That
* again might cause calls back into NMDefaultRouteManager, which finds dangling
* pointers.
* Guard every publicly accessible function to return early if the instance
* is already disposing. */
gboolean disposed;
NMPlatform *platform;
} NMDefaultRouteManagerPrivate;
#define NM_DEFAULT_ROUTE_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DEFAULT_ROUTE_MANAGER, NMDefaultRouteManagerPrivate))
G_DEFINE_TYPE (NMDefaultRouteManager, nm_default_route_manager, G_TYPE_OBJECT)
NM_DEFINE_SINGLETON_GETTER (NMDefaultRouteManager, nm_default_route_manager_get, NM_TYPE_DEFAULT_ROUTE_MANAGER);
#define _LOG(level, addr_family, ...) \
G_STMT_START { \
const int __addr_family = (addr_family); \
const NMLogLevel __level = (level); \
const NMLogDomain __domain = __addr_family == AF_INET ? LOGD_IP4 : (__addr_family == AF_INET6 ? LOGD_IP6 : LOGD_IP); \
\
if (nm_logging_enabled (__level, __domain)) { \
char __ch = __addr_family == AF_INET ? '4' : (__addr_family == AF_INET6 ? '6' : '-'); \
char __prefix[30] = "default-route"; \
\
if ((self) != singleton_instance) \
g_snprintf (__prefix, sizeof (__prefix), "default-route%c[%p]", __ch, (self)); \
else \
__prefix[STRLEN ("default-route")] = __ch; \
_nm_log (__level, __domain, 0, \
"%s: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \
__prefix _NM_UTILS_MACRO_REST(__VA_ARGS__)); \
} \
} G_STMT_END
#define _LOGD(addr_family, ...) _LOG (LOGL_DEBUG, addr_family, __VA_ARGS__)
#define _LOGI(addr_family, ...) _LOG (LOGL_INFO , addr_family, __VA_ARGS__)
#define _LOGW(addr_family, ...) _LOG (LOGL_WARN , addr_family, __VA_ARGS__)
#define _LOGE(addr_family, ...) _LOG (LOGL_ERR , addr_family, __VA_ARGS__)
#define LOG_ENTRY_FMT "entry[%u/%s:%p:%s:%c:%csync]"
#define LOG_ENTRY_ARGS(entry_idx, entry) \
(entry_idx), \
NM_IS_DEVICE ((entry)->source.pointer) ? "dev" : "vpn", \
(entry)->source.pointer, \
NM_IS_DEVICE ((entry)->source.pointer) ? nm_device_get_iface ((entry)->source.device) : nm_vpn_connection_get_connection_id ((entry)->source.vpn), \
((entry)->never_default ? '0' : '1'), \
((entry)->synced ? '+' : '-')
/***********************************************************************************/
static void _resync_idle_cancel (NMDefaultRouteManager *self);
/***********************************************************************************/
typedef struct {
union {
void *pointer;
GObject *object;
NMDevice *device;
NMVpnConnection *vpn;
} source;
NMPlatformIPXRoute route;
/* Whether the route is synced to platform and has a default route.
*
* ( synced && !never_default): the interface gets a default route that
* is enforced and managed by NMDefaultRouteManager.
*
* (!synced && !never_default): the interface has this route, but it is assumed.
* Assumed interfaces are those that have no tracked entry or that only have
* (!synced && !never_default) entries. NMDefaultRouteManager will not touch
* default routes on these interfaces.
* This combination makes only sense for device sources.
* They are tracked so that assumed devices can also be the best device.
*
* ( synced && never_default): entries of this kind are a placeholder
* to indicate that the ifindex is managed but has no default-route.
* Missing entries also indicate that a certain ifindex has no default-route.
* The difference is that missing entries are considered assumed while on
2015-02-17 07:09:57 +01:00
* (synced && never_default) entries the absence of the default route
* is enforced. NMDefaultRouteManager will actively remove any default
* route on such ifindexes.
* Also, for VPN sources in addition we track them so that a never-default
* VPN connection can be choosen by get_best_config() to receive the DNS configuration.
*
* (!synced && never_default): this combination makes no sense.
*/
gboolean synced;
gboolean never_default;
guint32 effective_metric;
} Entry;
typedef struct {
const NMPlatformVTableRoute *vt;
GPtrArray *(*get_entries) (NMDefaultRouteManagerPrivate *priv);
} VTableIP;
static const VTableIP vtable_ip4, vtable_ip6;
static NMPlatformIPRoute *
_vt_route_index (const VTableIP *vtable, GArray *routes, guint index)
{
if (vtable->vt->is_ip4)
return (NMPlatformIPRoute *) &g_array_index (routes, NMPlatformIP4Route, index);
else
return (NMPlatformIPRoute *) &g_array_index (routes, NMPlatformIP6Route, index);
}
static gboolean
_vt_routes_has_entry (const VTableIP *vtable, GArray *routes, const Entry *entry)
{
guint i;
NMPlatformIPXRoute route = entry->route;
route.rx.metric = entry->effective_metric;
if (vtable->vt->is_ip4) {
for (i = 0; i < routes->len; i++) {
NMPlatformIP4Route *r = &g_array_index (routes, NMPlatformIP4Route, i);
route.rx.source = r->source;
if (nm_platform_ip4_route_cmp (r, &route.r4) == 0)
return TRUE;
}
} else {
for (i = 0; i < routes->len; i++) {
NMPlatformIP6Route *r = &g_array_index (routes, NMPlatformIP6Route, i);
route.rx.source = r->source;
if (nm_platform_ip6_route_cmp (r, &route.r6) == 0)
return TRUE;
}
}
return FALSE;
}
static void
_entry_free (Entry *entry)
{
if (entry) {
g_object_unref (entry->source.object);
g_slice_free (Entry, entry);
}
}
static Entry *
_entry_find_by_source (GPtrArray *entries, gpointer source, guint *out_idx)
{
guint i;
for (i = 0; i < entries->len; i++) {
Entry *e = g_ptr_array_index (entries, i);
if (e->source.pointer == source) {
if (out_idx)
*out_idx = i;
return e;
}
}
if (out_idx)
*out_idx = G_MAXUINT;
return NULL;
}
static gboolean
_platform_route_sync_add (const VTableIP *vtable, NMDefaultRouteManager *self, guint32 metric)
{
NMDefaultRouteManagerPrivate *priv = NM_DEFAULT_ROUTE_MANAGER_GET_PRIVATE (self);
GPtrArray *entries = vtable->get_entries (priv);
guint i;
Entry *entry_unsynced = NULL;
Entry *entry = NULL;
gboolean success;
/* Find the entries for the given metric.
* The effective metric for synced entries is choosen in a way that it
* is unique (except for G_MAXUINT32, where a clash is not solvable). */
for (i = 0; i < entries->len; i++) {
Entry *e = g_ptr_array_index (entries, i);
if (e->never_default)
continue;
if (e->effective_metric != metric)
continue;
if (e->synced) {
g_assert (!entry || metric == G_MAXUINT32);
if (!entry)
entry = e;
} else
entry_unsynced = e;
}
/* We don't expect to have an unsynced *and* a synced entry for the same metric.
* Unless, (a) their metric is G_MAXUINT32, in which case we could not find an unused effective metric,
* or (b) if we have an unsynced and a synced entry for the same ifindex.
* The latter case happens for example when activating an openvpn connection (synced) and
* assuming the corresponding tun0 interface (unsynced). */
g_assert (!entry || !entry_unsynced || (entry->route.rx.ifindex == entry_unsynced->route.rx.ifindex) || metric == G_MAXUINT32);
/* we only add the route, if we have an (to be synced) entry for it. */
if (!entry)
return FALSE;
if (vtable->vt->is_ip4) {
platform: add self argument to platform functions Most nm_platform_*() functions operate on the platform singleton nm_platform_get(). That made sense because the NMPlatform instance was mainly to hook fake platform for testing. While the implicit argument saved some typing, I think explicit is better. Especially, because NMPlatform could become a more usable object then just a hook for testing. With this change, NMPlatform instances can be used individually, not only as a singleton instance. Before this change, the constructor of NMLinuxPlatform could not call any nm_platform_*() functions because the singleton was not yet initialized. We could only instantiate an incomplete instance, register it via nm_platform_setup(), and then complete initialization via singleton->setup(). With this change, we can create and fully initialize NMPlatform instances before/without setting them up them as singleton. Also, currently there is no clear distinction between functions that operate on the NMPlatform instance, and functions that can be used stand-alone (e.g. nm_platform_ip4_address_to_string()). The latter can not be mocked for testing. With this change, the distinction becomes obvious. That is also useful because it becomes clearer which functions make use of the platform cache and which not. Inside nm-linux-platform.c, continue the pattern that the self instance is named @platform. That makes sense because its type is NMPlatform, and not NMLinuxPlatform what we would expect from a paramter named @self. This is a major diff that causes some pain when rebasing. Try to rebase to the parent commit of this commit as a first step. Then rebase on top of this commit using merge-strategy "ours".
2015-04-18 12:36:09 +02:00
success = nm_platform_ip4_route_add (NM_PLATFORM_GET,
entry->route.rx.ifindex,
entry->route.rx.source,
0,
0,
entry->route.r4.gateway,
0,
entry->effective_metric,
entry->route.rx.mss);
} else {
platform: add self argument to platform functions Most nm_platform_*() functions operate on the platform singleton nm_platform_get(). That made sense because the NMPlatform instance was mainly to hook fake platform for testing. While the implicit argument saved some typing, I think explicit is better. Especially, because NMPlatform could become a more usable object then just a hook for testing. With this change, NMPlatform instances can be used individually, not only as a singleton instance. Before this change, the constructor of NMLinuxPlatform could not call any nm_platform_*() functions because the singleton was not yet initialized. We could only instantiate an incomplete instance, register it via nm_platform_setup(), and then complete initialization via singleton->setup(). With this change, we can create and fully initialize NMPlatform instances before/without setting them up them as singleton. Also, currently there is no clear distinction between functions that operate on the NMPlatform instance, and functions that can be used stand-alone (e.g. nm_platform_ip4_address_to_string()). The latter can not be mocked for testing. With this change, the distinction becomes obvious. That is also useful because it becomes clearer which functions make use of the platform cache and which not. Inside nm-linux-platform.c, continue the pattern that the self instance is named @platform. That makes sense because its type is NMPlatform, and not NMLinuxPlatform what we would expect from a paramter named @self. This is a major diff that causes some pain when rebasing. Try to rebase to the parent commit of this commit as a first step. Then rebase on top of this commit using merge-strategy "ours".
2015-04-18 12:36:09 +02:00
success = nm_platform_ip6_route_add (NM_PLATFORM_GET,
entry->route.rx.ifindex,
entry->route.rx.source,
in6addr_any,
0,
entry->route.r6.gateway,
entry->effective_metric,
entry->route.rx.mss);
}
if (!success) {
_LOGW (vtable->vt->addr_family, "failed to add default route %s with effective metric %u",
vtable->vt->route_to_string (&entry->route), (guint) entry->effective_metric);
}
return TRUE;
}
static gboolean
_platform_route_sync_flush (const VTableIP *vtable, NMDefaultRouteManager *self, int ifindex_to_flush)
{
NMDefaultRouteManagerPrivate *priv = NM_DEFAULT_ROUTE_MANAGER_GET_PRIVATE (self);
GPtrArray *entries = vtable->get_entries (priv);
GArray *routes;
guint i, j;
gboolean changed = FALSE;
/* prune all other default routes from this device. */
routes = vtable->vt->route_get_all (NM_PLATFORM_GET, 0, NM_PLATFORM_GET_ROUTE_FLAGS_WITH_DEFAULT);
for (i = 0; i < routes->len; i++) {
const NMPlatformIPRoute *route;
gboolean has_ifindex_synced = FALSE;
Entry *entry = NULL;
route = _vt_route_index (vtable, routes, i);
2015-02-17 07:09:57 +01:00
/* look at all entries and see if the route for this ifindex pair is
* a known entry. */
for (j = 0; j < entries->len; j++) {
Entry *e = g_ptr_array_index (entries, j);
if ( e->route.rx.ifindex == route->ifindex
&& e->synced) {
has_ifindex_synced = TRUE;
if ( !e->never_default
&& e->effective_metric == route->metric)
entry = e;
}
}
/* we only delete the route if we don't have a matching entry,
* and there is at least one entry that references this ifindex
* (indicating that the ifindex is managed by us -- not assumed).
*
* Otherwise, don't delete the route because it's configured
* externally (and will be assumed -- or already is assumed).
*/
if ( !entry
&& (has_ifindex_synced || ifindex_to_flush == route->ifindex)) {
platform: add self argument to platform functions Most nm_platform_*() functions operate on the platform singleton nm_platform_get(). That made sense because the NMPlatform instance was mainly to hook fake platform for testing. While the implicit argument saved some typing, I think explicit is better. Especially, because NMPlatform could become a more usable object then just a hook for testing. With this change, NMPlatform instances can be used individually, not only as a singleton instance. Before this change, the constructor of NMLinuxPlatform could not call any nm_platform_*() functions because the singleton was not yet initialized. We could only instantiate an incomplete instance, register it via nm_platform_setup(), and then complete initialization via singleton->setup(). With this change, we can create and fully initialize NMPlatform instances before/without setting them up them as singleton. Also, currently there is no clear distinction between functions that operate on the NMPlatform instance, and functions that can be used stand-alone (e.g. nm_platform_ip4_address_to_string()). The latter can not be mocked for testing. With this change, the distinction becomes obvious. That is also useful because it becomes clearer which functions make use of the platform cache and which not. Inside nm-linux-platform.c, continue the pattern that the self instance is named @platform. That makes sense because its type is NMPlatform, and not NMLinuxPlatform what we would expect from a paramter named @self. This is a major diff that causes some pain when rebasing. Try to rebase to the parent commit of this commit as a first step. Then rebase on top of this commit using merge-strategy "ours".
2015-04-18 12:36:09 +02:00
vtable->vt->route_delete_default (NM_PLATFORM_GET, route->ifindex, route->metric);
changed = TRUE;
}
}
g_array_free (routes, TRUE);
return changed;
}
static int
_sort_entries_cmp (gconstpointer a, gconstpointer b, gpointer user_data)
{
guint32 m_a, m_b;
const Entry *e_a = *((const Entry **) a);
const Entry *e_b = *((const Entry **) b);
/* when comparing routes, we consider the (original) metric. */
m_a = e_a->route.rx.metric;
m_b = e_b->route.rx.metric;
/* we normalize route.metric already in _ipx_update_default_route().
* so we can just compare the metrics numerically */
if (m_a != m_b)
return (m_a < m_b) ? -1 : 1;
/* If the metrics are equal, we prefer the one that is !never_default */
if (!!e_a->never_default != !!e_b->never_default)
return e_a->never_default ? 1 : -1;
/* If the metrics are equal, we prefer the one that is assumed (!synced).
* Entries that we sync, can be modified so that only the best
* entry has a (deterministically) lowest metric.
* With assumed devices we cannot increase/change the metric.
* For example: two devices, both metric 0. One is assumed the other is
* synced.
* If we would choose the synced entry as best, we cannot
* increase the metric of the assumed one and we would have non-determinism.
* If we instead prefer the assumed device, we can increase the metric
* of the synced device and the assumed device is (deterministically)
* prefered.
* If both devices are assumed, we also have non-determinism, but also
* we don't reorder either.
*/
if (!!e_a->synced != !!e_b->synced)
return e_a->synced ? 1 : -1;
/* otherwise, do not reorder */
return 0;
}
static GHashTable *
_get_assumed_interface_metrics (const VTableIP *vtable, NMDefaultRouteManager *self, GArray *routes)
{
NMDefaultRouteManagerPrivate *priv = NM_DEFAULT_ROUTE_MANAGER_GET_PRIVATE (self);
GPtrArray *entries;
guint i, j;
GHashTable *result;
/* create a list of all metrics that are currently assigned on an interface
* that is *not* already covered by one of our synced entries.
* IOW, returns the metrics that are in use by assumed interfaces
* that we want to preserve. */
entries = vtable->get_entries (priv);
result = g_hash_table_new (NULL, NULL);
for (i = 0; i < routes->len; i++) {
gboolean ifindex_has_synced_entry = FALSE;
const NMPlatformIPRoute *route;
route = _vt_route_index (vtable, routes, i);
for (j = 0; j < entries->len; j++) {
Entry *e = g_ptr_array_index (entries, j);
if ( e->synced
&& e->route.rx.ifindex == route->ifindex) {
ifindex_has_synced_entry = TRUE;
break;
}
}
if (!ifindex_has_synced_entry)
g_hash_table_add (result, GUINT_TO_POINTER (vtable->vt->metric_normalize (route->metric)));
}
/* also add all non-synced metrics from our entries list. We might have there some metrics that
* we track as non-synced but that are no longer part of platform routes. Anyway, for now
* we still want to treat them as assumed. */
for (i = 0; i < entries->len; i++) {
gboolean ifindex_has_synced_entry = FALSE;
Entry *e_i = g_ptr_array_index (entries, i);
if (e_i->synced)
continue;
for (j = 0; j < entries->len; j++) {
Entry *e_j = g_ptr_array_index (entries, j);
if ( j != i
&& (e_j->synced && e_j->route.rx.ifindex == e_i->route.rx.ifindex)) {
ifindex_has_synced_entry = TRUE;
break;
}
}
if (!ifindex_has_synced_entry)
g_hash_table_add (result, GUINT_TO_POINTER (vtable->vt->metric_normalize (e_i->route.rx.metric)));
}
return result;
}
static int
_sort_metrics_ascending_fcn (gconstpointer a, gconstpointer b)
{
guint32 m_a = *((guint32 *) a);
guint32 m_b = *((guint32 *) b);
if (m_a < m_b)
return -1;
return m_a == m_b ? 0 : 1;
}
static gboolean
_resync_all (const VTableIP *vtable, NMDefaultRouteManager *self, const Entry *changed_entry, const Entry *old_entry, gboolean external_change)
{
NMDefaultRouteManagerPrivate *priv = NM_DEFAULT_ROUTE_MANAGER_GET_PRIVATE (self);
Entry *entry;
guint i, j;
gint64 last_metric = -1;
guint32 expected_metric;
GPtrArray *entries;
GArray *changed_metrics = g_array_new (FALSE, FALSE, sizeof (guint32));
GHashTable *assumed_metrics;
GArray *routes;
gboolean changed = FALSE;
int ifindex_to_flush = 0;
g_assert (priv->resync.guard == 0);
priv->resync.guard++;
if (!external_change) {
if (vtable->vt->is_ip4)
priv->resync.has_v4_changes = FALSE;
else
priv->resync.has_v6_changes = FALSE;
if (!priv->resync.has_v4_changes && !priv->resync.has_v6_changes)
_resync_idle_cancel (self);
}
entries = vtable->get_entries (priv);
routes = vtable->vt->route_get_all (NM_PLATFORM_GET, 0, NM_PLATFORM_GET_ROUTE_FLAGS_WITH_DEFAULT);
assumed_metrics = _get_assumed_interface_metrics (vtable, self, routes);
if (old_entry && old_entry->synced && !old_entry->never_default) {
/* The old version obviously changed. */
g_array_append_val (changed_metrics, old_entry->effective_metric);
}
/* first iterate over all entries and adjust the effective metrics. */
for (i = 0; i < entries->len; i++) {
entry = g_ptr_array_index (entries, i);
g_assert (entry != old_entry);
if (entry->never_default)
continue;
if (!entry->synced) {
gboolean has_synced_entry = FALSE;
/* A non synced entry is completely ignored, if we have
* a synced entry for the same if index.
* Otherwise the metric of the entry is still remembered as
* last_metric to avoid reusing it. */
for (j = 0; j < entries->len; j++) {
const Entry *e = g_ptr_array_index (entries, j);
if ( e->synced
&& e->route.rx.ifindex == entry->route.rx.ifindex) {
has_synced_entry = TRUE;
break;
}
}
if (!has_synced_entry)
last_metric = MAX (last_metric, (gint64) entry->effective_metric);
continue;
}
expected_metric = entry->route.rx.metric;
if ((gint64) expected_metric <= last_metric)
expected_metric = last_metric == G_MAXUINT32 ? G_MAXUINT32 : last_metric + 1;
while ( expected_metric < G_MAXUINT32
&& g_hash_table_contains (assumed_metrics, GUINT_TO_POINTER (expected_metric))) {
gboolean has_metric_for_ifindex = FALSE;
/* Check if there are assumed devices that have default routes with this metric.
* If there are any, we have to pick another effective_metric. */
/* However, if there is a matching route (ifindex+metric) for our current entry, we are done. */
for (j = 0; j < routes->len; j++) {
const NMPlatformIPRoute *r = _vt_route_index (vtable, routes, i);
if ( r->metric == expected_metric
&& r->ifindex == entry->route.rx.ifindex) {
has_metric_for_ifindex = TRUE;
break;
}
}
if (has_metric_for_ifindex)
break;
expected_metric++;
}
if (changed_entry == entry) {
/* for the changed entry, the previous metric was either old_entry->effective_metric,
* or none. Hence, we only have to remember what is going to change. */
g_array_append_val (changed_metrics, expected_metric);
if (old_entry) {
_LOGD (vtable->vt->addr_family, LOG_ENTRY_FMT": sync:update %s (%u -> %u)", LOG_ENTRY_ARGS (i, entry),
vtable->vt->route_to_string (&entry->route), (guint) old_entry->effective_metric,
(guint) expected_metric);
} else {
_LOGD (vtable->vt->addr_family, LOG_ENTRY_FMT": sync:add %s (%u)", LOG_ENTRY_ARGS (i, entry),
vtable->vt->route_to_string (&entry->route), (guint) expected_metric);
}
} else if (entry->effective_metric != expected_metric) {
g_array_append_val (changed_metrics, entry->effective_metric);
g_array_append_val (changed_metrics, expected_metric);
_LOGD (vtable->vt->addr_family, LOG_ENTRY_FMT": sync:metric %s (%u -> %u)", LOG_ENTRY_ARGS (i, entry),
vtable->vt->route_to_string (&entry->route), (guint) entry->effective_metric,
(guint) expected_metric);
} else {
if (!_vt_routes_has_entry (vtable, routes, entry)) {
g_array_append_val (changed_metrics, entry->effective_metric);
_LOGD (vtable->vt->addr_family, LOG_ENTRY_FMT": sync:re-add %s (%u -> %u)", LOG_ENTRY_ARGS (i, entry),
vtable->vt->route_to_string (&entry->route), (guint) entry->effective_metric,
(guint) entry->effective_metric);
}
}
if (entry->effective_metric != expected_metric) {
entry->effective_metric = expected_metric;
changed = TRUE;
}
last_metric = expected_metric;
}
g_array_free (routes, TRUE);
g_array_sort (changed_metrics, _sort_metrics_ascending_fcn);
last_metric = -1;
for (j = 0; j < changed_metrics->len; j++) {
expected_metric = g_array_index (changed_metrics, guint32, j);
if (last_metric == (gint64) expected_metric) {
/* skip duplicates. */
continue;
}
changed |= _platform_route_sync_add (vtable, self, expected_metric);
last_metric = expected_metric;
}
if ( old_entry
&& !changed_entry
&& old_entry->synced
&& !old_entry->never_default) {
/* If we entriely remove an entry that was synced before, we must make
* sure to flush routes for this ifindex too. Otherwise they linger
* around as "assumed" routes */
ifindex_to_flush = old_entry->route.rx.ifindex;
}
changed |= _platform_route_sync_flush (vtable, self, ifindex_to_flush);
g_array_free (changed_metrics, TRUE);
g_hash_table_unref (assumed_metrics);
priv->resync.guard--;
return changed;
}
static void
_entry_at_idx_update (const VTableIP *vtable, NMDefaultRouteManager *self, guint entry_idx, const Entry *old_entry)
{
NMDefaultRouteManagerPrivate *priv = NM_DEFAULT_ROUTE_MANAGER_GET_PRIVATE (self);
Entry *entry;
GPtrArray *entries;
entries = vtable->get_entries (priv);
g_assert (entry_idx < entries->len);
entry = g_ptr_array_index (entries, entry_idx);
g_assert ( !old_entry
|| (entry->source.pointer == old_entry->source.pointer && entry->route.rx.ifindex == old_entry->route.rx.ifindex));
if (!entry->synced && !entry->never_default)
entry->effective_metric = entry->route.rx.metric;
_LOGD (vtable->vt->addr_family, LOG_ENTRY_FMT": %s %s (%"G_GUINT32_FORMAT")",
LOG_ENTRY_ARGS (entry_idx, entry),
old_entry ? "record:update" : "record:add ",
vtable->vt->route_to_string (&entry->route),
entry->effective_metric);
g_ptr_array_sort_with_data (entries, _sort_entries_cmp, NULL);
_resync_all (vtable, self, entry, old_entry, FALSE);
}
static void
_entry_at_idx_remove (const VTableIP *vtable, NMDefaultRouteManager *self, guint entry_idx)
{
NMDefaultRouteManagerPrivate *priv = NM_DEFAULT_ROUTE_MANAGER_GET_PRIVATE (self);
Entry *entry;
GPtrArray *entries;
entries = vtable->get_entries (priv);
g_assert (entry_idx < entries->len);
entry = g_ptr_array_index (entries, entry_idx);
_LOGD (vtable->vt->addr_family, LOG_ENTRY_FMT": record:remove %s (%u)", LOG_ENTRY_ARGS (entry_idx, entry),
vtable->vt->route_to_string (&entry->route), (guint) entry->effective_metric);
/* Remove the entry from the list (but don't free it yet) */
g_ptr_array_index (entries, entry_idx) = NULL;
g_ptr_array_remove_index (entries, entry_idx);
_resync_all (vtable, self, NULL, entry, FALSE);
_entry_free (entry);
}
/***********************************************************************************/
static void
_ipx_update_default_route (const VTableIP *vtable, NMDefaultRouteManager *self, gpointer source)
{
NMDefaultRouteManagerPrivate *priv;
Entry *entry;
guint entry_idx;
const NMPlatformIPRoute *default_route = NULL;
NMPlatformIPXRoute rt;
int ip_ifindex;
GPtrArray *entries;
NMDevice *device = NULL;
NMVpnConnection *vpn = NULL;
gboolean never_default = FALSE;
gboolean synced = FALSE;
g_return_if_fail (NM_IS_DEFAULT_ROUTE_MANAGER (self));
default-route-manager: avoid crash while disposing of NMDefaultRouteManager During dipose(), NMDefaultRouteManager unrefed all the source pointers in its list -- thereby having dangling pointers in the list of entries. The unrefing can cause the final destruction of the device (during shutdown), which would again call into NMDefaultRouteManager. Fix this by ensuring that after disposing starts, all external calls into NMDefaultRouteManager return early. #0 0x00007ffff4a2cc60 in g_logv (log_domain=0x535b51 "NetworkManager", log_level=G_LOG_LEVEL_CRITICAL, format=<optimized out>, args=args@entry=0x7fffffffd530) at gmessages.c:1046 #1 0x00007ffff4a2ce9f in g_log (log_domain=log_domain@entry=0x535b51 "NetworkManager", log_level=log_level@entry=G_LOG_LEVEL_CRITICAL, format=format@entry=0x528f68 "file %s: line %d (%s): should not be reached") at gmessages.c:1079 #2 0x000000000049b83b in _ipx_update_default_route (vtable=vtable@entry=0x7a49c0 <vtable_ip6>, self=0x7d1350 [NMDefaultRouteManager], source=source@entry=0x8b64e0) at nm-default-route-manager.c:659 #3 0x000000000049c652 in nm_default_route_manager_ip6_update_default_route (self=<optimized out>, source=source@entry=0x8b64e0) at nm-default-route-manager.c:819 #4 0x00000000004526a8 in _cleanup_generic_post (self=self@entry=0x8b64e0, deconfigure=deconfigure@entry=0) at devices/nm-device.c:7235 #5 0x0000000000452bc0 in dispose (object=0x8b64e0) at devices/nm-device.c:8324 #6 0x00007ffff4d29cbc in g_object_unref (_object=0x8b64e0) at gobject.c:3133 #7 0x0000000000499091 in _entry_free (entry=0x8bf140) at nm-default-route-manager.c:187 #8 0x00007ffff49fa82b in g_ptr_array_foreach (array=0x81d220, func=0x499080 <_entry_free>, user_data=0x0) at garray.c:1502 #9 0x00007ffff49fa8c0 in ptr_array_free (array=0x81d220, flags=FREE_SEGMENT) at garray.c:1088 #10 0x00007ffff49fa939 in g_ptr_array_free (array=<optimized out>, free_segment=free_segment@entry=1) at garray.c:1075 #11 0x000000000049abcf in dispose (object=0x7d1350 [NMDefaultRouteManager]) at nm-default-route-manager.c:1357 #12 0x00007ffff4d29cbc in g_object_unref (_object=0x7d1350) at gobject.c:3133 #13 0x00007ffff7deb507 in _dl_fini () at dl-fini.c:252 #14 0x00007ffff4658382 in __run_exit_handlers (status=status@entry=0, listp=0x7ffff49d66a0 <__exit_funcs>, run_list_atexit=run_list_atexit@entry=true) at exit.c:82 #15 0x00007ffff46583d5 in __GI_exit (status=status@entry=0) at exit.c:104 #16 0x0000000000432fd6 in main (argc=1, argv=0x7fffffffdeb8) at main.c:473
2015-02-17 07:53:24 +01:00
priv = NM_DEFAULT_ROUTE_MANAGER_GET_PRIVATE (self);
if (priv->disposed)
return;
if (NM_IS_DEVICE (source))
device = source;
else if (NM_IS_VPN_CONNECTION (source))
vpn = source;
else
g_return_if_reached ();
if (device)
ip_ifindex = nm_device_get_ip_ifindex (device);
else {
ip_ifindex = nm_vpn_connection_get_ip_ifindex (vpn);
if (ip_ifindex <= 0) {
NMDevice *parent = nm_active_connection_get_device (NM_ACTIVE_CONNECTION (vpn));
if (parent)
ip_ifindex = nm_device_get_ip_ifindex (parent);
}
}
entries = vtable->get_entries (priv);
entry = _entry_find_by_source (entries, source, &entry_idx);
if ( entry
&& entry->route.rx.ifindex != ip_ifindex) {
/* Strange... the ifindex changed... Remove the device and start again. */
_LOGD (vtable->vt->addr_family, "ifindex of "LOG_ENTRY_FMT" changed: %d -> %d",
LOG_ENTRY_ARGS (entry_idx, entry),
entry->route.rx.ifindex, ip_ifindex);
g_object_freeze_notify (G_OBJECT (self));
_entry_at_idx_remove (vtable, self, entry_idx);
g_assert (!_entry_find_by_source (entries, source, NULL));
_ipx_update_default_route (vtable, self, source);
g_object_thaw_notify (G_OBJECT (self));
return;
}
/* get the @default_route from the device. */
if (ip_ifindex > 0) {
if (device) {
gboolean is_assumed;
if (vtable->vt->is_ip4)
default_route = (const NMPlatformIPRoute *) nm_device_get_ip4_default_route (device, &is_assumed);
else
default_route = (const NMPlatformIPRoute *) nm_device_get_ip6_default_route (device, &is_assumed);
if (!default_route && !is_assumed) {
/* the device has no default route, but it is not assumed. That means, NMDefaultRouteManager
* enforces that the device has no default route.
*
* Hence we have to keep track of this entry, otherwise a missing entry tells us
* that the interface is assumed and NM would not remove the default routes on
* the device. */
memset (&rt, 0, sizeof (rt));
rt.rx.ifindex = ip_ifindex;
rt.rx.source = NM_IP_CONFIG_SOURCE_UNKNOWN;
rt.rx.metric = G_MAXUINT32;
default_route = &rt.rx;
never_default = TRUE;
synced = TRUE;
} else
synced = default_route && !is_assumed;
} else {
NMConnection *connection = nm_active_connection_get_connection ((NMActiveConnection *) vpn);
if ( connection
&& nm_vpn_connection_get_vpn_state (vpn) == NM_VPN_CONNECTION_STATE_ACTIVATED) {
memset (&rt, 0, sizeof (rt));
if (vtable->vt->is_ip4) {
NMIP4Config *vpn_config;
vpn_config = nm_vpn_connection_get_ip4_config (vpn);
if (vpn_config) {
never_default = nm_ip4_config_get_never_default (vpn_config);
rt.r4.ifindex = ip_ifindex;
rt.r4.source = NM_IP_CONFIG_SOURCE_VPN;
rt.r4.gateway = nm_vpn_connection_get_ip4_internal_gateway (vpn);
rt.r4.metric = nm_vpn_connection_get_ip4_route_metric (vpn);
rt.r4.mss = nm_ip4_config_get_mss (vpn_config);
default_route = &rt.rx;
}
} else {
NMIP6Config *vpn_config;
vpn_config = nm_vpn_connection_get_ip6_config (vpn);
if (vpn_config) {
const struct in6_addr *int_gw = nm_vpn_connection_get_ip6_internal_gateway (vpn);
never_default = nm_ip6_config_get_never_default (vpn_config);
rt.r6.ifindex = ip_ifindex;
rt.r6.source = NM_IP_CONFIG_SOURCE_VPN;
rt.r6.gateway = int_gw ? *int_gw : in6addr_any;
rt.r6.metric = nm_vpn_connection_get_ip6_route_metric (vpn);
rt.r6.mss = nm_ip6_config_get_mss (vpn_config);
default_route = &rt.rx;
}
}
}
synced = TRUE;
}
}
g_assert (!default_route || default_route->plen == 0);
if (!entry && !default_route)
/* nothing to do */;
else if (!entry) {
/* add */
entry = g_slice_new0 (Entry);
entry->source.object = g_object_ref (source);
if (vtable->vt->is_ip4)
entry->route.r4 = *((const NMPlatformIP4Route *) default_route);
else
entry->route.r6 = *((const NMPlatformIP6Route *) default_route);
/* only use normalized metrics */
entry->route.rx.metric = vtable->vt->metric_normalize (entry->route.rx.metric);
entry->route.rx.ifindex = ip_ifindex;
entry->never_default = never_default;
entry->effective_metric = entry->route.rx.metric;
entry->synced = synced;
g_ptr_array_add (entries, entry);
_entry_at_idx_update (vtable, self, entries->len - 1, NULL);
} else if (default_route) {
/* update */
Entry old_entry, new_entry;
new_entry = *entry;
if (vtable->vt->is_ip4)
new_entry.route.r4 = *((const NMPlatformIP4Route *) default_route);
else
new_entry.route.r6 = *((const NMPlatformIP6Route *) default_route);
/* only use normalized metrics */
new_entry.route.rx.metric = vtable->vt->metric_normalize (new_entry.route.rx.metric);
new_entry.route.rx.ifindex = ip_ifindex;
new_entry.never_default = never_default;
new_entry.synced = synced;
if (memcmp (entry, &new_entry, sizeof (new_entry)) == 0)
return;
old_entry = *entry;
*entry = new_entry;
_entry_at_idx_update (vtable, self, entry_idx, &old_entry);
} else {
/* delete */
_entry_at_idx_remove (vtable, self, entry_idx);
}
}
void
nm_default_route_manager_ip4_update_default_route (NMDefaultRouteManager *self, gpointer source)
{
_ipx_update_default_route (&vtable_ip4, self, source);
}
void
nm_default_route_manager_ip6_update_default_route (NMDefaultRouteManager *self, gpointer source)
{
_ipx_update_default_route (&vtable_ip6, self, source);
}
/***********************************************************************************/
static gboolean
_ipx_connection_has_default_route (const VTableIP *vtable, NMDefaultRouteManager *self, NMConnection *connection, gboolean *out_is_never_default)
{
const char *method;
NMSettingIPConfig *s_ip;
gboolean is_never_default = FALSE;
gboolean has_default_route = FALSE;
g_return_val_if_fail (NM_IS_DEFAULT_ROUTE_MANAGER (self), FALSE);
if (!connection)
goto out;
if (vtable->vt->is_ip4)
s_ip = nm_connection_get_setting_ip4_config (connection);
else
s_ip = nm_connection_get_setting_ip6_config (connection);
if (!s_ip)
goto out;
if (nm_setting_ip_config_get_never_default (s_ip)) {
is_never_default = TRUE;
goto out;
}
if (vtable->vt->is_ip4) {
method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP4_CONFIG);
if ( !method
|| !strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_DISABLED)
|| !strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_LINK_LOCAL))
goto out;
} else {
method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP6_CONFIG);
if ( !method
|| !strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_IGNORE)
|| !strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL))
goto out;
}
has_default_route = TRUE;
out:
if (out_is_never_default)
*out_is_never_default = is_never_default;
return has_default_route;
}
gboolean
nm_default_route_manager_ip4_connection_has_default_route (NMDefaultRouteManager *self, NMConnection *connection, gboolean *out_is_never_default)
{
return _ipx_connection_has_default_route (&vtable_ip4, self, connection, out_is_never_default);
}
gboolean
nm_default_route_manager_ip6_connection_has_default_route (NMDefaultRouteManager *self, NMConnection *connection, gboolean *out_is_never_default)
{
return _ipx_connection_has_default_route (&vtable_ip6, self, connection, out_is_never_default);
}
/***********************************************************************************/
static NMDevice *
_ipx_get_best_device (const VTableIP *vtable, NMDefaultRouteManager *self, const GSList *devices)
{
NMDefaultRouteManagerPrivate *priv;
GPtrArray *entries;
guint i;
g_return_val_if_fail (NM_IS_DEFAULT_ROUTE_MANAGER (self), NULL);
if (!devices)
return NULL;
priv = NM_DEFAULT_ROUTE_MANAGER_GET_PRIVATE (self);
default-route-manager: avoid crash while disposing of NMDefaultRouteManager During dipose(), NMDefaultRouteManager unrefed all the source pointers in its list -- thereby having dangling pointers in the list of entries. The unrefing can cause the final destruction of the device (during shutdown), which would again call into NMDefaultRouteManager. Fix this by ensuring that after disposing starts, all external calls into NMDefaultRouteManager return early. #0 0x00007ffff4a2cc60 in g_logv (log_domain=0x535b51 "NetworkManager", log_level=G_LOG_LEVEL_CRITICAL, format=<optimized out>, args=args@entry=0x7fffffffd530) at gmessages.c:1046 #1 0x00007ffff4a2ce9f in g_log (log_domain=log_domain@entry=0x535b51 "NetworkManager", log_level=log_level@entry=G_LOG_LEVEL_CRITICAL, format=format@entry=0x528f68 "file %s: line %d (%s): should not be reached") at gmessages.c:1079 #2 0x000000000049b83b in _ipx_update_default_route (vtable=vtable@entry=0x7a49c0 <vtable_ip6>, self=0x7d1350 [NMDefaultRouteManager], source=source@entry=0x8b64e0) at nm-default-route-manager.c:659 #3 0x000000000049c652 in nm_default_route_manager_ip6_update_default_route (self=<optimized out>, source=source@entry=0x8b64e0) at nm-default-route-manager.c:819 #4 0x00000000004526a8 in _cleanup_generic_post (self=self@entry=0x8b64e0, deconfigure=deconfigure@entry=0) at devices/nm-device.c:7235 #5 0x0000000000452bc0 in dispose (object=0x8b64e0) at devices/nm-device.c:8324 #6 0x00007ffff4d29cbc in g_object_unref (_object=0x8b64e0) at gobject.c:3133 #7 0x0000000000499091 in _entry_free (entry=0x8bf140) at nm-default-route-manager.c:187 #8 0x00007ffff49fa82b in g_ptr_array_foreach (array=0x81d220, func=0x499080 <_entry_free>, user_data=0x0) at garray.c:1502 #9 0x00007ffff49fa8c0 in ptr_array_free (array=0x81d220, flags=FREE_SEGMENT) at garray.c:1088 #10 0x00007ffff49fa939 in g_ptr_array_free (array=<optimized out>, free_segment=free_segment@entry=1) at garray.c:1075 #11 0x000000000049abcf in dispose (object=0x7d1350 [NMDefaultRouteManager]) at nm-default-route-manager.c:1357 #12 0x00007ffff4d29cbc in g_object_unref (_object=0x7d1350) at gobject.c:3133 #13 0x00007ffff7deb507 in _dl_fini () at dl-fini.c:252 #14 0x00007ffff4658382 in __run_exit_handlers (status=status@entry=0, listp=0x7ffff49d66a0 <__exit_funcs>, run_list_atexit=run_list_atexit@entry=true) at exit.c:82 #15 0x00007ffff46583d5 in __GI_exit (status=status@entry=0) at exit.c:104 #16 0x0000000000432fd6 in main (argc=1, argv=0x7fffffffdeb8) at main.c:473
2015-02-17 07:53:24 +01:00
if (priv->disposed)
return NULL;
entries = vtable->get_entries (priv);
for (i = 0; i < entries->len; i++) {
Entry *entry = g_ptr_array_index (entries, i);
default-route: don't return devices without active connection as best_config() or best_device() Since 0c136c1e2, we also track the default route for devices without active-connection (unmanaged). They must not be returned as best_config() or as best_device(), because the caller don't expect unmanaged devices here. This also gets closer to the original behavior of get_best_device() before merging default-route-manager in d4417e34606b1f08c361709ff9567055a1d6859e where we also would ignore devices depending on the state. This fixes an assertion when having an interface unmanaged and activating it externally: #0 0x00007ffff6806187 in __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:55 #1 0x00007ffff6807dea in __GI_abort () at abort.c:89 #2 0x00007ffff6c0aec5 in g_assertion_message (domain=domain@entry=0x5332d5 "NetworkManager", file=file@entry=0x539460 "nm-default-route-manager.c", line=line@entry=1056, func=func@entry=0x539920 <__FUNCTION__.28934> "_ipx_get_best_config", message=message@entry=0x8c9cb0 "assertion failed: (req)") at gtestutils.c:2356 #3 0x00007ffff6c0af5a in g_assertion_message_expr (domain=domain@entry=0x5332d5 "NetworkManager", file=file@entry=0x539460 "nm-default-route-manager.c", line=line@entry=1056, func=func@entry=0x539920 <__FUNCTION__.28934> "_ipx_get_best_config", expr=expr@entry=0x53132f "req") at gtestutils.c:2371 #4 0x000000000049a196 in _ipx_get_best_config (self=<optimized out>, ignore_never_default=ignore_never_default@entry=1, out_ip_iface=out_ip_iface@entry=0x7fffffffd310, out_ac=out_ac@entry=0x0, out_device=0x0, out_vpn=0x7fffffffd318, vtable=0x7a0a00 <vtable_ip4>, vtable=0x7a0a00 <vtable_ip4>) at nm-default-route-manager.c:1056 #5 0x000000000049a3f6 in nm_default_route_manager_ip4_get_best_config (self=<optimized out>, ignore_never_default=ignore_never_default@entry=1, out_ip_iface=out_ip_iface@entry=0x7fffffffd310, out_ac=out_ac@entry=0x0, out_device=out_device@entry=0x0, out_vpn=out_vpn@entry=0x7fffffffd318) at nm-default-route-manager.c:1079 #6 0x00000000004b7518 in update_ip4_dns (self=0x7ecac0 [NMPolicy], out_vpn=0x7fffffffd318, out_device=0x0, out_ac=0x0, out_ip_iface=0x7fffffffd310, ignore_never_default=1) at nm-policy.c:390 #7 0x00000000004b7518 in update_ip4_dns (dns_mgr=0x8623a0 [NMDnsManager], policy=0x7ecac0 [NMPolicy]) at nm-policy.c:406 #8 0x00000000004b99d5 in device_ip4_config_changed (device=0x8844b0 [NMDeviceEthernet], new_config=0x908aa0 [NMIP4Config], old_config=0x908aa0 [NMIP4Config], user_data=0x7ecac0) at nm-policy.c:1260 #9 0x000000368f405d60 in ffi_call_unix64 () at /lib64/libffi.so.6 #10 0x000000368f4057d1 in ffi_call () at /lib64/libffi.so.6 #11 0x00007ffff6ee5b8c in g_cclosure_marshal_generic_va (closure=0x886a40, return_value=0x0, instance=0x8844b0, args_list=<optimized out>, marshal_data=0x0, n_params=2, param_types=0x87d3a0) at gclosure.c:1541 #12 0x00007ffff6ee5144 in _g_closure_invoke_va (closure=closure@entry=0x886a40, return_value=return_value@entry=0x0, instance=instance@entry=0x8844b0, args=args@entry=0x7fffffffd810, n_params=<optimized out>, param_types=0x87d3a0) at gclosure.c:831 #13 0x00007ffff6eff900 in g_signal_emit_valist (instance=0x8844b0, signal_id=<optimized out>, detail=0, var_args=var_args@entry=0x7fffffffd810) at gsignal.c:3201 #14 0x00007ffff6f0014f in g_signal_emit (instance=instance@entry=0x8844b0, signal_id=<optimized out>, detail=detail@entry=0) at gsignal.c:3348 #15 0x000000000044a1f4 in nm_device_set_ip4_config (self=self@entry=0x8844b0 [NMDeviceEthernet], new_config=new_config@entry=0x9089a0 [NMIP4Config], default_route_metric=default_route_metric@entry=100, commit=commit@entry=0, reason=reason@entry=0x0) at devices/nm-device.c:5866 #16 0x000000000044a5af in ip4_config_merge_and_apply (self=self@entry=0x8844b0 [NMDeviceEthernet], config=config@entry=0x0, commit=commit@entry=0, out_reason=out_reason@entry=0x0) at devices/nm-device.c:3037 #17 0x000000000044b4fd in update_ip_config (self=self@entry=0x8844b0 [NMDeviceEthernet], initial=initial@entry=0) at devices/nm-device.c:6617 #18 0x000000000044bc39 in queued_ip_config_change (user_data=<optimized out>) at devices/nm-device.c:6688 #19 0x00007ffff6be4e1b in g_main_context_dispatch (context=0x7ba3a0) at gmain.c:3122 #20 0x00007ffff6be4e1b in g_main_context_dispatch (context=context@entry=0x7ba3a0) at gmain.c:3737 #21 0x00007ffff6be51b0 in g_main_context_iterate (context=0x7ba3a0, block=block@entry=1, dispatch=dispatch@entry=1, self=<optimized out>) at gmain.c:3808 #22 0x00007ffff6be54d2 in g_main_loop_run (loop=0x7ba460) at gmain.c:4002 #23 0x000000000043291d in main (argc=1, argv=0x7fffffffdef8) at main.c:442 Fixes: da708059dabb2854d11eed1a403398327b31535b
2015-01-26 15:03:48 +01:00
NMDeviceState state;
if (!NM_IS_DEVICE (entry->source.pointer))
continue;
if (entry->never_default)
continue;
default-route: don't return devices without active connection as best_config() or best_device() Since 0c136c1e2, we also track the default route for devices without active-connection (unmanaged). They must not be returned as best_config() or as best_device(), because the caller don't expect unmanaged devices here. This also gets closer to the original behavior of get_best_device() before merging default-route-manager in d4417e34606b1f08c361709ff9567055a1d6859e where we also would ignore devices depending on the state. This fixes an assertion when having an interface unmanaged and activating it externally: #0 0x00007ffff6806187 in __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:55 #1 0x00007ffff6807dea in __GI_abort () at abort.c:89 #2 0x00007ffff6c0aec5 in g_assertion_message (domain=domain@entry=0x5332d5 "NetworkManager", file=file@entry=0x539460 "nm-default-route-manager.c", line=line@entry=1056, func=func@entry=0x539920 <__FUNCTION__.28934> "_ipx_get_best_config", message=message@entry=0x8c9cb0 "assertion failed: (req)") at gtestutils.c:2356 #3 0x00007ffff6c0af5a in g_assertion_message_expr (domain=domain@entry=0x5332d5 "NetworkManager", file=file@entry=0x539460 "nm-default-route-manager.c", line=line@entry=1056, func=func@entry=0x539920 <__FUNCTION__.28934> "_ipx_get_best_config", expr=expr@entry=0x53132f "req") at gtestutils.c:2371 #4 0x000000000049a196 in _ipx_get_best_config (self=<optimized out>, ignore_never_default=ignore_never_default@entry=1, out_ip_iface=out_ip_iface@entry=0x7fffffffd310, out_ac=out_ac@entry=0x0, out_device=0x0, out_vpn=0x7fffffffd318, vtable=0x7a0a00 <vtable_ip4>, vtable=0x7a0a00 <vtable_ip4>) at nm-default-route-manager.c:1056 #5 0x000000000049a3f6 in nm_default_route_manager_ip4_get_best_config (self=<optimized out>, ignore_never_default=ignore_never_default@entry=1, out_ip_iface=out_ip_iface@entry=0x7fffffffd310, out_ac=out_ac@entry=0x0, out_device=out_device@entry=0x0, out_vpn=out_vpn@entry=0x7fffffffd318) at nm-default-route-manager.c:1079 #6 0x00000000004b7518 in update_ip4_dns (self=0x7ecac0 [NMPolicy], out_vpn=0x7fffffffd318, out_device=0x0, out_ac=0x0, out_ip_iface=0x7fffffffd310, ignore_never_default=1) at nm-policy.c:390 #7 0x00000000004b7518 in update_ip4_dns (dns_mgr=0x8623a0 [NMDnsManager], policy=0x7ecac0 [NMPolicy]) at nm-policy.c:406 #8 0x00000000004b99d5 in device_ip4_config_changed (device=0x8844b0 [NMDeviceEthernet], new_config=0x908aa0 [NMIP4Config], old_config=0x908aa0 [NMIP4Config], user_data=0x7ecac0) at nm-policy.c:1260 #9 0x000000368f405d60 in ffi_call_unix64 () at /lib64/libffi.so.6 #10 0x000000368f4057d1 in ffi_call () at /lib64/libffi.so.6 #11 0x00007ffff6ee5b8c in g_cclosure_marshal_generic_va (closure=0x886a40, return_value=0x0, instance=0x8844b0, args_list=<optimized out>, marshal_data=0x0, n_params=2, param_types=0x87d3a0) at gclosure.c:1541 #12 0x00007ffff6ee5144 in _g_closure_invoke_va (closure=closure@entry=0x886a40, return_value=return_value@entry=0x0, instance=instance@entry=0x8844b0, args=args@entry=0x7fffffffd810, n_params=<optimized out>, param_types=0x87d3a0) at gclosure.c:831 #13 0x00007ffff6eff900 in g_signal_emit_valist (instance=0x8844b0, signal_id=<optimized out>, detail=0, var_args=var_args@entry=0x7fffffffd810) at gsignal.c:3201 #14 0x00007ffff6f0014f in g_signal_emit (instance=instance@entry=0x8844b0, signal_id=<optimized out>, detail=detail@entry=0) at gsignal.c:3348 #15 0x000000000044a1f4 in nm_device_set_ip4_config (self=self@entry=0x8844b0 [NMDeviceEthernet], new_config=new_config@entry=0x9089a0 [NMIP4Config], default_route_metric=default_route_metric@entry=100, commit=commit@entry=0, reason=reason@entry=0x0) at devices/nm-device.c:5866 #16 0x000000000044a5af in ip4_config_merge_and_apply (self=self@entry=0x8844b0 [NMDeviceEthernet], config=config@entry=0x0, commit=commit@entry=0, out_reason=out_reason@entry=0x0) at devices/nm-device.c:3037 #17 0x000000000044b4fd in update_ip_config (self=self@entry=0x8844b0 [NMDeviceEthernet], initial=initial@entry=0) at devices/nm-device.c:6617 #18 0x000000000044bc39 in queued_ip_config_change (user_data=<optimized out>) at devices/nm-device.c:6688 #19 0x00007ffff6be4e1b in g_main_context_dispatch (context=0x7ba3a0) at gmain.c:3122 #20 0x00007ffff6be4e1b in g_main_context_dispatch (context=context@entry=0x7ba3a0) at gmain.c:3737 #21 0x00007ffff6be51b0 in g_main_context_iterate (context=0x7ba3a0, block=block@entry=1, dispatch=dispatch@entry=1, self=<optimized out>) at gmain.c:3808 #22 0x00007ffff6be54d2 in g_main_loop_run (loop=0x7ba460) at gmain.c:4002 #23 0x000000000043291d in main (argc=1, argv=0x7fffffffdef8) at main.c:442 Fixes: da708059dabb2854d11eed1a403398327b31535b
2015-01-26 15:03:48 +01:00
state = nm_device_get_state (entry->source.device);
if ( state <= NM_DEVICE_STATE_DISCONNECTED
|| state >= NM_DEVICE_STATE_DEACTIVATING) {
/* FIXME: we also track unmanaged devices with assumed default routes.
* Skip them, they are (currently) no candidates for best-device.
*
* Later we also want to properly assume connections for unmanaged devices.
*
* Also, we don't want to have DEACTIVATING devices returned as best_device(). */
continue;
}
if (g_slist_find ((GSList *) devices, entry->source.device)) {
g_return_val_if_fail (nm_device_get_act_request (entry->source.pointer), entry->source.pointer);
return entry->source.pointer;
default-route: don't return devices without active connection as best_config() or best_device() Since 0c136c1e2, we also track the default route for devices without active-connection (unmanaged). They must not be returned as best_config() or as best_device(), because the caller don't expect unmanaged devices here. This also gets closer to the original behavior of get_best_device() before merging default-route-manager in d4417e34606b1f08c361709ff9567055a1d6859e where we also would ignore devices depending on the state. This fixes an assertion when having an interface unmanaged and activating it externally: #0 0x00007ffff6806187 in __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:55 #1 0x00007ffff6807dea in __GI_abort () at abort.c:89 #2 0x00007ffff6c0aec5 in g_assertion_message (domain=domain@entry=0x5332d5 "NetworkManager", file=file@entry=0x539460 "nm-default-route-manager.c", line=line@entry=1056, func=func@entry=0x539920 <__FUNCTION__.28934> "_ipx_get_best_config", message=message@entry=0x8c9cb0 "assertion failed: (req)") at gtestutils.c:2356 #3 0x00007ffff6c0af5a in g_assertion_message_expr (domain=domain@entry=0x5332d5 "NetworkManager", file=file@entry=0x539460 "nm-default-route-manager.c", line=line@entry=1056, func=func@entry=0x539920 <__FUNCTION__.28934> "_ipx_get_best_config", expr=expr@entry=0x53132f "req") at gtestutils.c:2371 #4 0x000000000049a196 in _ipx_get_best_config (self=<optimized out>, ignore_never_default=ignore_never_default@entry=1, out_ip_iface=out_ip_iface@entry=0x7fffffffd310, out_ac=out_ac@entry=0x0, out_device=0x0, out_vpn=0x7fffffffd318, vtable=0x7a0a00 <vtable_ip4>, vtable=0x7a0a00 <vtable_ip4>) at nm-default-route-manager.c:1056 #5 0x000000000049a3f6 in nm_default_route_manager_ip4_get_best_config (self=<optimized out>, ignore_never_default=ignore_never_default@entry=1, out_ip_iface=out_ip_iface@entry=0x7fffffffd310, out_ac=out_ac@entry=0x0, out_device=out_device@entry=0x0, out_vpn=out_vpn@entry=0x7fffffffd318) at nm-default-route-manager.c:1079 #6 0x00000000004b7518 in update_ip4_dns (self=0x7ecac0 [NMPolicy], out_vpn=0x7fffffffd318, out_device=0x0, out_ac=0x0, out_ip_iface=0x7fffffffd310, ignore_never_default=1) at nm-policy.c:390 #7 0x00000000004b7518 in update_ip4_dns (dns_mgr=0x8623a0 [NMDnsManager], policy=0x7ecac0 [NMPolicy]) at nm-policy.c:406 #8 0x00000000004b99d5 in device_ip4_config_changed (device=0x8844b0 [NMDeviceEthernet], new_config=0x908aa0 [NMIP4Config], old_config=0x908aa0 [NMIP4Config], user_data=0x7ecac0) at nm-policy.c:1260 #9 0x000000368f405d60 in ffi_call_unix64 () at /lib64/libffi.so.6 #10 0x000000368f4057d1 in ffi_call () at /lib64/libffi.so.6 #11 0x00007ffff6ee5b8c in g_cclosure_marshal_generic_va (closure=0x886a40, return_value=0x0, instance=0x8844b0, args_list=<optimized out>, marshal_data=0x0, n_params=2, param_types=0x87d3a0) at gclosure.c:1541 #12 0x00007ffff6ee5144 in _g_closure_invoke_va (closure=closure@entry=0x886a40, return_value=return_value@entry=0x0, instance=instance@entry=0x8844b0, args=args@entry=0x7fffffffd810, n_params=<optimized out>, param_types=0x87d3a0) at gclosure.c:831 #13 0x00007ffff6eff900 in g_signal_emit_valist (instance=0x8844b0, signal_id=<optimized out>, detail=0, var_args=var_args@entry=0x7fffffffd810) at gsignal.c:3201 #14 0x00007ffff6f0014f in g_signal_emit (instance=instance@entry=0x8844b0, signal_id=<optimized out>, detail=detail@entry=0) at gsignal.c:3348 #15 0x000000000044a1f4 in nm_device_set_ip4_config (self=self@entry=0x8844b0 [NMDeviceEthernet], new_config=new_config@entry=0x9089a0 [NMIP4Config], default_route_metric=default_route_metric@entry=100, commit=commit@entry=0, reason=reason@entry=0x0) at devices/nm-device.c:5866 #16 0x000000000044a5af in ip4_config_merge_and_apply (self=self@entry=0x8844b0 [NMDeviceEthernet], config=config@entry=0x0, commit=commit@entry=0, out_reason=out_reason@entry=0x0) at devices/nm-device.c:3037 #17 0x000000000044b4fd in update_ip_config (self=self@entry=0x8844b0 [NMDeviceEthernet], initial=initial@entry=0) at devices/nm-device.c:6617 #18 0x000000000044bc39 in queued_ip_config_change (user_data=<optimized out>) at devices/nm-device.c:6688 #19 0x00007ffff6be4e1b in g_main_context_dispatch (context=0x7ba3a0) at gmain.c:3122 #20 0x00007ffff6be4e1b in g_main_context_dispatch (context=context@entry=0x7ba3a0) at gmain.c:3737 #21 0x00007ffff6be51b0 in g_main_context_iterate (context=0x7ba3a0, block=block@entry=1, dispatch=dispatch@entry=1, self=<optimized out>) at gmain.c:3808 #22 0x00007ffff6be54d2 in g_main_loop_run (loop=0x7ba460) at gmain.c:4002 #23 0x000000000043291d in main (argc=1, argv=0x7fffffffdef8) at main.c:442 Fixes: da708059dabb2854d11eed1a403398327b31535b
2015-01-26 15:03:48 +01:00
}
}
return NULL;
}
/** _ipx_get_best_activating_device:
* @vtable: the virtual table
* @self: #NMDefaultRouteManager
* @devices: list of devices to be searched. Only devices from this list will be considered
* @fully_activated: if #TRUE, only search for devices that are fully activated. Otherwise,
* search if there is a best device going to be activated. In the latter case, this will
* return NULL if the best device is already activated.
* @preferred_device: if not-NULL, this device is preferred if there are more devices with
* the same priority.
**/
static NMDevice *
_ipx_get_best_activating_device (const VTableIP *vtable, NMDefaultRouteManager *self, const GSList *devices, NMDevice *preferred_device)
{
NMDefaultRouteManagerPrivate *priv;
const GSList *iter;
NMDevice *best_device = NULL;
guint32 best_prio = G_MAXUINT32;
NMDevice *best_activated_device;
g_return_val_if_fail (NM_IS_DEFAULT_ROUTE_MANAGER (self), NULL);
priv = NM_DEFAULT_ROUTE_MANAGER_GET_PRIVATE (self);
default-route-manager: avoid crash while disposing of NMDefaultRouteManager During dipose(), NMDefaultRouteManager unrefed all the source pointers in its list -- thereby having dangling pointers in the list of entries. The unrefing can cause the final destruction of the device (during shutdown), which would again call into NMDefaultRouteManager. Fix this by ensuring that after disposing starts, all external calls into NMDefaultRouteManager return early. #0 0x00007ffff4a2cc60 in g_logv (log_domain=0x535b51 "NetworkManager", log_level=G_LOG_LEVEL_CRITICAL, format=<optimized out>, args=args@entry=0x7fffffffd530) at gmessages.c:1046 #1 0x00007ffff4a2ce9f in g_log (log_domain=log_domain@entry=0x535b51 "NetworkManager", log_level=log_level@entry=G_LOG_LEVEL_CRITICAL, format=format@entry=0x528f68 "file %s: line %d (%s): should not be reached") at gmessages.c:1079 #2 0x000000000049b83b in _ipx_update_default_route (vtable=vtable@entry=0x7a49c0 <vtable_ip6>, self=0x7d1350 [NMDefaultRouteManager], source=source@entry=0x8b64e0) at nm-default-route-manager.c:659 #3 0x000000000049c652 in nm_default_route_manager_ip6_update_default_route (self=<optimized out>, source=source@entry=0x8b64e0) at nm-default-route-manager.c:819 #4 0x00000000004526a8 in _cleanup_generic_post (self=self@entry=0x8b64e0, deconfigure=deconfigure@entry=0) at devices/nm-device.c:7235 #5 0x0000000000452bc0 in dispose (object=0x8b64e0) at devices/nm-device.c:8324 #6 0x00007ffff4d29cbc in g_object_unref (_object=0x8b64e0) at gobject.c:3133 #7 0x0000000000499091 in _entry_free (entry=0x8bf140) at nm-default-route-manager.c:187 #8 0x00007ffff49fa82b in g_ptr_array_foreach (array=0x81d220, func=0x499080 <_entry_free>, user_data=0x0) at garray.c:1502 #9 0x00007ffff49fa8c0 in ptr_array_free (array=0x81d220, flags=FREE_SEGMENT) at garray.c:1088 #10 0x00007ffff49fa939 in g_ptr_array_free (array=<optimized out>, free_segment=free_segment@entry=1) at garray.c:1075 #11 0x000000000049abcf in dispose (object=0x7d1350 [NMDefaultRouteManager]) at nm-default-route-manager.c:1357 #12 0x00007ffff4d29cbc in g_object_unref (_object=0x7d1350) at gobject.c:3133 #13 0x00007ffff7deb507 in _dl_fini () at dl-fini.c:252 #14 0x00007ffff4658382 in __run_exit_handlers (status=status@entry=0, listp=0x7ffff49d66a0 <__exit_funcs>, run_list_atexit=run_list_atexit@entry=true) at exit.c:82 #15 0x00007ffff46583d5 in __GI_exit (status=status@entry=0) at exit.c:104 #16 0x0000000000432fd6 in main (argc=1, argv=0x7fffffffdeb8) at main.c:473
2015-02-17 07:53:24 +01:00
if (priv->disposed)
return NULL;
best_activated_device = _ipx_get_best_device (vtable, self, devices);
for (iter = devices; iter; iter = g_slist_next (iter)) {
NMDevice *device = NM_DEVICE (iter->data);
guint32 prio;
Entry *entry;
entry = _entry_find_by_source (vtable->get_entries (priv), device, NULL);
if (entry) {
/* of all the device that have an entry, we already know that best_activated_device
* is the best. entry cannot be better. */
if (entry->source.device != best_activated_device)
continue;
prio = entry->effective_metric;
} else {
NMDeviceState state = nm_device_get_state (device);
if ( state <= NM_DEVICE_STATE_DISCONNECTED
|| state >= NM_DEVICE_STATE_DEACTIVATING)
continue;
if (!_ipx_connection_has_default_route (vtable, self, nm_device_get_connection (device), NULL))
continue;
prio = nm_device_get_ip4_route_metric (device);
}
prio = vtable->vt->metric_normalize (prio);
if ( !best_device
|| prio < best_prio
|| (prio == best_prio && preferred_device == device)) {
best_device = device;
best_prio = prio;
}
}
/* There's only a best activating device if the best device
* among all activating and already-activated devices is a
* still-activating one.
*/
if (best_device && nm_device_get_state (best_device) >= NM_DEVICE_STATE_SECONDARIES)
return NULL;
return best_device;
}
NMDevice *
nm_default_route_manager_ip4_get_best_device (NMDefaultRouteManager *self, const GSList *devices, gboolean fully_activated, NMDevice *preferred_device)
{
if (fully_activated)
return _ipx_get_best_device (&vtable_ip4, self, devices);
else
return _ipx_get_best_activating_device (&vtable_ip4, self, devices, preferred_device);
}
NMDevice *
nm_default_route_manager_ip6_get_best_device (NMDefaultRouteManager *self, const GSList *devices, gboolean fully_activated, NMDevice *preferred_device)
{
if (fully_activated)
return _ipx_get_best_device (&vtable_ip6, self, devices);
else
return _ipx_get_best_activating_device (&vtable_ip6, self, devices, preferred_device);
}
/***********************************************************************************/
static gpointer
_ipx_get_best_config (const VTableIP *vtable,
NMDefaultRouteManager *self,
gboolean ignore_never_default,
const char **out_ip_iface,
NMActiveConnection **out_ac,
NMDevice **out_device,
NMVpnConnection **out_vpn)
{
NMDefaultRouteManagerPrivate *priv;
GPtrArray *entries;
guint i;
gpointer config_result = NULL;
g_return_val_if_fail (NM_IS_DEFAULT_ROUTE_MANAGER (self), NULL);
if (out_ip_iface)
*out_ip_iface = NULL;
if (out_ac)
*out_ac = NULL;
if (out_device)
*out_device = NULL;
if (out_vpn)
*out_vpn = NULL;
priv = NM_DEFAULT_ROUTE_MANAGER_GET_PRIVATE (self);
default-route-manager: avoid crash while disposing of NMDefaultRouteManager During dipose(), NMDefaultRouteManager unrefed all the source pointers in its list -- thereby having dangling pointers in the list of entries. The unrefing can cause the final destruction of the device (during shutdown), which would again call into NMDefaultRouteManager. Fix this by ensuring that after disposing starts, all external calls into NMDefaultRouteManager return early. #0 0x00007ffff4a2cc60 in g_logv (log_domain=0x535b51 "NetworkManager", log_level=G_LOG_LEVEL_CRITICAL, format=<optimized out>, args=args@entry=0x7fffffffd530) at gmessages.c:1046 #1 0x00007ffff4a2ce9f in g_log (log_domain=log_domain@entry=0x535b51 "NetworkManager", log_level=log_level@entry=G_LOG_LEVEL_CRITICAL, format=format@entry=0x528f68 "file %s: line %d (%s): should not be reached") at gmessages.c:1079 #2 0x000000000049b83b in _ipx_update_default_route (vtable=vtable@entry=0x7a49c0 <vtable_ip6>, self=0x7d1350 [NMDefaultRouteManager], source=source@entry=0x8b64e0) at nm-default-route-manager.c:659 #3 0x000000000049c652 in nm_default_route_manager_ip6_update_default_route (self=<optimized out>, source=source@entry=0x8b64e0) at nm-default-route-manager.c:819 #4 0x00000000004526a8 in _cleanup_generic_post (self=self@entry=0x8b64e0, deconfigure=deconfigure@entry=0) at devices/nm-device.c:7235 #5 0x0000000000452bc0 in dispose (object=0x8b64e0) at devices/nm-device.c:8324 #6 0x00007ffff4d29cbc in g_object_unref (_object=0x8b64e0) at gobject.c:3133 #7 0x0000000000499091 in _entry_free (entry=0x8bf140) at nm-default-route-manager.c:187 #8 0x00007ffff49fa82b in g_ptr_array_foreach (array=0x81d220, func=0x499080 <_entry_free>, user_data=0x0) at garray.c:1502 #9 0x00007ffff49fa8c0 in ptr_array_free (array=0x81d220, flags=FREE_SEGMENT) at garray.c:1088 #10 0x00007ffff49fa939 in g_ptr_array_free (array=<optimized out>, free_segment=free_segment@entry=1) at garray.c:1075 #11 0x000000000049abcf in dispose (object=0x7d1350 [NMDefaultRouteManager]) at nm-default-route-manager.c:1357 #12 0x00007ffff4d29cbc in g_object_unref (_object=0x7d1350) at gobject.c:3133 #13 0x00007ffff7deb507 in _dl_fini () at dl-fini.c:252 #14 0x00007ffff4658382 in __run_exit_handlers (status=status@entry=0, listp=0x7ffff49d66a0 <__exit_funcs>, run_list_atexit=run_list_atexit@entry=true) at exit.c:82 #15 0x00007ffff46583d5 in __GI_exit (status=status@entry=0) at exit.c:104 #16 0x0000000000432fd6 in main (argc=1, argv=0x7fffffffdeb8) at main.c:473
2015-02-17 07:53:24 +01:00
if (priv->disposed)
return NULL;
g_return_val_if_fail (NM_IS_DEFAULT_ROUTE_MANAGER (self), NULL);
priv = NM_DEFAULT_ROUTE_MANAGER_GET_PRIVATE (self);
entries = vtable->get_entries (priv);
for (i = 0; i < entries->len; i++) {
Entry *entry = g_ptr_array_index (entries, i);
if (!NM_IS_DEVICE (entry->source.pointer)) {
NMVpnConnection *vpn = NM_VPN_CONNECTION (entry->source.vpn);
if (entry->never_default && !ignore_never_default)
continue;
if (vtable->vt->is_ip4)
config_result = nm_vpn_connection_get_ip4_config (vpn);
else
config_result = nm_vpn_connection_get_ip6_config (vpn);
g_assert (config_result);
if (out_vpn)
*out_vpn = vpn;
if (out_ac)
*out_ac = NM_ACTIVE_CONNECTION (vpn);
if (out_ip_iface)
*out_ip_iface = nm_vpn_connection_get_ip_iface (vpn);
} else {
NMDevice *device = entry->source.device;
NMActRequest *req;
default-route: don't return devices without active connection as best_config() or best_device() Since 0c136c1e2, we also track the default route for devices without active-connection (unmanaged). They must not be returned as best_config() or as best_device(), because the caller don't expect unmanaged devices here. This also gets closer to the original behavior of get_best_device() before merging default-route-manager in d4417e34606b1f08c361709ff9567055a1d6859e where we also would ignore devices depending on the state. This fixes an assertion when having an interface unmanaged and activating it externally: #0 0x00007ffff6806187 in __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:55 #1 0x00007ffff6807dea in __GI_abort () at abort.c:89 #2 0x00007ffff6c0aec5 in g_assertion_message (domain=domain@entry=0x5332d5 "NetworkManager", file=file@entry=0x539460 "nm-default-route-manager.c", line=line@entry=1056, func=func@entry=0x539920 <__FUNCTION__.28934> "_ipx_get_best_config", message=message@entry=0x8c9cb0 "assertion failed: (req)") at gtestutils.c:2356 #3 0x00007ffff6c0af5a in g_assertion_message_expr (domain=domain@entry=0x5332d5 "NetworkManager", file=file@entry=0x539460 "nm-default-route-manager.c", line=line@entry=1056, func=func@entry=0x539920 <__FUNCTION__.28934> "_ipx_get_best_config", expr=expr@entry=0x53132f "req") at gtestutils.c:2371 #4 0x000000000049a196 in _ipx_get_best_config (self=<optimized out>, ignore_never_default=ignore_never_default@entry=1, out_ip_iface=out_ip_iface@entry=0x7fffffffd310, out_ac=out_ac@entry=0x0, out_device=0x0, out_vpn=0x7fffffffd318, vtable=0x7a0a00 <vtable_ip4>, vtable=0x7a0a00 <vtable_ip4>) at nm-default-route-manager.c:1056 #5 0x000000000049a3f6 in nm_default_route_manager_ip4_get_best_config (self=<optimized out>, ignore_never_default=ignore_never_default@entry=1, out_ip_iface=out_ip_iface@entry=0x7fffffffd310, out_ac=out_ac@entry=0x0, out_device=out_device@entry=0x0, out_vpn=out_vpn@entry=0x7fffffffd318) at nm-default-route-manager.c:1079 #6 0x00000000004b7518 in update_ip4_dns (self=0x7ecac0 [NMPolicy], out_vpn=0x7fffffffd318, out_device=0x0, out_ac=0x0, out_ip_iface=0x7fffffffd310, ignore_never_default=1) at nm-policy.c:390 #7 0x00000000004b7518 in update_ip4_dns (dns_mgr=0x8623a0 [NMDnsManager], policy=0x7ecac0 [NMPolicy]) at nm-policy.c:406 #8 0x00000000004b99d5 in device_ip4_config_changed (device=0x8844b0 [NMDeviceEthernet], new_config=0x908aa0 [NMIP4Config], old_config=0x908aa0 [NMIP4Config], user_data=0x7ecac0) at nm-policy.c:1260 #9 0x000000368f405d60 in ffi_call_unix64 () at /lib64/libffi.so.6 #10 0x000000368f4057d1 in ffi_call () at /lib64/libffi.so.6 #11 0x00007ffff6ee5b8c in g_cclosure_marshal_generic_va (closure=0x886a40, return_value=0x0, instance=0x8844b0, args_list=<optimized out>, marshal_data=0x0, n_params=2, param_types=0x87d3a0) at gclosure.c:1541 #12 0x00007ffff6ee5144 in _g_closure_invoke_va (closure=closure@entry=0x886a40, return_value=return_value@entry=0x0, instance=instance@entry=0x8844b0, args=args@entry=0x7fffffffd810, n_params=<optimized out>, param_types=0x87d3a0) at gclosure.c:831 #13 0x00007ffff6eff900 in g_signal_emit_valist (instance=0x8844b0, signal_id=<optimized out>, detail=0, var_args=var_args@entry=0x7fffffffd810) at gsignal.c:3201 #14 0x00007ffff6f0014f in g_signal_emit (instance=instance@entry=0x8844b0, signal_id=<optimized out>, detail=detail@entry=0) at gsignal.c:3348 #15 0x000000000044a1f4 in nm_device_set_ip4_config (self=self@entry=0x8844b0 [NMDeviceEthernet], new_config=new_config@entry=0x9089a0 [NMIP4Config], default_route_metric=default_route_metric@entry=100, commit=commit@entry=0, reason=reason@entry=0x0) at devices/nm-device.c:5866 #16 0x000000000044a5af in ip4_config_merge_and_apply (self=self@entry=0x8844b0 [NMDeviceEthernet], config=config@entry=0x0, commit=commit@entry=0, out_reason=out_reason@entry=0x0) at devices/nm-device.c:3037 #17 0x000000000044b4fd in update_ip_config (self=self@entry=0x8844b0 [NMDeviceEthernet], initial=initial@entry=0) at devices/nm-device.c:6617 #18 0x000000000044bc39 in queued_ip_config_change (user_data=<optimized out>) at devices/nm-device.c:6688 #19 0x00007ffff6be4e1b in g_main_context_dispatch (context=0x7ba3a0) at gmain.c:3122 #20 0x00007ffff6be4e1b in g_main_context_dispatch (context=context@entry=0x7ba3a0) at gmain.c:3737 #21 0x00007ffff6be51b0 in g_main_context_iterate (context=0x7ba3a0, block=block@entry=1, dispatch=dispatch@entry=1, self=<optimized out>) at gmain.c:3808 #22 0x00007ffff6be54d2 in g_main_loop_run (loop=0x7ba460) at gmain.c:4002 #23 0x000000000043291d in main (argc=1, argv=0x7fffffffdef8) at main.c:442 Fixes: da708059dabb2854d11eed1a403398327b31535b
2015-01-26 15:03:48 +01:00
NMDeviceState state;
if (entry->never_default)
continue;
default-route: don't return devices without active connection as best_config() or best_device() Since 0c136c1e2, we also track the default route for devices without active-connection (unmanaged). They must not be returned as best_config() or as best_device(), because the caller don't expect unmanaged devices here. This also gets closer to the original behavior of get_best_device() before merging default-route-manager in d4417e34606b1f08c361709ff9567055a1d6859e where we also would ignore devices depending on the state. This fixes an assertion when having an interface unmanaged and activating it externally: #0 0x00007ffff6806187 in __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:55 #1 0x00007ffff6807dea in __GI_abort () at abort.c:89 #2 0x00007ffff6c0aec5 in g_assertion_message (domain=domain@entry=0x5332d5 "NetworkManager", file=file@entry=0x539460 "nm-default-route-manager.c", line=line@entry=1056, func=func@entry=0x539920 <__FUNCTION__.28934> "_ipx_get_best_config", message=message@entry=0x8c9cb0 "assertion failed: (req)") at gtestutils.c:2356 #3 0x00007ffff6c0af5a in g_assertion_message_expr (domain=domain@entry=0x5332d5 "NetworkManager", file=file@entry=0x539460 "nm-default-route-manager.c", line=line@entry=1056, func=func@entry=0x539920 <__FUNCTION__.28934> "_ipx_get_best_config", expr=expr@entry=0x53132f "req") at gtestutils.c:2371 #4 0x000000000049a196 in _ipx_get_best_config (self=<optimized out>, ignore_never_default=ignore_never_default@entry=1, out_ip_iface=out_ip_iface@entry=0x7fffffffd310, out_ac=out_ac@entry=0x0, out_device=0x0, out_vpn=0x7fffffffd318, vtable=0x7a0a00 <vtable_ip4>, vtable=0x7a0a00 <vtable_ip4>) at nm-default-route-manager.c:1056 #5 0x000000000049a3f6 in nm_default_route_manager_ip4_get_best_config (self=<optimized out>, ignore_never_default=ignore_never_default@entry=1, out_ip_iface=out_ip_iface@entry=0x7fffffffd310, out_ac=out_ac@entry=0x0, out_device=out_device@entry=0x0, out_vpn=out_vpn@entry=0x7fffffffd318) at nm-default-route-manager.c:1079 #6 0x00000000004b7518 in update_ip4_dns (self=0x7ecac0 [NMPolicy], out_vpn=0x7fffffffd318, out_device=0x0, out_ac=0x0, out_ip_iface=0x7fffffffd310, ignore_never_default=1) at nm-policy.c:390 #7 0x00000000004b7518 in update_ip4_dns (dns_mgr=0x8623a0 [NMDnsManager], policy=0x7ecac0 [NMPolicy]) at nm-policy.c:406 #8 0x00000000004b99d5 in device_ip4_config_changed (device=0x8844b0 [NMDeviceEthernet], new_config=0x908aa0 [NMIP4Config], old_config=0x908aa0 [NMIP4Config], user_data=0x7ecac0) at nm-policy.c:1260 #9 0x000000368f405d60 in ffi_call_unix64 () at /lib64/libffi.so.6 #10 0x000000368f4057d1 in ffi_call () at /lib64/libffi.so.6 #11 0x00007ffff6ee5b8c in g_cclosure_marshal_generic_va (closure=0x886a40, return_value=0x0, instance=0x8844b0, args_list=<optimized out>, marshal_data=0x0, n_params=2, param_types=0x87d3a0) at gclosure.c:1541 #12 0x00007ffff6ee5144 in _g_closure_invoke_va (closure=closure@entry=0x886a40, return_value=return_value@entry=0x0, instance=instance@entry=0x8844b0, args=args@entry=0x7fffffffd810, n_params=<optimized out>, param_types=0x87d3a0) at gclosure.c:831 #13 0x00007ffff6eff900 in g_signal_emit_valist (instance=0x8844b0, signal_id=<optimized out>, detail=0, var_args=var_args@entry=0x7fffffffd810) at gsignal.c:3201 #14 0x00007ffff6f0014f in g_signal_emit (instance=instance@entry=0x8844b0, signal_id=<optimized out>, detail=detail@entry=0) at gsignal.c:3348 #15 0x000000000044a1f4 in nm_device_set_ip4_config (self=self@entry=0x8844b0 [NMDeviceEthernet], new_config=new_config@entry=0x9089a0 [NMIP4Config], default_route_metric=default_route_metric@entry=100, commit=commit@entry=0, reason=reason@entry=0x0) at devices/nm-device.c:5866 #16 0x000000000044a5af in ip4_config_merge_and_apply (self=self@entry=0x8844b0 [NMDeviceEthernet], config=config@entry=0x0, commit=commit@entry=0, out_reason=out_reason@entry=0x0) at devices/nm-device.c:3037 #17 0x000000000044b4fd in update_ip_config (self=self@entry=0x8844b0 [NMDeviceEthernet], initial=initial@entry=0) at devices/nm-device.c:6617 #18 0x000000000044bc39 in queued_ip_config_change (user_data=<optimized out>) at devices/nm-device.c:6688 #19 0x00007ffff6be4e1b in g_main_context_dispatch (context=0x7ba3a0) at gmain.c:3122 #20 0x00007ffff6be4e1b in g_main_context_dispatch (context=context@entry=0x7ba3a0) at gmain.c:3737 #21 0x00007ffff6be51b0 in g_main_context_iterate (context=0x7ba3a0, block=block@entry=1, dispatch=dispatch@entry=1, self=<optimized out>) at gmain.c:3808 #22 0x00007ffff6be54d2 in g_main_loop_run (loop=0x7ba460) at gmain.c:4002 #23 0x000000000043291d in main (argc=1, argv=0x7fffffffdef8) at main.c:442 Fixes: da708059dabb2854d11eed1a403398327b31535b
2015-01-26 15:03:48 +01:00
state = nm_device_get_state (device);
if ( state <= NM_DEVICE_STATE_DISCONNECTED
|| state >= NM_DEVICE_STATE_DEACTIVATING) {
/* FIXME: the device has a default route, but we ignore it due to
* unexpected state. That happens for example for unmanaged devices.
*
* In the future, we want unmanaged devices also assume a connection
* if they are activated externally.
*
* Also, we don't want to have DEACTIVATING devices returned as best_config(). */
continue;
}
if (vtable->vt->is_ip4)
config_result = nm_device_get_ip4_config (device);
else
config_result = nm_device_get_ip6_config (device);
g_assert (config_result);
req = nm_device_get_act_request (device);
g_assert (req);
if (out_device)
*out_device = device;
if (out_ac)
*out_ac = NM_ACTIVE_CONNECTION (req);
if (out_ip_iface)
*out_ip_iface = nm_device_get_ip_iface (device);
}
break;
}
return config_result;
}
NMIP4Config *
nm_default_route_manager_ip4_get_best_config (NMDefaultRouteManager *self,
gboolean ignore_never_default,
const char **out_ip_iface,
NMActiveConnection **out_ac,
NMDevice **out_device,
NMVpnConnection **out_vpn)
{
return _ipx_get_best_config (&vtable_ip4,
self,
ignore_never_default,
out_ip_iface,
out_ac,
out_device,
out_vpn);
}
NMIP6Config *
nm_default_route_manager_ip6_get_best_config (NMDefaultRouteManager *self,
gboolean ignore_never_default,
const char **out_ip_iface,
NMActiveConnection **out_ac,
NMDevice **out_device,
NMVpnConnection **out_vpn)
{
return _ipx_get_best_config (&vtable_ip6,
self,
ignore_never_default,
out_ip_iface,
out_ac,
out_device,
out_vpn);
}
/***********************************************************************************/
static GPtrArray *
_v4_get_entries (NMDefaultRouteManagerPrivate *priv)
{
return priv->entries_ip4;
}
static GPtrArray *
_v6_get_entries (NMDefaultRouteManagerPrivate *priv)
{
return priv->entries_ip6;
}
static const VTableIP vtable_ip4 = {
.vt = &nm_platform_vtable_route_v4,
.get_entries = _v4_get_entries,
};
static const VTableIP vtable_ip6 = {
.vt = &nm_platform_vtable_route_v6,
.get_entries = _v6_get_entries,
};
/***********************************************************************************/
static gboolean
_resync_idle_now (NMDefaultRouteManager *self)
{
gboolean has_v4_changes, has_v6_changes;
gboolean changed = FALSE;
NMDefaultRouteManagerPrivate *priv = NM_DEFAULT_ROUTE_MANAGER_GET_PRIVATE (self);
has_v4_changes = priv->resync.has_v4_changes;
has_v6_changes = priv->resync.has_v6_changes;
_LOGD (0, "resync: sync now (%u) (IPv4 changes: %s, IPv6 changes: %s)", priv->resync.idle_handle,
has_v4_changes ? "yes" : "no", has_v6_changes ? "yes" : "no");
priv->resync.has_v4_changes = FALSE;
priv->resync.has_v6_changes = FALSE;
priv->resync.idle_handle = 0;
priv->resync.backoff_wait_time_ms =
priv->resync.backoff_wait_time_ms == 0
? 100
: priv->resync.backoff_wait_time_ms * 2;
if (has_v4_changes)
changed |= _resync_all (&vtable_ip4, self, NULL, NULL, TRUE);
if (has_v6_changes)
changed |= _resync_all (&vtable_ip6, self, NULL, NULL, TRUE);
if (!changed) {
/* Nothing changed: reset the backoff wait time */
_resync_idle_cancel (self);
}
return G_SOURCE_REMOVE;
}
static void
_resync_idle_cancel (NMDefaultRouteManager *self)
{
NMDefaultRouteManagerPrivate *priv = NM_DEFAULT_ROUTE_MANAGER_GET_PRIVATE (self);
if (priv->resync.idle_handle) {
_LOGD (0, "resync: cancelled (%u)", priv->resync.idle_handle);
g_source_remove (priv->resync.idle_handle);
priv->resync.idle_handle = 0;
}
priv->resync.backoff_wait_time_ms = 0;
priv->resync.has_v4_changes = FALSE;
priv->resync.has_v6_changes = FALSE;
}
static void
_resync_idle_reschedule (NMDefaultRouteManager *self)
{
NMDefaultRouteManagerPrivate *priv = NM_DEFAULT_ROUTE_MANAGER_GET_PRIVATE (self);
2015-04-20 11:27:49 +02:00
/* since we react on external changes and re-add/remove default routes for
* the interfaces we manage, there could be the erroneous situation where two applications
* fight over a certain default route.
2015-04-20 11:27:49 +02:00
* Avoid this, by increasingly wait longer to touch the system (backoff wait time). */
if (priv->resync.backoff_wait_time_ms == 0) {
/* for scheduling idle, always reschedule (to process all other events first) */
if (priv->resync.idle_handle)
g_source_remove (priv->resync.idle_handle);
else
_LOGD (0, "resync: schedule on idle");
priv->resync.idle_handle = g_idle_add ((GSourceFunc) _resync_idle_now, self);
} else if (!priv->resync.idle_handle) {
priv->resync.idle_handle = g_timeout_add (priv->resync.backoff_wait_time_ms, (GSourceFunc) _resync_idle_now, self);
_LOGD (0, "resync: schedule in %u.%03u seconds (%u)", priv->resync.backoff_wait_time_ms/1000,
priv->resync.backoff_wait_time_ms%1000, priv->resync.idle_handle);
}
}
static void
_platform_ipx_route_changed_cb (const VTableIP *vtable,
NMDefaultRouteManager *self,
const NMPlatformIPRoute *route)
{
NMDefaultRouteManagerPrivate *priv;
if (route && !NM_PLATFORM_IP_ROUTE_IS_DEFAULT (route)) {
/* we only care about address changes or changes of default route. */
return;
}
priv = NM_DEFAULT_ROUTE_MANAGER_GET_PRIVATE (self);
if (priv->resync.guard) {
/* callbacks while executing _resync_all() are ignored. */
return;
}
if (vtable->vt->is_ip4)
priv->resync.has_v4_changes = TRUE;
else
priv->resync.has_v6_changes = TRUE;
_resync_idle_reschedule (self);
}
static void
_platform_changed_cb (NMPlatform *platform,
NMPObjectType obj_type,
int ifindex,
gpointer platform_object,
NMPlatformSignalChangeType change_type,
NMPlatformReason reason,
NMDefaultRouteManager *self)
{
switch (obj_type) {
case NMP_OBJECT_TYPE_IP4_ADDRESS:
_platform_ipx_route_changed_cb (&vtable_ip4, self, NULL);
break;
case NMP_OBJECT_TYPE_IP6_ADDRESS:
_platform_ipx_route_changed_cb (&vtable_ip6, self, NULL);
break;
case NMP_OBJECT_TYPE_IP4_ROUTE:
_platform_ipx_route_changed_cb (&vtable_ip4, self, (const NMPlatformIPRoute *) platform_object);
break;
case NMP_OBJECT_TYPE_IP6_ROUTE:
_platform_ipx_route_changed_cb (&vtable_ip6, self, (const NMPlatformIPRoute *) platform_object);
break;
default:
g_return_if_reached ();
}
}
/***********************************************************************************/
static void
nm_default_route_manager_init (NMDefaultRouteManager *self)
{
NMDefaultRouteManagerPrivate *priv = NM_DEFAULT_ROUTE_MANAGER_GET_PRIVATE (self);
priv->entries_ip4 = g_ptr_array_new_full (0, (GDestroyNotify) _entry_free);
priv->entries_ip6 = g_ptr_array_new_full (0, (GDestroyNotify) _entry_free);
priv->platform = g_object_ref (nm_platform_get ());
g_signal_connect (priv->platform, NM_PLATFORM_SIGNAL_IP4_ADDRESS_CHANGED, G_CALLBACK (_platform_changed_cb), self);
g_signal_connect (priv->platform, NM_PLATFORM_SIGNAL_IP6_ADDRESS_CHANGED, G_CALLBACK (_platform_changed_cb), self);
g_signal_connect (priv->platform, NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED, G_CALLBACK (_platform_changed_cb), self);
g_signal_connect (priv->platform, NM_PLATFORM_SIGNAL_IP6_ROUTE_CHANGED, G_CALLBACK (_platform_changed_cb), self);
}
static void
dispose (GObject *object)
{
NMDefaultRouteManager *self = NM_DEFAULT_ROUTE_MANAGER (object);
NMDefaultRouteManagerPrivate *priv = NM_DEFAULT_ROUTE_MANAGER_GET_PRIVATE (self);
default-route-manager: avoid crash while disposing of NMDefaultRouteManager During dipose(), NMDefaultRouteManager unrefed all the source pointers in its list -- thereby having dangling pointers in the list of entries. The unrefing can cause the final destruction of the device (during shutdown), which would again call into NMDefaultRouteManager. Fix this by ensuring that after disposing starts, all external calls into NMDefaultRouteManager return early. #0 0x00007ffff4a2cc60 in g_logv (log_domain=0x535b51 "NetworkManager", log_level=G_LOG_LEVEL_CRITICAL, format=<optimized out>, args=args@entry=0x7fffffffd530) at gmessages.c:1046 #1 0x00007ffff4a2ce9f in g_log (log_domain=log_domain@entry=0x535b51 "NetworkManager", log_level=log_level@entry=G_LOG_LEVEL_CRITICAL, format=format@entry=0x528f68 "file %s: line %d (%s): should not be reached") at gmessages.c:1079 #2 0x000000000049b83b in _ipx_update_default_route (vtable=vtable@entry=0x7a49c0 <vtable_ip6>, self=0x7d1350 [NMDefaultRouteManager], source=source@entry=0x8b64e0) at nm-default-route-manager.c:659 #3 0x000000000049c652 in nm_default_route_manager_ip6_update_default_route (self=<optimized out>, source=source@entry=0x8b64e0) at nm-default-route-manager.c:819 #4 0x00000000004526a8 in _cleanup_generic_post (self=self@entry=0x8b64e0, deconfigure=deconfigure@entry=0) at devices/nm-device.c:7235 #5 0x0000000000452bc0 in dispose (object=0x8b64e0) at devices/nm-device.c:8324 #6 0x00007ffff4d29cbc in g_object_unref (_object=0x8b64e0) at gobject.c:3133 #7 0x0000000000499091 in _entry_free (entry=0x8bf140) at nm-default-route-manager.c:187 #8 0x00007ffff49fa82b in g_ptr_array_foreach (array=0x81d220, func=0x499080 <_entry_free>, user_data=0x0) at garray.c:1502 #9 0x00007ffff49fa8c0 in ptr_array_free (array=0x81d220, flags=FREE_SEGMENT) at garray.c:1088 #10 0x00007ffff49fa939 in g_ptr_array_free (array=<optimized out>, free_segment=free_segment@entry=1) at garray.c:1075 #11 0x000000000049abcf in dispose (object=0x7d1350 [NMDefaultRouteManager]) at nm-default-route-manager.c:1357 #12 0x00007ffff4d29cbc in g_object_unref (_object=0x7d1350) at gobject.c:3133 #13 0x00007ffff7deb507 in _dl_fini () at dl-fini.c:252 #14 0x00007ffff4658382 in __run_exit_handlers (status=status@entry=0, listp=0x7ffff49d66a0 <__exit_funcs>, run_list_atexit=run_list_atexit@entry=true) at exit.c:82 #15 0x00007ffff46583d5 in __GI_exit (status=status@entry=0) at exit.c:104 #16 0x0000000000432fd6 in main (argc=1, argv=0x7fffffffdeb8) at main.c:473
2015-02-17 07:53:24 +01:00
priv->disposed = TRUE;
if (priv->platform) {
g_signal_handlers_disconnect_by_func (priv->platform, G_CALLBACK (_platform_changed_cb), self);
g_clear_object (&priv->platform);
}
default-route-manager: avoid crash while disposing of NMDefaultRouteManager During dipose(), NMDefaultRouteManager unrefed all the source pointers in its list -- thereby having dangling pointers in the list of entries. The unrefing can cause the final destruction of the device (during shutdown), which would again call into NMDefaultRouteManager. Fix this by ensuring that after disposing starts, all external calls into NMDefaultRouteManager return early. #0 0x00007ffff4a2cc60 in g_logv (log_domain=0x535b51 "NetworkManager", log_level=G_LOG_LEVEL_CRITICAL, format=<optimized out>, args=args@entry=0x7fffffffd530) at gmessages.c:1046 #1 0x00007ffff4a2ce9f in g_log (log_domain=log_domain@entry=0x535b51 "NetworkManager", log_level=log_level@entry=G_LOG_LEVEL_CRITICAL, format=format@entry=0x528f68 "file %s: line %d (%s): should not be reached") at gmessages.c:1079 #2 0x000000000049b83b in _ipx_update_default_route (vtable=vtable@entry=0x7a49c0 <vtable_ip6>, self=0x7d1350 [NMDefaultRouteManager], source=source@entry=0x8b64e0) at nm-default-route-manager.c:659 #3 0x000000000049c652 in nm_default_route_manager_ip6_update_default_route (self=<optimized out>, source=source@entry=0x8b64e0) at nm-default-route-manager.c:819 #4 0x00000000004526a8 in _cleanup_generic_post (self=self@entry=0x8b64e0, deconfigure=deconfigure@entry=0) at devices/nm-device.c:7235 #5 0x0000000000452bc0 in dispose (object=0x8b64e0) at devices/nm-device.c:8324 #6 0x00007ffff4d29cbc in g_object_unref (_object=0x8b64e0) at gobject.c:3133 #7 0x0000000000499091 in _entry_free (entry=0x8bf140) at nm-default-route-manager.c:187 #8 0x00007ffff49fa82b in g_ptr_array_foreach (array=0x81d220, func=0x499080 <_entry_free>, user_data=0x0) at garray.c:1502 #9 0x00007ffff49fa8c0 in ptr_array_free (array=0x81d220, flags=FREE_SEGMENT) at garray.c:1088 #10 0x00007ffff49fa939 in g_ptr_array_free (array=<optimized out>, free_segment=free_segment@entry=1) at garray.c:1075 #11 0x000000000049abcf in dispose (object=0x7d1350 [NMDefaultRouteManager]) at nm-default-route-manager.c:1357 #12 0x00007ffff4d29cbc in g_object_unref (_object=0x7d1350) at gobject.c:3133 #13 0x00007ffff7deb507 in _dl_fini () at dl-fini.c:252 #14 0x00007ffff4658382 in __run_exit_handlers (status=status@entry=0, listp=0x7ffff49d66a0 <__exit_funcs>, run_list_atexit=run_list_atexit@entry=true) at exit.c:82 #15 0x00007ffff46583d5 in __GI_exit (status=status@entry=0) at exit.c:104 #16 0x0000000000432fd6 in main (argc=1, argv=0x7fffffffdeb8) at main.c:473
2015-02-17 07:53:24 +01:00
_resync_idle_cancel (self);
/* g_ptr_array_free() invokes the free function for all entries without actually
* removing them and having dangling pointers in the process. _entry_free()
* will unref the source, which might cause the destruction of the object, which
* might trigger calling into @self again. This is guarded by priv->dispose.
* If you remove priv->dispose, you must refactor the lines below to remove enties
* one-by-one.
*/
if (priv->entries_ip4) {
g_ptr_array_free (priv->entries_ip4, TRUE);
priv->entries_ip4 = NULL;
}
if (priv->entries_ip6) {
g_ptr_array_free (priv->entries_ip6, TRUE);
priv->entries_ip6 = NULL;
}
G_OBJECT_CLASS (nm_default_route_manager_parent_class)->dispose (object);
}
static void
nm_default_route_manager_class_init (NMDefaultRouteManagerClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
g_type_class_add_private (klass, sizeof (NMDefaultRouteManagerPrivate));
/* virtual methods */
object_class->dispose = dispose;
}