From a60a262574206976eacc405633c059e0f375f0a8 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Mon, 28 Mar 2022 22:26:15 +0200 Subject: [PATCH 1/6] platform: add nm_platform_ip_address_delete() helper --- src/libnm-platform/nm-platform.h | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/libnm-platform/nm-platform.h b/src/libnm-platform/nm-platform.h index d20b9e5543..c6d6d6917a 100644 --- a/src/libnm-platform/nm-platform.h +++ b/src/libnm-platform/nm-platform.h @@ -2129,6 +2129,29 @@ gboolean nm_platform_ip4_address_delete(NMPlatform *self, gboolean nm_platform_ip6_address_delete(NMPlatform *self, int ifindex, struct in6_addr address, guint8 plen); +static inline gboolean +nm_platform_ip_address_delete(NMPlatform *self, + int addr_family, + int ifindex, + gconstpointer /* (const NMPlatformIPAddress *) */ addr) +{ + if (NM_IS_IPv4(addr_family)) { + const NMPlatformIP4Address *a = addr; + + if (ifindex <= 0) + ifindex = a->ifindex; + + return nm_platform_ip4_address_delete(self, ifindex, a->address, a->plen, a->peer_address); + } else { + const NMPlatformIP6Address *a = addr; + + if (ifindex <= 0) + ifindex = a->ifindex; + + return nm_platform_ip6_address_delete(self, ifindex, a->address, a->plen); + } +} + gboolean nm_platform_ip_address_sync(NMPlatform *self, int addr_family, int ifindex, From 80f8e23992b58aa0b6fd88de0d3973eea51691a4 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Mon, 28 Mar 2022 21:13:09 +0200 Subject: [PATCH 2/6] platform: fix address order in nm_platform_ip_address_sync() In the past, nm_platform_ip_address_sync() only had the @known_addresses argument. We would figure out which addresses to delete and which to preserve, based on what addresses were known. That means, @known_addresses must have contained all the addresses we wanted to preserve, even the external ones. That approach was inherently racy. Instead, nowadays we have the addresses we want to configure (@known_addresses) and the addresses we want to delete (@prune_addresses). This started to change in commit dadfc3abd510 ('platform: allow injecting the list of addresses to prune'), but only commit 58287cbcc0c8 ('core: rework IP configuration in NetworkManager using layer 3 configuration') actually changed to pass separate @prune_addresses argument. However, the order of IP addresses matters and there is no sensible kernel API to configure the order (short of adding them in the right order), we still need to look at all the addresses, check their order, and possibly delete some. That is, we need to handle addresses we want to delete (@prune_addresses) but still look at all addresses in platform (@plat_addresses) to check their order. Now, first handle @prune_addresses. That's simple. These are just the addresses we want to delete. Second, get the list of all addresses in platform (@plat_addresses) and check the order. Note that if there is an external address that interferes with our desired order, we will leave it untouched. Thus, such external addresses might prevent us from getting the order as desired. But that's just how it is. Don't add addresses outside of NetworkManager to avoid that. Fixes: 58287cbcc0c8 ('core: rework IP configuration in NetworkManager using layer 3 configuration') --- src/libnm-platform/nm-platform.c | 207 +++++++++++++++++++------------ 1 file changed, 126 insertions(+), 81 deletions(-) diff --git a/src/libnm-platform/nm-platform.c b/src/libnm-platform/nm-platform.c index a6c07c33ae..617413d6e5 100644 --- a/src/libnm-platform/nm-platform.c +++ b/src/libnm-platform/nm-platform.c @@ -3953,9 +3953,8 @@ ip6_address_scope_cmp(gconstpointer p_a, gconstpointer p_b, gpointer increasing) * If platform has such an address configured, it will be deleted * at the beginning of the sync. Note that the array will be modified * by the function. - * Note that the addresses must be properly sorted, by their priority. - * Create this list with nm_platform_ip_address_get_prune_list() which - * gets the sorting right. + * Addresses that are both contained in @known_addresses and @addresses_prune + * will be configured. * * A convenience function to synchronize addresses for a specific interface * with the least possible disturbance. It simply removes addresses that are @@ -3970,11 +3969,12 @@ nm_platform_ip_address_sync(NMPlatform *self, GPtrArray *known_addresses, GPtrArray *addresses_prune) { - const gint32 now = nm_utils_get_monotonic_timestamp_sec(); - const int IS_IPv4 = NM_IS_IPv4(addr_family); + const gint32 now = nm_utils_get_monotonic_timestamp_sec(); + const int IS_IPv4 = NM_IS_IPv4(addr_family); + NMPLookup lookup; gs_unref_hashtable GHashTable *known_addresses_idx = NULL; - GPtrArray *plat_addresses; - GHashTable *known_subnets = NULL; + gs_unref_ptrarray GPtrArray *plat_addresses = NULL; + GHashTable *known_subnets = NULL; guint i_plat; guint i_know; guint i; @@ -3982,6 +3982,9 @@ nm_platform_ip_address_sync(NMPlatform *self, _CHECK_SELF(self, klass, FALSE); + /* @known_addresses (IPv4) are in decreasing priority order (highest priority addresses first). + * @known_addresses (IPv6) are in increasing priority order (highest priority addresses last) (we will sort them by scope next). */ + /* The order we want to enforce is only among addresses with the same * scope, as the kernel keeps addresses sorted by scope. Therefore, * apply the same sorting to known addresses, so that we don't try to @@ -4000,51 +4003,92 @@ nm_platform_ip_address_sync(NMPlatform *self, &known_addresses_idx)) known_addresses = NULL; - /* @plat_addresses must be sorted in decreasing priority order (highest priority addresses first), contrary to - * @known_addresses which is in increasing priority order (lowest priority addresses first). */ - plat_addresses = addresses_prune; + if (nm_g_ptr_array_len(addresses_prune) > 0) { + /* First delete addresses that we should prune (and which are no longer tracked + * as @known_addresses. */ + for (i = 0; i < addresses_prune->len; i++) { + const NMPObject *prune_obj = addresses_prune->pdata[i]; + + nm_assert(NM_IN_SET(NMP_OBJECT_GET_TYPE(prune_obj), + NMP_OBJECT_TYPE_IP4_ADDRESS, + NMP_OBJECT_TYPE_IP6_ADDRESS)); + + if (nm_g_hash_table_contains(known_addresses_idx, prune_obj)) + continue; + + nm_platform_ip_address_delete(self, + addr_family, + ifindex, + NMP_OBJECT_CAST_IP_ADDRESS(prune_obj)); + } + } + + /* @plat_addresses for IPv6 must be sorted in decreasing priority order (highest priority addresses first). + * IPv4 are probably unsorted or sorted with lowest priority first, but their order doesn't matter because + * we check the "secondary" flag. */ + plat_addresses = nm_platform_lookup_clone( + self, + nmp_lookup_init_object(&lookup, NMP_OBJECT_TYPE_IP_ADDRESS(IS_IPv4), ifindex), + NULL, + NULL); if (nm_g_ptr_array_len(plat_addresses) > 0) { - /* Delete unknown addresses */ + /* Delete addresses that interfere with our intended order. */ if (IS_IPv4) { - GHashTable *plat_subnets; + gs_free bool *plat_handled_to_free = NULL; + bool *plat_handled = NULL; + GHashTable *plat_subnets; + + /* For IPv4, we only consider it a conflict for addresses in the same + * subnet. That's where kernel will assign a primary/secondary flag. + * For different subnets, we don't define the order. */ plat_subnets = ip4_addr_subnets_build_index(plat_addresses, TRUE, TRUE); for (i = 0; i < plat_addresses->len; i++) { - const NMPObject *plat_obj; + const NMPObject *plat_obj = plat_addresses->pdata[i]; + const NMPObject *known_obj; const NMPlatformIP4Address *plat_address; const GPtrArray *addr_list; + gboolean secondary; - plat_obj = plat_addresses->pdata[i]; - if (!plat_obj) { - /* Already deleted */ + if (plat_handled && plat_handled[i]) + continue; + + known_obj = nm_g_hash_table_lookup(known_addresses_idx, plat_obj); + + if (!known_obj) { + /* this address is added externally. Even if it's presence would mess + * with our desired order, we cannot delete it. Skip it. */ + if (!plat_handled) { + plat_handled = nm_malloc0_maybe_a(300, + sizeof(bool) * plat_addresses->len, + &plat_handled_to_free); + } + plat_handled[i] = TRUE; continue; } + if (!known_subnets) + known_subnets = ip4_addr_subnets_build_index(known_addresses, FALSE, FALSE); + plat_address = NMP_OBJECT_CAST_IP4_ADDRESS(plat_obj); - if (known_addresses) { - const NMPObject *o; - - o = g_hash_table_lookup(known_addresses_idx, plat_obj); - if (o) { - gboolean secondary; - - if (!known_subnets) - known_subnets = - ip4_addr_subnets_build_index(known_addresses, FALSE, FALSE); - - secondary = - ip4_addr_subnets_is_secondary(o, known_subnets, known_addresses, NULL); - if (secondary == NM_FLAGS_HAS(plat_address->n_ifa_flags, IFA_F_SECONDARY)) { - /* if we have an existing known-address, with matching secondary role, - * do not delete the platform-address. */ - continue; - } - } + secondary = + ip4_addr_subnets_is_secondary(known_obj, known_subnets, known_addresses, NULL); + if (secondary == NM_FLAGS_HAS(plat_address->n_ifa_flags, IFA_F_SECONDARY)) { + /* if we have an existing known-address, with matching secondary role, + * do not delete the platform-address. */ + continue; } + if (!plat_handled) { + plat_handled = nm_malloc0_maybe_a(300, + sizeof(bool) * plat_addresses->len, + &plat_handled_to_free); + } + plat_handled[i] = TRUE; + nm_platform_ip4_address_delete(self, ifindex, plat_address->address, @@ -4063,22 +4107,27 @@ nm_platform_ip_address_sync(NMPlatform *self, * addresses are deleted, so that we can start with a clean * slate and add addresses in the right order. */ for (j = 1; j < addr_list->len; j++) { - const NMPObject **o; + const NMPObject **o = ip4_addr_subnets_addr_list_get(addr_list, j); + guint o_idx; - o = ip4_addr_subnets_addr_list_get(addr_list, j); - nm_assert(o); + o_idx = (o - ((const NMPObject **) &plat_addresses->pdata[0])); - if (*o) { - const NMPlatformIP4Address *a; + nm_assert(o_idx < plat_addresses->len); + nm_assert(o == ((const NMPObject **) &plat_addresses->pdata[o_idx])); - a = NMP_OBJECT_CAST_IP4_ADDRESS(*o); - nm_platform_ip4_address_delete(self, - ifindex, - a->address, - a->plen, - a->peer_address); - nmp_object_unref(*o); - *o = NULL; + if (plat_handled[o_idx]) + continue; + + plat_handled[o_idx] = TRUE; + + if (!nm_g_hash_table_contains(known_addresses_idx, *o)) { + /* Again, this is an external address. We cannot delete + * it to fix the address order. Pass. */ + } else { + nm_platform_ip_address_delete(self, + AF_INET, + ifindex, + NMP_OBJECT_CAST_IP4_ADDRESS(*o)); } } } @@ -4089,48 +4138,44 @@ nm_platform_ip_address_sync(NMPlatform *self, IP6AddrScope cur_scope; gboolean delete_remaining_addrs; + /* For IPv6, we only compare addresses per-scope. Addresses in different + * scopes don't have a defined order. */ + g_ptr_array_sort_with_data(plat_addresses, ip6_address_scope_cmp, GINT_TO_POINTER(FALSE)); known_addresses_len = known_addresses ? known_addresses->len : 0; - /* First, compare every address whether it is still a "known address", that is, whether - * to keep it or to delete it. - * - * If we don't find a matching valid address in @known_addresses, we will delete - * plat_addr. - * - * Certain addresses, like temporary addresses, are ignored by this function - * if not run with full_sync. These addresses are usually not managed by NetworkManager - * directly, or at least, they are not managed via nm_platform_ip6_address_sync(). - * Only in full_sync mode, we really want to get rid of them (usually, when we take - * the interface down). - * - * Note that we mark handled addresses by setting it to %NULL in @plat_addresses array. */ + /* First, check that existing addresses have a matching plen as the ones + * we are about to configure (@known_addresses). If not, delete them. */ for (i_plat = 0; i_plat < plat_addresses->len; i_plat++) { - const NMPObject *plat_obj = plat_addresses->pdata[i_plat]; - const NMPObject *know_obj; - const NMPlatformIP6Address *plat_addr = NMP_OBJECT_CAST_IP6_ADDRESS(plat_obj); + const NMPObject *plat_obj = plat_addresses->pdata[i_plat]; + const NMPObject *known_obj; - if (known_addresses_idx) { - know_obj = g_hash_table_lookup(known_addresses_idx, plat_obj); - if (know_obj - && plat_addr->plen == NMP_OBJECT_CAST_IP6_ADDRESS(know_obj)->plen) { - /* technically, plen is not part of the ID for IPv6 addresses and thus - * @plat_addr is essentially the same address as @know_addr (regrading - * its identity, not its other attributes). - * However, we cannot modify an existing addresses' plen without - * removing and readding it. Thus, only keep plat_addr, if the plen - * matches. - * - * keep this one, and continue */ - continue; - } + known_obj = nm_g_hash_table_lookup(known_addresses_idx, plat_obj); + if (!known_obj) { + /* We don't know this address. It was added externally. Keep it configured. + * We also don't want to delete the address below, so mark it as handled + * by clearing the pointer. */ + nm_clear_pointer(&plat_addresses->pdata[i_plat], nmp_object_unref); + continue; } - nm_platform_ip6_address_delete(self, ifindex, plat_addr->address, plat_addr->plen); - nmp_object_unref(g_steal_pointer(&plat_addresses->pdata[i_plat])); + if (NMP_OBJECT_CAST_IP6_ADDRESS(plat_obj)->plen + != NMP_OBJECT_CAST_IP6_ADDRESS(known_obj)->plen) { + /* technically, plen is not part of the ID for IPv6 addresses and thus + * @plat_addr is essentially the same address as @know_addr (w.r.t. + * its identity, not its other attributes). + * However, we cannot modify an existing addresses' plen without + * removing and readding it. Thus, we need to delete plat_addr. */ + nm_platform_ip_address_delete(self, + AF_INET6, + ifindex, + NMP_OBJECT_CAST_IP6_ADDRESS(plat_obj)); + /* Mark address as handled. */ + nm_clear_pointer(&plat_addresses->pdata[i_plat], nmp_object_unref); + } } /* Next, we must preserve the priority of the routes. That is, source address @@ -4141,7 +4186,7 @@ nm_platform_ip_address_sync(NMPlatform *self, * @known_addresses (which has lowest priority first). * * If we find a first discrepancy, we need to delete all remaining addresses - * with same scope from that point on, because below we must re-add all the + * for same scope from that point on, because below we must re-add all the * addresses in the right order to get their priority right. */ cur_scope = IP6_ADDR_SCOPE_LOOPBACK; delete_remaining_addrs = FALSE; From cedaa191d44fede4048a581f2cd132ec6b03d6e9 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Tue, 29 Mar 2022 16:44:29 +0200 Subject: [PATCH 3/6] platform: fix returning error from nm_platform_ip_address_sync() None of the callers really handle the return value of nm_platform_ip_address_sync() or whether the function encountered problems. What would they anyway do about that? For IPv4 we were already ignoring errors to add addresses, but for IPv6 we aborted. That seems wrong. As the caller does not really handle errors, I think we should follow through and add all addresses in case of error. Still, also collect a overall "success" of the function and return it. --- src/libnm-platform/nm-platform.c | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/src/libnm-platform/nm-platform.c b/src/libnm-platform/nm-platform.c index 617413d6e5..e67a37a746 100644 --- a/src/libnm-platform/nm-platform.c +++ b/src/libnm-platform/nm-platform.c @@ -3941,14 +3941,9 @@ ip6_address_scope_cmp(gconstpointer p_a, gconstpointer p_b, gpointer increasing) * @self: platform instance * @addr_family: the address family AF_INET or AF_INET6. * @ifindex: Interface index - * @known_addresses: List of addresses. The list will be modified and only - * addresses that were successfully added will be kept in the list. - * That means, expired addresses and addresses that could not be added - * will be dropped. - * Hence, the input argument @known_addresses is also an output argument - * telling which addresses were successfully added. - * Addresses are removed by unrefing the instance via nmp_object_unref() - * and leaving a NULL tombstone. + * @known_addresses: List of addresses. The list will be modified and + * expired addresses will be cleared (by calling nmp_object_unref() + * on the array element). * @addresses_prune: (allow-none): the list of addresses to delete. * If platform has such an address configured, it will be deleted * at the beginning of the sync. Note that the array will be modified @@ -3975,6 +3970,7 @@ nm_platform_ip_address_sync(NMPlatform *self, gs_unref_hashtable GHashTable *known_addresses_idx = NULL; gs_unref_ptrarray GPtrArray *plat_addresses = NULL; GHashTable *known_subnets = NULL; + gboolean success; guint i_plat; guint i_know; guint i; @@ -4145,7 +4141,7 @@ nm_platform_ip_address_sync(NMPlatform *self, ip6_address_scope_cmp, GINT_TO_POINTER(FALSE)); - known_addresses_len = known_addresses ? known_addresses->len : 0; + known_addresses_len = nm_g_ptr_array_len(known_addresses); /* First, check that existing addresses have a matching plen as the ones * we are about to configure (@known_addresses). If not, delete them. */ @@ -4246,6 +4242,8 @@ next_plat:; if (IS_IPv4) ip4_addr_subnets_destroy_index(known_subnets, known_addresses); + success = TRUE; + /* Add missing addresses. New addresses are added by kernel with top * priority. */ @@ -4281,9 +4279,8 @@ next_plat:; lifetime, preferred, IFA_F_NOPREFIXROUTE, - known_address->a4.label)) { - /* ignore error, for unclear reasons. */ - } + known_address->a4.label)) + success = FALSE; } else { if (!nm_platform_ip6_address_add(self, ifindex, @@ -4293,11 +4290,11 @@ next_plat:; lifetime, preferred, IFA_F_NOPREFIXROUTE | known_address->a6.n_ifa_flags)) - return FALSE; + success = FALSE; } } - return TRUE; + return success; } gboolean From 40f22e69c8c03fbbe40f3ba701c3540470f49dfe Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Thu, 31 Mar 2022 10:57:25 +0200 Subject: [PATCH 4/6] platform: fix undefined behavior for pointer comparison in ip4_addr_subnets_is_plain_address() Fixes: 2f68a5004153 ('platform: fix the order of addition of primary and secondary IPv4 addresses') --- src/libnm-platform/nm-platform.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libnm-platform/nm-platform.c b/src/libnm-platform/nm-platform.c index e67a37a746..af9e2bbf9f 100644 --- a/src/libnm-platform/nm-platform.c +++ b/src/libnm-platform/nm-platform.c @@ -3762,8 +3762,8 @@ clear_and_next: static gboolean ip4_addr_subnets_is_plain_address(const GPtrArray *addresses, gconstpointer needle) { - return needle >= (gconstpointer) &addresses->pdata[0] - && needle < (gconstpointer) &addresses->pdata[addresses->len]; + return nm_ptr_to_uintptr(needle) >= nm_ptr_to_uintptr(&addresses->pdata[0]) + && nm_ptr_to_uintptr(needle) < nm_ptr_to_uintptr(&addresses->pdata[addresses->len]); } static const NMPObject ** From e1431b43a2e02bdd010474df40ccf4417e8b7d08 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Sat, 2 Apr 2022 10:34:17 +0200 Subject: [PATCH 5/6] platform: move known_subnets variable to inner scope in nm_platform_ip_address_sync() --- src/libnm-platform/nm-platform.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/libnm-platform/nm-platform.c b/src/libnm-platform/nm-platform.c index af9e2bbf9f..0536f1bd3d 100644 --- a/src/libnm-platform/nm-platform.c +++ b/src/libnm-platform/nm-platform.c @@ -3969,7 +3969,6 @@ nm_platform_ip_address_sync(NMPlatform *self, NMPLookup lookup; gs_unref_hashtable GHashTable *known_addresses_idx = NULL; gs_unref_ptrarray GPtrArray *plat_addresses = NULL; - GHashTable *known_subnets = NULL; gboolean success; guint i_plat; guint i_know; @@ -4031,9 +4030,10 @@ nm_platform_ip_address_sync(NMPlatform *self, if (nm_g_ptr_array_len(plat_addresses) > 0) { /* Delete addresses that interfere with our intended order. */ if (IS_IPv4) { + GHashTable *known_subnets = NULL; + GHashTable *plat_subnets; gs_free bool *plat_handled_to_free = NULL; bool *plat_handled = NULL; - GHashTable *plat_subnets; /* For IPv4, we only consider it a conflict for addresses in the same * subnet. That's where kernel will assign a primary/secondary flag. @@ -4129,6 +4129,7 @@ nm_platform_ip_address_sync(NMPlatform *self, } } ip4_addr_subnets_destroy_index(plat_subnets, plat_addresses); + ip4_addr_subnets_destroy_index(known_subnets, known_addresses); } else { guint known_addresses_len; IP6AddrScope cur_scope; @@ -4239,9 +4240,6 @@ next_plat:; if (!known_addresses) return TRUE; - if (IS_IPv4) - ip4_addr_subnets_destroy_index(known_subnets, known_addresses); - success = TRUE; /* Add missing addresses. New addresses are added by kernel with top From 619dc2fcab809a1cae831c1866ce93189b575d53 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Sat, 2 Apr 2022 10:32:55 +0200 Subject: [PATCH 6/6] platform: track IPv4 subnets with prefix length in nm_platform_ip_address_sync() The entire point of the dance in nm_platform_ip_address_sync() is to ensure that conflicting IPv4 addresses are in their right order, that is, they have the right primary/secondary flag. Kernel only sets secondary flags for addresses that are in the same subnet, and we also only care about the relative order of addresses that are in the same subnet. In particular, because we rely on kernel's "secondary" flag to implement this. But kernel only treads addresses as secondary, if they share the exact same subnet. For example, 192.168.0.5/24 and 192.168.0.6/25 would not be treated as primary/secondary but just as unrelated addresses, even if the address cleared of it's host part is the same. This means, we must not only hash the network part of the addresses, but also the prefix length. Implement that, by tracking the full NMPObject. --- src/libnm-platform/nm-platform.c | 62 +++++++++++++++++++++----------- 1 file changed, 41 insertions(+), 21 deletions(-) diff --git a/src/libnm-platform/nm-platform.c b/src/libnm-platform/nm-platform.c index 0536f1bd3d..21a29004a8 100644 --- a/src/libnm-platform/nm-platform.c +++ b/src/libnm-platform/nm-platform.c @@ -3797,6 +3797,30 @@ ip4_addr_subnets_destroy_index(GHashTable *subnets, const GPtrArray *addresses) g_hash_table_unref(subnets); } +static guint +_ip4_addr_subnets_hash(gconstpointer ptr) +{ + const NMPlatformIP4Address *addr = NMP_OBJECT_CAST_IP4_ADDRESS(ptr); + NMHashState h; + + nm_hash_init(&h, 3282159733); + nm_hash_update_vals(&h, + addr->plen, + nm_utils_ip4_address_clear_host_address(addr->address, addr->plen)); + return nm_hash_complete(&h); +} + +static gboolean +_ip4_addr_subnets_equal(gconstpointer p_a, gconstpointer p_b) +{ + const NMPlatformIP4Address *a = NMP_OBJECT_CAST_IP4_ADDRESS(p_a); + const NMPlatformIP4Address *b = NMP_OBJECT_CAST_IP4_ADDRESS(p_b); + + return a->plen == b->plen + && (nm_utils_ip4_address_clear_host_address(a->address, a->plen) + == nm_utils_ip4_address_clear_host_address(b->address, b->plen)); +} + static GHashTable * ip4_addr_subnets_build_index(const GPtrArray *addresses, gboolean consider_flags, @@ -3807,34 +3831,35 @@ ip4_addr_subnets_build_index(const GPtrArray *addresses, nm_assert(addresses && addresses->len); - subnets = g_hash_table_new(nm_direct_hash, NULL); + subnets = g_hash_table_new(_ip4_addr_subnets_hash, _ip4_addr_subnets_equal); /* Build a hash table of all addresses per subnet */ for (i = 0; i < addresses->len; i++) { + const NMPObject **p_obj; + const NMPObject *obj; const NMPlatformIP4Address *address; - gpointer p_address; GPtrArray *addr_list; - guint32 net; int position; gpointer p; if (!addresses->pdata[i]) continue; - p_address = &addresses->pdata[i]; - address = NMP_OBJECT_CAST_IP4_ADDRESS(addresses->pdata[i]); + p_obj = (const NMPObject **) &addresses->pdata[i]; + obj = *p_obj; - net = address->address & _nm_utils_ip4_prefix_to_netmask(address->plen); - if (!g_hash_table_lookup_extended(subnets, GUINT_TO_POINTER(net), NULL, &p)) { - g_hash_table_insert(subnets, GUINT_TO_POINTER(net), p_address); + if (!g_hash_table_lookup_extended(subnets, obj, NULL, &p)) { + g_hash_table_insert(subnets, (gpointer) obj, p_obj); continue; } nm_assert(p); + address = NMP_OBJECT_CAST_IP4_ADDRESS(obj); + if (full_index) { if (ip4_addr_subnets_is_plain_address(addresses, p)) { addr_list = g_ptr_array_new(); - g_hash_table_insert(subnets, GUINT_TO_POINTER(net), addr_list); + g_hash_table_insert(subnets, (gpointer) obj, addr_list); g_ptr_array_add(addr_list, p); } else addr_list = p; @@ -3843,13 +3868,13 @@ ip4_addr_subnets_build_index(const GPtrArray *addresses, position = -1; /* append */ else position = 0; /* prepend */ - g_ptr_array_insert(addr_list, position, p_address); + g_ptr_array_insert(addr_list, position, p_obj); } else { /* we only care about the primary. No need to track the secondaries * as a GPtrArray. */ nm_assert(ip4_addr_subnets_is_plain_address(addresses, p)); if (consider_flags && !NM_FLAGS_HAS(address->n_ifa_flags, IFA_F_SECONDARY)) { - g_hash_table_insert(subnets, GUINT_TO_POINTER(net), p_address); + g_hash_table_insert(subnets, (gpointer) obj, p_obj); } } } @@ -3875,16 +3900,11 @@ ip4_addr_subnets_is_secondary(const NMPObject *address, const GPtrArray *addresses, const GPtrArray **out_addr_list) { - const NMPlatformIP4Address *a; - const GPtrArray *addr_list; - gconstpointer p; - guint32 net; - const NMPObject **o; + const GPtrArray *addr_list; + gconstpointer p; + const NMPObject **o; - a = NMP_OBJECT_CAST_IP4_ADDRESS(address); - - net = a->address & _nm_utils_ip4_prefix_to_netmask(a->plen); - p = g_hash_table_lookup(subnets, GUINT_TO_POINTER(net)); + p = g_hash_table_lookup(subnets, address); nm_assert(p); if (!ip4_addr_subnets_is_plain_address(addresses, p)) { addr_list = p; @@ -4030,7 +4050,7 @@ nm_platform_ip_address_sync(NMPlatform *self, if (nm_g_ptr_array_len(plat_addresses) > 0) { /* Delete addresses that interfere with our intended order. */ if (IS_IPv4) { - GHashTable *known_subnets = NULL; + GHashTable *known_subnets = NULL; GHashTable *plat_subnets; gs_free bool *plat_handled_to_free = NULL; bool *plat_handled = NULL;