From fd0cf6a46248b4e0187b0324b6f2170ee8a0f18e Mon Sep 17 00:00:00 2001 From: Beniamino Galvani Date: Mon, 11 Dec 2017 10:41:07 +0100 Subject: platform: improve ipv6 addresses synchronization nm_platform_ip6_address_sync() must take care not only of adding missing addresses and removing unknown addresses, but also of the order in which they are added. The order is important because it determines which address is preferred by kernel. Since we can only add addresses at the top of the list, in order to change the position of an address we must first remove it and then re-add it in the right position. --- src/platform/nm-platform.c | 50 ++++++++++++++++++++++++++++++++++++---------- 1 file changed, 40 insertions(+), 10 deletions(-) diff --git a/src/platform/nm-platform.c b/src/platform/nm-platform.c index 0427a3687e..b7cbd30ecb 100644 --- a/src/platform/nm-platform.c +++ b/src/platform/nm-platform.c @@ -3109,8 +3109,8 @@ nm_platform_ip6_address_get (NMPlatform *self, int ifindex, struct in6_addr addr return NMP_OBJECT_CAST_IP6_ADDRESS (obj); } -static gboolean -array_contains_ip6_address (const GPtrArray *addresses, const NMPlatformIP6Address *address, gint32 now) +static int +array_ip6_address_position (const GPtrArray *addresses, const NMPlatformIP6Address *address, gint32 now) { guint len = addresses ? addresses->len : 0; guint i; @@ -3123,11 +3123,11 @@ array_contains_ip6_address (const GPtrArray *addresses, const NMPlatformIP6Addre if (nm_utils_lifetime_get (candidate->timestamp, candidate->lifetime, candidate->preferred, now, &lifetime, &preferred)) - return TRUE; + return i; } } - return FALSE; + return -1; } static gboolean @@ -3482,26 +3482,56 @@ nm_platform_ip6_address_sync (NMPlatform *self, gs_unref_ptrarray GPtrArray *plat_addresses = NULL; NMPlatformIP6Address *address; gint32 now = nm_utils_get_monotonic_timestamp_s (); - guint i; + int i, position; NMPLookup lookup; guint32 ifa_flags; + gboolean remove = FALSE; - /* Delete unknown addresses */ plat_addresses = nm_platform_lookup_clone (self, nmp_lookup_init_addrroute (&lookup, NMP_OBJECT_TYPE_IP6_ADDRESS, ifindex), NULL, NULL); if (plat_addresses) { - for (i = 0; i < plat_addresses->len; i++) { + /* First, remove link-local and unknown addresses from platform */ + for (i = 0; i < plat_addresses->len; ) { address = NMP_OBJECT_CAST_IP6_ADDRESS (plat_addresses->pdata[i]); + if (IN6_IS_ADDR_LINKLOCAL (&address->address)) { + if (!keep_link_local) + nm_platform_ip6_address_delete (self, ifindex, address->address, address->plen); + /* Always remove link-local addresses from the array + * so that they are ignored when computing addresses + * positions below. */ + g_ptr_array_remove_index (plat_addresses, i); + continue; + } - /* Leave link local address management to the kernel */ - if (keep_link_local && IN6_IS_ADDR_LINKLOCAL (&address->address)) + position = array_ip6_address_position (known_addresses, address, now); + if (position < 0) { + /* Unknown address */ + nm_platform_ip6_address_delete (self, ifindex, address->address, address->plen); + g_ptr_array_remove_index (plat_addresses, i); continue; + } + + i++; + } - if (!array_contains_ip6_address (known_addresses, address, now)) + /* @plat_addresses and @known_addresses are in increasing priority + * order; when we add a new address to platform, kernel adds it with + * top priority. + * In the second pass, start from addresses with lower priority and + * remove them if they are in a wrong position. Removing an address + * also removes all addresses with higher priority. + * */ + for (i = 0; i < plat_addresses->len; i++) { + address = NMP_OBJECT_CAST_IP6_ADDRESS (plat_addresses->pdata[i]); + position = array_ip6_address_position (known_addresses, address, now); + nm_assert (i >= 0); + if (remove || i != position) { nm_platform_ip6_address_delete (self, ifindex, address->address, address->plen); + remove = TRUE; + } } } -- cgit v1.2.1