summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBeniamino Galvani <bgalvani@redhat.com>2017-12-11 10:41:07 +0100
committerBeniamino Galvani <bgalvani@redhat.com>2017-12-11 14:51:58 +0100
commitfd0cf6a46248b4e0187b0324b6f2170ee8a0f18e (patch)
treefcaf94e8b67311ba532649d5d551b64f855fb443
parent4d1796b2c7caf67805704218e554fc39ceb94614 (diff)
downloadNetworkManager-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.c50
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;
+ }
}
}