Blame SOURCES/1002-checkpoint-preserve-external-bridge-ports-rh2035519.patch

9faaa1
From b55842ac0803b59fe8675464191180e44634ce1f Mon Sep 17 00:00:00 2001
9faaa1
From: Thomas Haller <thaller@redhat.com>
9faaa1
Date: Tue, 22 Feb 2022 22:08:18 +0100
9faaa1
Subject: [PATCH 1/2] core: reject unsupported flags for CheckpointCreate D-Bus
9faaa1
 request
9faaa1
9faaa1
(cherry picked from commit df6ee44fb2b96cf05aaeeee500c75d7d91b37404)
9faaa1
(cherry picked from commit 4cfc2245d382b0b869bd52238eecd17f1c10af1c)
9faaa1
---
9faaa1
 src/core/nm-manager.c | 34 +++++++++++++++++++++++++---------
9faaa1
 1 file changed, 25 insertions(+), 9 deletions(-)
9faaa1
9faaa1
diff --git a/src/core/nm-manager.c b/src/core/nm-manager.c
9faaa1
index b440b22457f2..53ef1754bb72 100644
9faaa1
--- a/src/core/nm-manager.c
9faaa1
+++ b/src/core/nm-manager.c
9faaa1
@@ -7453,15 +7453,30 @@ impl_manager_checkpoint_create(NMDBusObject                      *obj,
9faaa1
                                GDBusMethodInvocation             *invocation,
9faaa1
                                GVariant                          *parameters)
9faaa1
 {
9faaa1
-    NMManager        *self = NM_MANAGER(obj);
9faaa1
-    NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE(self);
9faaa1
-    NMAuthChain      *chain;
9faaa1
-    char            **devices;
9faaa1
-    guint32           rollback_timeout;
9faaa1
-    guint32           flags;
9faaa1
+    NMManager         *self = NM_MANAGER(obj);
9faaa1
+    NMManagerPrivate  *priv = NM_MANAGER_GET_PRIVATE(self);
9faaa1
+    NMAuthChain       *chain;
9faaa1
+    gs_strfreev char **devices = NULL;
9faaa1
+    guint32            rollback_timeout;
9faaa1
+    guint32            flags;
9faaa1
 
9faaa1
     G_STATIC_ASSERT_EXPR(sizeof(flags) <= sizeof(NMCheckpointCreateFlags));
9faaa1
 
9faaa1
+    g_variant_get(parameters, "(^aouu)", &devices, &rollback_timeout, &flags);
9faaa1
+
9faaa1
+    if ((NMCheckpointCreateFlags) flags != flags
9faaa1
+        || NM_FLAGS_ANY(flags,
9faaa1
+                        ~((guint32) (NM_CHECKPOINT_CREATE_FLAG_DESTROY_ALL
9faaa1
+                                     | NM_CHECKPOINT_CREATE_FLAG_DELETE_NEW_CONNECTIONS
9faaa1
+                                     | NM_CHECKPOINT_CREATE_FLAG_DISCONNECT_NEW_DEVICES
9faaa1
+                                     | NM_CHECKPOINT_CREATE_FLAG_ALLOW_OVERLAPPING)))) {
9faaa1
+        g_dbus_method_invocation_return_error_literal(invocation,
9faaa1
+                                                      NM_MANAGER_ERROR,
9faaa1
+                                                      NM_MANAGER_ERROR_INVALID_ARGUMENTS,
9faaa1
+                                                      "Invalid flags");
9faaa1
+        return;
9faaa1
+    }
9faaa1
+
9faaa1
     chain = nm_auth_chain_new_context(invocation, checkpoint_auth_done_cb, self);
9faaa1
     if (!chain) {
9faaa1
         g_dbus_method_invocation_return_error_literal(invocation,
9faaa1
@@ -7471,11 +7486,12 @@ impl_manager_checkpoint_create(NMDBusObject                      *obj,
9faaa1
         return;
9faaa1
     }
9faaa1
 
9faaa1
-    g_variant_get(parameters, "(^aouu)", &devices, &rollback_timeout, &flags);
9faaa1
-
9faaa1
     c_list_link_tail(&priv->auth_lst_head, nm_auth_chain_parent_lst_list(chain));
9faaa1
     nm_auth_chain_set_data(chain, "audit-op", NM_AUDIT_OP_CHECKPOINT_CREATE, NULL);
9faaa1
-    nm_auth_chain_set_data(chain, "devices", devices, (GDestroyNotify) g_strfreev);
9faaa1
+    nm_auth_chain_set_data(chain,
9faaa1
+                           "devices",
9faaa1
+                           g_steal_pointer(&devices),
9faaa1
+                           (GDestroyNotify) g_strfreev);
9faaa1
     nm_auth_chain_set_data(chain, "flags", GUINT_TO_POINTER(flags), NULL);
9faaa1
     nm_auth_chain_set_data(chain, "timeout", GUINT_TO_POINTER(rollback_timeout), NULL);
9faaa1
     nm_auth_chain_add_call(chain, NM_AUTH_PERMISSION_CHECKPOINT_ROLLBACK, TRUE);
9faaa1
-- 
9faaa1
2.35.1
9faaa1
9faaa1
9faaa1
From 3c417c8338bf44292d4869763587286c7d492c0c Mon Sep 17 00:00:00 2001
9faaa1
From: Thomas Haller <thaller@redhat.com>
9faaa1
Date: Tue, 22 Feb 2022 21:55:57 +0100
9faaa1
Subject: [PATCH 2/2] core: preserve external ports during checkpoint rollback
9faaa1
9faaa1
When we have a bridge interface with ports attached externally (that is,
9faaa1
not by NetworkManager itself), then it can make sense that during
9faaa1
checkpoint rollback we want to keep those ports attached.
9faaa1
9faaa1
During rollback, we may need to deactivate the bridge device and
9faaa1
re-activate it. Implement this, by setting a flag before deactivating,
9faaa1
which prevents external ports to be detached. The flag gets cleared,
9faaa1
when the device state changes to activated (the following activation)
9faaa1
or unmanaged.
9faaa1
9faaa1
This is an ugly solution, for several reasons.
9faaa1
9faaa1
For one, NMDevice tracks its ports in the "slaves" list. But what
9faaa1
it does is ugly. There is no clear concept to understand what it
9faaa1
actually tacks. For example, it tracks externally added interfaces
9faaa1
(nm_device_sys_iface_state_is_external()) that are attached while
9faaa1
not being connected. But it also tracks interfaces that we want to attach
9faaa1
during activation (but which are not yet actually enslaved). It also tracks
9faaa1
slaves that have no actual netdev device (OVS). So it's not clear what this
9faaa1
list contains and what it should contain at any point in time. When we skip
9faaa1
the change of the slaves states during nm_device_master_release_slaves_all(),
9faaa1
it's not really clear what the effects are. It's ugly, but probably correct
9faaa1
enough. What would be better, if we had a clear purpose of what the
9faaa1
lists (or several lists) mean. E.g. a list of all ports that are
9faaa1
currently, physically attached vs. a list of ports we want to attach vs.
9faaa1
a list of OVS slaves that have no actual netdev device.
9faaa1
9faaa1
Another problem is that we attach state on the device
9faaa1
("activation_state_preserve_external_ports"), which should linger there
9faaa1
during the deactivation and reactivation. How can we be sure that we don't
9faaa1
leave that flag dangling there, and that the desired following activation
9faaa1
is the one we cared about? If the follow-up activation fails short (e.g. an
9faaa1
unmanaged command comes first), will we properly disconnect the slaves?
9faaa1
Should we even? In practice, it might be correct enough.
9faaa1
9faaa1
Also, we only implement this for bridges. I think this is where it makes
9faaa1
the most sense. And after all, it's an odd thing to preserve unknown,
9faaa1
external things during a rollback -- unknown, because we have no knowledge
9faaa1
about why these ports are attached and what to do with them.
9faaa1
9faaa1
Also, the change doesn't remember the ports that were attached when the
9faaa1
checkpoint was created. Instead, we preserve all ports that are attached
9faaa1
during rollback. That seems more useful and easier to implement. So we
9faaa1
don't actually rollback to the configuration when the checkpoint was
9faaa1
created. Instead, we rollback, but keep external devices.
9faaa1
9faaa1
Also, we do this now by default and introduce a flag to get the previous
9faaa1
behavior.
9faaa1
9faaa1
https://bugzilla.redhat.com/show_bug.cgi?id=2035519
9faaa1
https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/issues/ # 909
9faaa1
(cherry picked from commit 98b3056604fc565f273c264b892086a75a4db0e9)
9faaa1
(cherry picked from commit 351ca13358f62f85af675672c3399141bec092cd)
9faaa1
---
9faaa1
 src/core/devices/nm-device.c              | 71 ++++++++++++++++++++++-
9faaa1
 src/core/devices/nm-device.h              |  2 +
9faaa1
 src/core/nm-checkpoint.c                  |  5 ++
9faaa1
 src/core/nm-manager.c                     |  3 +-
9faaa1
 src/libnm-core-public/nm-dbus-interface.h | 16 +++--
9faaa1
 5 files changed, 90 insertions(+), 7 deletions(-)
9faaa1
9faaa1
diff --git a/src/core/devices/nm-device.c b/src/core/devices/nm-device.c
9faaa1
index 35360ceebb7b..a11486d54be3 100644
9faaa1
--- a/src/core/devices/nm-device.c
9faaa1
+++ b/src/core/devices/nm-device.c
9faaa1
@@ -76,6 +76,7 @@
9faaa1
 #include "nm-hostname-manager.h"
9faaa1
 
9faaa1
 #include "nm-device-generic.h"
9faaa1
+#include "nm-device-bridge.h"
9faaa1
 #include "nm-device-vlan.h"
9faaa1
 #include "nm-device-vrf.h"
9faaa1
 #include "nm-device-wireguard.h"
9faaa1
@@ -483,9 +484,12 @@ typedef struct _NMDevicePrivate {
9faaa1
 
9faaa1
     NMUtilsStableType current_stable_id_type : 3;
9faaa1
 
9faaa1
+    bool activation_state_preserve_external_ports : 1;
9faaa1
+
9faaa1
     bool nm_owned : 1; /* whether the device is a device owned and created by NM */
9faaa1
 
9faaa1
-    bool  assume_state_guess_assume : 1;
9faaa1
+    bool assume_state_guess_assume : 1;
9faaa1
+
9faaa1
     char *assume_state_connection_uuid;
9faaa1
 
9faaa1
     guint64 udi_id;
9faaa1
@@ -7666,8 +7670,19 @@ nm_device_master_release_slaves(NMDevice *self)
9faaa1
     c_list_for_each_safe (iter, safe, &priv->slaves) {
9faaa1
         SlaveInfo *info = c_list_entry(iter, SlaveInfo, lst_slave);
9faaa1
 
9faaa1
+        if (priv->activation_state_preserve_external_ports
9faaa1
+            && nm_device_sys_iface_state_is_external(info->slave)) {
9faaa1
+            _LOGT(LOGD_DEVICE,
9faaa1
+                  "master: preserve external port %s",
9faaa1
+                  nm_device_get_iface(info->slave));
9faaa1
+            continue;
9faaa1
+        }
9faaa1
         nm_device_master_release_one_slave(self, info->slave, TRUE, FALSE, reason);
9faaa1
     }
9faaa1
+
9faaa1
+    /* We only need this flag for a short time. It served its purpose. Clear
9faaa1
+     * it again. */
9faaa1
+    nm_device_activation_state_set_preserve_external_ports(self, FALSE);
9faaa1
 }
9faaa1
 
9faaa1
 /**
9faaa1
@@ -15386,6 +15401,16 @@ _set_state_full(NMDevice *self, NMDeviceState state, NMDeviceStateReason reason,
9faaa1
     if (state > NM_DEVICE_STATE_DISCONNECTED)
9faaa1
         nm_device_assume_state_reset(self);
9faaa1
 
9faaa1
+    if (state < NM_DEVICE_STATE_UNAVAILABLE
9faaa1
+        || (state >= NM_DEVICE_STATE_IP_CONFIG && state < NM_DEVICE_STATE_ACTIVATED)) {
9faaa1
+        /* preserve-external-ports is used by NMCheckpoint to activate a master
9faaa1
+         * device, and preserve already attached ports. This means, this state is only
9faaa1
+         * relevant during the deactivation and the following activation of the
9faaa1
+         * right profile. Once we are sufficiently far in the activation of the
9faaa1
+         * intended profile, we clear the state again. */
9faaa1
+        nm_device_activation_state_set_preserve_external_ports(self, FALSE);
9faaa1
+    }
9faaa1
+
9faaa1
     if (state <= NM_DEVICE_STATE_UNAVAILABLE) {
9faaa1
         if (available_connections_del_all(self))
9faaa1
             _notify(self, PROP_AVAILABLE_CONNECTIONS);
9faaa1
@@ -15790,6 +15815,50 @@ nm_device_get_state(NMDevice *self)
9faaa1
     return NM_DEVICE_GET_PRIVATE(self)->state;
9faaa1
 }
9faaa1
 
9faaa1
+/*****************************************************************************/
9faaa1
+
9faaa1
+/**
9faaa1
+ * nm_device_activation_state_set_preserve_external_ports:
9faaa1
+ * @self: the NMDevice.
9faaa1
+ * @flag: whether to set or clear the the flag.
9faaa1
+ *
9faaa1
+ * This sets an internal flag to true, which does something specific.
9faaa1
+ * For non-master devices, it has no effect. For master devices, this
9faaa1
+ * will prevent to detach all external ports, until the next activation
9faaa1
+ * completes.
9faaa1
+ *
9faaa1
+ * This is used during checkpoint/rollback. We may want to preserve
9faaa1
+ * externally attached ports during the restore. NMCheckpoint will
9faaa1
+ * call this before doing a re-activation. By setting the flag,
9faaa1
+ * we basically preserve such ports.
9faaa1
+ *
9faaa1
+ * Once we reach again ACTIVATED state, the flag gets cleared. This
9faaa1
+ * only has effect for the next activation cycle. */
9faaa1
+void
9faaa1
+nm_device_activation_state_set_preserve_external_ports(NMDevice *self, gboolean flag)
9faaa1
+{
9faaa1
+    NMDevicePrivate *priv;
9faaa1
+
9faaa1
+    g_return_if_fail(NM_IS_DEVICE(self));
9faaa1
+
9faaa1
+    priv = NM_DEVICE_GET_PRIVATE(self);
9faaa1
+
9faaa1
+    if (!NM_IS_DEVICE_BRIDGE(self)) {
9faaa1
+        /* This is actually only implemented for bridge devices. While it might
9faaa1
+         * make sense for bond/team or OVS, it's not clear that it is actually
9faaa1
+         * useful or desirable. */
9faaa1
+        return;
9faaa1
+    }
9faaa1
+
9faaa1
+    if (priv->activation_state_preserve_external_ports == flag)
9faaa1
+        return;
9faaa1
+
9faaa1
+    priv->activation_state_preserve_external_ports = flag;
9faaa1
+    _LOGD(LOGD_DEVICE,
9faaa1
+          "activation-state: preserve-external-ports %s",
9faaa1
+          flag ? "enabled" : "disabled");
9faaa1
+}
9faaa1
+
9faaa1
 /*****************************************************************************/
9faaa1
 /* NMConfigDevice interface related stuff */
9faaa1
 
9faaa1
diff --git a/src/core/devices/nm-device.h b/src/core/devices/nm-device.h
9faaa1
index cfcd4ade6d80..a7badb861087 100644
9faaa1
--- a/src/core/devices/nm-device.h
9faaa1
+++ b/src/core/devices/nm-device.h
9faaa1
@@ -444,6 +444,8 @@ NMDeviceType nm_device_get_device_type(NMDevice *dev);
9faaa1
 NMLinkType   nm_device_get_link_type(NMDevice *dev);
9faaa1
 NMMetered    nm_device_get_metered(NMDevice *dev);
9faaa1
 
9faaa1
+void nm_device_activation_state_set_preserve_external_ports(NMDevice *self, gboolean flag);
9faaa1
+
9faaa1
 guint32 nm_device_get_route_table(NMDevice *self, int addr_family);
9faaa1
 guint32 nm_device_get_route_metric(NMDevice *dev, int addr_family);
9faaa1
 
9faaa1
diff --git a/src/core/nm-checkpoint.c b/src/core/nm-checkpoint.c
9faaa1
index 0153af970de7..5b48f91aa515 100644
9faaa1
--- a/src/core/nm-checkpoint.c
9faaa1
+++ b/src/core/nm-checkpoint.c
9faaa1
@@ -282,6 +282,11 @@ restore_and_activate_connection(NMCheckpoint *self, DeviceCheckpoint *dev_checkp
9faaa1
          * an internal subject. */
9faaa1
         if (nm_device_get_state(dev_checkpoint->device) > NM_DEVICE_STATE_DISCONNECTED
9faaa1
             && nm_device_get_state(dev_checkpoint->device) < NM_DEVICE_STATE_DEACTIVATING) {
9faaa1
+            if (!NM_FLAGS_HAS(priv->flags, NM_CHECKPOINT_CREATE_FLAG_NO_PRESERVE_EXTERNAL_PORTS)) {
9faaa1
+                nm_device_activation_state_set_preserve_external_ports(dev_checkpoint->device,
9faaa1
+                                                                       TRUE);
9faaa1
+            }
9faaa1
+
9faaa1
             nm_device_state_changed(dev_checkpoint->device,
9faaa1
                                     NM_DEVICE_STATE_DEACTIVATING,
9faaa1
                                     NM_DEVICE_STATE_REASON_NEW_ACTIVATION);
9faaa1
diff --git a/src/core/nm-manager.c b/src/core/nm-manager.c
9faaa1
index 53ef1754bb72..6c73d237c845 100644
9faaa1
--- a/src/core/nm-manager.c
9faaa1
+++ b/src/core/nm-manager.c
9faaa1
@@ -7469,7 +7469,8 @@ impl_manager_checkpoint_create(NMDBusObject                      *obj,
9faaa1
                         ~((guint32) (NM_CHECKPOINT_CREATE_FLAG_DESTROY_ALL
9faaa1
                                      | NM_CHECKPOINT_CREATE_FLAG_DELETE_NEW_CONNECTIONS
9faaa1
                                      | NM_CHECKPOINT_CREATE_FLAG_DISCONNECT_NEW_DEVICES
9faaa1
-                                     | NM_CHECKPOINT_CREATE_FLAG_ALLOW_OVERLAPPING)))) {
9faaa1
+                                     | NM_CHECKPOINT_CREATE_FLAG_ALLOW_OVERLAPPING
9faaa1
+                                     | NM_CHECKPOINT_CREATE_FLAG_NO_PRESERVE_EXTERNAL_PORTS)))) {
9faaa1
         g_dbus_method_invocation_return_error_literal(invocation,
9faaa1
                                                       NM_MANAGER_ERROR,
9faaa1
                                                       NM_MANAGER_ERROR_INVALID_ARGUMENTS,
9faaa1
diff --git a/src/libnm-core-public/nm-dbus-interface.h b/src/libnm-core-public/nm-dbus-interface.h
9faaa1
index fe2a6c09db58..0d23c7d7a793 100644
9faaa1
--- a/src/libnm-core-public/nm-dbus-interface.h
9faaa1
+++ b/src/libnm-core-public/nm-dbus-interface.h
9faaa1
@@ -959,17 +959,23 @@ typedef enum {
9faaa1
  *   overlapping younger checkpoints. This opts-in that the
9faaa1
  *   checkpoint can be automatically destroyed by the rollback
9faaa1
  *   of an older checkpoint. Since: 1.12.
9faaa1
+ * @NM_CHECKPOINT_CREATE_FLAG_NO_PRESERVE_EXTERNAL_PORTS: during rollback,
9faaa1
+ *   by default externally added ports attached to bridge devices are preserved.
9faaa1
+ *   With this flag, the rollback detaches all external ports.
9faaa1
+ *   This only has an effect for bridge ports. Before 1.38, 1.36.2, this was the default
9faaa1
+ *   behavior. Since: 1.38, 1.36.2.
9faaa1
  *
9faaa1
  * The flags for CheckpointCreate call
9faaa1
  *
9faaa1
  * Since: 1.4 (gi flags generated since 1.12)
9faaa1
  */
9faaa1
 typedef enum { /*< flags >*/
9faaa1
-               NM_CHECKPOINT_CREATE_FLAG_NONE                   = 0,
9faaa1
-               NM_CHECKPOINT_CREATE_FLAG_DESTROY_ALL            = 0x01,
9faaa1
-               NM_CHECKPOINT_CREATE_FLAG_DELETE_NEW_CONNECTIONS = 0x02,
9faaa1
-               NM_CHECKPOINT_CREATE_FLAG_DISCONNECT_NEW_DEVICES = 0x04,
9faaa1
-               NM_CHECKPOINT_CREATE_FLAG_ALLOW_OVERLAPPING      = 0x08,
9faaa1
+               NM_CHECKPOINT_CREATE_FLAG_NONE                       = 0,
9faaa1
+               NM_CHECKPOINT_CREATE_FLAG_DESTROY_ALL                = 0x01,
9faaa1
+               NM_CHECKPOINT_CREATE_FLAG_DELETE_NEW_CONNECTIONS     = 0x02,
9faaa1
+               NM_CHECKPOINT_CREATE_FLAG_DISCONNECT_NEW_DEVICES     = 0x04,
9faaa1
+               NM_CHECKPOINT_CREATE_FLAG_ALLOW_OVERLAPPING          = 0x08,
9faaa1
+               NM_CHECKPOINT_CREATE_FLAG_NO_PRESERVE_EXTERNAL_PORTS = 0x10,
9faaa1
 } NMCheckpointCreateFlags;
9faaa1
 
9faaa1
 /**
9faaa1
-- 
9faaa1
2.35.1
9faaa1