]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
xfrm: add state synchronization after migration
authorAntony Antony <antony.antony@secunet.com>
Tue, 26 May 2026 19:08:17 +0000 (21:08 +0200)
committerSteffen Klassert <steffen.klassert@secunet.com>
Thu, 4 Jun 2026 10:22:40 +0000 (12:22 +0200)
Add xfrm_migrate_sync() to copy curlft and replay state from the old SA
to the new one before installation. The function allocates no memory, so
it can be called under a spinlock. In preparation for a subsequent patch
in this series.

A subsequent patch calls this under x->lock, atomically capturing the
latest lifetime counters and replay state from the original SA and
deleting it in the same critical section to prevent SN/IV reuse
for XFRM_MSG_MIGRATE_STATE method.

No functional change.

Signed-off-by: Antony Antony <antony.antony@secunet.com>
Signed-off-by: Steffen Klassert <steffen.klassert@secunet.com>
include/net/xfrm.h
net/xfrm/xfrm_state.c

index 4137986f15e241bf0bda2003be1da8deb5a58f0c..be22c26e4661b9cd5613878b7cc6fac20712ffc2 100644 (file)
@@ -2024,23 +2024,51 @@ static inline unsigned int xfrm_replay_state_esn_len(struct xfrm_replay_state_es
 
 #ifdef CONFIG_XFRM_MIGRATE
 static inline int xfrm_replay_clone(struct xfrm_state *x,
-                                    struct xfrm_state *orig)
+                                   const struct xfrm_state *orig)
 {
+       /* Counters synced later in xfrm_replay_sync() */
 
-       x->replay_esn = kmemdup(orig->replay_esn,
+       x->replay = orig->replay;
+       x->preplay = orig->preplay;
+
+       if (orig->replay_esn) {
+               x->replay_esn = kmemdup(orig->replay_esn,
                                xfrm_replay_state_esn_len(orig->replay_esn),
                                GFP_KERNEL);
-       if (!x->replay_esn)
-               return -ENOMEM;
-       x->preplay_esn = kmemdup(orig->preplay_esn,
-                                xfrm_replay_state_esn_len(orig->preplay_esn),
-                                GFP_KERNEL);
-       if (!x->preplay_esn)
-               return -ENOMEM;
+               if (!x->replay_esn)
+                       return -ENOMEM;
+               x->preplay_esn = kmemdup(orig->preplay_esn,
+                               xfrm_replay_state_esn_len(orig->preplay_esn),
+                               GFP_KERNEL);
+               if (!x->preplay_esn)
+                       return -ENOMEM;
+       }
 
        return 0;
 }
 
+static inline void xfrm_replay_sync(struct xfrm_state *x, const struct xfrm_state *orig)
+{
+       x->replay = orig->replay;
+       x->preplay = orig->preplay;
+
+       if (orig->replay_esn) {
+               memcpy(x->replay_esn, orig->replay_esn,
+                               xfrm_replay_state_esn_len(orig->replay_esn));
+
+               memcpy(x->preplay_esn, orig->preplay_esn,
+                               xfrm_replay_state_esn_len(orig->preplay_esn));
+       }
+}
+
+static inline void xfrm_migrate_sync(struct xfrm_state *x,
+                                         const struct xfrm_state *orig)
+{
+       /* called under lock so no race conditions or mallocs allowed */
+       memcpy(&x->curlft, &orig->curlft, sizeof(x->curlft));
+       xfrm_replay_sync(x, orig);
+}
+
 static inline struct xfrm_algo_aead *xfrm_algo_aead_clone(struct xfrm_algo_aead *orig)
 {
        return kmemdup(orig, aead_len(orig), GFP_KERNEL);
index 327a855253e62b789ded176efd07b54933c2dd51..fcf6f0c6400dcfa66ec7949212f9da70806d79dd 100644 (file)
@@ -2027,10 +2027,8 @@ static struct xfrm_state *xfrm_state_clone_and_setup(struct xfrm_state *orig,
                        goto error;
        }
 
-       if (orig->replay_esn) {
-               if (xfrm_replay_clone(x, orig))
-                       goto error;
-       }
+       if (xfrm_replay_clone(x, orig))
+               goto error;
 
        memcpy(&x->mark, &orig->mark, sizeof(x->mark));
        memcpy(&x->props.smark, &orig->props.smark, sizeof(x->props.smark));
@@ -2043,11 +2041,8 @@ static struct xfrm_state *xfrm_state_clone_and_setup(struct xfrm_state *orig,
        x->tfcpad = orig->tfcpad;
        x->replay_maxdiff = orig->replay_maxdiff;
        x->replay_maxage = orig->replay_maxage;
-       memcpy(&x->curlft, &orig->curlft, sizeof(x->curlft));
        x->km.state = orig->km.state;
        x->km.seq = orig->km.seq;
-       x->replay = orig->replay;
-       x->preplay = orig->preplay;
        x->lastused = orig->lastused;
        x->new_mapping = 0;
        x->new_mapping_sport = 0;
@@ -2193,6 +2188,8 @@ struct xfrm_state *xfrm_state_migrate(struct xfrm_state *x,
        if (!xc)
                return NULL;
 
+       xfrm_migrate_sync(xc, x);
+
        if (xfrm_state_migrate_install(x, xc, m, xuo, extack) < 0)
                return NULL;