Commit a39a3ae4 authored by Thomas Haller's avatar Thomas Haller

policy: track default route for VPN in NMDefaultRouteManager

Extend NMDefaultRouteManager to track NMVpnConnection beside
NMDevice. That way, all default routes are managed by
NMDefaultRouteManager.

For VPN connections the manager also tracks connections that are
set never_default. That is useful because NMPolicy still uses VPNs
without default route to setup DNS. Hence, NMDefaultRouteManager
trackes those connections to have the relative priority of the
devices.
Interestingly, that means that for VPNs that are ipv4.never-default,
ipv4.route-metric still has an effect in determining relative priorities
for DNS configuration.

This commit only adds the parts to track the default route. NMPolicy
still sets the route as before.
Signed-off-by: Thomas Haller's avatarThomas Haller <thaller@redhat.com>
parent a94605f9
......@@ -27,6 +27,7 @@
#include "nm-logging.h"
#include "nm-device.h"
#include "nm-vpn-connection.h"
#include "nm-platform.h"
#include "nm-ip4-config.h"
#include "nm-ip6-config.h"
......@@ -72,7 +73,7 @@ static NMDefaultRouteManager *_instance;
entry_idx, \
NM_IS_DEVICE (entry->source.pointer) ? "dev" : "vpn", \
entry->source.pointer, \
nm_device_get_iface (entry->source.device)
NM_IS_DEVICE (entry->source.pointer) ? nm_device_get_iface (entry->source.device) : nm_vpn_connection_get_connection_id (entry->source.vpn)
/***********************************************************************************/
......@@ -81,6 +82,7 @@ typedef struct {
void *pointer;
GObject *object;
NMDevice *device;
NMVpnConnection *vpn;
} source;
union {
NMPlatformIPRoute route;
......@@ -88,6 +90,12 @@ typedef struct {
NMPlatformIP6Route route6;
};
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
* actually adding a default route. This is useful to decide which
* DNS server to prefer. never_default entries are not synced to platform. */
gboolean never_default;
guint32 effective_metric;
} Entry;
......@@ -148,6 +156,9 @@ _platform_route_sync_add (const VTableIP *vtable, NMDefaultRouteManager *self, g
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;
......@@ -215,6 +226,9 @@ _platform_route_sync_flush (const VTableIP *vtable, NMDefaultRouteManager *self)
for (i = 0; i < entries->len; i++) {
Entry *e = g_ptr_array_index (entries, i);
if (e->never_default)
continue;
if ( e->route.ifindex == route->ifindex
&& e->synced) {
has_ifindex_synced = TRUE;
......@@ -253,6 +267,10 @@ _sort_entries_cmp (gconstpointer a, gconstpointer b, gpointer user_data)
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.
......@@ -300,6 +318,9 @@ _resync_all (const VTableIP *vtable, NMDefaultRouteManager *self, const Entry *c
g_assert (entry != old_entry);
if (entry->never_default)
continue;
if (!entry->synced) {
last_metric = MAX (last_metric, (gint64) entry->effective_metric);
continue;
......@@ -342,7 +363,7 @@ _entry_at_idx_update (const VTableIP *vtable, NMDefaultRouteManager *self, guint
{
NMDefaultRouteManagerPrivate *priv = NM_DEFAULT_ROUTE_MANAGER_GET_PRIVATE (self);
Entry *entry;
NMDevice *device;
NMDevice *device = NULL;
GPtrArray *entries;
entries = vtable->get_entries (priv);
......@@ -350,8 +371,8 @@ _entry_at_idx_update (const VTableIP *vtable, NMDefaultRouteManager *self, guint
entry = g_ptr_array_index (entries, entry_idx);
g_return_if_fail (NM_IS_DEVICE (entry->source.pointer));
device = entry->source.device;
if (NM_IS_DEVICE (entry->source.pointer))
device = entry->source.device;
g_assert ( !old_entry
|| (entry->source.pointer == old_entry->source.pointer && entry->route.ifindex == old_entry->route.ifindex));
......@@ -362,7 +383,7 @@ _entry_at_idx_update (const VTableIP *vtable, NMDefaultRouteManager *self, guint
LOG_ENTRY_ARGS (entry_idx, entry),
old_entry ? "update" : "add",
vtable->platform_route_to_string (&entry->route),
entry->synced ? "" : " (not synced)");
entry->never_default ? " (never-default)" : (entry->synced ? "" : " (not synced)"));
}
g_ptr_array_sort_with_data (entries, _sort_entries_cmp, NULL);
......@@ -403,18 +424,38 @@ _ipx_update_default_route (const VTableIP *vtable, NMDefaultRouteManager *self,
Entry *entry;
guint entry_idx;
const NMPlatformIPRoute *default_route = NULL;
union {
NMPlatformIPRoute vx;
NMPlatformIP4Route v4;
NMPlatformIP6Route v6;
} rt;
int ip_ifindex;
GPtrArray *entries;
NMDevice *device = NULL;
NMVpnConnection *vpn = NULL;
gboolean never_default = FALSE;
gboolean synced;
g_return_if_fail (NM_IS_DEFAULT_ROUTE_MANAGER (self));
if (NM_IS_DEVICE (source))
device = source;
else if (NM_IS_VPN_CONNECTION (source))
vpn = source;
else
g_return_if_reached ();
ip_ifindex = nm_device_get_ip_ifindex (device);
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);
}
}
priv = NM_DEFAULT_ROUTE_MANAGER_GET_PRIVATE (self);
......@@ -438,15 +479,60 @@ _ipx_update_default_route (const VTableIP *vtable, NMDefaultRouteManager *self,
/* get the @default_route from the device. */
if (ip_ifindex > 0) {
if (VTABLE_IS_IP4)
default_route = (const NMPlatformIPRoute *) nm_device_get_ip4_default_route (device);
else
default_route = (const NMPlatformIPRoute *) nm_device_get_ip6_default_route (device);
if (device) {
if (VTABLE_IS_IP4)
default_route = (const NMPlatformIPRoute *) nm_device_get_ip4_default_route (device);
else
default_route = (const NMPlatformIPRoute *) nm_device_get_ip6_default_route (device);
} else {
NMConnection *connection = nm_active_connection_get_connection ((NMActiveConnection *) vpn);
if ( connection
&& nm_vpn_connection_get_vpn_state (vpn) == NM_VPN_CONNECTION_STATE_ACTIVATED) {
if (VTABLE_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);
memset (&rt.v4, 0, sizeof (rt.v4));
rt.v4.ifindex = ip_ifindex;
rt.v4.source = NM_IP_CONFIG_SOURCE_VPN;
rt.v4.gateway = nm_vpn_connection_get_ip4_internal_gateway (vpn);
rt.v4.metric = nm_vpn_connection_get_ip4_route_metric (vpn);
rt.v4.mss = nm_ip4_config_get_mss (vpn_config);
default_route = &rt.vx;
}
} 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);
memset (&rt.v6, 0, sizeof (rt.v6));
rt.v6.ifindex = ip_ifindex;
rt.v6.source = NM_IP_CONFIG_SOURCE_VPN;
rt.v6.gateway = int_gw ? *int_gw : in6addr_any;
rt.v6.metric = nm_vpn_connection_get_ip6_route_metric (vpn);
rt.v6.mss = nm_ip6_config_get_mss (vpn_config);
default_route = &rt.vx;
}
}
}
/* FIXME: for now, only track the default route for VPN.
* Enable actual configuration of the route later. */
never_default = TRUE;
}
}
g_assert (!default_route || default_route->plen == 0);
/* if the device uses an assumed connection, we don't sync the route. */
synced = !nm_device_uses_assumed_connection (device);
/* if the source is never_default or the device uses an assumed connection,
* we don't sync the route. */
synced = !never_default && (!device || !nm_device_uses_assumed_connection (device));
if (!entry && !default_route)
/* nothing to do */;
......@@ -463,6 +549,7 @@ _ipx_update_default_route (const VTableIP *vtable, NMDefaultRouteManager *self,
/* only use normalized metrics */
entry->route.metric = vtable->route_metric_normalize (entry->route.metric);
entry->route.ifindex = ip_ifindex;
entry->never_default = never_default;
entry->effective_metric = entry->route.metric;
entry->synced = synced;
......@@ -480,6 +567,7 @@ _ipx_update_default_route (const VTableIP *vtable, NMDefaultRouteManager *self,
/* only use normalized metrics */
new_entry.route.metric = vtable->route_metric_normalize (new_entry.route.metric);
new_entry.route.ifindex = ip_ifindex;
new_entry.never_default = never_default;
new_entry.synced = synced;
if (memcmp (entry, &new_entry, sizeof (new_entry)) == 0)
......@@ -515,7 +603,7 @@ _ipx_remove_default_route (const VTableIP *vtable, NMDefaultRouteManager *self,
guint entry_idx;
g_return_if_fail (NM_IS_DEFAULT_ROUTE_MANAGER (self));
g_return_if_fail (NM_IS_DEVICE (source));
g_return_if_fail (NM_IS_DEVICE (source) || NM_IS_VPN_CONNECTION (source));
priv = NM_DEFAULT_ROUTE_MANAGER_GET_PRIVATE (self);
......
......@@ -41,6 +41,7 @@
#include "nm-dispatcher.h"
#include "nm-agent-manager.h"
#include "nm-core-internal.h"
#include "nm-default-route-manager.h"
#include "nm-vpn-connection-glue.h"
......@@ -234,6 +235,9 @@ vpn_cleanup (NMVpnConnection *connection, NMDevice *parent_dev)
nm_platform_address_flush (priv->ip_ifindex);
}
nm_default_route_manager_ip4_remove_default_route (nm_default_route_manager_get (), connection);
nm_default_route_manager_ip6_remove_default_route (nm_default_route_manager_get (), connection);
nm_device_set_vpn4_config (parent_dev, NULL);
nm_device_set_vpn6_config (parent_dev, NULL);
......@@ -338,6 +342,9 @@ _set_vpn_state (NMVpnConnection *connection,
g_object_notify (G_OBJECT (connection), NM_VPN_CONNECTION_VPN_STATE);
}
nm_default_route_manager_ip4_update_default_route (nm_default_route_manager_get (), connection);
nm_default_route_manager_ip6_update_default_route (nm_default_route_manager_get (), connection);
switch (vpn_state) {
case STATE_NEED_AUTH:
/* Do nothing; not part of 'default' because we don't want to touch
......@@ -942,6 +949,9 @@ nm_vpn_connection_apply_config (NMVpnConnection *connection)
apply_parent_device_config (connection);
nm_default_route_manager_ip4_update_default_route (nm_default_route_manager_get (), connection);
nm_default_route_manager_ip6_update_default_route (nm_default_route_manager_get (), connection);
nm_log_info (LOGD_VPN, "VPN connection '%s' (IP Config Get) complete.",
nm_connection_get_id (priv->connection));
_set_vpn_state (connection, STATE_PRE_UP, NM_VPN_CONNECTION_STATE_REASON_NONE, FALSE);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment