summaryrefslogtreecommitdiff
path: root/src/platform/nm-platform.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/platform/nm-platform.c')
-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;
+ }
}
}