diff --git a/src/Makefile.am b/src/Makefile.am index 60f7ef6a30..a0447bdd42 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -281,6 +281,8 @@ nm_sources = \ nm-dbus-manager.h \ nm-dcb.c \ nm-dcb.h \ + nm-default-route-manager.c \ + nm-default-route-manager.h \ nm-dhcp4-config.c \ nm-dhcp4-config.h \ nm-dhcp6-config.c \ diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c index 4d5f8a449b..3d7a34ba8a 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -68,6 +68,7 @@ #include "nm-config.h" #include "nm-dns-manager.h" #include "nm-core-internal.h" +#include "nm-default-route-manager.h" #include "nm-device-logging.h" _LOG_DECLARE_SELF (NMDevice); @@ -237,6 +238,12 @@ typedef struct { NMIP4Config * dev_ip4_config; /* Config from DHCP, PPP, LLv4, etc */ NMIP4Config * ext_ip4_config; /* Stuff added outside NM */ NMIP4Config * wwan_ip4_config; /* WWAN configuration */ + struct { + gboolean v4_has; + NMPlatformIP4Route v4; + gboolean v6_has; + NMPlatformIP6Route v6; + } default_route; /* DHCPv4 tracking */ NMDhcpClient * dhcp4_client; @@ -713,6 +720,30 @@ nm_device_get_ip6_route_metric (NMDevice *self) return route_metric >= 0 ? route_metric : nm_device_get_priority (self); } +const NMPlatformIP4Route * +nm_device_get_ip4_default_route (NMDevice *self) +{ + NMDevicePrivate *priv; + + g_return_val_if_fail (NM_IS_DEVICE (self), NULL); + + priv = NM_DEVICE_GET_PRIVATE (self); + + return priv->default_route.v4_has ? &priv->default_route.v4 : NULL; +} + +const NMPlatformIP6Route * +nm_device_get_ip6_default_route (NMDevice *self) +{ + NMDevicePrivate *priv; + + g_return_val_if_fail (NM_IS_DEVICE (self), NULL); + + priv = NM_DEVICE_GET_PRIVATE (self); + + return priv->default_route.v6_has ? &priv->default_route.v6 : NULL; +} + const char * nm_device_get_type_desc (NMDevice *self) { @@ -2636,6 +2667,52 @@ aipd_start (NMDevice *self, NMDeviceStateReason *reason) return NM_ACT_STAGE_RETURN_POSTPONE; } +/*********************************************/ + +static gboolean +_device_get_default_route_from_platform (NMDevice *self, int addr_family, NMPlatformIPRoute *out_route) +{ + gboolean success = FALSE; + int ifindex = nm_device_get_ip_ifindex (self); + GArray *routes; + + if (addr_family == AF_INET) + routes = nm_platform_ip4_route_get_all (ifindex, NM_PLATFORM_GET_ROUTE_MODE_ONLY_DEFAULT); + else + routes = nm_platform_ip6_route_get_all (ifindex, NM_PLATFORM_GET_ROUTE_MODE_ONLY_DEFAULT); + + if (routes) { + guint route_metric = G_MAXUINT32, m; + const NMPlatformIPRoute *route = NULL, *r; + guint i; + + /* if there are several default routes, find the one with the best metric */ + for (i = 0; i < routes->len; i++) { + if (addr_family == AF_INET) { + r = (const NMPlatformIPRoute *) &g_array_index (routes, NMPlatformIP4Route, i); + m = r->metric; + } else { + r = (const NMPlatformIPRoute *) &g_array_index (routes, NMPlatformIP6Route, i); + m = nm_utils_ip6_route_metric_normalize (r->metric); + } + if (!route || m < route_metric) { + route = r; + route_metric = m; + } + } + + if (route) { + if (addr_family == AF_INET) + *((NMPlatformIP4Route *) out_route) = *((NMPlatformIP4Route *) route); + else + *((NMPlatformIP6Route *) out_route) = *((NMPlatformIP6Route *) route); + success = TRUE; + } + g_array_free (routes, TRUE); + } + return success; +} + /*********************************************/ /* DHCPv4 stuff */ @@ -2701,11 +2778,58 @@ ip4_config_merge_and_apply (NMDevice *self, * be redundant, so don't bother. */ connection = nm_device_get_connection (self); - if ( connection - && !nm_settings_connection_get_nm_generated_assumed (NM_SETTINGS_CONNECTION (connection))) { - nm_ip4_config_merge_setting (composite, - nm_connection_get_setting_ip4_config (connection), - nm_device_get_ip4_route_metric (self)); + priv->default_route.v4_has = FALSE; + if (connection) { + gboolean assumed = nm_device_uses_assumed_connection (self); + + if (!nm_settings_connection_get_nm_generated_assumed (NM_SETTINGS_CONNECTION (connection))) { + nm_ip4_config_merge_setting (composite, + nm_connection_get_setting_ip4_config (connection), + nm_device_get_ip4_route_metric (self)); + } + + /* Add the default route. + * + * We keep track of the default route of a device in a private field. + * NMDevice needs to know the default route at this point, because the gateway + * might require a direct route (see below). + * + * But also, we don't want to add the default route to priv->ip4_config, + * because the default route from the setting might not be the same that + * NMDefaultRouteManager eventually configures (because the it might + * tweak the effective metric). + */ + if (nm_default_route_manager_ip4_connection_has_default_route (nm_default_route_manager_get (), connection)) { + guint32 gateway = 0; + NMPlatformIP4Route *route = &priv->default_route.v4; + + if (assumed) + priv->default_route.v4_has = _device_get_default_route_from_platform (self, AF_INET, (NMPlatformIPRoute *) route); + else { + gateway = nm_ip4_config_get_gateway (composite); + if ( gateway + || nm_device_get_device_type (self) == NM_DEVICE_TYPE_MODEM) { + memset (route, 0, sizeof (*route)); + route->source = NM_IP_CONFIG_SOURCE_USER; + route->gateway = gateway; + route->metric = nm_device_get_ip4_route_metric (self); + route->mss = nm_ip4_config_get_mss (composite); + priv->default_route.v4_has = TRUE; + + if ( gateway + && !nm_ip4_config_get_subnet_for_host (composite, gateway) + && !nm_ip4_config_get_direct_route_for_host (composite, gateway)) { + /* add a direct route to the gateway */ + NMPlatformIP4Route r = *route; + + r.network = gateway; + r.plen = 32; + r.gateway = 0; + nm_ip4_config_add_route (composite, &r); + } + } + } + } } /* Allow setting MTU etc */ @@ -3202,11 +3326,58 @@ ip6_config_merge_and_apply (NMDevice *self, * be redundant, so don't bother. */ connection = nm_device_get_connection (self); - if ( connection - && !nm_settings_connection_get_nm_generated_assumed (NM_SETTINGS_CONNECTION (connection))) { - nm_ip6_config_merge_setting (composite, - nm_connection_get_setting_ip6_config (connection), - nm_device_get_ip6_route_metric (self)); + priv->default_route.v6_has = FALSE; + if (connection) { + gboolean assumed = nm_device_uses_assumed_connection (self); + + if (!nm_settings_connection_get_nm_generated_assumed (NM_SETTINGS_CONNECTION (connection))) { + nm_ip6_config_merge_setting (composite, + nm_connection_get_setting_ip6_config (connection), + nm_device_get_ip6_route_metric (self)); + } + + /* Add the default route. + * + * We keep track of the default route of a device in a private field. + * NMDevice needs to know the default route at this point, because the gateway + * might require a direct route (see below). + * + * But also, we don't want to add the default route to priv->ip4_config, + * because the default route from the setting might not be the same that + * NMDefaultRouteManager eventually configures (because the it might + * tweak the effective metric). + */ + if (nm_default_route_manager_ip6_connection_has_default_route (nm_default_route_manager_get (), connection)) { + const struct in6_addr *gateway = NULL; + NMPlatformIP6Route *route = &priv->default_route.v6; + + if (assumed) + priv->default_route.v6_has = _device_get_default_route_from_platform (self, AF_INET, (NMPlatformIPRoute *) route); + else { + gateway = nm_ip6_config_get_gateway (composite); + if ( gateway + || nm_device_get_device_type (self) == NM_DEVICE_TYPE_MODEM) { + memset (route, 0, sizeof (*route)); + route->source = NM_IP_CONFIG_SOURCE_USER; + route->gateway = gateway ? *gateway : in6addr_any; + route->metric = nm_device_get_ip6_route_metric (self); + route->mss = nm_ip6_config_get_mss (composite); + priv->default_route.v6_has = TRUE; + + if ( gateway + && !nm_ip6_config_get_subnet_for_host (composite, gateway) + && !nm_ip6_config_get_direct_route_for_host (composite, gateway)) { + /* add a direct route to the gateway */ + NMPlatformIP6Route r = *route; + + r.network = *gateway; + r.plen = 128; + r.gateway = in6addr_any; + nm_ip6_config_add_route (composite, &r); + } + } + } + } } nm_ip6_config_addresses_sort (composite, @@ -5405,6 +5576,8 @@ nm_device_set_ip4_config (NMDevice *self, g_clear_object (&priv->dev_ip4_config); } + nm_default_route_manager_ip4_update_default_route (nm_default_route_manager_get (), self); + if (has_changes) { _update_ip4_address (self); @@ -5526,6 +5699,8 @@ nm_device_set_ip6_config (NMDevice *self, nm_ip6_config_get_dbus_path (old_config)); } + nm_default_route_manager_ip6_update_default_route (nm_default_route_manager_get (), self); + if (has_changes) { if (old_config != priv->ip6_config) g_object_notify (G_OBJECT (self), NM_DEVICE_IP6_CONFIG); @@ -6720,6 +6895,12 @@ _cleanup_generic_post (NMDevice *self, gboolean deconfigure) NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); NMDeviceStateReason ignored = NM_DEVICE_STATE_REASON_NONE; + priv->default_route.v4_has = FALSE; + priv->default_route.v6_has = FALSE; + + nm_default_route_manager_ip4_remove_default_route (nm_default_route_manager_get (), self); + nm_default_route_manager_ip6_remove_default_route (nm_default_route_manager_get (), self); + /* Clean up IP configs; this does not actually deconfigure the * interface; the caller must flush routes and addresses explicitly. */ diff --git a/src/devices/nm-device.h b/src/devices/nm-device.h index 6b732cae96..f7b91e00cc 100644 --- a/src/devices/nm-device.h +++ b/src/devices/nm-device.h @@ -362,6 +362,9 @@ gboolean nm_device_owns_iface (NMDevice *device, const char *iface); NMConnection *nm_device_new_default_connection (NMDevice *self); +const NMPlatformIP4Route *nm_device_get_ip4_default_route (NMDevice *self); +const NMPlatformIP6Route *nm_device_get_ip6_default_route (NMDevice *self); + G_END_DECLS /* For testing only */ diff --git a/src/nm-default-route-manager.c b/src/nm-default-route-manager.c new file mode 100644 index 0000000000..2ff75ae34e --- /dev/null +++ b/src/nm-default-route-manager.c @@ -0,0 +1,715 @@ +/* -*- 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 "nm-default-route-manager.h" + +#include "config.h" + +#include "string.h" + +#include "nm-logging.h" +#include "nm-device.h" +#include "nm-platform.h" +#include "nm-ip4-config.h" +#include "nm-ip6-config.h" + +typedef struct { + GPtrArray *entries_ip4; + GPtrArray *entries_ip6; +} 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) + +static NMDefaultRouteManager *_instance; + +#define _LOG(level, addr_family, ...) \ + G_STMT_START { \ + int __addr_family = (addr_family); \ + guint64 __domain = __addr_family == AF_INET ? LOGD_IP4 : LOGD_IP6; \ + \ + if (nm_logging_enabled ((level), (__domain))) { \ + char __ch = __addr_family == AF_INET ? '4' : '6'; \ + char __prefix[30] = "default-route"; \ + \ + if ((self) != _instance) \ + g_snprintf (__prefix, sizeof (__prefix), "default-route%c[%p]", __ch, (self)); \ + else \ + __prefix[STRLEN ("default-route")] = __ch; \ + nm_log ((level), (__domain), \ + "%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]" +#define LOG_ENTRY_ARGS(entry_idx, entry) \ + entry_idx, \ + NM_IS_DEVICE (entry->source.pointer) ? "dev" : "vpn", \ + entry->source.pointer, \ + nm_device_get_iface (entry->source.device) + +/***********************************************************************************/ + +typedef struct { + union { + void *pointer; + GObject *object; + NMDevice *device; + } source; + union { + NMPlatformIPRoute route; + NMPlatformIP4Route route4; + NMPlatformIP6Route route6; + }; + gboolean synced; /* if true, we synced the entry to platform. We don't sync assumed devices */ + guint32 effective_metric; +} Entry; + +typedef struct { + int addr_family; + GPtrArray *(*get_entries) (NMDefaultRouteManagerPrivate *priv); + const char *(*platform_route_to_string) (const NMPlatformIPRoute *route); + gboolean (*platform_route_delete_default) (int ifindex, guint32 metric); + guint32 (*route_metric_normalize) (guint32 metric); +} VTableIP; + +static const VTableIP vtable_ip4, vtable_ip6; + +#define VTABLE_IS_IP4 (vtable->addr_family == AF_INET) + +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 void +_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; + gboolean has_unsynced_entry = FALSE; + 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->effective_metric != metric) + continue; + + if (e->synced) { + g_assert (!entry || metric == G_MAXUINT32); + entry = e; + } else + has_unsynced_entry = TRUE; + } + + /* For synced entries, we expect that the metric is chosen uniquely. */ + g_assert (!entry || !has_unsynced_entry || metric == G_MAXUINT32); + + /* we only add the route, if we have an (to be synced) entry for it. */ + if (!entry) + return; + + if (VTABLE_IS_IP4) { + success = nm_platform_ip4_route_add (entry->route.ifindex, + entry->route.source, + 0, + 0, + entry->route4.gateway, + entry->effective_metric, + entry->route.mss); + } else { + success = nm_platform_ip6_route_add (entry->route.ifindex, + entry->route.source, + in6addr_any, + 0, + entry->route6.gateway, + entry->effective_metric, + entry->route.mss); + } + if (!success) + _LOGW (vtable->addr_family, "failed to add default route %s with effective metric %u", vtable->platform_route_to_string (&entry->route), (guint) entry->effective_metric); +} + +static void +_platform_route_sync_flush (const VTableIP *vtable, NMDefaultRouteManager *self) +{ + NMDefaultRouteManagerPrivate *priv = NM_DEFAULT_ROUTE_MANAGER_GET_PRIVATE (self); + GPtrArray *entries = vtable->get_entries (priv); + GArray *routes; + guint i; + + /* prune all other default routes from this device. */ + if (VTABLE_IS_IP4) + routes = nm_platform_ip4_route_get_all (0, NM_PLATFORM_GET_ROUTE_MODE_ONLY_DEFAULT); + else + routes = nm_platform_ip6_route_get_all (0, NM_PLATFORM_GET_ROUTE_MODE_ONLY_DEFAULT); + + for (i = 0; i < routes->len; i++) { + const NMPlatformIPRoute *route; + gboolean has_ifindex_synced = FALSE; + Entry *entry = NULL; + + if (VTABLE_IS_IP4) + route = (const NMPlatformIPRoute *) &g_array_index (routes, NMPlatformIP4Route, i); + else + route = (const NMPlatformIPRoute *) &g_array_index (routes, NMPlatformIP6Route, i); + + /* look at all entires and see if the route for this ifindex pair is + * a known entry. */ + for (i = 0; i < entries->len; i++) { + Entry *e = g_ptr_array_index (entries, i); + + if ( e->route.ifindex == route->ifindex + && e->synced) { + has_ifindex_synced = TRUE; + if (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 (has_ifindex_synced && !entry) + vtable->platform_route_delete_default (route->ifindex, route->metric); + } + g_array_free (routes, TRUE); +} + +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.metric; + m_b = e_b->route.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 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 void +_resync_all (const VTableIP *vtable, NMDefaultRouteManager *self, const Entry *changed_entry, const Entry *old_entry, gboolean do_sync) +{ + NMDefaultRouteManagerPrivate *priv = NM_DEFAULT_ROUTE_MANAGER_GET_PRIVATE (self); + Entry *entry; + guint i; + gint64 last_metric = -1; + guint32 expected_metric; + GPtrArray *entries; + GHashTableIter iter; + gpointer ptr; + GHashTable *changed_metrics = g_hash_table_new (NULL, NULL); + + entries = vtable->get_entries (priv); + + if (old_entry && old_entry->synced) { + /* The old version obviously changed. */ + g_hash_table_add (changed_metrics, GUINT_TO_POINTER (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->synced) { + last_metric = MAX (last_metric, (gint64) entry->effective_metric); + continue; + } + + expected_metric = entry->route.metric; + if ((gint64) expected_metric <= last_metric) + expected_metric = last_metric == G_MAXUINT32 ? G_MAXUINT32 : last_metric + 1; + + 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_hash_table_add (changed_metrics, GUINT_TO_POINTER (expected_metric)); + if (old_entry) + _LOGD (vtable->addr_family, LOG_ENTRY_FMT": update %s (%u -> %u)", LOG_ENTRY_ARGS (i, entry), vtable->platform_route_to_string (&entry->route), (guint) old_entry->effective_metric, (guint) expected_metric); + else + _LOGD (vtable->addr_family, LOG_ENTRY_FMT": add %s (%u)", LOG_ENTRY_ARGS (i, entry), vtable->platform_route_to_string (&entry->route), (guint) expected_metric); + } else if (entry->effective_metric != expected_metric) { + g_hash_table_add (changed_metrics, GUINT_TO_POINTER (entry->effective_metric)); + g_hash_table_add (changed_metrics, GUINT_TO_POINTER (expected_metric)); + _LOGD (vtable->addr_family, LOG_ENTRY_FMT": resync metric %s (%u -> %u)", LOG_ENTRY_ARGS (i, entry), vtable->platform_route_to_string (&entry->route), (guint) entry->effective_metric, (guint) expected_metric); + } + + entry->effective_metric = expected_metric; + last_metric = expected_metric; + } + + if (do_sync) { + g_hash_table_iter_init (&iter, changed_metrics); + while (g_hash_table_iter_next (&iter, &ptr, NULL)) + _platform_route_sync_add (vtable, self, GPOINTER_TO_UINT (ptr)); + _platform_route_sync_flush (vtable, self); + } + + g_hash_table_unref (changed_metrics); +} + +static void +_entry_at_idx_update (const VTableIP *vtable, NMDefaultRouteManager *self, guint entry_idx, const Entry *old_entry, gboolean do_sync) +{ + NMDefaultRouteManagerPrivate *priv = NM_DEFAULT_ROUTE_MANAGER_GET_PRIVATE (self); + Entry *entry; + NMDevice *device; + GPtrArray *entries; + + entries = vtable->get_entries (priv); + g_assert (entry_idx < entries->len); + + entry = g_ptr_array_index (entries, entry_idx); + + g_return_if_fail (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)); + + if (!entry->synced) { + entry->effective_metric = entry->route.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), + entry->synced ? "" : " (not synced)"); + } + + g_ptr_array_sort_with_data (entries, _sort_entries_cmp, NULL); + + _resync_all (vtable, self, entry, old_entry, do_sync); +} + +static void +_entry_at_idx_remove (const VTableIP *vtable, NMDefaultRouteManager *self, guint entry_idx, gboolean do_sync) +{ + 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->addr_family, LOG_ENTRY_FMT": remove %s (%u%s)", LOG_ENTRY_ARGS (entry_idx, entry), vtable->platform_route_to_string (&entry->route), (guint) entry->effective_metric, entry->synced ? "" : ", not synced"); + + /* 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, do_sync); + + _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; + int ip_ifindex; + GPtrArray *entries; + NMDevice *device = NULL; + gboolean synced; + + g_return_if_fail (NM_IS_DEFAULT_ROUTE_MANAGER (self)); + if (NM_IS_DEVICE (source)) + device = source; + else + g_return_if_reached (); + + ip_ifindex = nm_device_get_ip_ifindex (device); + + priv = NM_DEFAULT_ROUTE_MANAGER_GET_PRIVATE (self); + + entries = vtable->get_entries (priv); + entry = _entry_find_by_source (entries, source, &entry_idx); + + if ( entry + && entry->route.ifindex != ip_ifindex) { + /* Strange... the ifindex changed... Remove the device and start again. */ + _LOGD (vtable->addr_family, "ifindex of "LOG_ENTRY_FMT" changed: %d -> %d", + LOG_ENTRY_ARGS (entry_idx, entry), + entry->route.ifindex, ip_ifindex); + + g_object_freeze_notify (G_OBJECT (self)); + _entry_at_idx_remove (vtable, self, entry_idx, FALSE); + 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 (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); + } + 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 (!entry && !default_route) + /* nothing to do */; + else if (!entry) { + /* add */ + entry = g_slice_new0 (Entry); + entry->source.object = g_object_ref (source); + + if (VTABLE_IS_IP4) + entry->route4 = *((const NMPlatformIP4Route *) default_route); + else + entry->route6 = *((const NMPlatformIP6Route *) default_route); + + /* only use normalized metrics */ + entry->route.metric = vtable->route_metric_normalize (entry->route.metric); + entry->route.ifindex = ip_ifindex; + entry->effective_metric = entry->route.metric; + entry->synced = synced; + + g_ptr_array_add (entries, entry); + _entry_at_idx_update (vtable, self, entries->len - 1, NULL, TRUE); + } else if (default_route) { + /* update */ + Entry old_entry, new_entry; + + new_entry = *entry; + if (VTABLE_IS_IP4) + new_entry.route4 = *((const NMPlatformIP4Route *) default_route); + else + new_entry.route6 = *((const NMPlatformIP6Route *) default_route); + /* only use normalized metrics */ + new_entry.route.metric = vtable->route_metric_normalize (new_entry.route.metric); + new_entry.route.ifindex = ip_ifindex; + 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, TRUE); + } else { + /* delete */ + _entry_at_idx_remove (vtable, self, entry_idx, TRUE); + } +} + +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 void +_ipx_remove_default_route (const VTableIP *vtable, NMDefaultRouteManager *self, gpointer source) +{ + NMDefaultRouteManagerPrivate *priv; + guint entry_idx; + + g_return_if_fail (NM_IS_DEFAULT_ROUTE_MANAGER (self)); + g_return_if_fail (NM_IS_DEVICE (source)); + + priv = NM_DEFAULT_ROUTE_MANAGER_GET_PRIVATE (self); + + if (_entry_find_by_source (vtable->get_entries (priv), source, &entry_idx)) + _entry_at_idx_remove (vtable, self, entry_idx, TRUE); +} + +void +nm_default_route_manager_ip4_remove_default_route (NMDefaultRouteManager *self, gpointer source) +{ + _ipx_remove_default_route (&vtable_ip4, self, source); +} + +void +nm_default_route_manager_ip6_remove_default_route (NMDefaultRouteManager *self, gpointer source) +{ + _ipx_remove_default_route (&vtable_ip6, self, source); +} + +/***********************************************************************************/ + +static gint64 +_ipx_get_effective_metric (const VTableIP *vtable, NMDefaultRouteManager *self, NMDevice *device) +{ + NMDefaultRouteManagerPrivate *priv; + Entry *entry; + + g_return_val_if_fail (NM_IS_DEFAULT_ROUTE_MANAGER (self), -1); + g_return_val_if_fail (NM_IS_DEVICE (device), -1); + + priv = NM_DEFAULT_ROUTE_MANAGER_GET_PRIVATE (self); + + entry = _entry_find_by_source (vtable->get_entries (priv), device, NULL); + if (entry) + return entry->effective_metric; + return -1; +} + +gint64 +nm_default_route_manager_ip4_get_effective_metric (NMDefaultRouteManager *self, NMDevice *device) +{ + return _ipx_get_effective_metric (&vtable_ip4, self, device); +} + + +gint64 +nm_default_route_manager_ip6_get_effective_metric (NMDefaultRouteManager *self, NMDevice *device) +{ + return _ipx_get_effective_metric (&vtable_ip4, self, device); +} + +/***********************************************************************************/ + +static gboolean +_ipx_connection_has_default_route (const VTableIP *vtable, NMDefaultRouteManager *self, NMConnection *connection) +{ + const char *method; + NMSettingIPConfig *s_ip; + + g_return_val_if_fail (NM_IS_DEFAULT_ROUTE_MANAGER (self), FALSE); + g_return_val_if_fail (NM_IS_CONNECTION (connection), FALSE); + + if (VTABLE_IS_IP4) + s_ip = nm_connection_get_setting_ip4_config (connection); + else + s_ip = nm_connection_get_setting_ip6_config (connection); + if (!s_ip || nm_setting_ip_config_get_never_default (s_ip)) + return FALSE; + + if (VTABLE_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)) + return FALSE; + } 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)) + return FALSE; + } + + return TRUE; +} + +gboolean +nm_default_route_manager_ip4_connection_has_default_route (NMDefaultRouteManager *self, NMConnection *connection) +{ + return _ipx_connection_has_default_route (&vtable_ip4, self, connection); +} + +gboolean +nm_default_route_manager_ip6_connection_has_default_route (NMDefaultRouteManager *self, NMConnection *connection) +{ + return _ipx_connection_has_default_route (&vtable_ip6, self, connection); +} + +/***********************************************************************************/ + +static GPtrArray * +_v4_get_entries (NMDefaultRouteManagerPrivate *priv) +{ + return priv->entries_ip4; +} + +static GPtrArray * +_v6_get_entries (NMDefaultRouteManagerPrivate *priv) +{ + return priv->entries_ip6; +} + +static gboolean +_v4_platform_route_delete_default (int ifindex, guint32 metric) +{ + return nm_platform_ip4_route_delete (ifindex, 0, 0, metric); +} + +static gboolean +_v6_platform_route_delete_default (int ifindex, guint32 metric) +{ + return nm_platform_ip6_route_delete (ifindex, in6addr_any, 0, metric); +} + +static guint32 +_v4_route_metric_normalize (guint32 metric) +{ + return metric; +} + +static const VTableIP vtable_ip4 = { + .addr_family = AF_INET, + .get_entries = _v4_get_entries, + .platform_route_to_string = (const char *(*)(const NMPlatformIPRoute *)) nm_platform_ip4_route_to_string, + .platform_route_delete_default = _v4_platform_route_delete_default, + .route_metric_normalize = _v4_route_metric_normalize, +}; + +static const VTableIP vtable_ip6 = { + .addr_family = AF_INET6, + .get_entries = _v6_get_entries, + .platform_route_to_string = (const char *(*)(const NMPlatformIPRoute *)) nm_platform_ip6_route_to_string, + .platform_route_delete_default = _v6_platform_route_delete_default, + .route_metric_normalize = nm_utils_ip6_route_metric_normalize, +}; + +/***********************************************************************************/ + +NMDefaultRouteManager * +nm_default_route_manager_get () +{ + if (G_UNLIKELY (!_instance)) { + _instance = NM_DEFAULT_ROUTE_MANAGER (g_object_new (NM_TYPE_DEFAULT_ROUTE_MANAGER, NULL)); + g_object_add_weak_pointer (G_OBJECT (_instance), (gpointer *) &_instance); + } + return _instance; +} + +/***********************************************************************************/ + +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); +} + +static void +dispose (GObject *object) +{ + NMDefaultRouteManager *self = NM_DEFAULT_ROUTE_MANAGER (object); + NMDefaultRouteManagerPrivate *priv = NM_DEFAULT_ROUTE_MANAGER_GET_PRIVATE (self); + + 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; +} + diff --git a/src/nm-default-route-manager.h b/src/nm-default-route-manager.h new file mode 100644 index 0000000000..d60a2f86d4 --- /dev/null +++ b/src/nm-default-route-manager.h @@ -0,0 +1,64 @@ +/* -*- 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 + +#include "nm-connection.h" +#include "nm-types.h" + +#ifndef __NETWORKMANAGER_DEFAULT_ROUTE_MANAGER_H__ +#define __NETWORKMANAGER_DEFAULT_ROUTE_MANAGER_H__ + + +#define NM_TYPE_DEFAULT_ROUTE_MANAGER (nm_default_route_manager_get_type ()) +#define NM_DEFAULT_ROUTE_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DEFAULT_ROUTE_MANAGER, NMDefaultRouteManager)) +#define NM_DEFAULT_ROUTE_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_DEFAULT_ROUTE_MANAGER, NMDefaultRouteManagerClass)) +#define NM_IS_DEFAULT_ROUTE_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_DEFAULT_ROUTE_MANAGER)) +#define NM_IS_DEFAULT_ROUTE_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_DEFAULT_ROUTE_MANAGER)) +#define NM_DEFAULT_ROUTE_MANAGER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_DEFAULT_ROUTE_MANAGER, NMDefaultRouteManagerClass)) + + + +struct _NMDefaultRouteManager { + GObject parent; +}; + +typedef struct { + GObjectClass parent; +} NMDefaultRouteManagerClass; + +GType nm_default_route_manager_get_type (void); + +NMDefaultRouteManager *nm_default_route_manager_get (void); + +void nm_default_route_manager_ip4_update_default_route (NMDefaultRouteManager *manager, gpointer source); +void nm_default_route_manager_ip6_update_default_route (NMDefaultRouteManager *manager, gpointer source); + +void nm_default_route_manager_ip4_remove_default_route (NMDefaultRouteManager *manager, gpointer source); +void nm_default_route_manager_ip6_remove_default_route (NMDefaultRouteManager *manager, gpointer source); + +gint64 nm_default_route_manager_ip4_get_effective_metric (NMDefaultRouteManager *manager, NMDevice *device); +gint64 nm_default_route_manager_ip6_get_effective_metric (NMDefaultRouteManager *manager, NMDevice *device); + +gboolean nm_default_route_manager_ip4_connection_has_default_route (NMDefaultRouteManager *manager, NMConnection *connection); +gboolean nm_default_route_manager_ip6_connection_has_default_route (NMDefaultRouteManager *manager, NMConnection *connection); + +#endif /* NM_DEFAULT_ROUTE_MANAGER_H */ + diff --git a/src/nm-ip4-config.c b/src/nm-ip4-config.c index 3566b68569..5fddc73798 100644 --- a/src/nm-ip4-config.c +++ b/src/nm-ip4-config.c @@ -1844,8 +1844,14 @@ get_property (GObject *object, guint prop_id, for (i = 0; i < nroutes; i++) { const NMPlatformIP4Route *route = nm_ip4_config_get_route (config, i); - GArray *array = g_array_sized_new (FALSE, TRUE, sizeof (guint32), 4); + GArray *array; + /* legacy versions of nm_ip4_route_set_prefix() in libnm-util assert that the + * plen is positive. Skip the default routes not to break older clients. */ + if (NM_PLATFORM_IP_ROUTE_IS_DEFAULT (route)) + continue; + + array = g_array_sized_new (FALSE, TRUE, sizeof (guint32), 4); g_array_append_val (array, route->network); g_array_append_val (array, route->plen); g_array_append_val (array, route->gateway); diff --git a/src/nm-ip6-config.c b/src/nm-ip6-config.c index 3b0f291a91..9423d8a0f9 100644 --- a/src/nm-ip6-config.c +++ b/src/nm-ip6-config.c @@ -1743,12 +1743,18 @@ get_property (GObject *object, guint prop_id, int i; for (i = 0; i < nroutes; i++) { + GValueArray *array; const NMPlatformIP6Route *route = nm_ip6_config_get_route (config, i); - - GValueArray *array = g_value_array_new (4); GByteArray *ba; GValue element = G_VALUE_INIT; + /* legacy versions of nm_ip6_route_set_prefix() in libnm-util assert that the + * plen is positive. Skip the default routes not to break older clients. */ + if (NM_PLATFORM_IP_ROUTE_IS_DEFAULT (route)) + continue; + + array = g_value_array_new (4); + g_value_init (&element, DBUS_TYPE_G_UCHAR_ARRAY); ba = g_byte_array_new (); g_byte_array_append (ba, (guint8 *) &route->network, sizeof (route->network)); diff --git a/src/nm-policy.c b/src/nm-policy.c index 73f3bf4846..938343a274 100644 --- a/src/nm-policy.c +++ b/src/nm-policy.c @@ -32,6 +32,7 @@ #include "nm-activation-request.h" #include "nm-logging.h" #include "nm-device.h" +#include "nm-default-route-manager.h" #include "nm-dbus-manager.h" #include "nm-setting-ip4-config.h" #include "nm-setting-connection.h" @@ -102,17 +103,12 @@ get_best_ip4_device (NMPolicy *self, gboolean fully_activated) NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (self); const GSList *iter; NMDevice *best = NULL; - int best_prio = G_MAXINT; + guint32 best_prio = G_MAXUINT32; for (iter = nm_manager_get_devices (priv->manager); iter; iter = g_slist_next (iter)) { NMDevice *dev = NM_DEVICE (iter->data); - NMDeviceType devtype = nm_device_get_device_type (dev); NMDeviceState state = nm_device_get_state (dev); - NMActRequest *req; - NMConnection *connection; - NMSettingIPConfig *s_ip4; - int prio; - const char *method = NULL; + guint32 prio; if ( state <= NM_DEVICE_STATE_DISCONNECTED || state >= NM_DEVICE_STATE_DEACTIVATING) @@ -121,40 +117,19 @@ get_best_ip4_device (NMPolicy *self, gboolean fully_activated) if (fully_activated && state < NM_DEVICE_STATE_SECONDARIES) continue; - req = nm_device_get_act_request (dev); - g_assert (req); - connection = nm_act_request_get_connection (req); - g_assert (connection); - - method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP4_CONFIG); - /* If IPv4 is disabled or link-local-only, it can't be the default */ - if ( !strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_DISABLED) - || !strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_LINK_LOCAL)) - continue; - - /* 'never-default' devices can't ever be the default */ - s_ip4 = nm_connection_get_setting_ip4_config (connection); - g_assert (s_ip4); - if (nm_setting_ip_config_get_never_default (s_ip4)) + if (!nm_default_route_manager_ip4_connection_has_default_route (nm_default_route_manager_get (), + nm_device_get_connection (dev))) continue; if (fully_activated) { - NMIP4Config *ip4_config; + gint64 effective_metric = nm_default_route_manager_ip4_get_effective_metric (nm_default_route_manager_get (), dev); - ip4_config = nm_device_get_ip4_config (dev); - if (!ip4_config) + if (effective_metric == -1) continue; + prio = (guint32) effective_metric; + } else + prio = nm_device_get_ip6_route_metric (dev); - /* Make sure the device has a gateway */ - if (!nm_ip4_config_get_gateway (ip4_config) && (devtype != NM_DEVICE_TYPE_MODEM)) - continue; - - /* 'never-default' devices can't ever be the default */ - if (nm_ip4_config_get_never_default (ip4_config)) - continue; - } - - prio = nm_device_get_priority (dev); if ( prio < best_prio || (priv->default_device4 == dev && prio == best_prio) || !best) { @@ -186,17 +161,12 @@ get_best_ip6_device (NMPolicy *self, gboolean fully_activated) NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (self); const GSList *iter; NMDevice *best = NULL; - int best_prio = G_MAXINT; + guint32 best_prio = G_MAXUINT32; for (iter = nm_manager_get_devices (priv->manager); iter; iter = g_slist_next (iter)) { NMDevice *dev = NM_DEVICE (iter->data); - NMDeviceType devtype = nm_device_get_device_type (dev); NMDeviceState state = nm_device_get_state (dev); - NMActRequest *req; - NMConnection *connection; - NMSettingIPConfig *s_ip6; - int prio; - const char *method = NULL; + guint32 prio; if ( state <= NM_DEVICE_STATE_DISCONNECTED || state >= NM_DEVICE_STATE_DEACTIVATING) @@ -205,36 +175,20 @@ get_best_ip6_device (NMPolicy *self, gboolean fully_activated) if (fully_activated && state < NM_DEVICE_STATE_SECONDARIES) continue; - req = nm_device_get_act_request (dev); - g_assert (req); - connection = nm_act_request_get_connection (req); - g_assert (connection); - - method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP6_CONFIG); - if ( !strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_IGNORE) - || !strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL)) - continue; - - s_ip6 = nm_connection_get_setting_ip6_config (connection); - g_assert (s_ip6); - if (nm_setting_ip_config_get_never_default (s_ip6)) + if (!nm_default_route_manager_ip6_connection_has_default_route (nm_default_route_manager_get (), + nm_device_get_connection (dev))) continue; if (fully_activated) { - NMIP6Config *ip6_config; + gint64 effective_metric = nm_default_route_manager_ip6_get_effective_metric (nm_default_route_manager_get (), dev); - ip6_config = nm_device_get_ip6_config (dev); - if (!ip6_config) + if (effective_metric == -1) continue; + prio = (guint32) effective_metric; + } else + prio = nm_device_get_ip6_route_metric (dev); + prio = nm_utils_ip6_route_metric_normalize (prio); - if (!nm_ip6_config_get_gateway (ip6_config) && (devtype != NM_DEVICE_TYPE_MODEM)) - continue; - - if (nm_ip6_config_get_never_default (ip6_config)) - continue; - } - - prio = nm_device_get_priority (dev); if ( prio < best_prio || (priv->default_device6 == dev && prio == best_prio) || !best) { @@ -668,6 +622,7 @@ update_ip4_routing (NMPolicy *policy, gboolean force_update) if (vpn) { in_addr_t int_gw = nm_vpn_connection_get_ip4_internal_gateway (vpn); int mss = nm_ip4_config_get_mss (ip4_config); + guint32 route_metric = nm_vpn_connection_get_ip4_route_metric (vpn); /* If no VPN interface, use the parent interface */ if (ip_ifindex <= 0) @@ -675,39 +630,22 @@ update_ip4_routing (NMPolicy *policy, gboolean force_update) if (!nm_platform_ip4_route_add (ip_ifindex, NM_IP_CONFIG_SOURCE_VPN, 0, 0, int_gw, - NM_PLATFORM_ROUTE_METRIC_DEFAULT, mss)) { + route_metric, mss)) { if (int_gw) { (void) nm_platform_ip4_route_add (ip_ifindex, NM_IP_CONFIG_SOURCE_VPN, int_gw, 32, 0, - NM_PLATFORM_ROUTE_METRIC_DEFAULT, mss); + route_metric, mss); if (!nm_platform_ip4_route_add (ip_ifindex, NM_IP_CONFIG_SOURCE_VPN, 0, 0, int_gw, - NM_PLATFORM_ROUTE_METRIC_DEFAULT, mss)) + route_metric, mss)) nm_log_err (LOGD_IP4 | LOGD_VPN, "Failed to set IPv4 default route via VPN."); } else nm_log_err (LOGD_IP4 | LOGD_VPN, "Failed to set IPv4 default route via VPN."); } default_device = nm_active_connection_get_device (NM_ACTIVE_CONNECTION (vpn)); - } else { - int mss = nm_ip4_config_get_mss (ip4_config); - - g_assert (ip_iface); - if (!nm_platform_ip4_route_add (ip_ifindex, NM_IP_CONFIG_SOURCE_USER, - 0, 0, gw_addr, - NM_PLATFORM_ROUTE_METRIC_DEFAULT, mss)) { - (void) nm_platform_ip4_route_add (ip_ifindex, NM_IP_CONFIG_SOURCE_USER, - gw_addr, 32, 0, - NM_PLATFORM_ROUTE_METRIC_DEFAULT, mss); - if (!nm_platform_ip4_route_add (ip_ifindex, NM_IP_CONFIG_SOURCE_USER, - 0, 0, gw_addr, - NM_PLATFORM_ROUTE_METRIC_DEFAULT, mss)) { - nm_log_err (LOGD_IP4, "Failed to set default route."); - } - } - + } else default_device = best; - } update_default_ac (policy, best_ac, nm_active_connection_set_default); @@ -875,6 +813,7 @@ update_ip6_routing (NMPolicy *policy, gboolean force_update) if (vpn) { const struct in6_addr *int_gw = nm_vpn_connection_get_ip6_internal_gateway (vpn); int mss = nm_ip6_config_get_mss (ip6_config); + guint32 route_metric = nm_vpn_connection_get_ip6_route_metric (vpn); if (!int_gw) int_gw = &in6addr_any; @@ -885,37 +824,22 @@ update_ip6_routing (NMPolicy *policy, gboolean force_update) if (!nm_platform_ip6_route_add (ip_ifindex, NM_IP_CONFIG_SOURCE_VPN, in6addr_any, 0, *int_gw, - NM_PLATFORM_ROUTE_METRIC_DEFAULT, mss)) { + route_metric, mss)) { if (!IN6_IS_ADDR_UNSPECIFIED (int_gw)) { (void) nm_platform_ip6_route_add (ip_ifindex, NM_IP_CONFIG_SOURCE_VPN, *int_gw, 128, in6addr_any, - NM_PLATFORM_ROUTE_METRIC_DEFAULT, mss); + route_metric, mss); if (!nm_platform_ip6_route_add (ip_ifindex, NM_IP_CONFIG_SOURCE_VPN, in6addr_any, 0, *int_gw, - NM_PLATFORM_ROUTE_METRIC_DEFAULT, mss)) + route_metric, mss)) nm_log_err (LOGD_IP6 | LOGD_VPN, "Failed to set IPv6 default route via VPN."); } else nm_log_err (LOGD_IP6 | LOGD_VPN, "Failed to set IPv6 default route via VPN."); } default_device6 = nm_active_connection_get_device (NM_ACTIVE_CONNECTION (vpn)); - } else { - int mss = nm_ip6_config_get_mss (ip6_config); - - if (!nm_platform_ip6_route_add (ip_ifindex, NM_IP_CONFIG_SOURCE_USER, - in6addr_any, 0, *gw_addr, - NM_PLATFORM_ROUTE_METRIC_DEFAULT, mss)) { - (void) nm_platform_ip6_route_add (ip_ifindex, NM_IP_CONFIG_SOURCE_USER, - *gw_addr, 128, in6addr_any, - NM_PLATFORM_ROUTE_METRIC_DEFAULT, mss); - if (!nm_platform_ip6_route_add (ip_ifindex, NM_IP_CONFIG_SOURCE_USER, - in6addr_any, 0, *gw_addr, - NM_PLATFORM_ROUTE_METRIC_DEFAULT, mss)) - nm_log_err (LOGD_IP6, "Failed to set default route."); - } - + } else default_device6 = best; - } update_default_ac (policy, best_ac, nm_active_connection_set_default6); diff --git a/src/nm-types.h b/src/nm-types.h index c1c02ac60e..4a93567e11 100644 --- a/src/nm-types.h +++ b/src/nm-types.h @@ -29,6 +29,7 @@ typedef struct _NMAuthSubject NMAuthSubject; typedef struct _NMConnectionProvider NMConnectionProvider; typedef struct _NMConnectivity NMConnectivity; typedef struct _NMDBusManager NMDBusManager; +typedef struct _NMDefaultRouteManager NMDefaultRouteManager; typedef struct _NMDevice NMDevice; typedef struct _NMDhcp4Config NMDhcp4Config; typedef struct _NMDhcp6Config NMDhcp6Config;