From 64523313068d1d7995e95aae19ddf333120c5e09 Mon Sep 17 00:00:00 2001 From: eabdullin Date: Thu, 13 Jun 2024 07:39:51 +0000 Subject: [PATCH] import UBI NetworkManager-1.46.0-8.el9_4 --- ...-lldp-for-ovs-bridge-ports-rhel31766.patch | 40 ++ ...atform-avoid-routes-resync-rhel36162.patch | 210 ++++++++ ...t-preserve-in-memory-state-rhel32493.patch | 487 ++++++++++++++++++ SPECS/NetworkManager.spec | 17 +- 4 files changed, 753 insertions(+), 1 deletion(-) create mode 100644 SOURCES/1006-fix-lldp-for-ovs-bridge-ports-rhel31766.patch create mode 100644 SOURCES/1007-platform-avoid-routes-resync-rhel36162.patch create mode 100644 SOURCES/1008-checkpoint-preserve-in-memory-state-rhel32493.patch diff --git a/SOURCES/1006-fix-lldp-for-ovs-bridge-ports-rhel31766.patch b/SOURCES/1006-fix-lldp-for-ovs-bridge-ports-rhel31766.patch new file mode 100644 index 0000000..914288c --- /dev/null +++ b/SOURCES/1006-fix-lldp-for-ovs-bridge-ports-rhel31766.patch @@ -0,0 +1,40 @@ +From 6394c2b262d86824a41ca82ad76288c06bfd1989 Mon Sep 17 00:00:00 2001 +From: Fernando Fernandez Mancera +Date: Tue, 26 Mar 2024 12:53:27 +0100 +Subject: [PATCH] libnm-lldp: use ETH_P_ALL instead of NM_ETHERTYPE_LLDP for + the socket + +When creating the socket for listening to LLDP frames we are setting +NM_ETHERTYPE_LLDP (0x88cc) as protocol. In most of the cases, that is +correct but when the interface is attached as a port to a OVS bridge, +kernel is not matching the protocol correctly. The reason might be that +some metadata is added to the packet, but we are not completely sure +about it. + +Instead, we should use ETH_P_ALL to match all the protocols. Later, we +have a eBPF filter to drop the packet by multicast MAC address or +protocol. This is how lldpd is doing it for example. + +https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/1903 +(cherry picked from commit 9ac1d6e22bfac7f576dec034a26ac7c9012e5b80) +(cherry picked from commit 2fac176986f3afaa84242e069613cc543bfcc58c) +--- + src/libnm-lldp/nm-lldp-network.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/libnm-lldp/nm-lldp-network.c b/src/libnm-lldp/nm-lldp-network.c +index 811c3a7291..28cc745249 100644 +--- a/src/libnm-lldp/nm-lldp-network.c ++++ b/src/libnm-lldp/nm-lldp-network.c +@@ -46,7 +46,7 @@ nm_lldp_network_bind_raw_socket(int ifindex) + + assert(ifindex > 0); + +- fd = socket(AF_PACKET, SOCK_RAW | SOCK_CLOEXEC | SOCK_NONBLOCK, htobe16(NM_ETHERTYPE_LLDP)); ++ fd = socket(AF_PACKET, SOCK_RAW | SOCK_CLOEXEC | SOCK_NONBLOCK, htobe16(ETH_P_ALL)); + if (fd < 0) + return -errno; + +-- +2.44.0 + diff --git a/SOURCES/1007-platform-avoid-routes-resync-rhel36162.patch b/SOURCES/1007-platform-avoid-routes-resync-rhel36162.patch new file mode 100644 index 0000000..91ebcc4 --- /dev/null +++ b/SOURCES/1007-platform-avoid-routes-resync-rhel36162.patch @@ -0,0 +1,210 @@ +From ed5cbbc5847527ed0cfc33f521f7c724975c846b Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?=C3=8D=C3=B1igo=20Huguet?= +Date: Tue, 30 Apr 2024 12:45:04 +0200 +Subject: [PATCH] platform: avoid routes resync for routes that we don't track + +When we recibe a Netlink message with a "route change" event, normally +we just ignore it if it's a route that we don't track (i.e. because of +the route protocol). + +However, it's not that easy if it has the NLM_F_REPLACE flag because +that means that it might be replacing another route. If the kernel has +similar routes which are candidates for the replacement, it's hard for +NM to guess which one of those is being replaced (as the kernel doesn't +have a "route ID" or similar field to indicate it). Moreover, the kernel +might choose to replace a route that we don't have on cache, so we know +nothing about it. + +It is important to note that we cannot just discard Netlink messages of +routes that we don't track if they has the NLM_F_REPLACE. For example, +if we are tracking a route with proto=static, we might receive a replace +message, changing that route to proto=other_proto_that_we_dont_track. We +need to process that message and remove the route from our cache. + +As NM doesn't know what route is being replaced, trying to guess will +lead to errors that will leave the cache in an inconsistent state. +Because of that, it just do a cache resync for the routes. + +For IPv4 there was an optimization to this: if we don't have in the +cache any route candidate for the replacement there are only 2 possible +options: either add the new route to the cache or discard it if we are +not interested on it. We don't need a resync for that. + +This commit is extending that optimization to IPv6 routes. There is no +reason why it shouldn't work in the same way than with IPv4. This +optimization will only work well as long as we find potential candidate +routes in the same way than the kernel (comparing the same fields). NM +calls to this "comparing by WEAK_ID". But this can also happen with IPv4 +routes. + +It is worth it to enable this optimization because there are routing +daemons using custom routing protocols that makes tens or hundreds of +updates per second. If they use NLM_F_REPLACE, this caused NM to do a +resync hundreds of times per second leading to a 100% CPU usage: +https://issues.redhat.com/browse/RHEL-26195 + +An additional but smaller optimization is done in this commit: if we +receive a route message for routes that we don't track AND doesn't have +the NLM_F_REPLACE flag, we can ignore the entire message, thus avoiding +the memory allocation of the nmp_object. That nmp_object was going to be +ignored later, anyway, so better to avoid these allocations that, with +the routing daemon of the above's example, can happen hundreds of times +per second. + +With this changes, the CPU usage doing `ip route replace` 300 times/s +drops from 100% to 1%. Doing `ip route replace` as fast as possible, +without any rate limitting, still keeps NM with a 3% CPU usage in the +system that I have used to test. + +(cherry picked from commit 4d426f581de402e0aebd2ab273ff6649a0a6fee6) +(cherry picked from commit 15ffa8ec6ff7bf43ed1eb123c0d419d6fab8b268) +--- + src/libnm-platform/nm-linux-platform.c | 69 ++++++++++++++++---------- + src/libnm-platform/nmp-object.c | 22 +++++--- + 2 files changed, 57 insertions(+), 34 deletions(-) + +diff --git a/src/libnm-platform/nm-linux-platform.c b/src/libnm-platform/nm-linux-platform.c +index 9ecac2d9b3..5b595a9b71 100644 +--- a/src/libnm-platform/nm-linux-platform.c ++++ b/src/libnm-platform/nm-linux-platform.c +@@ -3903,6 +3903,34 @@ _new_from_nl_addr(const struct nlmsghdr *nlh, gboolean id_only) + return g_steal_pointer(&obj); + } + ++static gboolean ++ip_route_is_tracked(guint8 proto, guint8 type) ++{ ++ if (proto > RTPROT_STATIC && !NM_IN_SET(proto, RTPROT_DHCP, RTPROT_RA)) { ++ /* We ignore certain rtm_protocol, because NetworkManager would only ever ++ * configure certain protocols. Other routes are not configured by NetworkManager ++ * and we don't track them in the platform cache. ++ * ++ * This is to help with the performance overhead of a huge number of ++ * routes, for example with the bird BGP software, that adds routes ++ * with RTPROT_BIRD protocol. */ ++ return FALSE; ++ } ++ ++ if (!NM_IN_SET(type, ++ RTN_UNICAST, ++ RTN_LOCAL, ++ RTN_BLACKHOLE, ++ RTN_UNREACHABLE, ++ RTN_PROHIBIT, ++ RTN_THROW)) { ++ /* Certain route types are ignored and not placed into the cache. */ ++ return FALSE; ++ } ++ ++ return TRUE; ++} ++ + /* Copied and heavily modified from libnl3's rtnl_route_parse() and parse_multipath(). */ + static NMPObject * + _new_from_nl_route(const struct nlmsghdr *nlh, gboolean id_only, ParseNlmsgIter *parse_nlmsg_iter) +@@ -3963,6 +3991,16 @@ _new_from_nl_route(const struct nlmsghdr *nlh, gboolean id_only, ParseNlmsgIter + * only handle ~supported~ routes. + *****************************************************************/ + ++ /* If it's a route that we don't need to track, abort here to avoid unnecessary ++ * memory allocations to create the nmp_object. However, if the message has the ++ * NLM_F_REPLACE flag, it might be replacing a route that we were tracking so we ++ * have to stop tracking it. That means that we have to process all messages with ++ * NLM_F_REPLACE. See nmp_cache_update_netlink_route(). ++ */ ++ if (!ip_route_is_tracked(rtm->rtm_protocol, rtm->rtm_type) ++ && !(nlh->nlmsg_flags & NLM_F_REPLACE)) ++ return NULL; ++ + addr_family = rtm->rtm_family; + + if (addr_family == AF_INET) +@@ -5519,39 +5557,18 @@ ip_route_get_lock_flag(const NMPlatformIPRoute *route) + static gboolean + ip_route_is_alive(const NMPlatformIPRoute *route) + { +- guint8 prot; ++ guint8 proto, type; + + nm_assert(route); + nm_assert(route->rt_source >= NM_IP_CONFIG_SOURCE_RTPROT_UNSPEC + && route->rt_source <= _NM_IP_CONFIG_SOURCE_RTPROT_LAST); + +- prot = route->rt_source - 1; +- +- nm_assert(nmp_utils_ip_config_source_from_rtprot(prot) == route->rt_source); +- +- if (prot > RTPROT_STATIC && !NM_IN_SET(prot, RTPROT_DHCP, RTPROT_RA)) { +- /* We ignore certain rtm_protocol, because NetworkManager would only ever +- * configure certain protocols. Other routes are not configured by NetworkManager +- * and we don't track them in the platform cache. +- * +- * This is to help with the performance overhead of a huge number of +- * routes, for example with the bird BGP software, that adds routes +- * with RTPROT_BIRD protocol. */ +- return FALSE; +- } ++ proto = route->rt_source - 1; ++ type = nm_platform_route_type_uncoerce(route->type_coerced); + +- if (!NM_IN_SET(nm_platform_route_type_uncoerce(route->type_coerced), +- RTN_UNICAST, +- RTN_LOCAL, +- RTN_BLACKHOLE, +- RTN_UNREACHABLE, +- RTN_PROHIBIT, +- RTN_THROW)) { +- /* Certain route types are ignored and not placed into the cache. */ +- return FALSE; +- } ++ nm_assert(nmp_utils_ip_config_source_from_rtprot(proto) == route->rt_source); + +- return TRUE; ++ return ip_route_is_tracked(proto, type); + } + + /* Copied and modified from libnl3's build_route_msg() and rtnl_route_build_msg(). */ +diff --git a/src/libnm-platform/nmp-object.c b/src/libnm-platform/nmp-object.c +index 4090da71a3..cb4e9764d1 100644 +--- a/src/libnm-platform/nmp-object.c ++++ b/src/libnm-platform/nmp-object.c +@@ -2988,6 +2988,13 @@ nmp_cache_update_netlink_route(NMPCache *cache, + * Since we don't cache all routes (see "route_is_alive"), we cannot know + * with certainty which route was replaced. + * ++ * For example, the kernel might have 3 similar routes (same WEAK_ID), one ++ * of which is not tracked by us so we don't have it into the cache. If we ++ * receive a route replace message, we don't know to what of the 3 routes ++ * it affects (one of the 3 we don't even know that exists). Moreover, if ++ * we only have one route on cache, we don't know if the replace is for a ++ * different one that we don't track. ++ * + * Even if we would cache *all* routes (which we cannot, if kernel adds new + * routing features that modify the known nmp_object_id_equal()), it would + * be hard to find the right route that was replaced. Well, probably we +@@ -3002,15 +3009,14 @@ nmp_cache_update_netlink_route(NMPCache *cache, + * [2] https://bugzilla.redhat.com/show_bug.cgi?id=1337860 + * + * We need to resync. ++ * ++ * However, a resync is expensive. Think of a routing daemon that updates ++ * hundreds of routes per second, the performance penalty is huge. We can ++ * optimize it: if we don't have any matching route on cache (by WEAK_ID), ++ * we don't have anything to replace and we don't need a full resync, but ++ * only to add or discard the new route as usual. + */ +- if (NMP_OBJECT_GET_TYPE(obj_hand_over) == NMP_OBJECT_TYPE_IP4_ROUTE +- && !nmp_cache_lookup_all(cache, NMP_CACHE_ID_TYPE_ROUTES_BY_WEAK_ID, obj_hand_over)) { +- /* For IPv4, we can do a small optimization. We skip the resync, if we have +- * no conflicting routes (by weak-id). +- * +- * This optimization does not work for IPv6 (maybe should be fixed). +- */ +- } else { ++ if (nmp_cache_lookup_all(cache, NMP_CACHE_ID_TYPE_ROUTES_BY_WEAK_ID, obj_hand_over)) { + entry_replace = NULL; + resync_required = TRUE; + goto out; +-- +2.44.0 + diff --git a/SOURCES/1008-checkpoint-preserve-in-memory-state-rhel32493.patch b/SOURCES/1008-checkpoint-preserve-in-memory-state-rhel32493.patch new file mode 100644 index 0000000..e61ee26 --- /dev/null +++ b/SOURCES/1008-checkpoint-preserve-in-memory-state-rhel32493.patch @@ -0,0 +1,487 @@ +From d6837f0bd30da069d327099cb555854630cd4584 Mon Sep 17 00:00:00 2001 +From: Beniamino Galvani +Date: Thu, 2 May 2024 16:40:26 +0200 +Subject: [PATCH 1/2] settings: add + nm_settings_connection_persist_mode_to_string() + +(cherry picked from commit a48b7fe7b9d8adf4902c7b3cfcc4d89bc46cbbef) +(cherry picked from commit e5837aa1d3960b743adcff0a5041445ccd65fb93) +--- + src/core/settings/nm-settings-connection.c | 23 ++++++++++++++++++++++ + src/core/settings/nm-settings-connection.h | 4 ++++ + 2 files changed, 27 insertions(+) + +diff --git a/src/core/settings/nm-settings-connection.c b/src/core/settings/nm-settings-connection.c +index 176cc2c252..459c60ad1e 100644 +--- a/src/core/settings/nm-settings-connection.c ++++ b/src/core/settings/nm-settings-connection.c +@@ -226,6 +226,29 @@ static guint _get_seen_bssids(NMSettingsConnection *self, + + /*****************************************************************************/ + ++char * ++nm_settings_connection_persist_mode_to_string(NMSettingsConnectionPersistMode mode) ++{ ++ switch (mode) { ++ case NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY: ++ return "in-memory"; ++ case NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_DETACHED: ++ return "in-memory-detached"; ++ case NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_ONLY: ++ return "in-memory-only"; ++ case NM_SETTINGS_CONNECTION_PERSIST_MODE_KEEP: ++ return "keep"; ++ case NM_SETTINGS_CONNECTION_PERSIST_MODE_NO_PERSIST: ++ return "no-persist"; ++ case NM_SETTINGS_CONNECTION_PERSIST_MODE_TO_DISK: ++ return "to-disk"; ++ } ++ ++ return nm_assert_unreachable_val(NULL); ++} ++ ++/*****************************************************************************/ ++ + NMSettings * + nm_settings_connection_get_settings(NMSettingsConnection *self) + { +diff --git a/src/core/settings/nm-settings-connection.h b/src/core/settings/nm-settings-connection.h +index 835a978e40..d15a75b749 100644 +--- a/src/core/settings/nm-settings-connection.h ++++ b/src/core/settings/nm-settings-connection.h +@@ -379,4 +379,8 @@ void _nm_settings_connection_emit_signal_updated_internal( + + void _nm_settings_connection_cleanup_after_remove(NMSettingsConnection *self); + ++/*****************************************************************************/ ++ ++char *nm_settings_connection_persist_mode_to_string(NMSettingsConnectionPersistMode mode); ++ + #endif /* __NETWORKMANAGER_SETTINGS_CONNECTION_H__ */ +-- +2.41.0 + +From c6f9d0a6d5c864ba0141b6e985727cd69c5560fa Mon Sep 17 00:00:00 2001 +From: Beniamino Galvani +Date: Mon, 15 Apr 2024 10:51:24 +0200 +Subject: [PATCH 2/2] checkpoint: preserve in-memory state of connections + +If a connection is in-memory (i.e. has flag "unsaved"), after a +checkpoint and rollback it can be wrongly persisted to disk: + + - if the connection was modified and written to disk after the + rollback, during the rollback we update it again with persist mode + "keep", which keeps it on disk; + + - if the connection was deleted after the rollback, during the + rollback we add it again with persist mode "to-disk". + +Instead, remember whether the connection had the "unsaved" flag set +and try to restore the previous state. + +However, this is not straightforward as there are 4 different possible +states for the settings connection: persistent; in-memory only; +in-memory shadowing a persistent file; in-memory shadowing a detached +persistent file (i.e. the deletion of the connection doesn't delete +the persistent file). Handle all those cases. + +Fixes: 3e09aed2a09f ('checkpoint: add create, rollback and destroy D-Bus API') +(cherry picked from commit c979bfeb8b0d3bed19bac2ad01a6a6ed899f924e) +(cherry picked from commit ebf25794d9cd89190775ac401c36d63aa1c108f7) +--- + NEWS | 8 ++ + src/core/nm-checkpoint.c | 242 ++++++++++++++++++++++++++++++++------- + 2 files changed, 211 insertions(+), 39 deletions(-) + +diff --git a/NEWS b/NEWS +index 6ac3118db9..e33152c6f4 100644 +--- a/NEWS ++++ b/NEWS +@@ -1,3 +1,11 @@ ++=============================================== ++NetworkManager-1.46.2 ++Overview of changes since NetworkManager-1.46.0 ++=============================================== ++ ++* Properly restore in-memory connection profiles during the rollback ++ of a checkpoint. ++ + ============================================= + NetworkManager-1.46 + Overview of changes since NetworkManager-1.44 +diff --git a/src/core/nm-checkpoint.c b/src/core/nm-checkpoint.c +index cc5c189bf9..ffcf6e3aad 100644 +--- a/src/core/nm-checkpoint.c ++++ b/src/core/nm-checkpoint.c +@@ -10,6 +10,7 @@ + #include "nm-active-connection.h" + #include "nm-act-request.h" + #include "libnm-core-aux-intern/nm-auth-subject.h" ++#include "libnm-core-intern/nm-keyfile-internal.h" + #include "nm-core-utils.h" + #include "nm-dbus-interface.h" + #include "devices/nm-device.h" +@@ -17,6 +18,7 @@ + #include "nm-manager.h" + #include "settings/nm-settings.h" + #include "settings/nm-settings-connection.h" ++#include "settings/plugins/keyfile/nms-keyfile-storage.h" + #include "nm-simple-connection.h" + #include "nm-utils.h" + +@@ -29,11 +31,14 @@ typedef struct { + NMDevice *device; + NMConnection *applied_connection; + NMConnection *settings_connection; ++ NMConnection *settings_connection_shadowed; + guint64 ac_version_id; + NMDeviceState state; + bool is_software : 1; + bool realized : 1; + bool activation_lifetime_bound_to_profile_visibility : 1; ++ bool settings_connection_is_unsaved : 1; ++ bool settings_connection_is_shadowed_owned : 1; + NMUnmanFlagOp unmanaged_explicit; + NMActivationReason activation_reason; + gulong dev_exported_change_id; +@@ -150,37 +155,111 @@ nm_checkpoint_includes_devices_of(NMCheckpoint *self, NMCheckpoint *cp_for_devic + return NULL; + } + ++static NMConnection * ++parse_connection_from_shadowed_file(const char *path, GError **error) ++{ ++ nm_auto_unref_keyfile GKeyFile *keyfile = NULL; ++ gs_free char *base_dir = NULL; ++ char *sep; ++ ++ keyfile = g_key_file_new(); ++ if (!g_key_file_load_from_file(keyfile, path, G_KEY_FILE_NONE, error)) ++ return NULL; ++ ++ sep = strrchr(path, '/'); ++ base_dir = g_strndup(path, sep - path); ++ ++ return nm_keyfile_read(keyfile, base_dir, NM_KEYFILE_HANDLER_FLAGS_NONE, NULL, NULL, error); ++} ++ + static NMSettingsConnection * +-find_settings_connection(NMCheckpoint *self, +- DeviceCheckpoint *dev_checkpoint, +- gboolean *need_update, +- gboolean *need_activation) ++find_settings_connection(NMCheckpoint *self, ++ DeviceCheckpoint *dev_checkpoint, ++ gboolean *need_update, ++ gboolean *need_update_shadowed, ++ gboolean *need_activation, ++ NMSettingsConnectionPersistMode *persist_mode) + { + NMCheckpointPrivate *priv = NM_CHECKPOINT_GET_PRIVATE(self); + NMActiveConnection *active; + NMSettingsConnection *sett_conn; ++ const char *shadowed_file; ++ NMConnection *shadowed_connection = NULL; + const char *uuid, *ac_uuid; + const CList *tmp_clist; +- +- *need_activation = FALSE; +- *need_update = FALSE; ++ gboolean sett_conn_unsaved; ++ NMSettingsStorage *storage; ++ ++ *need_activation = FALSE; ++ *need_update = FALSE; ++ *need_update_shadowed = FALSE; ++ ++ /* With regard to storage, there are 4 different possible states for the settings ++ * connection: 1) persistent; 2) in-memory only; 3) in-memory shadowing a persistent ++ * file; 4) in-memory shadowing a detached persistent file (i.e. the deletion of ++ * the connection doesn't delete the persistent file). ++ */ ++ if (dev_checkpoint->settings_connection_is_unsaved) { ++ if (dev_checkpoint->settings_connection_shadowed) { ++ if (dev_checkpoint->settings_connection_is_shadowed_owned) ++ *persist_mode = NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY; ++ else ++ *persist_mode = NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_DETACHED; ++ } else ++ *persist_mode = NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_ONLY; ++ } else { ++ *persist_mode = NM_SETTINGS_CONNECTION_PERSIST_MODE_TO_DISK; ++ } + + uuid = nm_connection_get_uuid(dev_checkpoint->settings_connection); + sett_conn = nm_settings_get_connection_by_uuid(NM_SETTINGS_GET, uuid); + +- if (!sett_conn) +- return NULL; +- +- /* Now check if the connection changed, ... */ +- if (!nm_connection_compare(dev_checkpoint->settings_connection, +- nm_settings_connection_get_connection(sett_conn), +- NM_SETTING_COMPARE_FLAG_EXACT)) { ++ /* Check if the connection changed */ ++ if (sett_conn ++ && !nm_connection_compare(dev_checkpoint->settings_connection, ++ nm_settings_connection_get_connection(sett_conn), ++ NM_SETTING_COMPARE_FLAG_IGNORE_TIMESTAMP)) { + _LOGT("rollback: settings connection %s changed", uuid); + *need_update = TRUE; + *need_activation = TRUE; + } + +- /* ... is active, ... */ ++ storage = sett_conn ? nm_settings_connection_get_storage(sett_conn) : NULL; ++ shadowed_file = storage ? nm_settings_storage_get_shadowed_storage(storage, NULL) : NULL; ++ shadowed_connection = ++ shadowed_file ? parse_connection_from_shadowed_file(shadowed_file, NULL) : NULL; ++ ++ if (dev_checkpoint->settings_connection_shadowed) { ++ if (!shadowed_connection ++ || !nm_connection_compare(dev_checkpoint->settings_connection_shadowed, ++ shadowed_connection, ++ NM_SETTING_COMPARE_FLAG_IGNORE_TIMESTAMP)) { ++ _LOGT("rollback: shadowed connection changed for %s", uuid); ++ *need_update_shadowed = TRUE; ++ *need_update = TRUE; ++ } ++ } else { ++ if (shadowed_connection) { ++ _LOGT("rollback: shadowed connection changed for %s", uuid); ++ *need_update = TRUE; ++ } ++ } ++ ++ if (!sett_conn) ++ return NULL; ++ ++ /* Check if the connection unsaved flag changed */ ++ sett_conn_unsaved = NM_FLAGS_HAS(nm_settings_connection_get_flags(sett_conn), ++ NM_SETTINGS_CONNECTION_INT_FLAGS_UNSAVED); ++ if (sett_conn_unsaved != dev_checkpoint->settings_connection_is_unsaved) { ++ _LOGT("rollback: storage changed for settings connection %s: unsaved (%d -> %d)", ++ uuid, ++ dev_checkpoint->settings_connection_is_unsaved, ++ sett_conn_unsaved); ++ *need_update = TRUE; ++ } ++ ++ /* Check if the active state changed */ + nm_manager_for_each_active_connection (priv->manager, active, tmp_clist) { + ac_uuid = + nm_settings_connection_get_uuid(nm_active_connection_get_settings_connection(active)); +@@ -196,7 +275,7 @@ find_settings_connection(NMCheckpoint *self, + return sett_conn; + } + +- /* ... or if the connection was reactivated/reapplied */ ++ /* Check if the connection was reactivated/reapplied */ + if (nm_active_connection_version_id_get(active) != dev_checkpoint->ac_version_id) { + _LOGT("rollback: active connection version id of %s changed", uuid); + *need_activation = TRUE; +@@ -212,12 +291,19 @@ restore_and_activate_connection(NMCheckpoint *self, DeviceCheckpoint *dev_checkp + NMSettingsConnection *connection; + gs_unref_object NMAuthSubject *subject = NULL; + GError *local_error = NULL; +- gboolean need_update, need_activation; ++ gboolean need_update; ++ gboolean need_update_shadowed; ++ gboolean need_activation; + NMSettingsConnectionPersistMode persist_mode; + NMSettingsConnectionIntFlags sett_flags; + NMSettingsConnectionIntFlags sett_mask; + +- connection = find_settings_connection(self, dev_checkpoint, &need_update, &need_activation); ++ connection = find_settings_connection(self, ++ dev_checkpoint, ++ &need_update, ++ &need_update_shadowed, ++ &need_activation, ++ &persist_mode); + + /* FIXME: we need to ensure to re-create/update the profile for the + * same settings plugin. E.g. if it was a keyfile in /run or /etc, +@@ -229,9 +315,26 @@ restore_and_activate_connection(NMCheckpoint *self, DeviceCheckpoint *dev_checkp + sett_mask = NM_SETTINGS_CONNECTION_INT_FLAGS_NONE; + + if (connection) { ++ if (need_update_shadowed) { ++ _LOGD("rollback: updating shadowed file for connection %s", ++ nm_connection_get_uuid(dev_checkpoint->settings_connection)); ++ nm_settings_connection_update( ++ connection, ++ NULL, ++ dev_checkpoint->settings_connection_shadowed, ++ NM_SETTINGS_CONNECTION_PERSIST_MODE_TO_DISK, ++ sett_flags, ++ sett_mask, ++ NM_SETTINGS_CONNECTION_UPDATE_REASON_RESET_SYSTEM_SECRETS ++ | NM_SETTINGS_CONNECTION_UPDATE_REASON_UPDATE_NON_SECRET, ++ "checkpoint-rollback", ++ NULL); ++ } ++ + if (need_update) { +- _LOGD("rollback: updating connection %s", nm_settings_connection_get_uuid(connection)); +- persist_mode = NM_SETTINGS_CONNECTION_PERSIST_MODE_KEEP; ++ _LOGD("rollback: updating connection %s with persist mode \"%s\"", ++ nm_connection_get_uuid(dev_checkpoint->settings_connection), ++ nm_settings_connection_persist_mode_to_string(persist_mode)); + nm_settings_connection_update( + connection, + NULL, +@@ -246,21 +349,54 @@ restore_and_activate_connection(NMCheckpoint *self, DeviceCheckpoint *dev_checkp + } + } else { + /* The connection was deleted, recreate it */ +- _LOGD("rollback: adding connection %s again", +- nm_connection_get_uuid(dev_checkpoint->settings_connection)); +- +- persist_mode = NM_SETTINGS_CONNECTION_PERSIST_MODE_TO_DISK; +- if (!nm_settings_add_connection(NM_SETTINGS_GET, +- NULL, +- dev_checkpoint->settings_connection, +- persist_mode, +- NM_SETTINGS_CONNECTION_ADD_REASON_NONE, +- sett_flags, +- &connection, +- &local_error)) { +- _LOGD("rollback: connection add failure: %s", local_error->message); +- g_clear_error(&local_error); +- return FALSE; ++ if (need_update_shadowed) { ++ _LOGD("rollback: adding back shadowed file for connection %s", ++ nm_connection_get_uuid(dev_checkpoint->settings_connection)); ++ ++ if (!nm_settings_add_connection(NM_SETTINGS_GET, ++ NULL, ++ dev_checkpoint->settings_connection_shadowed, ++ NM_SETTINGS_CONNECTION_PERSIST_MODE_TO_DISK, ++ NM_SETTINGS_CONNECTION_ADD_REASON_NONE, ++ sett_flags, ++ &connection, ++ &local_error)) { ++ _LOGD("rollback: connection add failure: %s", local_error->message); ++ g_clear_error(&local_error); ++ return FALSE; ++ } ++ ++ _LOGD("rollback: updating connection %s with persist mode \"%s\"", ++ nm_connection_get_uuid(dev_checkpoint->settings_connection), ++ nm_settings_connection_persist_mode_to_string(persist_mode)); ++ ++ nm_settings_connection_update( ++ connection, ++ NULL, ++ dev_checkpoint->settings_connection, ++ persist_mode, ++ sett_flags, ++ sett_mask, ++ NM_SETTINGS_CONNECTION_UPDATE_REASON_RESET_SYSTEM_SECRETS ++ | NM_SETTINGS_CONNECTION_UPDATE_REASON_UPDATE_NON_SECRET, ++ "checkpoint-rollback", ++ NULL); ++ } else { ++ _LOGD("rollback: adding back connection %s with persist mode \"%s\"", ++ nm_connection_get_uuid(dev_checkpoint->settings_connection), ++ nm_settings_connection_persist_mode_to_string(persist_mode)); ++ if (!nm_settings_add_connection(NM_SETTINGS_GET, ++ NULL, ++ dev_checkpoint->settings_connection, ++ persist_mode, ++ NM_SETTINGS_CONNECTION_ADD_REASON_NONE, ++ sett_flags, ++ &connection, ++ &local_error)) { ++ _LOGD("rollback: connection add failure: %s", local_error->message); ++ g_clear_error(&local_error); ++ return FALSE; ++ } + } + need_activation = TRUE; + } +@@ -362,11 +498,15 @@ nm_checkpoint_rollback(NMCheckpoint *self) + while (g_hash_table_iter_next(&iter, (gpointer *) &device, (gpointer *) &dev_checkpoint)) { + guint32 result = NM_ROLLBACK_RESULT_OK; + +- _LOGD("rollback: restoring device %s (state %d, realized %d, explicitly unmanaged %d)", ++ _LOGD("rollback: restoring device %s (state %d, realized %d, explicitly unmanaged %d, " ++ "connection-unsaved %d, connection-shadowed %d, connection-shadowed-owned %d)", + dev_checkpoint->original_dev_name, + (int) dev_checkpoint->state, + dev_checkpoint->realized, +- dev_checkpoint->unmanaged_explicit); ++ dev_checkpoint->unmanaged_explicit, ++ dev_checkpoint->settings_connection_is_unsaved, ++ !!dev_checkpoint->settings_connection_shadowed, ++ dev_checkpoint->settings_connection_is_shadowed_owned); + + if (nm_device_is_real(device)) { + if (!dev_checkpoint->realized) { +@@ -518,6 +658,7 @@ device_checkpoint_destroy(gpointer data) + g_clear_object(&dev_checkpoint->applied_connection); + g_clear_object(&dev_checkpoint->settings_connection); + g_clear_object(&dev_checkpoint->device); ++ g_clear_object(&dev_checkpoint->settings_connection_shadowed); + g_free(dev_checkpoint->original_dev_path); + g_free(dev_checkpoint->original_dev_name); + +@@ -555,7 +696,7 @@ _dev_exported_changed(NMDBusObject *obj, NMCheckpoint *checkpoint) + } + + static DeviceCheckpoint * +-device_checkpoint_create(NMCheckpoint *checkpoint, NMDevice *device) ++device_checkpoint_create(NMCheckpoint *self, NMDevice *device) + { + DeviceCheckpoint *dev_checkpoint; + NMConnection *applied_connection; +@@ -579,7 +720,7 @@ device_checkpoint_create(NMCheckpoint *checkpoint, NMDevice *device) + dev_checkpoint->dev_exported_change_id = g_signal_connect(device, + NM_DBUS_OBJECT_EXPORTED_CHANGED, + G_CALLBACK(_dev_exported_changed), +- checkpoint); ++ self); + + if (nm_device_get_unmanaged_mask(device, NM_UNMANAGED_USER_EXPLICIT)) { + dev_checkpoint->unmanaged_explicit = +@@ -589,6 +730,11 @@ device_checkpoint_create(NMCheckpoint *checkpoint, NMDevice *device) + + act_request = nm_device_get_act_request(device); + if (act_request) { ++ NMSettingsStorage *storage; ++ gboolean shadowed_owned = FALSE; ++ const char *shadowed_file; ++ gs_free_error GError *error = NULL; ++ + settings_connection = nm_act_request_get_settings_connection(act_request); + applied_connection = nm_act_request_get_applied_connection(act_request); + +@@ -602,6 +748,24 @@ device_checkpoint_create(NMCheckpoint *checkpoint, NMDevice *device) + dev_checkpoint->activation_lifetime_bound_to_profile_visibility = + NM_FLAGS_HAS(nm_active_connection_get_state_flags(NM_ACTIVE_CONNECTION(act_request)), + NM_ACTIVATION_STATE_FLAG_LIFETIME_BOUND_TO_PROFILE_VISIBILITY); ++ ++ dev_checkpoint->settings_connection_is_unsaved = ++ NM_FLAGS_HAS(nm_settings_connection_get_flags(settings_connection), ++ NM_SETTINGS_CONNECTION_INT_FLAGS_UNSAVED); ++ ++ storage = nm_settings_connection_get_storage(settings_connection); ++ shadowed_file = ++ storage ? nm_settings_storage_get_shadowed_storage(storage, &shadowed_owned) : NULL; ++ if (shadowed_file) { ++ dev_checkpoint->settings_connection_is_shadowed_owned = shadowed_owned; ++ dev_checkpoint->settings_connection_shadowed = ++ parse_connection_from_shadowed_file(shadowed_file, &error); ++ if (!dev_checkpoint->settings_connection_shadowed) { ++ _LOGW("error reading shadowed connection file for %s: %s", ++ nm_device_get_iface(device), ++ error->message); ++ } ++ } + } + + return dev_checkpoint; +-- +2.41.0 + diff --git a/SPECS/NetworkManager.spec b/SPECS/NetworkManager.spec index e75de93..50ef6fc 100644 --- a/SPECS/NetworkManager.spec +++ b/SPECS/NetworkManager.spec @@ -7,7 +7,7 @@ %global epoch_version 1 %global real_version 1.46.0 %global rpm_version %{real_version} -%global release_version 4 +%global release_version 8 %global snapshot %{nil} %global git_sha %{nil} %global bcond_default_debug 0 @@ -218,6 +218,9 @@ Patch1002: 1002-allow-rollback-on-internal-global-dns-rhel-29725.patch Patch1003: 1003-do-not-allow-ovs-bridge-and-port-to-be-parent-rhel-28545.patch Patch1004: 1004-nm-dispatcher-fix-crash-rhel28973.patch Patch1005: 1005-fix-race-condition-while-enumerating-devices-rhel25808.patch +Patch1006: 1006-fix-lldp-for-ovs-bridge-ports-rhel31766.patch +Patch1007: 1007-platform-avoid-routes-resync-rhel36162.patch +Patch1008: 1008-checkpoint-preserve-in-memory-state-rhel32493.patch Requires(post): systemd %if 0%{?fedora} || 0%{?rhel} >= 8 @@ -1272,6 +1275,18 @@ fi %changelog +* Thu May 23 2024 Beniamino Galvani - 1:1.46.0-8 +- Preserve in-memory state of connections after checkpoint/rollback (RHEL-32493) + +* Tue May 14 2024 Íñigo Huguet - 1:1.46.0-7 +- Fix CPU usage of 100% when updating routes cache (RHEL-36162) + +* Mon Apr 08 2024 Fernando Fernandez Mancera - 1:1.46.0-6 +- Rebuild because build must go on 0day not 9.4.0 + +* Fri Apr 05 2024 Fernando Fernandez Mancera - 1:1.46.0-5 +- Fix LLDP for OVS Bridge ports (RHEL-31766) + * Tue Mar 26 2024 Beniamino Galvani - 1:1.46.0-4 - Fix nm-dispatcher crash (RHEL-28973) - Fix race condition while enumerating devices (RHEL-25808)