diff options
author | Beniamino Galvani <bgalvani@redhat.com> | 2016-11-14 20:52:24 +0100 |
---|---|---|
committer | Beniamino Galvani <bgalvani@redhat.com> | 2016-11-14 21:38:15 +0100 |
commit | 969350812b052833b10c7d1c5f831832039b2d65 (patch) | |
tree | 13c3cdd119ed99bb12cc9e8be982f1861ad91ea7 | |
parent | 4c0f29f7da673cf8177537b1d18d9a8f19518e10 (diff) | |
download | NetworkManager-bg/rh1394500.tar.gz |
platform: fix the order of addition of primary and secondary IPv4 addressesbg/rh1394500
nm_platform_ip4_address_sync() tries to apply the new configuration
with the minimum effort and doesn't delete addresses if they are
already present on the interface. This can break the ordering, as an
existing address would be promoted by kernel to primary, even if it
was last in our configuration.
Add some logic to ensure the correct order of addresses is always
enforced.
-rw-r--r-- | src/platform/nm-platform.c | 63 |
1 files changed, 59 insertions, 4 deletions
diff --git a/src/platform/nm-platform.c b/src/platform/nm-platform.c index 0b1d25d302..48e2de6812 100644 --- a/src/platform/nm-platform.c +++ b/src/platform/nm-platform.c @@ -2672,6 +2672,7 @@ array_contains_ip4_address (const GArray *addresses, const NMPlatformIP4Address if ( candidate->address == address->address && candidate->plen == address->plen + && (candidate->n_ifa_flags & IFA_F_SECONDARY) == (address->n_ifa_flags & IFA_F_SECONDARY) && ((candidate->peer_address ^ address->peer_address) & nm_utils_ip4_prefix_to_netmask (address->plen)) == 0) { guint32 lifetime, preferred; @@ -2727,17 +2728,71 @@ nm_platform_ip4_address_sync (NMPlatform *self, int ifindex, const GArray *known GArray *addresses; NMPlatformIP4Address *address; gint32 now = nm_utils_get_monotonic_timestamp_s (); - int i; + gs_unref_hashtable GHashTable *subnets = NULL; + GPtrArray *ptr; + guint32 net; + int i, j; _CHECK_SELF (self, klass, FALSE); - /* Delete unknown addresses */ + subnets = g_hash_table_new_full (g_direct_hash, + g_direct_equal, + NULL, + (GDestroyNotify) g_ptr_array_unref); addresses = nm_platform_ip4_address_get_all (self, ifindex); + + /* Build a hash table of all addresses per subnet */ + for (i = 0; i < addresses->len; i++) { + address = &g_array_index (addresses, NMPlatformIP4Address, i); + net = address->address & nm_utils_ip4_prefix_to_netmask (address->plen); + ptr = g_hash_table_lookup (subnets, GUINT_TO_POINTER (net)); + if (!ptr) { + ptr = g_ptr_array_new (); + g_hash_table_insert (subnets, GUINT_TO_POINTER (net), ptr); + } + g_ptr_array_insert (ptr, + (address->n_ifa_flags & IFA_F_SECONDARY) ? -1 : 0, + address); + } + + /* Delete unknown addresses */ for (i = 0; i < addresses->len; i++) { address = &g_array_index (addresses, NMPlatformIP4Address, i); - if (!array_contains_ip4_address (known_addresses, address, now)) - nm_platform_ip4_address_delete (self, ifindex, address->address, address->plen, address->peer_address); + if (!address->ifindex) { + /* Already deleted */ + continue; + } + + if (!array_contains_ip4_address (known_addresses, address, now)) { + nm_platform_ip4_address_delete (self, ifindex, + address->address, + address->plen, + address->peer_address); + + /* Check if we just deleted a primary addresses with secondary ones ... */ + net = address->address & nm_utils_ip4_prefix_to_netmask (address->plen); + ptr = g_hash_table_lookup (subnets, GUINT_TO_POINTER (net)); + g_return_val_if_fail (ptr, FALSE); + + if (ptr->len > 1 && ptr->pdata[0] == address) { + /* ... if so, the kernel can do two things, depending on version + * and sysctl setting: delete also secondary addresses or + * promote a secondary to primary. We could resync the platform + * cache to know what happened, but probably it's not a good + * idea doing it here since it would cause the execution of + * handlers. Instead, just ensure that secondary addresses are + * deleted, so that we can start with a clean slate. */ + for (j = 1; j < ptr->len; j++) { + address = ptr->pdata[j]; + nm_platform_ip4_address_delete (self, ifindex, + address->address, + address->plen, + address->peer_address); + address->ifindex = 0; + } + } + } } g_array_free (addresses, TRUE); |