summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Haller <thaller@redhat.com>2022-04-02 10:32:55 +0200
committerThomas Haller <thaller@redhat.com>2022-04-08 16:27:00 +0200
commit0bdb2e97d9a6bcd86889fb09765835a5886d13fb (patch)
tree45972cb3fdd25ee825cd4ce59a2981e73709d81c
parenta8e96e3c4b539391833b74432c3200df4e3a8223 (diff)
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. (cherry picked from commit 619dc2fcab809a1cae831c1866ce93189b575d53)
-rw-r--r--src/libnm-platform/nm-platform.c62
1 files 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;
-
- a = NMP_OBJECT_CAST_IP4_ADDRESS(address);
+ const GPtrArray *addr_list;
+ gconstpointer p;
+ const NMPObject **o;
- 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;