diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c index 3d0bd936aa..ab105b5a7a 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -6332,10 +6332,11 @@ nm_device_set_ip4_config (NMDevice *self, nm_device_set_mtu (self, nm_ip4_config_get_mtu (new_config)); - /* for assumed devices we set the device_route_metric to the default which will - * stop nm_platform_ip4_address_sync() to replace the device routes. */ + /* For assumed devices we must not touch the kernel-routes, such as the device-route. + * FIXME: this is wrong in case where "assumed" means "take-over-seamlessly". In this + * case, we should manage the device route, for example on new DHCP lease. */ success = nm_ip4_config_commit (new_config, ip_ifindex, - assumed ? NM_PLATFORM_ROUTE_METRIC_IP4_DEVICE_ROUTE : default_route_metric); + assumed ? (gint64) -1 : (gint64) default_route_metric); if (!success) reason_local = NM_DEVICE_STATE_REASON_CONFIG_FAILED; } @@ -7133,6 +7134,11 @@ update_ip4_config (NMDevice *self, gboolean initial) capture_lease_config (self, priv->ext_ip4_config, &priv->dev_ip4_config, NULL, NULL); } + /* FIXME: ext_ip4_config does not contain routes with source==RTPROT_KERNEL. + * Hence, we will wrongly remove device-routes with metric=0 if they were added by + * the user on purpose. This should be fixed by also tracking and exposing + * kernel routes. */ + /* This function was called upon external changes. Remove the configuration * (adresses,routes) that is no longer present externally from the interal * config. This way, we don't readd addresses that were manually removed diff --git a/src/nm-ip4-config.c b/src/nm-ip4-config.c index e5fd25df06..e32f750395 100644 --- a/src/nm-ip4-config.c +++ b/src/nm-ip4-config.c @@ -34,6 +34,7 @@ #include "NetworkManagerUtils.h" #include "nm-core-internal.h" #include "nm-route-manager.h" +#include "gsystem-local-alloc.h" G_DEFINE_TYPE (NMIP4Config, nm_ip4_config, G_TYPE_OBJECT) @@ -253,25 +254,59 @@ nm_ip4_config_capture (int ifindex, gboolean capture_resolv_conf) } gboolean -nm_ip4_config_commit (const NMIP4Config *config, int ifindex, guint32 default_route_metric) +nm_ip4_config_commit (const NMIP4Config *config, int ifindex, gint64 default_route_metric) { NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); int i; + gs_unref_ptrarray GPtrArray *added_addresses = NULL; g_return_val_if_fail (ifindex > 0, FALSE); g_return_val_if_fail (config != NULL, FALSE); /* Addresses */ - nm_platform_ip4_address_sync (NM_PLATFORM_GET, ifindex, priv->addresses, default_route_metric); + nm_platform_ip4_address_sync (NM_PLATFORM_GET, ifindex, priv->addresses, + default_route_metric >= 0 ? &added_addresses : NULL); /* Routes */ { int count = nm_ip4_config_get_num_routes (config); GArray *routes = g_array_sized_new (FALSE, FALSE, sizeof (NMPlatformIP4Route), count); - const NMPlatformIP4Route *route; gboolean success; + gs_unref_array GArray *device_route_purge_list = NULL; + + if ( default_route_metric >= 0 + && added_addresses) { + /* For IPv6, we explicitly add the device-routes (onlink) to NMIP6Config. + * As we don't do that for IPv4, add it here shortly before syncing + * the routes. For NMRouteManager these routes are very much important. */ + for (i = 0; i < added_addresses->len; i++) { + const NMPlatformIP4Address *addr = added_addresses->pdata[i]; + NMPlatformIP4Route route = { 0 }; + + if (addr->plen == 0) + continue; + + route.ifindex = ifindex; + route.source = NM_IP_CONFIG_SOURCE_KERNEL; + route.network = nm_utils_ip4_address_clear_host_address (addr->address, addr->plen); + route.plen = addr->plen; + route.pref_src = addr->address; + route.metric = default_route_metric; + + g_array_append_val (routes, route); + + if (default_route_metric != NM_PLATFORM_ROUTE_METRIC_IP4_DEVICE_ROUTE) { + if (!device_route_purge_list) + device_route_purge_list = g_array_new (FALSE, FALSE, sizeof (NMPlatformIP4Route)); + route.metric = NM_PLATFORM_ROUTE_METRIC_IP4_DEVICE_ROUTE; + g_array_append_val (device_route_purge_list, route); + } + } + } for (i = 0; i < count; i++) { + const NMPlatformIP4Route *route; + route = nm_ip4_config_get_route (config, i); /* Don't add the route if it's more specific than one of the subnets @@ -281,10 +316,14 @@ nm_ip4_config_commit (const NMIP4Config *config, int ifindex, guint32 default_ro && nm_ip4_config_destination_is_direct (config, route->network, route->plen)) continue; + /* duplicates in @routes are no problem as route-manager handles them + * gracefully (by ignoring them). */ g_array_append_vals (routes, route, 1); } - success = nm_route_manager_ip4_route_sync (nm_route_manager_get (), ifindex, routes, TRUE); + nm_route_manager_ip4_route_register_device_route_purge_list (nm_route_manager_get (), device_route_purge_list); + + success = nm_route_manager_ip4_route_sync (nm_route_manager_get (), ifindex, routes, default_route_metric < 0); g_array_unref (routes); if (!success) return FALSE; diff --git a/src/nm-ip4-config.h b/src/nm-ip4-config.h index 2b3b24e6af..cd6efcc5c5 100644 --- a/src/nm-ip4-config.h +++ b/src/nm-ip4-config.h @@ -64,7 +64,7 @@ const char * nm_ip4_config_get_dbus_path (const NMIP4Config *config); /* Integration with nm-platform and nm-setting */ NMIP4Config *nm_ip4_config_capture (int ifindex, gboolean capture_resolv_conf); -gboolean nm_ip4_config_commit (const NMIP4Config *config, int ifindex, guint32 default_route_metric); +gboolean nm_ip4_config_commit (const NMIP4Config *config, int ifindex, gint64 default_route_metric); void nm_ip4_config_merge_setting (NMIP4Config *config, NMSettingIPConfig *setting, guint32 default_route_metric); NMSetting *nm_ip4_config_create_setting (const NMIP4Config *config); diff --git a/src/nm-route-manager.c b/src/nm-route-manager.c index a6763a2f83..403d9e7a2f 100644 --- a/src/nm-route-manager.c +++ b/src/nm-route-manager.c @@ -24,10 +24,18 @@ #include "nm-route-manager.h" #include "nm-platform.h" +#include "nmp-object.h" +#include "nm-core-internal.h" #include "nm-logging.h" #include "gsystem-local-alloc.h" #include "NetworkManagerUtils.h" +/* if within half a second after adding an IP address a matching device-route shows + * up, we delete it. */ +#define IP4_DEVICE_ROUTES_WAIT_TIME_NS (NM_UTILS_NS_PER_SECOND / 2) + +#define IP4_DEVICE_ROUTES_GC_INTERVAL_SEC (IP4_DEVICE_ROUTES_WAIT_TIME_NS * 2) + typedef struct { guint len; NMPlatformIPXRoute *entries[1]; @@ -38,11 +46,22 @@ typedef struct { RouteIndex *index; } RouteEntries; +typedef struct { + NMRouteManager *self; + gint64 scheduled_at_ns; + guint idle_id; + NMPObject *obj; +} IP4DeviceRoutePurgeEntry; + typedef struct { NMPlatform *platform; RouteEntries ip4_routes; RouteEntries ip6_routes; + struct { + GHashTable *entries; + guint gc_id; + } ip4_device_routes; } NMRouteManagerPrivate; #define NM_ROUTE_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_ROUTE_MANAGER, NMRouteManagerPrivate)) @@ -125,6 +144,10 @@ static const VTableIP vtable_v4, vtable_v6; /*********************************************************************************************/ +static gboolean _ip4_device_routes_cancel (NMRouteManager *self); + +/*********************************************************************************************/ + #if defined (NM_MORE_ASSERTS) && !defined (G_DISABLE_ASSERT) inline static void ASSERT_route_index_valid (const VTableIP *vtable, const GArray *entries, const RouteIndex *index, gboolean unique_ifindexes) @@ -234,6 +257,41 @@ _route_index_create (const VTableIP *vtable, const GArray *routes) return index; } +static int +_vx_route_id_cmp_full (const NMPlatformIPXRoute *r1, const NMPlatformIPXRoute *r2, const VTableIP *vtable) +{ + return vtable->route_id_cmp (r1, r2); +} + +static gssize +_route_index_find (const VTableIP *vtable, const RouteIndex *index, const NMPlatformIPXRoute *needle) +{ + gssize idx, idx2; + + idx = _nm_utils_ptrarray_find_binary_search ((gpointer *) index->entries, index->len, (gpointer) needle, (GCompareDataFunc) _vx_route_id_cmp_full, (gpointer) vtable); + if (idx < 0) + return idx; + + /* we only know that the route at index @idx has matching destination. Also find the one with the right + * ifindex by searching the neighbours */ + + idx2 = idx; + do { + if (index->entries[idx2]->rx.ifindex == needle->rx.ifindex) + return idx2; + } while ( idx2 > 0 + && vtable->route_id_cmp (index->entries[--idx2], needle) != 0); + + for (idx++; idx < index->len; idx++ ){ + if (vtable->route_id_cmp (index->entries[idx], needle) != 0) + break; + if (index->entries[idx]->rx.ifindex == needle->rx.ifindex) + return idx; + } + + return ~idx; +} + static guint _route_index_reverse_idx (const VTableIP *vtable, const RouteIndex *index, guint idx_idx, const GArray *routes) { @@ -638,6 +696,182 @@ nm_route_manager_route_flush (NMRouteManager *self, int ifindex) /*********************************************************************************************/ +static gboolean +_ip4_device_routes_entry_expired (const IP4DeviceRoutePurgeEntry *entry, gint64 now) +{ + return entry->scheduled_at_ns + IP4_DEVICE_ROUTES_WAIT_TIME_NS < now; +} + +static IP4DeviceRoutePurgeEntry * +_ip4_device_routes_purge_entry_create (NMRouteManager *self, const NMPlatformIP4Route *route, gint64 now_ns) +{ + IP4DeviceRoutePurgeEntry *entry; + + entry = g_slice_new (IP4DeviceRoutePurgeEntry); + + entry->self = self; + entry->scheduled_at_ns = now_ns; + entry->idle_id = 0; + entry->obj = nmp_object_new (NMP_OBJECT_TYPE_IP4_ROUTE, (NMPlatformObject *) route); + return entry; +} + +static void +_ip4_device_routes_purge_entry_free (IP4DeviceRoutePurgeEntry *entry) +{ + nmp_object_unref (entry->obj); + nm_clear_g_source (&entry->idle_id); + g_slice_free (IP4DeviceRoutePurgeEntry, entry); +} + +static gboolean +_ip4_device_routes_idle_cb (IP4DeviceRoutePurgeEntry *entry) +{ + NMRouteManager *self; + NMRouteManagerPrivate *priv; + + nm_clear_g_source (&entry->idle_id); + + self = entry->self; + priv = NM_ROUTE_MANAGER_GET_PRIVATE (self); + if (_route_index_find (&vtable_v4, priv->ip4_routes.index, &entry->obj->ipx_route) >= 0) { + /* we have an identical route in our list. Don't delete it. */ + return G_SOURCE_REMOVE; + } + + _LOGT (vtable_v4.vt->addr_family, "device-route: delete %s", nmp_object_to_string (entry->obj, NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0)); + + nm_platform_ip4_route_delete (priv->platform, + entry->obj->ip4_route.ifindex, + entry->obj->ip4_route.network, + entry->obj->ip4_route.plen, + entry->obj->ip4_route.metric); + + g_hash_table_remove (priv->ip4_device_routes.entries, entry->obj); + _ip4_device_routes_cancel (self); + return G_SOURCE_REMOVE; +} + +static void +_ip4_device_routes_ip4_route_changed (NMPlatform *platform, + NMPObjectType obj_type, + int ifindex, + const NMPlatformIP4Route *route, + NMPlatformSignalChangeType change_type, + NMPlatformReason reason, + NMRouteManager *self) +{ + NMRouteManagerPrivate *priv; + NMPObject obj_needle; + IP4DeviceRoutePurgeEntry *entry; + + if (change_type == NM_PLATFORM_SIGNAL_REMOVED) + return; + + if ( route->source != NM_IP_CONFIG_SOURCE_RTPROT_KERNEL + || route->metric != 0) { + /* we don't have an automatically created device route at hand. Bail out early. */ + return; + } + + priv = NM_ROUTE_MANAGER_GET_PRIVATE (self); + + entry = g_hash_table_lookup (priv->ip4_device_routes.entries, + nmp_object_stackinit (&obj_needle, NMP_OBJECT_TYPE_IP4_ROUTE, (NMPlatformObject *) route)); + if (!entry) + return; + + if (_ip4_device_routes_entry_expired (entry, nm_utils_get_monotonic_timestamp_ns ())) { + _LOGT (vtable_v4.vt->addr_family, "device-route: cleanup-ch %s", nmp_object_to_string (entry->obj, NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0)); + g_hash_table_remove (priv->ip4_device_routes.entries, entry->obj); + _ip4_device_routes_cancel (self); + return; + } + + if (entry->idle_id == 0) { + _LOGT (vtable_v4.vt->addr_family, "device-route: schedule %s", nmp_object_to_string (entry->obj, NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0)); + entry->idle_id = g_idle_add ((GSourceFunc) _ip4_device_routes_idle_cb, entry); + } +} + +static gboolean +_ip4_device_routes_cancel (NMRouteManager *self) +{ + NMRouteManagerPrivate *priv = NM_ROUTE_MANAGER_GET_PRIVATE (self); + + if (priv->ip4_device_routes.gc_id) { + if (g_hash_table_size (priv->ip4_device_routes.entries) > 0) + return G_SOURCE_CONTINUE; + _LOGT (vtable_v4.vt->addr_family, "device-route: cancel"); + if (priv->platform) + g_signal_handlers_disconnect_by_func (priv->platform, G_CALLBACK (_ip4_device_routes_ip4_route_changed), self); + nm_clear_g_source (&priv->ip4_device_routes.gc_id); + } + return G_SOURCE_REMOVE; +} + +static gboolean +_ip4_device_routes_gc (NMRouteManager *self) +{ + NMRouteManagerPrivate *priv; + GHashTableIter iter; + IP4DeviceRoutePurgeEntry *entry; + gint64 now = nm_utils_get_monotonic_timestamp_ns (); + + priv = NM_ROUTE_MANAGER_GET_PRIVATE (self); + + g_hash_table_iter_init (&iter, priv->ip4_device_routes.entries); + while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &entry)) { + if (_ip4_device_routes_entry_expired (entry, now)) { + _LOGT (vtable_v4.vt->addr_family, "device-route: cleanup-gc %s", nmp_object_to_string (entry->obj, NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0)); + g_hash_table_iter_remove (&iter); + } + } + + return _ip4_device_routes_cancel (self); +} + +/** + * nm_route_manager_ip4_route_register_device_route_purge_list: + * + * When adding an IPv4 address, kernel will automatically add a device route with + * metric zero. We don't want that route and want to delete it. However, the route + * by kernel immediately, but some time after. That means during nm_route_manager_ip4_route_sync() + * such a route doesn't exist yet. We must remember that we expect such a route to appear later + * and to remove it. */ +void +nm_route_manager_ip4_route_register_device_route_purge_list (NMRouteManager *self, GArray *device_route_purge_list) +{ + NMRouteManagerPrivate *priv; + guint i; + gint64 now_ns; + + if (!device_route_purge_list || device_route_purge_list->len == 0) + return; + + priv = NM_ROUTE_MANAGER_GET_PRIVATE (self); + + now_ns = nm_utils_get_monotonic_timestamp_ns (); + for (i = 0; i < device_route_purge_list->len; i++) { + IP4DeviceRoutePurgeEntry *entry; + + entry = _ip4_device_routes_purge_entry_create (self, &g_array_index (device_route_purge_list, NMPlatformIP4Route, i), now_ns); + _LOGT (vtable_v4.vt->addr_family, "device-route: watch (%s) %s", + g_hash_table_contains (priv->ip4_device_routes.entries, entry->obj) + ? "update" : "new", + nmp_object_to_string (entry->obj, NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0)); + g_hash_table_replace (priv->ip4_device_routes.entries, + nmp_object_ref (entry->obj), + entry); + } + if (priv->ip4_device_routes.gc_id == 0) { + g_signal_connect (priv->platform, NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED, G_CALLBACK (_ip4_device_routes_ip4_route_changed), self); + priv->ip4_device_routes.gc_id = g_timeout_add (IP4_DEVICE_ROUTES_GC_INTERVAL_SEC, (GSourceFunc) _ip4_device_routes_gc, self); + } +} + +/*********************************************************************************************/ + static const VTableIP vtable_v4 = { .vt = &nm_platform_vtable_route_v4, .route_id_cmp = (int (*) (const NMPlatformIPXRoute *, const NMPlatformIPXRoute *)) _v4_route_id_cmp, @@ -661,13 +895,21 @@ nm_route_manager_init (NMRouteManager *self) priv->ip6_routes.entries = g_array_new (FALSE, FALSE, sizeof (NMPlatformIP6Route)); priv->ip4_routes.index = _route_index_create (&vtable_v4, priv->ip4_routes.entries); priv->ip6_routes.index = _route_index_create (&vtable_v6, priv->ip6_routes.entries); + priv->ip4_device_routes.entries = g_hash_table_new_full ((GHashFunc) nmp_object_id_hash, + (GEqualFunc) nmp_object_id_equal, + (GDestroyNotify) nmp_object_unref, + (GDestroyNotify) _ip4_device_routes_purge_entry_free); } static void dispose (GObject *object) { + NMRouteManager *self = NM_ROUTE_MANAGER (object); NMRouteManagerPrivate *priv = NM_ROUTE_MANAGER_GET_PRIVATE (object); + g_hash_table_remove_all (priv->ip4_device_routes.entries); + _ip4_device_routes_cancel (self); + g_clear_object (&priv->platform); G_OBJECT_CLASS (nm_route_manager_parent_class)->dispose (object); @@ -683,6 +925,8 @@ finalize (GObject *object) g_free (priv->ip4_routes.index); g_free (priv->ip6_routes.index); + g_hash_table_unref (priv->ip4_device_routes.entries); + G_OBJECT_CLASS (nm_route_manager_parent_class)->finalize (object); } diff --git a/src/nm-route-manager.h b/src/nm-route-manager.h index 7b2d16f756..fdd310b73b 100644 --- a/src/nm-route-manager.h +++ b/src/nm-route-manager.h @@ -46,6 +46,8 @@ gboolean nm_route_manager_ip4_route_sync (NMRouteManager *self, int ifindex, con gboolean nm_route_manager_ip6_route_sync (NMRouteManager *self, int ifindex, const GArray *known_routes, gboolean ignore_kernel_routes); gboolean nm_route_manager_route_flush (NMRouteManager *self, int ifindex); +void nm_route_manager_ip4_route_register_device_route_purge_list (NMRouteManager *self, GArray *device_route_purge_list); + NMRouteManager *nm_route_manager_get (void); #endif /* NM_ROUTE_MANAGER_H */ diff --git a/src/platform/nm-fake-platform.c b/src/platform/nm-fake-platform.c index 37bec90884..147e956167 100644 --- a/src/platform/nm-fake-platform.c +++ b/src/platform/nm-fake-platform.c @@ -1025,12 +1025,6 @@ ip6_address_exists (NMPlatform *platform, int ifindex, struct in6_addr addr, int return FALSE; } -static gboolean -ip4_check_reinstall_device_route (NMPlatform *platform, int ifindex, const NMPlatformIP4Address *address, guint32 device_route_metric) -{ - return FALSE; -} - /******************************************************************/ static GArray * @@ -1468,8 +1462,6 @@ nm_fake_platform_class_init (NMFakePlatformClass *klass) platform_class->ip4_address_exists = ip4_address_exists; platform_class->ip6_address_exists = ip6_address_exists; - platform_class->ip4_check_reinstall_device_route = ip4_check_reinstall_device_route; - platform_class->ip4_route_get_all = ip4_route_get_all; platform_class->ip6_route_get_all = ip6_route_get_all; platform_class->ip4_route_add = ip4_route_add; diff --git a/src/platform/nm-linux-platform.c b/src/platform/nm-linux-platform.c index a993d3fe1d..7d457fe4eb 100644 --- a/src/platform/nm-linux-platform.c +++ b/src/platform/nm-linux-platform.c @@ -4182,64 +4182,6 @@ ip6_address_exists (NMPlatform *platform, int ifindex, struct in6_addr addr, int return nmp_object_is_visible (nmp_cache_lookup_obj (NM_LINUX_PLATFORM_GET_PRIVATE (platform)->cache, &obj_needle)); } -static gboolean -ip4_check_reinstall_device_route (NMPlatform *platform, int ifindex, const NMPlatformIP4Address *address, guint32 device_route_metric) -{ - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - guint32 device_network; - NMPObject obj_needle; - const NMPlatformIP4Address *const *addresses; - const NMPlatformIP4Route *const *routes; - - device_network = nm_utils_ip4_address_clear_host_address (address->address, address->plen); - - /* in many cases we expect the route to already exist. So first do an exact lookup - * to save the O(n) access below. */ - nmp_object_stackinit_id_ip4_route (&obj_needle, ifindex, device_network, address->plen, device_route_metric); - if (nmp_cache_lookup_obj (priv->cache, &obj_needle)) { - /* There is already a route with metric 0 or the metric we want to install - * for the same subnet. */ - return FALSE; - } - if (obj_needle.ip4_route.metric != 0) { - obj_needle.ip4_route.metric = 0; - if (nmp_cache_lookup_obj (priv->cache, &obj_needle)) - return FALSE; - } - - /* also check whether we already have the same address configured on *any* device. */ - addresses = cache_lookup_all_objects (NMPlatformIP4Address, platform, NMP_OBJECT_TYPE_IP4_ADDRESS, FALSE); - if (addresses) { - for (; *addresses; addresses++) { - const NMPlatformIP4Address *addr_candidate = *addresses; - - if ( addr_candidate->plen == address->plen - && addr_candidate->address == device_network) { - /* If we already have the same address installed on any interface, - * we back off. */ - return FALSE; - } - } - } - - routes = cache_lookup_all_objects (NMPlatformIP4Route, platform, NMP_OBJECT_TYPE_IP4_ROUTE, FALSE); - if (routes) { - for (; *routes; routes++) { - const NMPlatformIP4Route *route_candidate = *routes; - - if ( route_candidate->network == device_network - && route_candidate->plen == address->plen - && (route_candidate->metric == 0 || route_candidate->metric == device_route_metric)) { - /* If we already have the same address installed on any interface, - * we back off. */ - return FALSE; - } - } - } - - return TRUE; -} - /******************************************************************/ static GArray * @@ -5053,8 +4995,6 @@ nm_linux_platform_class_init (NMLinuxPlatformClass *klass) platform_class->ip4_address_exists = ip4_address_exists; platform_class->ip6_address_exists = ip6_address_exists; - platform_class->ip4_check_reinstall_device_route = ip4_check_reinstall_device_route; - platform_class->ip4_route_get_all = ip4_route_get_all; platform_class->ip6_route_get_all = ip6_route_get_all; platform_class->ip4_route_add = ip4_route_add; diff --git a/src/platform/nm-platform.c b/src/platform/nm-platform.c index 41bc19a0b5..6faad3b2ce 100644 --- a/src/platform/nm-platform.c +++ b/src/platform/nm-platform.c @@ -1991,32 +1991,15 @@ array_contains_ip6_address (const GArray *addresses, const NMPlatformIP6Address return FALSE; } -gboolean -nm_platform_ip4_check_reinstall_device_route (NMPlatform *self, int ifindex, const NMPlatformIP4Address *address, guint32 device_route_metric) -{ - _CHECK_SELF (self, klass, FALSE); - - if ( ifindex <= 0 - || address->plen <= 0 - || address->plen >= 32) - return FALSE; - - if (device_route_metric == NM_PLATFORM_ROUTE_METRIC_IP4_DEVICE_ROUTE) { - /* The automatically added route would be already our desired priority. - * Nothing to do. */ - return FALSE; - } - - return klass->ip4_check_reinstall_device_route (self, ifindex, address, device_route_metric); -} - /** * nm_platform_ip4_address_sync: * @self: platform instance * @ifindex: Interface index * @known_addresses: List of addresses - * @device_route_metric: the route metric for adding subnet routes (replaces - * the kernel added routes). + * @out_added_addresses: (out): (allow-none): if not %NULL, return a #GPtrArray + * with the addresses added. The pointers point into @known_addresses. + * It possibly does not contain all addresses from @known_address because + * some addresses might be expired. * * A convenience function to synchronize addresses for a specific interface * with the least possible disturbance. It simply removes addresses that are @@ -2025,7 +2008,7 @@ nm_platform_ip4_check_reinstall_device_route (NMPlatform *self, int ifindex, con * Returns: %TRUE on success. */ gboolean -nm_platform_ip4_address_sync (NMPlatform *self, int ifindex, const GArray *known_addresses, guint32 device_route_metric) +nm_platform_ip4_address_sync (NMPlatform *self, int ifindex, const GArray *known_addresses, GPtrArray **out_added_addresses) { GArray *addresses; NMPlatformIP4Address *address; @@ -2044,6 +2027,9 @@ nm_platform_ip4_address_sync (NMPlatform *self, int ifindex, const GArray *known } g_array_free (addresses, TRUE); + if (out_added_addresses) + *out_added_addresses = NULL; + if (!known_addresses) return TRUE; @@ -2051,33 +2037,18 @@ nm_platform_ip4_address_sync (NMPlatform *self, int ifindex, const GArray *known for (i = 0; i < known_addresses->len; i++) { const NMPlatformIP4Address *known_address = &g_array_index (known_addresses, NMPlatformIP4Address, i); guint32 lifetime, preferred; - guint32 network; - gboolean reinstall_device_route = FALSE; if (!nmp_utils_lifetime_get (known_address->timestamp, known_address->lifetime, known_address->preferred, now, ADDRESS_LIFETIME_PADDING, &lifetime, &preferred)) continue; - if (nm_platform_ip4_check_reinstall_device_route (self, ifindex, known_address, device_route_metric)) - reinstall_device_route = TRUE; - if (!nm_platform_ip4_address_add (self, ifindex, known_address->address, known_address->peer_address, known_address->plen, lifetime, preferred, known_address->label)) return FALSE; - if (reinstall_device_route) { - /* Kernel automatically adds a device route for us with metric 0. That is not what we want. - * Remove it, and re-add it. - * - * In face of having the same subnets on two different interfaces with the same metric, - * this is a problem. Surprisingly, kernel is able to add two routes for the same subnet/prefix,metric - * to different interfaces. We cannot. Adding one, would replace the other. This is avoided - * by the above nm_platform_ip4_check_reinstall_device_route() check. - */ - network = nm_utils_ip4_address_clear_host_address (known_address->address, known_address->plen); - (void) nm_platform_ip4_route_add (self, ifindex, NM_IP_CONFIG_SOURCE_KERNEL, network, known_address->plen, - 0, known_address->address, device_route_metric, 0); - (void) nm_platform_ip4_route_delete (self, ifindex, network, known_address->plen, - NM_PLATFORM_ROUTE_METRIC_IP4_DEVICE_ROUTE); + if (out_added_addresses) { + if (!*out_added_addresses) + *out_added_addresses = g_ptr_array_new (); + g_ptr_array_add (*out_added_addresses, (gpointer) known_address); } } @@ -2145,8 +2116,8 @@ nm_platform_address_flush (NMPlatform *self, int ifindex) { _CHECK_SELF (self, klass, FALSE); - return nm_platform_ip4_address_sync (self, ifindex, NULL, 0) - && nm_platform_ip6_address_sync (self, ifindex, NULL, FALSE); + return nm_platform_ip4_address_sync (self, ifindex, NULL, NULL) + && nm_platform_ip6_address_sync (self, ifindex, NULL, FALSE); } /******************************************************************/ diff --git a/src/platform/nm-platform.h b/src/platform/nm-platform.h index e6305c325e..a8bc5a43b2 100644 --- a/src/platform/nm-platform.h +++ b/src/platform/nm-platform.h @@ -520,8 +520,6 @@ typedef struct { gboolean (*ip4_address_exists) (NMPlatform *, int ifindex, in_addr_t address, int plen); gboolean (*ip6_address_exists) (NMPlatform *, int ifindex, struct in6_addr address, int plen); - gboolean (*ip4_check_reinstall_device_route) (NMPlatform *, int ifindex, const NMPlatformIP4Address *address, guint32 device_route_metric); - GArray * (*ip4_route_get_all) (NMPlatform *, int ifindex, NMPlatformGetRouteFlags flags); GArray * (*ip6_route_get_all) (NMPlatform *, int ifindex, NMPlatformGetRouteFlags flags); gboolean (*ip4_route_add) (NMPlatform *, int ifindex, NMIPConfigSource source, @@ -706,12 +704,10 @@ gboolean nm_platform_ip4_address_delete (NMPlatform *self, int ifindex, in_addr_ gboolean nm_platform_ip6_address_delete (NMPlatform *self, int ifindex, struct in6_addr address, int plen); gboolean nm_platform_ip4_address_exists (NMPlatform *self, int ifindex, in_addr_t address, int plen); gboolean nm_platform_ip6_address_exists (NMPlatform *self, int ifindex, struct in6_addr address, int plen); -gboolean nm_platform_ip4_address_sync (NMPlatform *self, int ifindex, const GArray *known_addresses, guint32 device_route_metric); +gboolean nm_platform_ip4_address_sync (NMPlatform *self, int ifindex, const GArray *known_addresses, GPtrArray **out_added_addresses); gboolean nm_platform_ip6_address_sync (NMPlatform *self, int ifindex, const GArray *known_addresses, gboolean keep_link_local); gboolean nm_platform_address_flush (NMPlatform *self, int ifindex); -gboolean nm_platform_ip4_check_reinstall_device_route (NMPlatform *self, int ifindex, const NMPlatformIP4Address *address, guint32 device_route_metric); - GArray *nm_platform_ip4_route_get_all (NMPlatform *self, int ifindex, NMPlatformGetRouteFlags flags); GArray *nm_platform_ip6_route_get_all (NMPlatform *self, int ifindex, NMPlatformGetRouteFlags flags); gboolean nm_platform_ip4_route_add (NMPlatform *self, int ifindex, NMIPConfigSource source,