diff --git a/SOURCES/1005-fix-dhcp-loses-lease-when-restarting-rhbz2090280.patch b/SOURCES/1005-fix-dhcp-loses-lease-when-restarting-rhbz2090280.patch new file mode 100644 index 0000000..a9a9b10 --- /dev/null +++ b/SOURCES/1005-fix-dhcp-loses-lease-when-restarting-rhbz2090280.patch @@ -0,0 +1,1994 @@ +From dda06a471935a453f5e30d995c0a16b5f977397a Mon Sep 17 00:00:00 2001 +From: Thomas Haller +Date: Mon, 28 Mar 2022 22:26:15 +0200 +Subject: [PATCH 01/11] platform: add nm_platform_ip_address_delete() helper + +(cherry picked from commit a60a262574206976eacc405633c059e0f375f0a8) +(cherry picked from commit 0fc40735ab582f2ff9f319043d77d5f40253f103) +(cherry picked from commit 264296868b32a93bdbb21246828c52a282a53d50) +--- + 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 6f5cd5248f..baa3967db0 100644 +--- a/src/libnm-platform/nm-platform.h ++++ b/src/libnm-platform/nm-platform.h +@@ -2097,6 +2097,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, +-- +2.35.1 + + +From d403f25a67a48221e80971b6b0509b63c6de0a92 Mon Sep 17 00:00:00 2001 +From: Thomas Haller +Date: Mon, 28 Mar 2022 21:13:09 +0200 +Subject: [PATCH 02/11] 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') +(cherry picked from commit 80f8e23992b58aa0b6fd88de0d3973eea51691a4) +(cherry picked from commit 4c3197b37790c6c89c7b3df0e92a26e1f8719a5a) +(cherry picked from commit cd4601802de52f0239b5b8c19bd90ed9ccb6a50c) +--- + 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 922f412df7..61fcf459ab 100644 +--- a/src/libnm-platform/nm-platform.c ++++ b/src/libnm-platform/nm-platform.c +@@ -3889,9 +3889,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 +@@ -3906,11 +3905,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; +@@ -3918,6 +3918,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 +@@ -3936,50 +3939,91 @@ 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; +- } + +- plat_address = NMP_OBJECT_CAST_IP4_ADDRESS(plat_obj); ++ known_obj = nm_g_hash_table_lookup(known_addresses_idx, plat_obj); + +- if (known_addresses) { +- const NMPObject *o; ++ 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; ++ } + +- 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); + +- if (!known_subnets) +- known_subnets = +- ip4_addr_subnets_build_index(known_addresses, FALSE, FALSE); ++ plat_address = NMP_OBJECT_CAST_IP4_ADDRESS(plat_obj); + +- 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, +@@ -3999,22 +4043,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)); + } + } + } +@@ -4025,48 +4074,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); +- +- 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; +- } ++ const NMPObject *plat_obj = plat_addresses->pdata[i_plat]; ++ const NMPObject *known_obj; ++ ++ 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 +@@ -4077,7 +4122,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; +-- +2.35.1 + + +From e88a5a73f07d1593b1abe1e89a2b252559770d98 Mon Sep 17 00:00:00 2001 +From: Thomas Haller +Date: Tue, 29 Mar 2022 16:44:29 +0200 +Subject: [PATCH 03/11] 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. + +(cherry picked from commit cedaa191d44fede4048a581f2cd132ec6b03d6e9) +(cherry picked from commit 8736cc86187d176ca7a7f1dbe5bdee0786c2e037) +(cherry picked from commit ebfbba550343a24b9e7b73051b0515c7fe637881) +--- + 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 61fcf459ab..97a86d8061 100644 +--- a/src/libnm-platform/nm-platform.c ++++ b/src/libnm-platform/nm-platform.c +@@ -3877,14 +3877,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 +@@ -3911,6 +3906,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; +@@ -4081,7 +4077,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. */ +@@ -4182,6 +4178,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. + */ +@@ -4217,9 +4215,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, +@@ -4229,11 +4226,11 @@ next_plat:; + lifetime, + preferred, + IFA_F_NOPREFIXROUTE | known_address->a6.n_ifa_flags)) +- return FALSE; ++ success = FALSE; + } + } + +- return TRUE; ++ return success; + } + + gboolean +-- +2.35.1 + + +From 87f194fddde0b54407d99748b7f6761e9bf5632a Mon Sep 17 00:00:00 2001 +From: Thomas Haller +Date: Thu, 31 Mar 2022 10:57:25 +0200 +Subject: [PATCH 04/11] 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') +(cherry picked from commit 40f22e69c8c03fbbe40f3ba701c3540470f49dfe) +(cherry picked from commit 41b56cb2b9397407d24e00f95ba4ffb009212040) +(cherry picked from commit cea335c454fed307903835eb78b2bb715e0ce377) +--- + 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 97a86d8061..a61c8ae823 100644 +--- a/src/libnm-platform/nm-platform.c ++++ b/src/libnm-platform/nm-platform.c +@@ -3698,8 +3698,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 ** +-- +2.35.1 + + +From 887239f75b382f46a8d89865de0a09484aaae93b Mon Sep 17 00:00:00 2001 +From: Thomas Haller +Date: Sat, 2 Apr 2022 10:34:17 +0200 +Subject: [PATCH 05/11] platform: move known_subnets variable to inner scope in + nm_platform_ip_address_sync() + +(cherry picked from commit e1431b43a2e02bdd010474df40ccf4417e8b7d08) +(cherry picked from commit a8e96e3c4b539391833b74432c3200df4e3a8223) +(cherry picked from commit 0f0d7d801b95198dd5f578dad4e719388cfd9147) +--- + 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 a61c8ae823..06db58ad00 100644 +--- a/src/libnm-platform/nm-platform.c ++++ b/src/libnm-platform/nm-platform.c +@@ -3905,7 +3905,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; +@@ -3967,9 +3966,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. +@@ -4065,6 +4065,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; +@@ -4175,9 +4176,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 +-- +2.35.1 + + +From e97eda3965d925c39081830e75cd0397dbc5f03a Mon Sep 17 00:00:00 2001 +From: Thomas Haller +Date: Sat, 2 Apr 2022 10:32:55 +0200 +Subject: [PATCH 06/11] 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) +(cherry picked from commit 0bdb2e97d9a6bcd86889fb09765835a5886d13fb) +(cherry picked from commit 9149237287a550e44b3e3196dbb6786ccc3ea05c) +--- + 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 06db58ad00..00a3fb2a0a 100644 +--- a/src/libnm-platform/nm-platform.c ++++ b/src/libnm-platform/nm-platform.c +@@ -3733,6 +3733,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, +@@ -3743,34 +3767,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; +@@ -3779,13 +3804,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); + } + } + } +@@ -3811,16 +3836,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; +@@ -3966,7 +3986,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; +-- +2.35.1 + + +From d3effaf3f81c763158c726045df750dd5c3c5e2b Mon Sep 17 00:00:00 2001 +From: Thomas Haller +Date: Tue, 29 Mar 2022 22:45:26 +0200 +Subject: [PATCH 07/11] glib-aux: add + nm_utils_get_monotonic_timestamp_sec_cached() helper + +(cherry picked from commit 3f4586532ffb8db2136bbb4ef906fd21d17d5bd2) +(cherry picked from commit 66237888e78aeae2f348b6b97c39c203a34ab7be) +(cherry picked from commit 754942038f536fcc79e476ab0ac38dbcf8e80a51) +--- + src/libnm-glib-aux/nm-time-utils.h | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/src/libnm-glib-aux/nm-time-utils.h b/src/libnm-glib-aux/nm-time-utils.h +index 3c3e935f8d..461d6845fb 100644 +--- a/src/libnm-glib-aux/nm-time-utils.h ++++ b/src/libnm-glib-aux/nm-time-utils.h +@@ -41,6 +41,12 @@ nm_utils_get_monotonic_timestamp_msec_cached(gint64 *cache_now) + return (*cache_now) ?: (*cache_now = nm_utils_get_monotonic_timestamp_msec()); + } + ++static inline gint32 ++nm_utils_get_monotonic_timestamp_sec_cached(gint32 *cache_now) ++{ ++ return (*cache_now) ?: (*cache_now = nm_utils_get_monotonic_timestamp_sec()); ++} ++ + gint64 nm_utils_clock_gettime_nsec(clockid_t clockid); + gint64 nm_utils_clock_gettime_msec(clockid_t clockid); + +-- +2.35.1 + + +From 8ddff3cdccfb547b04a55a6a44221abf4e0525e8 Mon Sep 17 00:00:00 2001 +From: Thomas Haller +Date: Tue, 29 Mar 2022 22:45:56 +0200 +Subject: [PATCH 08/11] platform: make "now" timestamp an in/out parameter to + nmp_utils_lifetime_get() + +nmp_utils_lifetime_get() calculates the lifetime of addresses, +and it bases the result on a "now" timestamp. + +If you have two addresses and calculate their expiry, then we want to +base it on top of the same "now" timestamp, meaning, we should +only call nm_utils_get_monotonic_timestamp_sec() once. This is also a +performance optimization. But much more importantly, when we make a +comparison at a certain moment, we need that all sides have the same +understanding of the current timestamp. + +But nmp_utils_lifetime_get() does not always require the now timestamp. +And the caller doesn't know, whether it will need it (short of knowing +how nmp_utils_lifetime_get() is implemented). So, make the now parameter +an in/out argument. If we pass in an already valid now timestamp, use +that. Otherwise, fetch the current time and also return it. + +(cherry picked from commit deb37401e95d4ea0025e406424c8da7c10bc9712) +(cherry picked from commit 9e40474c715e995c000b29db030b4a4990cc6e51) +(cherry picked from commit 99d77596cdc0a5d3861aebc03485d8cf9c02dd1e) +--- + src/core/ndisc/nm-ndisc.c | 3 ++- + src/libnm-platform/nm-platform-utils.c | 12 +++++++----- + src/libnm-platform/nm-platform-utils.h | 2 +- + src/libnm-platform/nm-platform.c | 13 +++++++------ + 4 files changed, 17 insertions(+), 13 deletions(-) + +diff --git a/src/core/ndisc/nm-ndisc.c b/src/core/ndisc/nm-ndisc.c +index 969eacfaba..04b673e51d 100644 +--- a/src/core/ndisc/nm-ndisc.c ++++ b/src/core/ndisc/nm-ndisc.c +@@ -996,6 +996,7 @@ nm_ndisc_set_config(NMNDisc *ndisc, const NML3ConfigData *l3cd) + const NMPObject *obj; + guint len; + guint i; ++ gint32 fake_now = NM_NDISC_EXPIRY_BASE_TIMESTAMP / 1000; + + nm_assert(NM_IS_NDISC(ndisc)); + nm_assert(nm_ndisc_get_node_type(ndisc) == NM_NDISC_NODE_TYPE_ROUTER); +@@ -1018,7 +1019,7 @@ nm_ndisc_set_config(NMNDisc *ndisc, const NML3ConfigData *l3cd) + lifetime = nmp_utils_lifetime_get(addr->timestamp, + addr->lifetime, + addr->preferred, +- NM_NDISC_EXPIRY_BASE_TIMESTAMP / 1000, ++ &fake_now, + &preferred); + if (!lifetime) + continue; +diff --git a/src/libnm-platform/nm-platform-utils.c b/src/libnm-platform/nm-platform-utils.c +index 9ad030df76..bebc53a851 100644 +--- a/src/libnm-platform/nm-platform-utils.c ++++ b/src/libnm-platform/nm-platform-utils.c +@@ -2132,12 +2132,15 @@ guint32 + nmp_utils_lifetime_get(guint32 timestamp, + guint32 lifetime, + guint32 preferred, +- gint32 now, ++ gint32 *cached_now, + guint32 *out_preferred) + { +- guint32 t_lifetime, t_preferred; ++ guint32 t_lifetime; ++ guint32 t_preferred; ++ gint32 now; + +- nm_assert(now >= 0); ++ nm_assert(cached_now); ++ nm_assert(*cached_now >= 0); + + if (timestamp == 0 && lifetime == 0) { + /* We treat lifetime==0 && timestamp==0 addresses as permanent addresses to allow easy +@@ -2150,8 +2153,7 @@ nmp_utils_lifetime_get(guint32 timestamp, + return NM_PLATFORM_LIFETIME_PERMANENT; + } + +- if (now <= 0) +- now = nm_utils_get_monotonic_timestamp_sec(); ++ now = nm_utils_get_monotonic_timestamp_sec_cached(cached_now); + + t_lifetime = nmp_utils_lifetime_rebase_relative_time_on_now(timestamp, lifetime, now); + if (!t_lifetime) { +diff --git a/src/libnm-platform/nm-platform-utils.h b/src/libnm-platform/nm-platform-utils.h +index a9ccebb3e2..9f17da4886 100644 +--- a/src/libnm-platform/nm-platform-utils.h ++++ b/src/libnm-platform/nm-platform-utils.h +@@ -86,7 +86,7 @@ nmp_utils_lifetime_rebase_relative_time_on_now(guint32 timestamp, guint32 durati + guint32 nmp_utils_lifetime_get(guint32 timestamp, + guint32 lifetime, + guint32 preferred, +- gint32 now, ++ gint32 *cached_now, + guint32 *out_preferred); + + int nmp_utils_modprobe(GError **error, gboolean suppress_error_logging, const char *arg1, ...) +diff --git a/src/libnm-platform/nm-platform.c b/src/libnm-platform/nm-platform.c +index 00a3fb2a0a..ea7aad9398 100644 +--- a/src/libnm-platform/nm-platform.c ++++ b/src/libnm-platform/nm-platform.c +@@ -3632,7 +3632,7 @@ static gboolean + _addr_array_clean_expired(int addr_family, + int ifindex, + GPtrArray *array, +- guint32 now, ++ gint32 *cached_now, + GHashTable **idx) + { + guint i; +@@ -3640,7 +3640,8 @@ _addr_array_clean_expired(int addr_family, + + nm_assert_addr_family(addr_family); + nm_assert(ifindex > 0); +- nm_assert(now > 0); ++ nm_assert(cached_now); ++ nm_assert(*cached_now >= 0); + + if (!array) + return FALSE; +@@ -3674,7 +3675,7 @@ _addr_array_clean_expired(int addr_family, + goto clear_and_next; + } + +- if (!nmp_utils_lifetime_get(a->timestamp, a->lifetime, a->preferred, now, NULL)) ++ if (!nmp_utils_lifetime_get(a->timestamp, a->lifetime, a->preferred, cached_now, NULL)) + goto clear_and_next; + + if (idx) { +@@ -3920,7 +3921,7 @@ nm_platform_ip_address_sync(NMPlatform *self, + GPtrArray *known_addresses, + GPtrArray *addresses_prune) + { +- const gint32 now = nm_utils_get_monotonic_timestamp_sec(); ++ gint32 now = 0; + const int IS_IPv4 = NM_IS_IPv4(addr_family); + NMPLookup lookup; + gs_unref_hashtable GHashTable *known_addresses_idx = NULL; +@@ -3950,7 +3951,7 @@ nm_platform_ip_address_sync(NMPlatform *self, + if (!_addr_array_clean_expired(addr_family, + ifindex, + known_addresses, +- now, ++ &now, + &known_addresses_idx)) + known_addresses = NULL; + +@@ -4218,7 +4219,7 @@ next_plat:; + lifetime = nmp_utils_lifetime_get(known_address->ax.timestamp, + known_address->ax.lifetime, + known_address->ax.preferred, +- now, ++ &now, + &preferred); + nm_assert(lifetime > 0); + +-- +2.35.1 + + +From 09a9467c8112ce29d097bc0c5d236e2da2108afe Mon Sep 17 00:00:00 2001 +From: Thomas Haller +Date: Tue, 29 Mar 2022 18:21:49 +0200 +Subject: [PATCH 09/11] platform: make NMPlatformVTableAddress struct smaller + and pack NMPObjectType + +(cherry picked from commit 7c92663f8d79375c78f6917d4c6e005d7accf2a6) +(cherry picked from commit 3a98ecfa0edce51c5ed8446bc3a74efc6ec6ac65) +(cherry picked from commit de4d10f1c1f754a1ea954e8bf073d7cdaa269f96) +--- + src/libnm-platform/nm-platform.h | 6 +++--- + src/libnm-platform/nmp-base.h | 2 +- + 2 files changed, 4 insertions(+), 4 deletions(-) + +diff --git a/src/libnm-platform/nm-platform.h b/src/libnm-platform/nm-platform.h +index baa3967db0..d978c91b80 100644 +--- a/src/libnm-platform/nm-platform.h ++++ b/src/libnm-platform/nm-platform.h +@@ -776,10 +776,10 @@ typedef struct { + #undef __NMPlatformObjWithIfindex_COMMON + + typedef struct { +- gboolean is_ip4; ++ bool is_ip4; ++ gint8 addr_family; ++ guint8 sizeof_route; + NMPObjectType obj_type; +- int addr_family; +- gsize sizeof_route; + int (*route_cmp)(const NMPlatformIPXRoute *a, + const NMPlatformIPXRoute *b, + NMPlatformIPRouteCmpType cmp_type); +diff --git a/src/libnm-platform/nmp-base.h b/src/libnm-platform/nmp-base.h +index a80fd4d389..4863168855 100644 +--- a/src/libnm-platform/nmp-base.h ++++ b/src/libnm-platform/nmp-base.h +@@ -110,7 +110,7 @@ typedef struct _NMPlatformIP6Route NMPlatformIP6Route; + typedef struct _NMPlatformLink NMPlatformLink; + typedef struct _NMPObject NMPObject; + +-typedef enum { ++typedef enum _nm_packed { + NMP_OBJECT_TYPE_UNKNOWN, + NMP_OBJECT_TYPE_LINK, + +-- +2.35.1 + + +From efbeb187430ffec0d50cd79d0867013ea289f5fa Mon Sep 17 00:00:00 2001 +From: Thomas Haller +Date: Tue, 29 Mar 2022 18:20:31 +0200 +Subject: [PATCH 10/11] platform: add semantic comparison for IP addresses and + add "nm_platform_vtable_address" + +We already have a comparison of NMPlatformIPXAddress with the modes +"full" and "id". The former is needed to fully compare two addresses, +the latter as identity for tracking addresses in the cache. + +In NetworkManager we also use the NMPlatformIP[46]Address structure to +track the addresses we want to configure. When we add them in kernel, +we will later see them in the platform cache. However, some fields +will be slightly different. For example, "addr_source" address will +always be "kernel", because that one is not a field we configure in +kernel. Also, the "n_ifa_flags" probably differ (getting "permanent" +and "secondary" flags). + +Add a compare function that can ignore such differences. + +Also add nm_platform_vtable_address for accessing the IPv4 and IPv6 +methods generically (based on an "IS_IPv4" variable). + +(cherry picked from commit ef1b60c061f85b60329d37d62dc81683ff56f4b7) +(cherry picked from commit ea6625ce97629b287f484e0d5caeb0d08ed44843) +(cherry picked from commit 8f83aec9d3dd172dd297ba5394e61c85641dd754) +--- + src/libnm-platform/nm-platform.c | 165 +++++++++++++++++++++++++------ + src/libnm-platform/nm-platform.h | 49 ++++++++- + src/libnm-platform/nmp-object.c | 82 +++++++-------- + 3 files changed, 224 insertions(+), 72 deletions(-) + +diff --git a/src/libnm-platform/nm-platform.c b/src/libnm-platform/nm-platform.c +index ea7aad9398..8459231dc2 100644 +--- a/src/libnm-platform/nm-platform.c ++++ b/src/libnm-platform/nm-platform.c +@@ -7831,6 +7831,25 @@ _address_pretty_sort_get_prio_6(const struct in6_addr *addr) + return 6; + } + ++static int ++_address_cmp_expiry(const NMPlatformIPAddress *a, const NMPlatformIPAddress *b) ++{ ++ guint32 lifetime_a; ++ guint32 lifetime_b; ++ guint32 preferred_a; ++ guint32 preferred_b; ++ gint32 now = 0; ++ ++ lifetime_a = ++ nmp_utils_lifetime_get(a->timestamp, a->lifetime, a->preferred, &now, &preferred_a); ++ lifetime_b = ++ nmp_utils_lifetime_get(b->timestamp, b->lifetime, b->preferred, &now, &preferred_b); ++ ++ NM_CMP_DIRECT(lifetime_a, lifetime_b); ++ NM_CMP_DIRECT(preferred_a, preferred_b); ++ return 0; ++} ++ + int + nm_platform_ip6_address_pretty_sort_cmp(const NMPlatformIP6Address *a1, + const NMPlatformIP6Address *a2, +@@ -7910,26 +7929,55 @@ nm_platform_ip4_address_hash_update(const NMPlatformIP4Address *obj, NMHashState + } + + int +-nm_platform_ip4_address_cmp(const NMPlatformIP4Address *a, const NMPlatformIP4Address *b) ++nm_platform_ip4_address_cmp(const NMPlatformIP4Address *a, ++ const NMPlatformIP4Address *b, ++ NMPlatformIPAddressCmpType cmp_type) + { + NM_CMP_SELF(a, b); ++ + NM_CMP_FIELD(a, b, ifindex); +- NM_CMP_FIELD(a, b, address); + NM_CMP_FIELD(a, b, plen); +- NM_CMP_FIELD(a, b, peer_address); +- NM_CMP_FIELD_UNSAFE(a, b, use_ip4_broadcast_address); +- if (a->use_ip4_broadcast_address) +- NM_CMP_FIELD(a, b, broadcast_address); +- NM_CMP_FIELD(a, b, addr_source); +- NM_CMP_FIELD(a, b, timestamp); +- NM_CMP_FIELD(a, b, lifetime); +- NM_CMP_FIELD(a, b, preferred); +- NM_CMP_FIELD(a, b, n_ifa_flags); +- NM_CMP_FIELD_STR(a, b, label); +- NM_CMP_FIELD_UNSAFE(a, b, a_acd_not_ready); +- NM_CMP_FIELD_UNSAFE(a, b, a_assume_config_once); +- NM_CMP_FIELD_UNSAFE(a, b, a_force_commit); +- return 0; ++ NM_CMP_FIELD(a, b, address); ++ ++ switch (cmp_type) { ++ case NM_PLATFORM_IP_ADDRESS_CMP_TYPE_ID: ++ /* for IPv4 addresses, you can add the same local address with differing peer-address ++ * (IFA_ADDRESS), provided that their net-part differs. */ ++ NM_CMP_DIRECT_IN4ADDR_SAME_PREFIX(a->peer_address, b->peer_address, a->plen); ++ return 0; ++ case NM_PLATFORM_IP_ADDRESS_CMP_TYPE_SEMANTICALLY: ++ case NM_PLATFORM_IP_ADDRESS_CMP_TYPE_FULL: ++ NM_CMP_FIELD(a, b, peer_address); ++ NM_CMP_FIELD_STR(a, b, label); ++ if (cmp_type == NM_PLATFORM_IP_ADDRESS_CMP_TYPE_SEMANTICALLY) { ++ NM_CMP_RETURN(_address_cmp_expiry((const NMPlatformIPAddress *) a, ++ (const NMPlatformIPAddress *) b)); ++ ++ /* Most flags are set by kernel. We only compare the ones that ++ * NetworkManager actively sets. ++ * ++ * NM actively only sets IFA_F_NOPREFIXROUTE (and IFA_F_MANAGETEMPADDR for IPv6), ++ * where nm_platform_ip_address_sync() always sets IFA_F_NOPREFIXROUTE. ++ * There are thus no flags to compare for IPv4. */ ++ ++ NM_CMP_DIRECT(nm_platform_ip4_broadcast_address_from_addr(a), ++ nm_platform_ip4_broadcast_address_from_addr(b)); ++ } else { ++ NM_CMP_FIELD(a, b, timestamp); ++ NM_CMP_FIELD(a, b, lifetime); ++ NM_CMP_FIELD(a, b, preferred); ++ NM_CMP_FIELD(a, b, n_ifa_flags); ++ NM_CMP_FIELD(a, b, addr_source); ++ NM_CMP_FIELD_UNSAFE(a, b, use_ip4_broadcast_address); ++ if (a->use_ip4_broadcast_address) ++ NM_CMP_FIELD(a, b, broadcast_address); ++ NM_CMP_FIELD_UNSAFE(a, b, a_acd_not_ready); ++ NM_CMP_FIELD_UNSAFE(a, b, a_assume_config_once); ++ NM_CMP_FIELD_UNSAFE(a, b, a_force_commit); ++ } ++ return 0; ++ } ++ return nm_assert_unreachable_val(0); + } + + void +@@ -7950,25 +7998,51 @@ nm_platform_ip6_address_hash_update(const NMPlatformIP6Address *obj, NMHashState + } + + int +-nm_platform_ip6_address_cmp(const NMPlatformIP6Address *a, const NMPlatformIP6Address *b) ++nm_platform_ip6_address_cmp(const NMPlatformIP6Address *a, ++ const NMPlatformIP6Address *b, ++ NMPlatformIPAddressCmpType cmp_type) + { + const struct in6_addr *p_a, *p_b; + + NM_CMP_SELF(a, b); ++ + NM_CMP_FIELD(a, b, ifindex); +- NM_CMP_FIELD_MEMCMP(a, b, address); +- NM_CMP_FIELD(a, b, plen); +- p_a = nm_platform_ip6_address_get_peer(a); +- p_b = nm_platform_ip6_address_get_peer(b); +- NM_CMP_DIRECT_MEMCMP(p_a, p_b, sizeof(*p_a)); +- NM_CMP_FIELD(a, b, addr_source); +- NM_CMP_FIELD(a, b, timestamp); +- NM_CMP_FIELD(a, b, lifetime); +- NM_CMP_FIELD(a, b, preferred); +- NM_CMP_FIELD(a, b, n_ifa_flags); +- NM_CMP_FIELD_UNSAFE(a, b, a_assume_config_once); +- NM_CMP_FIELD_UNSAFE(a, b, a_force_commit); +- return 0; ++ NM_CMP_FIELD_IN6ADDR(a, b, address); ++ ++ switch (cmp_type) { ++ case NM_PLATFORM_IP_ADDRESS_CMP_TYPE_ID: ++ /* for IPv6 addresses, the prefix length is not part of the primary identifier. */ ++ return 0; ++ case NM_PLATFORM_IP_ADDRESS_CMP_TYPE_SEMANTICALLY: ++ case NM_PLATFORM_IP_ADDRESS_CMP_TYPE_FULL: ++ NM_CMP_FIELD(a, b, plen); ++ p_a = nm_platform_ip6_address_get_peer(a); ++ p_b = nm_platform_ip6_address_get_peer(b); ++ NM_CMP_DIRECT_MEMCMP(p_a, p_b, sizeof(*p_a)); ++ if (cmp_type == NM_PLATFORM_IP_ADDRESS_CMP_TYPE_SEMANTICALLY) { ++ NM_CMP_RETURN(_address_cmp_expiry((const NMPlatformIPAddress *) a, ++ (const NMPlatformIPAddress *) b)); ++ ++ /* Most flags are set by kernel. We only compare the ones that ++ * NetworkManager actively sets. ++ * ++ * NM actively only sets IFA_F_NOPREFIXROUTE and IFA_F_MANAGETEMPADDR, ++ * where nm_platform_ip_address_sync() always sets IFA_F_NOPREFIXROUTE. ++ * We thus only care about IFA_F_MANAGETEMPADDR. */ ++ NM_CMP_DIRECT(a->n_ifa_flags & IFA_F_MANAGETEMPADDR, ++ b->n_ifa_flags & IFA_F_MANAGETEMPADDR); ++ } else { ++ NM_CMP_FIELD(a, b, timestamp); ++ NM_CMP_FIELD(a, b, lifetime); ++ NM_CMP_FIELD(a, b, preferred); ++ NM_CMP_FIELD(a, b, n_ifa_flags); ++ NM_CMP_FIELD(a, b, addr_source); ++ NM_CMP_FIELD_UNSAFE(a, b, a_assume_config_once); ++ NM_CMP_FIELD_UNSAFE(a, b, a_force_commit); ++ } ++ return 0; ++ } ++ return nm_assert_unreachable_val(0); + } + + void +@@ -8924,6 +8998,37 @@ nm_platform_netns_push(NMPlatform *self, NMPNetns **netns) + + /*****************************************************************************/ + ++const _NMPlatformVTableAddressUnion nm_platform_vtable_address = { ++ .v4 = ++ { ++ .is_ip4 = TRUE, ++ .obj_type = NMP_OBJECT_TYPE_IP4_ADDRESS, ++ .addr_family = AF_INET, ++ .sizeof_address = sizeof(NMPlatformIP4Address), ++ .address_cmp = ++ (int (*)(const NMPlatformIPXAddress *a, ++ const NMPlatformIPXAddress *b, ++ NMPlatformIPAddressCmpType cmp_type)) nm_platform_ip4_address_cmp, ++ .address_to_string = (const char *(*) (const NMPlatformIPXAddress *address, ++ char *buf, ++ gsize len)) nm_platform_ip4_address_to_string, ++ }, ++ .v6 = ++ { ++ .is_ip4 = FALSE, ++ .obj_type = NMP_OBJECT_TYPE_IP6_ADDRESS, ++ .addr_family = AF_INET6, ++ .sizeof_address = sizeof(NMPlatformIP6Address), ++ .address_cmp = ++ (int (*)(const NMPlatformIPXAddress *a, ++ const NMPlatformIPXAddress *b, ++ NMPlatformIPAddressCmpType cmp_type)) nm_platform_ip6_address_cmp, ++ .address_to_string = (const char *(*) (const NMPlatformIPXAddress *address, ++ char *buf, ++ gsize len)) nm_platform_ip6_address_to_string, ++ }, ++}; ++ + const _NMPlatformVTableRouteUnion nm_platform_vtable_route = { + .v4 = + { +diff --git a/src/libnm-platform/nm-platform.h b/src/libnm-platform/nm-platform.h +index d978c91b80..203f8c7bbd 100644 +--- a/src/libnm-platform/nm-platform.h ++++ b/src/libnm-platform/nm-platform.h +@@ -94,6 +94,14 @@ typedef enum { + NMP_NLM_FLAG_TEST = NMP_NLM_FLAG_F_EXCL, + } NMPNlmFlags; + ++typedef enum { ++ NM_PLATFORM_IP_ADDRESS_CMP_TYPE_ID, ++ ++ NM_PLATFORM_IP_ADDRESS_CMP_TYPE_SEMANTICALLY, ++ ++ NM_PLATFORM_IP_ADDRESS_CMP_TYPE_FULL, ++} NMPlatformIPAddressCmpType; ++ + typedef enum { + /* compare fields which kernel considers as similar routes. + * It is a looser comparisong then NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID +@@ -775,6 +783,27 @@ typedef struct { + + #undef __NMPlatformObjWithIfindex_COMMON + ++typedef struct { ++ bool is_ip4; ++ NMPObjectType obj_type; ++ gint8 addr_family; ++ guint8 sizeof_address; ++ int (*address_cmp)(const NMPlatformIPXAddress *a, ++ const NMPlatformIPXAddress *b, ++ NMPlatformIPAddressCmpType cmp_type); ++ const char *(*address_to_string)(const NMPlatformIPXAddress *address, char *buf, gsize len); ++} NMPlatformVTableAddress; ++ ++typedef union { ++ struct { ++ NMPlatformVTableAddress v6; ++ NMPlatformVTableAddress v4; ++ }; ++ NMPlatformVTableAddress vx[2]; ++} _NMPlatformVTableAddressUnion; ++ ++extern const _NMPlatformVTableAddressUnion nm_platform_vtable_address; ++ + typedef struct { + bool is_ip4; + gint8 addr_family; +@@ -2311,8 +2340,24 @@ int nm_platform_lnk_vlan_cmp(const NMPlatformLnkVlan *a, const NMPlatformLnkVlan + int nm_platform_lnk_vrf_cmp(const NMPlatformLnkVrf *a, const NMPlatformLnkVrf *b); + int nm_platform_lnk_vxlan_cmp(const NMPlatformLnkVxlan *a, const NMPlatformLnkVxlan *b); + int nm_platform_lnk_wireguard_cmp(const NMPlatformLnkWireGuard *a, const NMPlatformLnkWireGuard *b); +-int nm_platform_ip4_address_cmp(const NMPlatformIP4Address *a, const NMPlatformIP4Address *b); +-int nm_platform_ip6_address_cmp(const NMPlatformIP6Address *a, const NMPlatformIP6Address *b); ++int nm_platform_ip4_address_cmp(const NMPlatformIP4Address *a, ++ const NMPlatformIP4Address *b, ++ NMPlatformIPAddressCmpType cmp_type); ++int nm_platform_ip6_address_cmp(const NMPlatformIP6Address *a, ++ const NMPlatformIP6Address *b, ++ NMPlatformIPAddressCmpType cmp_type); ++ ++static inline int ++nm_platform_ip4_address_cmp_full(const NMPlatformIP4Address *a, const NMPlatformIP4Address *b) ++{ ++ return nm_platform_ip4_address_cmp(a, b, NM_PLATFORM_IP_ADDRESS_CMP_TYPE_FULL); ++} ++ ++static inline int ++nm_platform_ip6_address_cmp_full(const NMPlatformIP6Address *a, const NMPlatformIP6Address *b) ++{ ++ return nm_platform_ip6_address_cmp(a, b, NM_PLATFORM_IP_ADDRESS_CMP_TYPE_FULL); ++} + + int nm_platform_ip4_address_pretty_sort_cmp(const NMPlatformIP4Address *a1, + const NMPlatformIP4Address *a2); +diff --git a/src/libnm-platform/nmp-object.c b/src/libnm-platform/nmp-object.c +index d518e6e589..0102e943ff 100644 +--- a/src/libnm-platform/nmp-object.c ++++ b/src/libnm-platform/nmp-object.c +@@ -1523,20 +1523,21 @@ nmp_object_id_cmp(const NMPObject *obj1, const NMPObject *obj2) + + _vt_cmd_plobj_id_cmp(link, NMPlatformLink, { NM_CMP_FIELD(obj1, obj2, ifindex); }); + +-_vt_cmd_plobj_id_cmp(ip4_address, NMPlatformIP4Address, { +- NM_CMP_FIELD(obj1, obj2, ifindex); +- NM_CMP_FIELD(obj1, obj2, plen); +- NM_CMP_FIELD(obj1, obj2, address); +- /* for IPv4 addresses, you can add the same local address with differing peer-address +- * (IFA_ADDRESS), provided that their net-part differs. */ +- NM_CMP_DIRECT_IN4ADDR_SAME_PREFIX(obj1->peer_address, obj2->peer_address, obj1->plen); +-}); ++static int ++_vt_cmd_plobj_id_cmp_ip4_address(const NMPlatformObject *obj1, const NMPlatformObject *obj2) ++{ ++ return nm_platform_ip4_address_cmp((const NMPlatformIP4Address *) obj1, ++ (const NMPlatformIP4Address *) obj2, ++ NM_PLATFORM_IP_ADDRESS_CMP_TYPE_ID); ++} + +-_vt_cmd_plobj_id_cmp(ip6_address, NMPlatformIP6Address, { +- NM_CMP_FIELD(obj1, obj2, ifindex); +- /* for IPv6 addresses, the prefix length is not part of the primary identifier. */ +- NM_CMP_FIELD_IN6ADDR(obj1, obj2, address); +-}); ++static int ++_vt_cmd_plobj_id_cmp_ip6_address(const NMPlatformObject *obj1, const NMPlatformObject *obj2) ++{ ++ return nm_platform_ip6_address_cmp((const NMPlatformIP6Address *) obj1, ++ (const NMPlatformIP6Address *) obj2, ++ NM_PLATFORM_IP_ADDRESS_CMP_TYPE_ID); ++} + + _vt_cmd_plobj_id_cmp(qdisc, NMPlatformQdisc, { + NM_CMP_FIELD(obj1, obj2, ifindex); +@@ -1551,24 +1552,24 @@ _vt_cmd_plobj_id_cmp(tfilter, NMPlatformTfilter, { + static int + _vt_cmd_plobj_id_cmp_ip4_route(const NMPlatformObject *obj1, const NMPlatformObject *obj2) + { +- return nm_platform_ip4_route_cmp((NMPlatformIP4Route *) obj1, +- (NMPlatformIP4Route *) obj2, ++ return nm_platform_ip4_route_cmp((const NMPlatformIP4Route *) obj1, ++ (const NMPlatformIP4Route *) obj2, + NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID); + } + + static int + _vt_cmd_plobj_id_cmp_ip6_route(const NMPlatformObject *obj1, const NMPlatformObject *obj2) + { +- return nm_platform_ip6_route_cmp((NMPlatformIP6Route *) obj1, +- (NMPlatformIP6Route *) obj2, ++ return nm_platform_ip6_route_cmp((const NMPlatformIP6Route *) obj1, ++ (const NMPlatformIP6Route *) obj2, + NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID); + } + + static int + _vt_cmd_plobj_id_cmp_routing_rule(const NMPlatformObject *obj1, const NMPlatformObject *obj2) + { +- return nm_platform_routing_rule_cmp((NMPlatformRoutingRule *) obj1, +- (NMPlatformRoutingRule *) obj2, ++ return nm_platform_routing_rule_cmp((const NMPlatformRoutingRule *) obj1, ++ (const NMPlatformRoutingRule *) obj2, + NM_PLATFORM_ROUTING_RULE_CMP_TYPE_ID); + } + +@@ -3158,28 +3159,29 @@ const NMPClass _nmp_classes[NMP_OBJECT_TYPE_MAX] = { + .cmd_plobj_to_string_id = _vt_cmd_plobj_to_string_id_ip4_address, + .cmd_plobj_to_string = (CmdPlobjToStringFunc) nm_platform_ip4_address_to_string, + .cmd_plobj_hash_update = (CmdPlobjHashUpdateFunc) nm_platform_ip4_address_hash_update, +- .cmd_plobj_cmp = (CmdPlobjCmpFunc) nm_platform_ip4_address_cmp, ++ .cmd_plobj_cmp = (CmdPlobjCmpFunc) nm_platform_ip4_address_cmp_full, ++ }, ++ [NMP_OBJECT_TYPE_IP6_ADDRESS - 1] = ++ { ++ .parent = DEDUP_MULTI_OBJ_CLASS_INIT(), ++ .obj_type = NMP_OBJECT_TYPE_IP6_ADDRESS, ++ .sizeof_data = sizeof(NMPObjectIP6Address), ++ .sizeof_public = sizeof(NMPlatformIP6Address), ++ .obj_type_name = "ip6-address", ++ .addr_family = AF_INET6, ++ .rtm_gettype = RTM_GETADDR, ++ .signal_type_id = NM_PLATFORM_SIGNAL_ID_IP6_ADDRESS, ++ .signal_type = NM_PLATFORM_SIGNAL_IP6_ADDRESS_CHANGED, ++ .supported_cache_ids = _supported_cache_ids_ipx_address, ++ .cmd_obj_is_alive = _vt_cmd_obj_is_alive_ipx_address, ++ .cmd_plobj_id_copy = _vt_cmd_plobj_id_copy_ip6_address, ++ .cmd_plobj_id_cmp = _vt_cmd_plobj_id_cmp_ip6_address, ++ .cmd_plobj_id_hash_update = _vt_cmd_plobj_id_hash_update_ip6_address, ++ .cmd_plobj_to_string_id = _vt_cmd_plobj_to_string_id_ip6_address, ++ .cmd_plobj_to_string = (CmdPlobjToStringFunc) nm_platform_ip6_address_to_string, ++ .cmd_plobj_hash_update = (CmdPlobjHashUpdateFunc) nm_platform_ip6_address_hash_update, ++ .cmd_plobj_cmp = (CmdPlobjCmpFunc) nm_platform_ip6_address_cmp_full, + }, +- [NMP_OBJECT_TYPE_IP6_ADDRESS +- - 1] = {.parent = DEDUP_MULTI_OBJ_CLASS_INIT(), +- .obj_type = NMP_OBJECT_TYPE_IP6_ADDRESS, +- .sizeof_data = sizeof(NMPObjectIP6Address), +- .sizeof_public = sizeof(NMPlatformIP6Address), +- .obj_type_name = "ip6-address", +- .addr_family = AF_INET6, +- .rtm_gettype = RTM_GETADDR, +- .signal_type_id = NM_PLATFORM_SIGNAL_ID_IP6_ADDRESS, +- .signal_type = NM_PLATFORM_SIGNAL_IP6_ADDRESS_CHANGED, +- .supported_cache_ids = _supported_cache_ids_ipx_address, +- .cmd_obj_is_alive = _vt_cmd_obj_is_alive_ipx_address, +- .cmd_plobj_id_copy = _vt_cmd_plobj_id_copy_ip6_address, +- .cmd_plobj_id_cmp = _vt_cmd_plobj_id_cmp_ip6_address, +- .cmd_plobj_id_hash_update = _vt_cmd_plobj_id_hash_update_ip6_address, +- .cmd_plobj_to_string_id = _vt_cmd_plobj_to_string_id_ip6_address, +- .cmd_plobj_to_string = (CmdPlobjToStringFunc) nm_platform_ip6_address_to_string, +- .cmd_plobj_hash_update = +- (CmdPlobjHashUpdateFunc) nm_platform_ip6_address_hash_update, +- .cmd_plobj_cmp = (CmdPlobjCmpFunc) nm_platform_ip6_address_cmp}, + [NMP_OBJECT_TYPE_IP4_ROUTE - 1] = + { + .parent = DEDUP_MULTI_OBJ_CLASS_INIT(), +-- +2.35.1 + + +From b9cb6401cc5efb4092e8a92af6ed509a986fe495 Mon Sep 17 00:00:00 2001 +From: Fernando Fernandez Mancera +Date: Tue, 19 Apr 2022 18:39:37 +0200 +Subject: [PATCH 11/11] l3cfg: drop NM_L3_CFG_COMMIT_TYPE_ASSUME and + assume_config_once + +ASSUME is causing more troubles than benefits it provides. This patch is +dropping NM_L3_CFG_COMMIT_TYPE_ASSUME and assume_config_once. NM3LCfg +will commit as if the sys-iface-state is MANAGED. + +This patch is part of the effort to remove ASSUME from NetworkManager. +After ASSUME is dropped when starting NetworkManager it will take full +control of the interface, re-configuring it. The interface will be +managed from the start instead of assumed and then managed. + +This will solve the situations where an interface is half-up and then a +restart happens. When NetworkManager is back it won't add the missing +addresses (which is what assume does) so the interface will fail during +the activation and will require a full activation. + +https://bugzilla.redhat.com/show_bug.cgi?id=2050216 +https://bugzilla.redhat.com/show_bug.cgi?id=2077605 +https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/1196 +(cherry picked from commit bf5927b978fccec1390bcc7d3d5719d7fe7c3450) +(cherry picked from commit a494c00901a56afe422c4944875de0340d305e8a) +(cherry picked from commit 05c3f384b641624edc1a951899bcb0f770972e28) +--- + src/core/devices/nm-device.c | 4 ++- + src/core/nm-l3-config-data.c | 36 +++---------------------- + src/core/nm-l3-config-data.h | 1 - + src/core/nm-l3-ipv4ll.c | 2 +- + src/core/nm-l3-ipv6ll.c | 9 +++---- + src/core/nm-l3cfg.c | 32 +++------------------- + src/core/nm-l3cfg.h | 9 ------- + src/core/tests/test-l3cfg.c | 12 ++++----- + src/libnm-platform/nm-platform.c | 46 ++++++++++---------------------- + src/libnm-platform/nm-platform.h | 5 ---- + src/libnm-platform/nmp-object.h | 15 ----------- + 11 files changed, 34 insertions(+), 137 deletions(-) + +diff --git a/src/core/devices/nm-device.c b/src/core/devices/nm-device.c +index a11486d54b..264d75d936 100644 +--- a/src/core/devices/nm-device.c ++++ b/src/core/devices/nm-device.c +@@ -3976,7 +3976,9 @@ _dev_l3_cfg_commit_type_reset(NMDevice *self) + commit_type = NM_L3_CFG_COMMIT_TYPE_NONE; + goto do_set; + case NM_DEVICE_SYS_IFACE_STATE_ASSUME: +- commit_type = NM_L3_CFG_COMMIT_TYPE_ASSUME; ++ /* TODO: NM_DEVICE_SYS_IFACE_STATE_ASSUME, will be dropped from the code. ++ * Meanwhile, the commit type must be updated. */ ++ commit_type = NM_L3_CFG_COMMIT_TYPE_UPDATE; + goto do_set; + case NM_DEVICE_SYS_IFACE_STATE_MANAGED: + commit_type = NM_L3_CFG_COMMIT_TYPE_UPDATE; +diff --git a/src/core/nm-l3-config-data.c b/src/core/nm-l3-config-data.c +index 03593ea28c..1cabdb6c9b 100644 +--- a/src/core/nm-l3-config-data.c ++++ b/src/core/nm-l3-config-data.c +@@ -1172,13 +1172,6 @@ _l3_config_data_add_obj(NMDedupMultiIndex *multi_idx, + modified = TRUE; + } + +- /* OR assume_config_once flag */ +- if (obj_new->ip_address.a_assume_config_once +- && !obj_old->ip_address.a_assume_config_once) { +- obj_new = nmp_object_stackinit_obj(&obj_new_stackinit, obj_new); +- obj_new_stackinit.ip_address.a_assume_config_once = TRUE; +- modified = TRUE; +- } + break; + case NMP_OBJECT_TYPE_IP4_ROUTE: + case NMP_OBJECT_TYPE_IP6_ROUTE: +@@ -1189,13 +1182,6 @@ _l3_config_data_add_obj(NMDedupMultiIndex *multi_idx, + modified = TRUE; + } + +- /* OR assume_config_once flag */ +- if (obj_new->ip_route.r_assume_config_once +- && !obj_old->ip_route.r_assume_config_once) { +- obj_new = nmp_object_stackinit_obj(&obj_new_stackinit, obj_new); +- obj_new_stackinit.ip_route.r_assume_config_once = TRUE; +- modified = TRUE; +- } + break; + default: + nm_assert_not_reached(); +@@ -3056,9 +3042,8 @@ nm_l3_config_data_merge(NML3ConfigData *self, + const NMPlatformIPAddress *a_src = NMP_OBJECT_CAST_IP_ADDRESS(obj); + NMPlatformIPXAddress a; + NML3ConfigMergeHookResult hook_result = { +- .ip4acd_not_ready = NM_OPTION_BOOL_DEFAULT, +- .assume_config_once = NM_OPTION_BOOL_DEFAULT, +- .force_commit = NM_OPTION_BOOL_DEFAULT, ++ .ip4acd_not_ready = NM_OPTION_BOOL_DEFAULT, ++ .force_commit = NM_OPTION_BOOL_DEFAULT, + }; + + #define _ensure_a() \ +@@ -3091,12 +3076,6 @@ nm_l3_config_data_merge(NML3ConfigData *self, + a.a4.a_acd_not_ready = (!!hook_result.ip4acd_not_ready); + } + +- if (hook_result.assume_config_once != NM_OPTION_BOOL_DEFAULT +- && (!!hook_result.assume_config_once) != a_src->a_assume_config_once) { +- _ensure_a(); +- a.ax.a_assume_config_once = (!!hook_result.assume_config_once); +- } +- + if (hook_result.force_commit != NM_OPTION_BOOL_DEFAULT + && (!!hook_result.force_commit) != a_src->a_force_commit) { + _ensure_a(); +@@ -3121,9 +3100,8 @@ nm_l3_config_data_merge(NML3ConfigData *self, + const NMPlatformIPRoute *r_src = NMP_OBJECT_CAST_IP_ROUTE(obj); + NMPlatformIPXRoute r; + NML3ConfigMergeHookResult hook_result = { +- .ip4acd_not_ready = NM_OPTION_BOOL_DEFAULT, +- .assume_config_once = NM_OPTION_BOOL_DEFAULT, +- .force_commit = NM_OPTION_BOOL_DEFAULT, ++ .ip4acd_not_ready = NM_OPTION_BOOL_DEFAULT, ++ .force_commit = NM_OPTION_BOOL_DEFAULT, + }; + + #define _ensure_r() \ +@@ -3149,12 +3127,6 @@ nm_l3_config_data_merge(NML3ConfigData *self, + r.rx.ifindex = self->ifindex; + } + +- if (hook_result.assume_config_once != NM_OPTION_BOOL_DEFAULT +- && (!!hook_result.assume_config_once) != r_src->r_assume_config_once) { +- _ensure_r(); +- r.rx.r_assume_config_once = (!!hook_result.assume_config_once); +- } +- + if (hook_result.force_commit != NM_OPTION_BOOL_DEFAULT + && (!!hook_result.force_commit) != r_src->r_force_commit) { + _ensure_r(); +diff --git a/src/core/nm-l3-config-data.h b/src/core/nm-l3-config-data.h +index b7a1bb32f5..20a32c62aa 100644 +--- a/src/core/nm-l3-config-data.h ++++ b/src/core/nm-l3-config-data.h +@@ -137,7 +137,6 @@ NML3ConfigData *nm_l3_config_data_new_from_platform(NMDedupMultiIndex *mu + + typedef struct { + NMOptionBool ip4acd_not_ready; +- NMOptionBool assume_config_once; + NMOptionBool force_commit; + } NML3ConfigMergeHookResult; + +diff --git a/src/core/nm-l3-ipv4ll.c b/src/core/nm-l3-ipv4ll.c +index 68cb17fb09..473f3c0f03 100644 +--- a/src/core/nm-l3-ipv4ll.c ++++ b/src/core/nm-l3-ipv4ll.c +@@ -600,7 +600,7 @@ _l3cd_config_add(NML3IPv4LL *self) + nm_assert_not_reached(); + + self->l3cfg_commit_handle = nm_l3cfg_commit_type_register(self->l3cfg, +- NM_L3_CFG_COMMIT_TYPE_ASSUME, ++ NM_L3_CFG_COMMIT_TYPE_UPDATE, + self->l3cfg_commit_handle, + "ipv4ll"); + nm_l3cfg_commit_on_idle_schedule(self->l3cfg, NM_L3_CFG_COMMIT_TYPE_AUTO); +diff --git a/src/core/nm-l3-ipv6ll.c b/src/core/nm-l3-ipv6ll.c +index 60da4ee8d7..2b9a1a0ef4 100644 +--- a/src/core/nm-l3-ipv6ll.c ++++ b/src/core/nm-l3-ipv6ll.c +@@ -398,10 +398,7 @@ _lladdr_handle_changed(NML3IPv6LL *self) + * NML3IPv4LL, where we use NM_L3_CONFIG_MERGE_FLAGS_ONLY_FOR_ACD. The difference + * is that for IPv6 we let kernel do DAD, so we need to actually configure the + * address. For IPv4, we can run ACD without configuring anything in kernel, +- * and let the user decide how to proceed. +- * +- * Also in this case, we use the most graceful commit-type (NM_L3_CFG_COMMIT_TYPE_ASSUME), +- * but for that to work, we also need NM_L3CFG_CONFIG_FLAGS_ASSUME_CONFIG_ONCE flag. */ ++ * and let the user decide how to proceed. */ + + l3cd = nm_l3_ipv6ll_get_l3cd(self); + +@@ -421,7 +418,7 @@ _lladdr_handle_changed(NML3IPv6LL *self) + NM_DNS_PRIORITY_DEFAULT_NORMAL, + NM_L3_ACD_DEFEND_TYPE_ALWAYS, + 0, +- NM_L3CFG_CONFIG_FLAGS_ASSUME_CONFIG_ONCE, ++ NM_L3CFG_CONFIG_FLAGS_NONE, + NM_L3_CONFIG_MERGE_FLAGS_NONE)) + changed = TRUE; + } else { +@@ -430,7 +427,7 @@ _lladdr_handle_changed(NML3IPv6LL *self) + } + + self->l3cfg_commit_handle = nm_l3cfg_commit_type_register(self->l3cfg, +- l3cd ? NM_L3_CFG_COMMIT_TYPE_ASSUME ++ l3cd ? NM_L3_CFG_COMMIT_TYPE_UPDATE + : NM_L3_CFG_COMMIT_TYPE_NONE, + self->l3cfg_commit_handle, + "ipv6ll"); +diff --git a/src/core/nm-l3cfg.c b/src/core/nm-l3cfg.c +index eeb041d042..20058f2321 100644 +--- a/src/core/nm-l3cfg.c ++++ b/src/core/nm-l3cfg.c +@@ -363,7 +363,6 @@ static NM_UTILS_ENUM2STR_DEFINE(_l3_cfg_commit_type_to_string, + NML3CfgCommitType, + NM_UTILS_ENUM2STR(NM_L3_CFG_COMMIT_TYPE_AUTO, "auto"), + NM_UTILS_ENUM2STR(NM_L3_CFG_COMMIT_TYPE_NONE, "none"), +- NM_UTILS_ENUM2STR(NM_L3_CFG_COMMIT_TYPE_ASSUME, "assume"), + NM_UTILS_ENUM2STR(NM_L3_CFG_COMMIT_TYPE_UPDATE, "update"), + NM_UTILS_ENUM2STR(NM_L3_CFG_COMMIT_TYPE_REAPPLY, "reapply"), ); + +@@ -768,14 +767,6 @@ _nm_n_acd_data_probe_new(NML3Cfg *self, in_addr_t addr, guint32 timeout_msec, gp + } \ + G_STMT_END + +-static gboolean +-_obj_state_data_get_assume_config_once(const ObjStateData *obj_state) +-{ +- nm_assert_obj_state(NULL, obj_state); +- +- return nmp_object_get_assume_config_once(obj_state->obj); +-} +- + static ObjStateData * + _obj_state_data_new(const NMPObject *obj, const NMPObject *plobj) + { +@@ -1054,10 +1045,6 @@ _obj_states_sync_filter(NML3Cfg *self, const NMPObject *obj, NML3CfgCommitType c + nm_assert(c_list_is_empty(&obj_state->os_zombie_lst)); + + if (!obj_state->os_nm_configured) { +- if (commit_type == NM_L3_CFG_COMMIT_TYPE_ASSUME +- && !_obj_state_data_get_assume_config_once(obj_state)) +- return FALSE; +- + obj_state->os_nm_configured = TRUE; + + _LOGD("obj-state: configure-first-time: %s", +@@ -3088,7 +3075,6 @@ nm_l3cfg_commit_on_idle_schedule(NML3Cfg *self, NML3CfgCommitType commit_type) + nm_assert(NM_IS_L3CFG(self)); + nm_assert(NM_IN_SET(commit_type, + NM_L3_CFG_COMMIT_TYPE_AUTO, +- NM_L3_CFG_COMMIT_TYPE_ASSUME, + NM_L3_CFG_COMMIT_TYPE_UPDATE, + NM_L3_CFG_COMMIT_TYPE_REAPPLY)); + +@@ -3503,7 +3489,6 @@ out_clear: + typedef struct { + NML3Cfg *self; + gconstpointer tag; +- bool assume_config_once; + bool to_commit; + bool force_commit_once; + } L3ConfigMergeHookAddObjData; +@@ -3523,11 +3508,9 @@ _l3_hook_add_obj_cb(const NML3ConfigData *l3cd, + nm_assert(obj); + nm_assert(hook_result); + nm_assert(hook_result->ip4acd_not_ready == NM_OPTION_BOOL_DEFAULT); +- nm_assert(hook_result->assume_config_once == NM_OPTION_BOOL_DEFAULT); + nm_assert(hook_result->force_commit == NM_OPTION_BOOL_DEFAULT); + +- hook_result->assume_config_once = hook_data->assume_config_once; +- hook_result->force_commit = hook_data->force_commit_once; ++ hook_result->force_commit = hook_data->force_commit_once; + + switch (NMP_OBJECT_GET_TYPE(obj)) { + case NMP_OBJECT_TYPE_IP4_ADDRESS: +@@ -3683,9 +3666,7 @@ _l3cfg_update_combined_config(NML3Cfg *self, + if (NM_FLAGS_HAS(l3cd_data->config_flags, NM_L3CFG_CONFIG_FLAGS_ONLY_FOR_ACD)) + continue; + +- hook_data.tag = l3cd_data->tag_confdata; +- hook_data.assume_config_once = +- NM_FLAGS_HAS(l3cd_data->config_flags, NM_L3CFG_CONFIG_FLAGS_ASSUME_CONFIG_ONCE); ++ hook_data.tag = l3cd_data->tag_confdata; + hook_data.force_commit_once = l3cd_data->force_commit_once; + + nm_l3_config_data_merge(l3cd, +@@ -4212,8 +4193,7 @@ _l3_commit_one(NML3Cfg *self, + nm_assert(NM_IN_SET(commit_type, + NM_L3_CFG_COMMIT_TYPE_NONE, + NM_L3_CFG_COMMIT_TYPE_REAPPLY, +- NM_L3_CFG_COMMIT_TYPE_UPDATE, +- NM_L3_CFG_COMMIT_TYPE_ASSUME)); ++ NM_L3_CFG_COMMIT_TYPE_UPDATE)); + nm_assert_addr_family(addr_family); + + _LOGT("committing IPv%c configuration (%s)", +@@ -4299,7 +4279,6 @@ _l3_commit(NML3Cfg *self, NML3CfgCommitType commit_type, gboolean is_idle) + nm_assert(NM_IN_SET(commit_type, + NM_L3_CFG_COMMIT_TYPE_NONE, + NM_L3_CFG_COMMIT_TYPE_AUTO, +- NM_L3_CFG_COMMIT_TYPE_ASSUME, + NM_L3_CFG_COMMIT_TYPE_UPDATE, + NM_L3_CFG_COMMIT_TYPE_REAPPLY)); + nm_assert(self->priv.p->commit_reentrant_count == 0); +@@ -4423,10 +4402,7 @@ nm_l3cfg_commit_type_register(NML3Cfg *self, + char buf[64]; + + nm_assert(NM_IS_L3CFG(self)); +- nm_assert(NM_IN_SET(commit_type, +- NM_L3_CFG_COMMIT_TYPE_NONE, +- NM_L3_CFG_COMMIT_TYPE_ASSUME, +- NM_L3_CFG_COMMIT_TYPE_UPDATE)); ++ nm_assert(NM_IN_SET(commit_type, NM_L3_CFG_COMMIT_TYPE_NONE, NM_L3_CFG_COMMIT_TYPE_UPDATE)); + + /* It would be easy (and maybe convenient) to allow that @existing_handle + * can currently be registered on another NML3Cfg instance. But then we couldn't +diff --git a/src/core/nm-l3cfg.h b/src/core/nm-l3cfg.h +index 0ea6864661..f6ec39ced8 100644 +--- a/src/core/nm-l3cfg.h ++++ b/src/core/nm-l3cfg.h +@@ -363,15 +363,6 @@ typedef enum _nm_packed { + /* Don't touch the interface. */ + NM_L3_CFG_COMMIT_TYPE_NONE, + +- /* ASSUME means to keep any pre-existing extra routes/addresses, while +- * also not adding routes/addresses that are not present yet. This is to +- * gracefully take over after restart, where the existing IP configuration +- * should not change. +- * +- * The flag NM_L3CFG_CONFIG_FLAGS_ASSUME_CONFIG_ONCE can make certain addresses/ +- * routes commitable also during "assume". */ +- NM_L3_CFG_COMMIT_TYPE_ASSUME, +- + /* UPDATE means to add new addresses/routes, while also removing addresses/routes + * that are no longer present (but were previously configured by NetworkManager). + * Routes/addresses that were removed externally won't be re-added, and routes/addresses +diff --git a/src/core/tests/test-l3cfg.c b/src/core/tests/test-l3cfg.c +index 5501079ec4..eebfbcf58e 100644 +--- a/src/core/tests/test-l3cfg.c ++++ b/src/core/tests/test-l3cfg.c +@@ -382,13 +382,11 @@ test_l3cfg(gconstpointer test_data) + nm_l3cfg_commit_type_register(l3cfg0, NM_L3_CFG_COMMIT_TYPE_UPDATE, NULL, "test1"); + + if (!nmtst_get_rand_one_case_in(4)) { +- commit_type_2 = +- nm_l3cfg_commit_type_register(l3cfg0, +- nmtst_rand_select(NM_L3_CFG_COMMIT_TYPE_NONE, +- NM_L3_CFG_COMMIT_TYPE_ASSUME, +- NM_L3_CFG_COMMIT_TYPE_UPDATE), +- NULL, +- "test2"); ++ commit_type_2 = nm_l3cfg_commit_type_register( ++ l3cfg0, ++ nmtst_rand_select(NM_L3_CFG_COMMIT_TYPE_NONE, NM_L3_CFG_COMMIT_TYPE_UPDATE), ++ NULL, ++ "test2"); + } else + commit_type_2 = NULL; + +diff --git a/src/libnm-platform/nm-platform.c b/src/libnm-platform/nm-platform.c +index 8459231dc2..b1ae168f66 100644 +--- a/src/libnm-platform/nm-platform.c ++++ b/src/libnm-platform/nm-platform.c +@@ -6312,7 +6312,6 @@ nm_platform_ip4_address_to_string(const NMPlatformIP4Address *address, char *buf + "%s" /* label */ + " src %s" + "%s" /* a_acd_not_ready */ +- "%s" /* a_assume_config_once */ + "%s" /* a_force_commit */ + "", + s_address, +@@ -6332,7 +6331,6 @@ nm_platform_ip4_address_to_string(const NMPlatformIP4Address *address, char *buf + str_label, + nmp_utils_ip_config_source_to_string(address->addr_source, s_source, sizeof(s_source)), + address->a_acd_not_ready ? " ip4acd-not-ready" : "", +- address->a_assume_config_once ? " assume-config-once" : "", + address->a_force_commit ? " force-commit" : ""); + g_free(str_peer); + return buf; +@@ -6453,7 +6451,6 @@ nm_platform_ip6_address_to_string(const NMPlatformIP6Address *address, char *buf + buf, + len, + "%s/%d lft %s pref %s%s%s%s%s src %s" +- "%s" /* a_assume_config_once */ + "%s" /* a_force_commit */ + "", + s_address, +@@ -6465,7 +6462,6 @@ nm_platform_ip6_address_to_string(const NMPlatformIP6Address *address, char *buf + str_dev, + _to_string_ifa_flags(address->n_ifa_flags, s_flags, sizeof(s_flags)), + nmp_utils_ip_config_source_to_string(address->addr_source, s_source, sizeof(s_source)), +- address->a_assume_config_once ? " assume-config-once" : "", + address->a_force_commit ? " force-commit" : ""); + g_free(str_peer); + return buf; +@@ -6569,7 +6565,6 @@ nm_platform_ip4_route_to_string(const NMPlatformIP4Route *route, char *buf, gsiz + "%s" /* initcwnd */ + "%s" /* initrwnd */ + "%s" /* mtu */ +- "%s" /* r_assume_config_once */ + "%s" /* r_force_commit */ + "", + nm_net_aux_rtnl_rtntype_n2a_maybe_buf(nm_platform_route_type_uncoerce(route->type_coerced), +@@ -6628,7 +6623,6 @@ nm_platform_ip4_route_to_string(const NMPlatformIP4Route *route, char *buf, gsiz + route->lock_mtu ? "lock " : "", + route->mtu) + : "", +- route->r_assume_config_once ? " assume-config-once" : "", + route->r_force_commit ? " force-commit" : ""); + return buf; + } +@@ -6704,7 +6698,6 @@ nm_platform_ip6_route_to_string(const NMPlatformIP6Route *route, char *buf, gsiz + "%s" /* initrwnd */ + "%s" /* mtu */ + "%s" /* pref */ +- "%s" /* r_assume_config_once */ + "%s" /* r_force_commit */ + "", + nm_net_aux_rtnl_rtntype_n2a_maybe_buf(nm_platform_route_type_uncoerce(route->type_coerced), +@@ -6767,7 +6760,6 @@ nm_platform_ip6_route_to_string(const NMPlatformIP6Route *route, char *buf, gsiz + " pref %s", + nm_icmpv6_router_pref_to_string(route->rt_pref, str_pref2, sizeof(str_pref2))) + : "", +- route->r_assume_config_once ? " assume-config-once" : "", + route->r_force_commit ? " force-commit" : ""); + + return buf; +@@ -7923,7 +7915,6 @@ nm_platform_ip4_address_hash_update(const NMPlatformIP4Address *obj, NMHashState + NM_HASH_COMBINE_BOOLS(guint8, + obj->use_ip4_broadcast_address, + obj->a_acd_not_ready, +- obj->a_assume_config_once, + obj->a_force_commit)); + nm_hash_update_strarr(h, obj->label); + } +@@ -7972,7 +7963,6 @@ nm_platform_ip4_address_cmp(const NMPlatformIP4Address *a, + if (a->use_ip4_broadcast_address) + NM_CMP_FIELD(a, b, broadcast_address); + NM_CMP_FIELD_UNSAFE(a, b, a_acd_not_ready); +- NM_CMP_FIELD_UNSAFE(a, b, a_assume_config_once); + NM_CMP_FIELD_UNSAFE(a, b, a_force_commit); + } + return 0; +@@ -7983,18 +7973,17 @@ nm_platform_ip4_address_cmp(const NMPlatformIP4Address *a, + void + nm_platform_ip6_address_hash_update(const NMPlatformIP6Address *obj, NMHashState *h) + { +- nm_hash_update_vals( +- h, +- obj->ifindex, +- obj->addr_source, +- obj->timestamp, +- obj->lifetime, +- obj->preferred, +- obj->n_ifa_flags, +- obj->plen, +- obj->address, +- obj->peer_address, +- NM_HASH_COMBINE_BOOLS(guint8, obj->a_assume_config_once, obj->a_force_commit)); ++ nm_hash_update_vals(h, ++ obj->ifindex, ++ obj->addr_source, ++ obj->timestamp, ++ obj->lifetime, ++ obj->preferred, ++ obj->n_ifa_flags, ++ obj->plen, ++ obj->address, ++ obj->peer_address, ++ NM_HASH_COMBINE_BOOLS(guint8, obj->a_force_commit)); + } + + int +@@ -8037,7 +8026,6 @@ nm_platform_ip6_address_cmp(const NMPlatformIP6Address *a, + NM_CMP_FIELD(a, b, preferred); + NM_CMP_FIELD(a, b, n_ifa_flags); + NM_CMP_FIELD(a, b, addr_source); +- NM_CMP_FIELD_UNSAFE(a, b, a_assume_config_once); + NM_CMP_FIELD_UNSAFE(a, b, a_force_commit); + } + return 0; +@@ -8142,7 +8130,7 @@ nm_platform_ip4_route_hash_update(const NMPlatformIP4Route *obj, + obj->initrwnd, + obj->mtu, + obj->r_rtm_flags, +- NM_HASH_COMBINE_BOOLS(guint16, ++ NM_HASH_COMBINE_BOOLS(guint8, + obj->metric_any, + obj->table_any, + obj->lock_window, +@@ -8150,7 +8138,6 @@ nm_platform_ip4_route_hash_update(const NMPlatformIP4Route *obj, + obj->lock_initcwnd, + obj->lock_initrwnd, + obj->lock_mtu, +- obj->r_assume_config_once, + obj->r_force_commit)); + break; + } +@@ -8241,10 +8228,8 @@ nm_platform_ip4_route_cmp(const NMPlatformIP4Route *a, + NM_CMP_FIELD(a, b, initcwnd); + NM_CMP_FIELD(a, b, initrwnd); + NM_CMP_FIELD(a, b, mtu); +- if (cmp_type == NM_PLATFORM_IP_ROUTE_CMP_TYPE_FULL) { +- NM_CMP_FIELD_UNSAFE(a, b, r_assume_config_once); ++ if (cmp_type == NM_PLATFORM_IP_ROUTE_CMP_TYPE_FULL) + NM_CMP_FIELD_UNSAFE(a, b, r_force_commit); +- } + break; + } + return 0; +@@ -8337,7 +8322,6 @@ nm_platform_ip6_route_hash_update(const NMPlatformIP6Route *obj, + obj->lock_initcwnd, + obj->lock_initrwnd, + obj->lock_mtu, +- obj->r_assume_config_once, + obj->r_force_commit), + obj->window, + obj->cwnd, +@@ -8421,10 +8405,8 @@ nm_platform_ip6_route_cmp(const NMPlatformIP6Route *a, + NM_CMP_DIRECT(_route_pref_normalize(a->rt_pref), _route_pref_normalize(b->rt_pref)); + else + NM_CMP_FIELD(a, b, rt_pref); +- if (cmp_type == NM_PLATFORM_IP_ROUTE_CMP_TYPE_FULL) { +- NM_CMP_FIELD_UNSAFE(a, b, r_assume_config_once); ++ if (cmp_type == NM_PLATFORM_IP_ROUTE_CMP_TYPE_FULL) + NM_CMP_FIELD_UNSAFE(a, b, r_force_commit); +- } + break; + } + return 0; +diff --git a/src/libnm-platform/nm-platform.h b/src/libnm-platform/nm-platform.h +index 203f8c7bbd..dcbafdc6e8 100644 +--- a/src/libnm-platform/nm-platform.h ++++ b/src/libnm-platform/nm-platform.h +@@ -331,8 +331,6 @@ typedef enum { + /* Meta flags not honored by NMPlatform (netlink code). Instead, they can be + * used by the upper layers which use NMPlatformIPRoute to track addresses that + * should be configured. */ \ +- /* Whether the address is should be configured once during assume. */ \ +- bool a_assume_config_once : 1; \ + bool a_force_commit : 1; \ + \ + guint8 plen; \ +@@ -476,12 +474,9 @@ typedef union { + * This field overrides "table_coerced" field. If "table_any" is true, then + * the "table_coerced" field is ignored (unlike for the metric). */ \ + bool table_any : 1; \ +- \ + /* Meta flags not honored by NMPlatform (netlink code). Instead, they can be + * used by the upper layers which use NMPlatformIPRoute to track routes that + * should be configured. */ \ +- /* Whether the route is should be configured once during assume. */ \ +- bool r_assume_config_once : 1; \ + /* Whether the route should be committed even if it was removed externally. */ \ + bool r_force_commit : 1; \ + \ +diff --git a/src/libnm-platform/nmp-object.h b/src/libnm-platform/nmp-object.h +index 784dcc2dec..36d7fefb72 100644 +--- a/src/libnm-platform/nmp-object.h ++++ b/src/libnm-platform/nmp-object.h +@@ -1097,21 +1097,6 @@ nm_platform_lookup_object_by_addr_family(NMPlatform *platform, + + /*****************************************************************************/ + +-static inline gboolean +-nmp_object_get_assume_config_once(const NMPObject *obj) +-{ +- switch (NMP_OBJECT_GET_TYPE(obj)) { +- case NMP_OBJECT_TYPE_IP4_ADDRESS: +- case NMP_OBJECT_TYPE_IP6_ADDRESS: +- return NMP_OBJECT_CAST_IP_ADDRESS(obj)->a_assume_config_once; +- case NMP_OBJECT_TYPE_IP4_ROUTE: +- case NMP_OBJECT_TYPE_IP6_ROUTE: +- return NMP_OBJECT_CAST_IP_ROUTE(obj)->r_assume_config_once; +- default: +- return nm_assert_unreachable_val(FALSE); +- } +-} +- + static inline gboolean + nmp_object_get_force_commit(const NMPObject *obj) + { +-- +2.35.1 + diff --git a/SOURCES/1006-dhcp-routes-src-rh2092807.patch b/SOURCES/1006-dhcp-routes-src-rh2092807.patch new file mode 100644 index 0000000..c5fee4c --- /dev/null +++ b/SOURCES/1006-dhcp-routes-src-rh2092807.patch @@ -0,0 +1,309 @@ +From 3547c4d09a1d10b150a61bcbdc2418d750f7f616 Mon Sep 17 00:00:00 2001 +From: Thomas Haller +Date: Fri, 1 Apr 2022 08:39:56 +0200 +Subject: [PATCH 1/2] dhcp: set "src" for DHCPv4 routes + +Let's set the "src" (RTA_PREFSRC) of DHCP routes. +This helps with source address selection. + +This can matter if the interface also has static addresses +configured. + +Systemd-networkd also does this ([1], [2]). + +[1] https://github.com/systemd/systemd/commit/ac2dce5f36bb8b1a877ff765e6a4dfde6bfb2d49 +[2] https://github.com/systemd/systemd/blob/5b89bff55f45235f72d30d90fd489fe2247ad00d/src/network/networkd-dhcp4.c#L395 + +Related: https://bugzilla.redhat.com/show_bug.cgi?id=1995372 + +https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/1173 +(cherry picked from commit 2dc7a3d9f9135959adf415405bdcb05a7387c1d4) +(cherry picked from commit 10b9e07bfc3ae044b35a7dc6559aa6a4583bd7e8) +(cherry picked from commit f2942d11a75e5fb0bda35f8b659d0643f1f418b2) +--- + src/core/dhcp/nm-dhcp-nettools.c | 16 ++++++++++++---- + src/core/dhcp/nm-dhcp-systemd.c | 4 +++- + src/core/dhcp/nm-dhcp-utils.c | 26 +++++++++++++++----------- + 3 files changed, 30 insertions(+), 16 deletions(-) + +diff --git a/src/core/dhcp/nm-dhcp-nettools.c b/src/core/dhcp/nm-dhcp-nettools.c +index d7fbe3561599..769b0325f23d 100644 +--- a/src/core/dhcp/nm-dhcp-nettools.c ++++ b/src/core/dhcp/nm-dhcp-nettools.c +@@ -154,6 +154,7 @@ static gboolean + lease_parse_address(NDhcp4ClientLease *lease, + NML3ConfigData *l3cd, + GHashTable *options, ++ in_addr_t *out_address, + GError **error) + { + struct in_addr a_address; +@@ -268,6 +269,8 @@ lease_parse_address(NDhcp4ClientLease *lease, + .preferred = a_lifetime, + })); + ++ NM_SET_OUT(out_address, a_address.s_addr); ++ + return TRUE; + } + +@@ -326,6 +329,7 @@ lease_parse_address_list(NDhcp4ClientLease *lease, + static void + lease_parse_routes(NDhcp4ClientLease *lease, + NML3ConfigData *l3cd, ++ in_addr_t lease_address, + GHashTable *options, + NMStrBuf *sbuf) + { +@@ -373,10 +377,11 @@ lease_parse_routes(NDhcp4ClientLease *lease, + + nm_l3_config_data_add_route_4(l3cd, + &((const NMPlatformIP4Route){ ++ .rt_source = NM_IP_CONFIG_SOURCE_DHCP, + .network = dest, + .plen = plen, + .gateway = gateway, +- .rt_source = NM_IP_CONFIG_SOURCE_DHCP, ++ .pref_src = lease_address, + .table_any = TRUE, + .table_coerced = 0, + .metric_any = TRUE, +@@ -416,10 +421,11 @@ lease_parse_routes(NDhcp4ClientLease *lease, + + nm_l3_config_data_add_route_4(l3cd, + &((const NMPlatformIP4Route){ ++ .rt_source = NM_IP_CONFIG_SOURCE_DHCP, + .network = dest, + .plen = plen, + .gateway = gateway, +- .rt_source = NM_IP_CONFIG_SOURCE_DHCP, ++ .pref_src = lease_address, + .table_any = TRUE, + .table_coerced = 0, + .metric_any = TRUE, +@@ -464,6 +470,7 @@ lease_parse_routes(NDhcp4ClientLease *lease, + &((const NMPlatformIP4Route){ + .rt_source = NM_IP_CONFIG_SOURCE_DHCP, + .gateway = gateway, ++ .pref_src = lease_address, + .table_any = TRUE, + .table_coerced = 0, + .metric_any = TRUE, +@@ -547,6 +554,7 @@ lease_to_ip4_config(NMDedupMultiIndex *multi_idx, + const char *v_str; + guint16 v_u16; + in_addr_t v_inaddr; ++ in_addr_t lease_address; + struct in_addr v_inaddr_s; + int r; + +@@ -556,7 +564,7 @@ lease_to_ip4_config(NMDedupMultiIndex *multi_idx, + + options = nm_dhcp_option_create_options_dict(); + +- if (!lease_parse_address(lease, l3cd, options, error)) ++ if (!lease_parse_address(lease, l3cd, options, &lease_address, error)) + return NULL; + + r = n_dhcp4_client_lease_get_server_identifier(lease, &v_inaddr_s); +@@ -575,7 +583,7 @@ lease_to_ip4_config(NMDedupMultiIndex *multi_idx, + v_inaddr); + } + +- lease_parse_routes(lease, l3cd, options, &sbuf); ++ lease_parse_routes(lease, l3cd, lease_address, options, &sbuf); + + lease_parse_address_list(lease, l3cd, NM_DHCP_OPTION_DHCP4_DOMAIN_NAME_SERVER, options, &sbuf); + +diff --git a/src/core/dhcp/nm-dhcp-systemd.c b/src/core/dhcp/nm-dhcp-systemd.c +index 0884def35dc6..d17646154f67 100644 +--- a/src/core/dhcp/nm-dhcp-systemd.c ++++ b/src/core/dhcp/nm-dhcp-systemd.c +@@ -309,10 +309,11 @@ lease_to_ip4_config(NMDedupMultiIndex *multi_idx, + + nm_l3_config_data_add_route_4(l3cd, + &((const NMPlatformIP4Route){ ++ .rt_source = NM_IP_CONFIG_SOURCE_DHCP, + .network = network_net, + .plen = r_plen, + .gateway = r_gateway.s_addr, +- .rt_source = NM_IP_CONFIG_SOURCE_DHCP, ++ .pref_src = a_address.s_addr, + .metric_any = TRUE, + .metric = m, + .table_any = TRUE, +@@ -366,6 +367,7 @@ lease_to_ip4_config(NMDedupMultiIndex *multi_idx, + &((const NMPlatformIP4Route){ + .rt_source = NM_IP_CONFIG_SOURCE_DHCP, + .gateway = a_router[i].s_addr, ++ .pref_src = a_address.s_addr, + .table_any = TRUE, + .table_coerced = 0, + .metric_any = TRUE, +diff --git a/src/core/dhcp/nm-dhcp-utils.c b/src/core/dhcp/nm-dhcp-utils.c +index 4a138086b957..c71796f8bd9d 100644 +--- a/src/core/dhcp/nm-dhcp-utils.c ++++ b/src/core/dhcp/nm-dhcp-utils.c +@@ -28,7 +28,8 @@ static gboolean + ip4_process_dhcpcd_rfc3442_routes(const char *iface, + const char *str, + NML3ConfigData *l3cd, +- guint32 *gwaddr) ++ in_addr_t address, ++ guint32 *out_gwaddr) + { + gs_free const char **routes = NULL; + const char **r; +@@ -79,7 +80,7 @@ ip4_process_dhcpcd_rfc3442_routes(const char *iface, + have_routes = TRUE; + if (rt_cidr == 0 && rt_addr == 0) { + /* FIXME: how to handle multiple routers? */ +- *gwaddr = rt_route; ++ *out_gwaddr = rt_route; + } else { + _LOG2I(LOGD_DHCP4, + iface, +@@ -91,13 +92,13 @@ ip4_process_dhcpcd_rfc3442_routes(const char *iface, + nm_l3_config_data_add_route_4( + l3cd, + &((const NMPlatformIP4Route){ ++ .rt_source = NM_IP_CONFIG_SOURCE_DHCP, + .network = nm_utils_ip4_address_clear_host_address(rt_addr, rt_cidr), + .plen = rt_cidr, + .gateway = rt_route, +- .rt_source = NM_IP_CONFIG_SOURCE_DHCP, ++ .pref_src = address, + .metric_any = TRUE, + .table_any = TRUE, +- + })); + } + } +@@ -158,7 +159,8 @@ static gboolean + ip4_process_dhclient_rfc3442_routes(const char *iface, + const char *str, + NML3ConfigData *l3cd, +- guint32 *gwaddr) ++ in_addr_t address, ++ guint32 *out_gwaddr) + { + gs_free const char **octets = NULL; + const char *const *o; +@@ -182,13 +184,14 @@ ip4_process_dhclient_rfc3442_routes(const char *iface, + have_routes = TRUE; + if (!route.plen) { + /* gateway passed as classless static route */ +- *gwaddr = route.gateway; ++ *out_gwaddr = route.gateway; + } else { + char b1[INET_ADDRSTRLEN]; + char b2[INET_ADDRSTRLEN]; + + /* normal route */ + route.rt_source = NM_IP_CONFIG_SOURCE_DHCP; ++ route.pref_src = address; + route.table_any = TRUE; + route.table_coerced = 0; + route.metric_any = TRUE; +@@ -212,14 +215,15 @@ static gboolean + ip4_process_classless_routes(const char *iface, + GHashTable *options, + NML3ConfigData *l3cd, +- guint32 *gwaddr) ++ in_addr_t address, ++ guint32 *out_gwaddr) + { + const char *str, *p; + + g_return_val_if_fail(options != NULL, FALSE); + g_return_val_if_fail(l3cd != NULL, FALSE); + +- *gwaddr = 0; ++ *out_gwaddr = 0; + + /* dhcpd/dhclient in Fedora has support for rfc3442 implemented using a + * slightly different format: +@@ -266,10 +270,10 @@ ip4_process_classless_routes(const char *iface, + + if (strchr(str, '/')) { + /* dhcpcd format */ +- return ip4_process_dhcpcd_rfc3442_routes(iface, str, l3cd, gwaddr); ++ return ip4_process_dhcpcd_rfc3442_routes(iface, str, l3cd, address, out_gwaddr); + } + +- return ip4_process_dhclient_rfc3442_routes(iface, str, l3cd, gwaddr); ++ return ip4_process_dhclient_rfc3442_routes(iface, str, l3cd, address, out_gwaddr); + } + + static void +@@ -422,7 +426,7 @@ nm_dhcp_utils_ip4_config_from_options(NMDedupMultiIndex *multi_idx, + /* Routes: if the server returns classless static routes, we MUST ignore + * the 'static_routes' option. + */ +- if (!ip4_process_classless_routes(iface, options, l3cd, &gateway)) ++ if (!ip4_process_classless_routes(iface, options, l3cd, address.address, &gateway)) + process_classful_routes(iface, options, l3cd); + + if (gateway) { +-- +2.36.1 + + +From ebfc7c2c58e6125346baf9b530e71b2571dc0c10 Mon Sep 17 00:00:00 2001 +From: Thomas Haller +Date: Wed, 13 Apr 2022 10:43:13 +0200 +Subject: [PATCH 2/2] dhcp/dhclient: fix setting "src" attribute for certain + routes + +Fixes: 2dc7a3d9f913 ('dhcp: set "src" for DHCPv4 routes') +(cherry picked from commit 197e73ac7c53556b32ff048c9720907be3217487) +(cherry picked from commit 0c6d242dc0b67b6269657acf33bf9d1f0830f0b4) +(cherry picked from commit b0a7dda2eae1493a3a285ed1d08178409266ba07) +--- + src/core/dhcp/nm-dhcp-utils.c | 9 +++++++-- + 1 file changed, 7 insertions(+), 2 deletions(-) + +diff --git a/src/core/dhcp/nm-dhcp-utils.c b/src/core/dhcp/nm-dhcp-utils.c +index c71796f8bd9d..35a2c6543759 100644 +--- a/src/core/dhcp/nm-dhcp-utils.c ++++ b/src/core/dhcp/nm-dhcp-utils.c +@@ -277,7 +277,10 @@ ip4_process_classless_routes(const char *iface, + } + + static void +-process_classful_routes(const char *iface, GHashTable *options, NML3ConfigData *l3cd) ++process_classful_routes(const char *iface, ++ GHashTable *options, ++ NML3ConfigData *l3cd, ++ in_addr_t address) + { + gs_free const char **searches = NULL; + const char **s; +@@ -325,6 +328,7 @@ process_classful_routes(const char *iface, GHashTable *options, NML3ConfigData * + route.plen = 32; + } + route.gateway = rt_route; ++ route.pref_src = address; + route.rt_source = NM_IP_CONFIG_SOURCE_DHCP; + route.table_any = TRUE; + route.table_coerced = 0; +@@ -427,7 +431,7 @@ nm_dhcp_utils_ip4_config_from_options(NMDedupMultiIndex *multi_idx, + * the 'static_routes' option. + */ + if (!ip4_process_classless_routes(iface, options, l3cd, address.address, &gateway)) +- process_classful_routes(iface, options, l3cd); ++ process_classful_routes(iface, options, l3cd, address.address); + + if (gateway) { + _LOG2I(LOGD_DHCP4, iface, " gateway %s", _nm_utils_inet4_ntop(gateway, sbuf)); +@@ -457,6 +461,7 @@ nm_dhcp_utils_ip4_config_from_options(NMDedupMultiIndex *multi_idx, + const NMPlatformIP4Route r = { + .rt_source = NM_IP_CONFIG_SOURCE_DHCP, + .gateway = gateway, ++ .pref_src = address.address, + .table_any = TRUE, + .table_coerced = 0, + .metric_any = TRUE, +-- +2.36.1 + diff --git a/SOURCES/1007-platform-workaround-for-preserving-ipv6-address-rhbz2090280.patch b/SOURCES/1007-platform-workaround-for-preserving-ipv6-address-rhbz2090280.patch new file mode 100644 index 0000000..4ab3065 --- /dev/null +++ b/SOURCES/1007-platform-workaround-for-preserving-ipv6-address-rhbz2090280.patch @@ -0,0 +1,77 @@ +From 4c556203d93fdd143630431dded4e0e6ea24824e Mon Sep 17 00:00:00 2001 +From: Thomas Haller +Date: Thu, 9 Jun 2022 10:00:47 +0200 +Subject: [PATCH 1/1] platform: workaround for preserving IPv6 address order + +https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/issues/ ## 1021 +--- + src/libnm-platform/nm-platform.c | 15 +++++++++++++++ + 1 file changed, 15 insertions(+) + +diff --git a/src/libnm-platform/nm-platform.c b/src/libnm-platform/nm-platform.c +index f264ed7a45b2..120e50b3c772 100644 +--- a/src/libnm-platform/nm-platform.c ++++ b/src/libnm-platform/nm-platform.c +@@ -3961,45 +3961,60 @@ nm_platform_ip_address_sync(NMPlatform *self, + 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. */ ++ if (IS_IPv4) { + plat_addresses = nm_platform_lookup_clone( + self, + nmp_lookup_init_object(&lookup, NMP_OBJECT_TYPE_IP_ADDRESS(IS_IPv4), ifindex), + NULL, + NULL); ++ } else { ++ /* HACK: early 1.36 versions had a bug of not actually reordering the IPv6 addresses. ++ * This was fixed by commit cd4601802de5 ('platform: fix address order in ++ * nm_platform_ip_address_sync()'). ++ * ++ * However, also in 1.36, the actually implemented order of IPv6 addresses is not ++ * the one we want ([1]). So disable the fix again, to not reorder IPv6 addresses. ++ * ++ * The effect is, that DHCPv6 addresses end up being preferred over SLAAC, because ++ * they get added later during activation. Of course, if any address gets added ++ * even later (like a new router appearing), then the order will be wrong again. ++ * ++ * [1] https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/issues/1021 */ ++ } + + 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; + + /* 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 = plat_addresses->pdata[i]; + const NMPObject *known_obj; + const NMPlatformIP4Address *plat_address; + const GPtrArray *addr_list; +-- +2.36.1 diff --git a/SPECS/NetworkManager.spec b/SPECS/NetworkManager.spec index 641ee43..b27ec22 100644 --- a/SPECS/NetworkManager.spec +++ b/SPECS/NetworkManager.spec @@ -7,7 +7,7 @@ %global epoch_version 1 %global rpm_version 1.36.0 %global real_version 1.36.0 -%global release_version 4 +%global release_version 7 %global snapshot %{nil} %global git_sha %{nil} @@ -198,6 +198,9 @@ Patch1001: 1001-wwan-dns-fix-rh2059138.patch Patch1002: 1002-checkpoint-preserve-external-bridge-ports-rh2035519.patch Patch1003: 1003-fix-ovsdb-removal-ports-rhbz1935026.patch Patch1004: 1004-n-dhcp4-discard-NAKs-from-other-servers-rhbz2059673.patch +Patch1005: 1005-fix-dhcp-loses-lease-when-restarting-rhbz2090280.patch +Patch1006: 1006-dhcp-routes-src-rh2092807.patch +Patch1007: 1007-platform-workaround-for-preserving-ipv6-address-rhbz2090280.patch # The pregenerated docs contain default values and paths that depend # on the configure options when creating the source tarball. @@ -1199,6 +1202,18 @@ fi %changelog +* Thu Jun 09 2022 Fernando Fernandez Mancera - 1:1.36.0-7 +- platform: workaround for preserving IPv6 address order (rh #2090280) + +* Tue Jun 7 2022 Thomas Haller - 1:1.36.0-6 +- core: set "src" attribute for routes from DHCPv4 (rh #2092807) + +* Wed May 25 2022 Fernando Fernandez Mancera - 1:1.36.0-5 +- Fix DHCP loses lease when restarting (rh #2090280) + +* Mon Mar 21 2022 Fernando Fernandez Mancera - 1:1.36.0-4 +- n-dhcp4: discard NAKs from different servers in SELECTING (rh #2059673) + * Fri Mar 11 2022 Thomas Haller - 1:1.36.0-3 - core: preserve external bridge ports during checkpoint rollback (rh #2035519) - ovs-port: fix removal of ovsdb entry if the interface goes away (rh #1935026)