diff options
-rw-r--r-- | src/nm-default-route-manager.c | 114 | ||||
-rw-r--r-- | src/vpn-manager/nm-vpn-connection.c | 10 |
2 files changed, 111 insertions, 13 deletions
diff --git a/src/nm-default-route-manager.c b/src/nm-default-route-manager.c index ac00ab21e1..8c0529e356 100644 --- a/src/nm-default-route-manager.c +++ b/src/nm-default-route-manager.c @@ -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); diff --git a/src/vpn-manager/nm-vpn-connection.c b/src/vpn-manager/nm-vpn-connection.c index 444b1948d4..fe5d812791 100644 --- a/src/vpn-manager/nm-vpn-connection.c +++ b/src/vpn-manager/nm-vpn-connection.c @@ -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); |