summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Haller <thaller@redhat.com>2014-11-18 16:29:05 +0100
committerThomas Haller <thaller@redhat.com>2014-11-19 17:37:27 +0100
commited383ab721fa4566a6c08a056059be34d7034a59 (patch)
tree3966d3cfec70a3b4c9b111423033f92dfd274be4
parent7d0845860b88688c5caf9be1136d801c7233238a (diff)
downloadNetworkManager-th/bgo735512_route_metric_v2.tar.gz
policy: fix handling managed devices without default routeth/bgo735512_route_metric_v2
Before, we would only track a device in NMDefaultRouteManager if it had a default route. Otherwise the entry for the device was removed. That was wrong, because having no entry meant that the interface is assumed and hence we would not touch the interface. Instead we must esplicitly track devices without default route to know when an interface has no default route. Signed-off-by: Thomas Haller <thaller@redhat.com>
-rw-r--r--src/devices/nm-device.c17
-rw-r--r--src/nm-default-route-manager.c126
2 files changed, 102 insertions, 41 deletions
diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c
index 2a93f3e0dc..2a3ef8da69 100644
--- a/src/devices/nm-device.c
+++ b/src/devices/nm-device.c
@@ -737,7 +737,7 @@ nm_device_get_ip4_default_route (NMDevice *self, gboolean *out_is_assumed)
priv = NM_DEVICE_GET_PRIVATE (self);
if (out_is_assumed)
- *out_is_assumed = priv->default_route.v4_has && priv->default_route.v4_is_assumed;
+ *out_is_assumed = priv->default_route.v4_is_assumed;
return priv->default_route.v4_has ? &priv->default_route.v4 : NULL;
}
@@ -752,7 +752,7 @@ nm_device_get_ip6_default_route (NMDevice *self, gboolean *out_is_assumed)
priv = NM_DEVICE_GET_PRIVATE (self);
if (out_is_assumed)
- *out_is_assumed = priv->default_route.v6_has && priv->default_route.v6_is_assumed;
+ *out_is_assumed = priv->default_route.v6_is_assumed;
return priv->default_route.v6_has ? &priv->default_route.v6 : NULL;
}
@@ -2792,6 +2792,7 @@ ip4_config_merge_and_apply (NMDevice *self,
*/
connection = nm_device_get_connection (self);
priv->default_route.v4_has = FALSE;
+ priv->default_route.v4_is_assumed = TRUE;
if (connection) {
gboolean assumed = nm_device_uses_assumed_connection (self);
NMPlatformIP4Route *route = &priv->default_route.v4;
@@ -2817,6 +2818,7 @@ ip4_config_merge_and_apply (NMDevice *self,
&& nm_default_route_manager_ip4_connection_has_default_route (nm_default_route_manager_get (), connection)) {
guint32 gateway = 0;
+ priv->default_route.v4_is_assumed = FALSE;
if ( (!commit && priv->ext_ip4_config_had_any_addresses)
|| ( commit && nm_ip4_config_get_num_addresses (composite))) {
/* For managed interfaces, we can only configure a gateway, if either the external config indicates
@@ -2832,7 +2834,6 @@ ip4_config_merge_and_apply (NMDevice *self,
route->metric = nm_device_get_ip4_route_metric (self);
route->mss = nm_ip4_config_get_mss (composite);
priv->default_route.v4_has = TRUE;
- priv->default_route.v4_is_assumed = FALSE;
if ( gateway
&& !nm_ip4_config_get_subnet_for_host (composite, gateway)
@@ -2851,7 +2852,6 @@ ip4_config_merge_and_apply (NMDevice *self,
/* For interfaces that are assumed and that have no default-route by configuration, we assume
* the default connection and pick up whatever is configured. */
priv->default_route.v4_has = _device_get_default_route_from_platform (self, AF_INET, (NMPlatformIPRoute *) route);
- priv->default_route.v4_is_assumed = TRUE;
}
}
@@ -3351,6 +3351,7 @@ ip6_config_merge_and_apply (NMDevice *self,
*/
connection = nm_device_get_connection (self);
priv->default_route.v6_has = FALSE;
+ priv->default_route.v6_is_assumed = TRUE;
if (connection) {
gboolean assumed = nm_device_uses_assumed_connection (self);
NMPlatformIP6Route *route = &priv->default_route.v6;
@@ -3376,6 +3377,7 @@ ip6_config_merge_and_apply (NMDevice *self,
&& nm_default_route_manager_ip6_connection_has_default_route (nm_default_route_manager_get (), connection)) {
const struct in6_addr *gateway = NULL;
+ priv->default_route.v6_is_assumed = FALSE;
if ( (!commit && priv->ext_ip6_config_had_any_addresses)
|| ( commit && nm_ip6_config_get_num_addresses (composite))) {
/* For managed interfaces, we can only configure a gateway, if either the external config indicates
@@ -3390,7 +3392,6 @@ ip6_config_merge_and_apply (NMDevice *self,
route->metric = nm_device_get_ip6_route_metric (self);
route->mss = nm_ip6_config_get_mss (composite);
priv->default_route.v6_has = TRUE;
- priv->default_route.v6_is_assumed = FALSE;
if ( gateway
&& !nm_ip6_config_get_subnet_for_host (composite, gateway)
@@ -3409,7 +3410,6 @@ ip6_config_merge_and_apply (NMDevice *self,
/* For interfaces that are assumed and that have no default-route by configuration, we assume
* the default connection and pick up whatever is configured. */
priv->default_route.v6_has = _device_get_default_route_from_platform (self, AF_INET6, (NMPlatformIPRoute *) route);
- priv->default_route.v6_is_assumed = TRUE;
}
}
@@ -6966,7 +6966,9 @@ _cleanup_generic_post (NMDevice *self, gboolean deconfigure)
NMDeviceStateReason ignored = NM_DEVICE_STATE_REASON_NONE;
priv->default_route.v4_has = FALSE;
+ priv->default_route.v4_is_assumed = TRUE;
priv->default_route.v6_has = FALSE;
+ priv->default_route.v6_is_assumed = TRUE;
nm_default_route_manager_ip4_update_default_route (nm_default_route_manager_get (), self);
nm_default_route_manager_ip6_update_default_route (nm_default_route_manager_get (), self);
@@ -7766,6 +7768,9 @@ nm_device_init (NMDevice *self)
priv->unmanaged_flags = NM_UNMANAGED_INTERNAL;
priv->available_connections = g_hash_table_new_full (g_direct_hash, g_direct_equal, g_object_unref, NULL);
priv->ip6_saved_properties = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, g_free);
+
+ priv->default_route.v4_is_assumed = TRUE;
+ priv->default_route.v6_is_assumed = TRUE;
}
/*
diff --git a/src/nm-default-route-manager.c b/src/nm-default-route-manager.c
index d0aa3f4bfe..76403b330c 100644
--- a/src/nm-default-route-manager.c
+++ b/src/nm-default-route-manager.c
@@ -76,12 +76,14 @@ static NMDefaultRouteManager *_instance;
#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]"
+#define LOG_ENTRY_FMT "entry[%u/%s:%p:%s:%c%c]"
#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_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 ? 'N' : 'n'), \
+ ((entry)->synced ? 'S' : 's')
/***********************************************************************************/
@@ -97,11 +99,32 @@ typedef struct {
NMVpnConnection *vpn;
} source;
NMPlatformIPXRoute route;
- gboolean synced; /* if true, we synced the entry to platform. We don't sync assumed devices */
- /* it makes sense to order sources based on their priority, without
+ /* Whether the route is synced to platform.
+ *
+ * For VPN sources, the means that the entry is ignored for most puposes.
+ * It is only tracked by NMDefaultRouteManager for get_best_config() where
+ * we want to prefer a VPN connection for DNS even if it does not receive
+ * the default route.
+ *
+ * For Device sources, a non synced route means that the route is assumed
+ * and we should not touch the corresponding interface (unless we have a
+ * synced entry for the very same ifindex).
+ * A synced entry can be never-default. In that case the interface will
+ * still not get a default route, but it notes that the interface is
+ * managed (not assumed) and we enforce not having a default route.
+ * This distincion is necessary, because usually if we see a default
+ * route on an interface that has no entry, we ignore it as because
+ * it is assumed. Hence, a missing entry really means: ignore the route.
+ * But a non-synced, never-default entry means: enforce no default route. */
+ gboolean synced;
+
+ /* For VPN sources, it makes sense to order sources based on their priority, without
* actually adding a default route. This is useful to decide which
- * DNS server to prefer. never_default entries are not synced to platform. */
+ * DNS server to prefer.
+ *
+ * For Device source, never_default entries enforce no default route on the
+ * interface. */
gboolean never_default;
guint32 effective_metric;
@@ -252,7 +275,7 @@ _platform_route_sync_add (const VTableIP *vtable, NMDefaultRouteManager *self, g
}
static gboolean
-_platform_route_sync_flush (const VTableIP *vtable, NMDefaultRouteManager *self)
+_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);
@@ -275,13 +298,15 @@ _platform_route_sync_flush (const VTableIP *vtable, NMDefaultRouteManager *self)
for (j = 0; j < entries->len; j++) {
Entry *e = g_ptr_array_index (entries, j);
- if (e->never_default)
+ if ( e->never_default
+ && !NM_IS_DEVICE (e->source.object))
continue;
if ( e->route.rx.ifindex == route->ifindex
&& e->synced) {
has_ifindex_synced = TRUE;
- if (e->effective_metric == route->metric)
+ if ( !e->never_default
+ && e->effective_metric == route->metric)
entry = e;
}
}
@@ -293,7 +318,8 @@ _platform_route_sync_flush (const VTableIP *vtable, NMDefaultRouteManager *self)
* Otherwise, don't delete the route because it's configured
* externally (and will be assumed -- or already is assumed).
*/
- if (has_ifindex_synced && !entry) {
+ if ( !entry
+ && (has_ifindex_synced || ifindex_to_flush == route->ifindex)) {
vtable->platform_route_delete_default (route->ifindex, route->metric);
changed = TRUE;
}
@@ -370,8 +396,11 @@ _get_assumed_interface_metrics (const VTableIP *vtable, NMDefaultRouteManager *s
for (j = 0; j < entries->len; j++) {
Entry *e = g_ptr_array_index (entries, j);
- if ( !e->never_default
- && e->synced
+ if ( e->never_default
+ && !NM_IS_DEVICE (e->source.object))
+ continue;
+
+ if ( e->synced
&& e->route.rx.ifindex == route->ifindex) {
ifindex_has_synced_entry = TRUE;
break;
@@ -409,6 +438,7 @@ _resync_all (const VTableIP *vtable, NMDefaultRouteManager *self, const Entry *c
GHashTable *assumed_metrics;
GArray *routes;
gboolean changed = FALSE;
+ int ifindex_to_flush = 0;
g_assert (priv->resync.guard == 0);
priv->resync.guard++;
@@ -428,7 +458,7 @@ _resync_all (const VTableIP *vtable, NMDefaultRouteManager *self, const Entry *c
assumed_metrics = _get_assumed_interface_metrics (vtable, self, routes);
- if (old_entry && old_entry->synced) {
+ 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);
}
@@ -452,8 +482,7 @@ _resync_all (const VTableIP *vtable, NMDefaultRouteManager *self, const Entry *c
for (j = 0; j < entries->len; j++) {
const Entry *e = g_ptr_array_index (entries, j);
- if ( !e->never_default
- && e->synced
+ if ( e->synced
&& e->route.rx.ifindex == entry->route.rx.ifindex) {
has_synced_entry = TRUE;
break;
@@ -539,7 +568,17 @@ _resync_all (const VTableIP *vtable, NMDefaultRouteManager *self, const Entry *c
last_metric = expected_metric;
}
- changed |= _platform_route_sync_flush (vtable, self);
+ 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);
@@ -563,14 +602,13 @@ _entry_at_idx_update (const VTableIP *vtable, NMDefaultRouteManager *self, guint
g_assert ( !old_entry
|| (entry->source.pointer == old_entry->source.pointer && entry->route.rx.ifindex == old_entry->route.rx.ifindex));
- if (!entry->synced) {
+ if (!entry->synced && !entry->never_default)
entry->effective_metric = entry->route.rx.metric;
- _LOGD (vtable->addr_family, LOG_ENTRY_FMT": %s %s%s",
- LOG_ENTRY_ARGS (entry_idx, entry),
- old_entry ? "update" : "add",
- vtable->platform_route_to_string (&entry->route.rx),
- entry->never_default ? " (never-default)" : (entry->synced ? "" : " (not synced)"));
- }
+
+ _LOGD (vtable->addr_family, LOG_ENTRY_FMT": %s %s",
+ LOG_ENTRY_ARGS (entry_idx, entry),
+ old_entry ? "update" : "add",
+ vtable->platform_route_to_string (&entry->route.rx));
g_ptr_array_sort_with_data (entries, _sort_entries_cmp, NULL);
@@ -590,9 +628,8 @@ _entry_at_idx_remove (const VTableIP *vtable, NMDefaultRouteManager *self, guint
entry = g_ptr_array_index (entries, entry_idx);
- _LOGD (vtable->addr_family, LOG_ENTRY_FMT": remove %s (%u%s)", LOG_ENTRY_ARGS (entry_idx, entry),
- vtable->platform_route_to_string (&entry->route.rx), (guint) entry->effective_metric,
- entry->synced ? "" : ", not synced");
+ _LOGD (vtable->addr_family, LOG_ENTRY_FMT": remove %s (%u)", LOG_ENTRY_ARGS (entry_idx, entry),
+ vtable->platform_route_to_string (&entry->route.rx), (guint) entry->effective_metric);
/* Remove the entry from the list (but don't free it yet) */
g_ptr_array_index (entries, entry_idx) = NULL;
@@ -618,8 +655,7 @@ _ipx_update_default_route (const VTableIP *vtable, NMDefaultRouteManager *self,
NMDevice *device = NULL;
NMVpnConnection *vpn = NULL;
gboolean never_default = FALSE;
- gboolean synced;
- gboolean is_assumed = FALSE;
+ gboolean synced = FALSE;
g_return_if_fail (NM_IS_DEFAULT_ROUTE_MANAGER (self));
if (NM_IS_DEVICE (source))
@@ -665,10 +701,29 @@ _ipx_update_default_route (const VTableIP *vtable, NMDefaultRouteManager *self,
/* get the @default_route from the device. */
if (ip_ifindex > 0) {
if (device) {
+ gboolean is_assumed;
+
if (VTABLE_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);
@@ -706,14 +761,11 @@ _ipx_update_default_route (const VTableIP *vtable, NMDefaultRouteManager *self,
}
}
}
+ synced = default_route && !never_default;
}
}
g_assert (!default_route || default_route->plen == 0);
- /* if the source is never_default or the device uses an assumed connection,
- * we don't sync the route. */
- synced = !never_default && !is_assumed;
-
if (!entry && !default_route)
/* nothing to do */;
else if (!entry) {
@@ -844,7 +896,8 @@ _ipx_get_best_device (const VTableIP *vtable, NMDefaultRouteManager *self, const
if (!NM_IS_DEVICE (entry->source.pointer))
continue;
- g_assert (!entry->never_default);
+ if (entry->never_default)
+ continue;
if (g_slist_find ((GSList *) devices, entry->source.device))
return entry->source.pointer;
@@ -998,6 +1051,9 @@ _ipx_get_best_config (const VTableIP *vtable,
NMDevice *device = entry->source.device;
NMActRequest *req;
+ if (entry->never_default)
+ continue;
+
if (VTABLE_IS_IP4)
config_result = nm_device_get_ip4_config (device);
else