xfrm: add state synchronization after migration

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>
This commit is contained in:
Antony Antony 2026-05-26 21:08:17 +02:00 committed by Steffen Klassert
parent b2cb192b95
commit bac7a60e26
2 changed files with 41 additions and 16 deletions

View 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);

View 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;