diff options
author | Beniamino Galvani <bgalvani@redhat.com> | 2017-12-11 10:41:07 +0100 |
---|---|---|
committer | Beniamino Galvani <bgalvani@redhat.com> | 2017-12-11 14:51:58 +0100 |
commit | fd0cf6a46248b4e0187b0324b6f2170ee8a0f18e (patch) | |
tree | fcaf94e8b67311ba532649d5d551b64f855fb443 | |
parent | 4d1796b2c7caf67805704218e554fc39ceb94614 (diff) | |
download | NetworkManager-bg/ipv6-order.tar.gz |
platform: improve ipv6 addresses synchronizationbg/ipv6-order
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.
-rw-r--r-- | src/platform/nm-platform.c | 50 |
1 files 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; + } } } |