* [PATCH ipsec-next v8 02/14] xfrm: add extack to xfrm_init_state
From: Antony Antony @ 2026-05-05 4:32 UTC (permalink / raw)
To: Antony Antony, Steffen Klassert, Herbert Xu, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Simon Horman,
David Ahern, Masahide NAKAMURA, Paul Moore, Stephen Smalley,
Ondrej Mosnacek, Jonathan Corbet, Shuah Khan
Cc: Sabrina Dubroca, netdev, linux-kernel, selinux, linux-doc,
Chiachang Wang, Yan Yan, devel
In-Reply-To: <migrate-state-v8-0-4578fb016965@secunet.com>
Add a struct extack parameter to xfrm_init_state() and pass it
through to __xfrm_init_state(). This allows validation errors detected
during state initialization to propagate meaningful error messages back
to userspace.
xfrm_state_migrate_create() now passes extack so that errors from the
XFRM_MSG_MIGRATE_STATE path are properly reported. Callers without an
extack context (af_key, ipcomp4, ipcomp6) pass NULL, preserving their
existing behaviour.
Signed-off-by: Antony Antony <antony.antony@secunet.com>
---
v5->v6: added this patch
---
include/net/xfrm.h | 2 +-
net/ipv4/ipcomp.c | 2 +-
net/ipv6/ipcomp6.c | 2 +-
net/key/af_key.c | 2 +-
net/xfrm/xfrm_state.c | 6 +++---
5 files changed, 7 insertions(+), 7 deletions(-)
diff --git a/include/net/xfrm.h b/include/net/xfrm.h
index 10d3edde6b2f..0c035955d87d 100644
--- a/include/net/xfrm.h
+++ b/include/net/xfrm.h
@@ -1774,7 +1774,7 @@ u32 xfrm_replay_seqhi(struct xfrm_state *x, __be32 net_seq);
int xfrm_init_replay(struct xfrm_state *x, struct netlink_ext_ack *extack);
u32 xfrm_state_mtu(struct xfrm_state *x, int mtu);
int __xfrm_init_state(struct xfrm_state *x, struct netlink_ext_ack *extack);
-int xfrm_init_state(struct xfrm_state *x);
+int xfrm_init_state(struct xfrm_state *x, struct netlink_ext_ack *extack);
int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type);
int xfrm_input_resume(struct sk_buff *skb, int nexthdr);
int xfrm_trans_queue_net(struct net *net, struct sk_buff *skb,
diff --git a/net/ipv4/ipcomp.c b/net/ipv4/ipcomp.c
index 9a45aed508d1..b1ea2d37e8c5 100644
--- a/net/ipv4/ipcomp.c
+++ b/net/ipv4/ipcomp.c
@@ -77,7 +77,7 @@ static struct xfrm_state *ipcomp_tunnel_create(struct xfrm_state *x)
memcpy(&t->mark, &x->mark, sizeof(t->mark));
t->if_id = x->if_id;
- if (xfrm_init_state(t))
+ if (xfrm_init_state(t, NULL))
goto error;
atomic_set(&t->tunnel_users, 1);
diff --git a/net/ipv6/ipcomp6.c b/net/ipv6/ipcomp6.c
index 8607569de34f..b340d67eb1d9 100644
--- a/net/ipv6/ipcomp6.c
+++ b/net/ipv6/ipcomp6.c
@@ -95,7 +95,7 @@ static struct xfrm_state *ipcomp6_tunnel_create(struct xfrm_state *x)
memcpy(&t->mark, &x->mark, sizeof(t->mark));
t->if_id = x->if_id;
- if (xfrm_init_state(t))
+ if (xfrm_init_state(t, NULL))
goto error;
atomic_set(&t->tunnel_users, 1);
diff --git a/net/key/af_key.c b/net/key/af_key.c
index a166a88d8788..842bf5786e3f 100644
--- a/net/key/af_key.c
+++ b/net/key/af_key.c
@@ -1299,7 +1299,7 @@ static struct xfrm_state * pfkey_msg2xfrm_state(struct net *net,
}
}
- err = xfrm_init_state(x);
+ err = xfrm_init_state(x, NULL);
if (err)
goto out;
diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c
index 9417a025270c..53d88b87bdbd 100644
--- a/net/xfrm/xfrm_state.c
+++ b/net/xfrm/xfrm_state.c
@@ -2143,7 +2143,7 @@ struct xfrm_state *xfrm_state_migrate(struct xfrm_state *x,
if (!xc)
return NULL;
- if (xfrm_init_state(xc) < 0)
+ if (xfrm_init_state(xc, extack) < 0)
goto error;
/* configure the hardware if offload is requested */
@@ -3238,11 +3238,11 @@ int __xfrm_init_state(struct xfrm_state *x, struct netlink_ext_ack *extack)
EXPORT_SYMBOL(__xfrm_init_state);
-int xfrm_init_state(struct xfrm_state *x)
+int xfrm_init_state(struct xfrm_state *x, struct netlink_ext_ack *extack)
{
int err;
- err = __xfrm_init_state(x, NULL);
+ err = __xfrm_init_state(x, extack);
if (err)
return err;
--
2.47.3
^ permalink raw reply related
* [PATCH ipsec-next v8 03/14] xfrm: allow migration from UDP encapsulated to non-encapsulated ESP
From: Antony Antony @ 2026-05-05 4:32 UTC (permalink / raw)
To: Antony Antony, Steffen Klassert, Herbert Xu, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Simon Horman,
David Ahern, Masahide NAKAMURA, Paul Moore, Stephen Smalley,
Ondrej Mosnacek, Jonathan Corbet, Shuah Khan
Cc: Sabrina Dubroca, netdev, linux-kernel, selinux, linux-doc,
Chiachang Wang, Yan Yan, devel
In-Reply-To: <migrate-state-v8-0-4578fb016965@secunet.com>
The current code prevents migrating an SA from UDP encapsulation to
plain ESP. This is needed when moving from a NATed path to a non-NATed
one, for example when switching from IPv4+NAT to IPv6.
Only copy the existing encapsulation during migration if the encap
attribute is explicitly provided.
Note: PF_KEY's SADB_X_MIGRATE always passes encap=NULL and never
supported encapsulation in migration. PF_KEY is deprecated and was
in feature freeze when UDP encapsulation was added to xfrm.
Signed-off-by: Antony Antony <antony.antony@secunet.com>
Tested-by: Yan Yan <evitayan@google.com>
---
net/xfrm/xfrm_state.c | 10 ++--------
1 file changed, 2 insertions(+), 8 deletions(-)
diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c
index 53d88b87bdbd..933541bc9093 100644
--- a/net/xfrm/xfrm_state.c
+++ b/net/xfrm/xfrm_state.c
@@ -2008,14 +2008,8 @@ static struct xfrm_state *xfrm_state_clone_and_setup(struct xfrm_state *orig,
}
x->props.calgo = orig->props.calgo;
- if (encap || orig->encap) {
- if (encap)
- x->encap = kmemdup(encap, sizeof(*x->encap),
- GFP_KERNEL);
- else
- x->encap = kmemdup(orig->encap, sizeof(*x->encap),
- GFP_KERNEL);
-
+ if (encap) {
+ x->encap = kmemdup(encap, sizeof(*x->encap), GFP_KERNEL);
if (!x->encap)
goto error;
}
--
2.47.3
^ permalink raw reply related
* [PATCH ipsec-next v8 04/14] xfrm: fix NAT-related field inheritance in SA migration
From: Antony Antony @ 2026-05-05 4:32 UTC (permalink / raw)
To: Antony Antony, Steffen Klassert, Herbert Xu, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Simon Horman,
David Ahern, Masahide NAKAMURA, Paul Moore, Stephen Smalley,
Ondrej Mosnacek, Jonathan Corbet, Shuah Khan
Cc: Sabrina Dubroca, netdev, linux-kernel, selinux, linux-doc,
Chiachang Wang, Yan Yan, devel
In-Reply-To: <migrate-state-v8-0-4578fb016965@secunet.com>
During SA migration via xfrm_state_clone_and_setup(),
nat_keepalive_interval was silently dropped and never copied to the new
SA. mapping_maxage was unconditionally copied even when migrating to a
non-encapsulated SA.
Both fields are only meaningful when UDP encapsulation (NAT-T) is in
use. Move mapping_maxage and add nat_keepalive_interval inside the
existing if (encap) block, so both are inherited when migrating with
encapsulation and correctly absent when migrating without it.
Signed-off-by: Antony Antony <antony.antony@secunet.com>
---
v5->v6: added this patch
---
net/xfrm/xfrm_state.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c
index 933541bc9093..b9de931d84c1 100644
--- a/net/xfrm/xfrm_state.c
+++ b/net/xfrm/xfrm_state.c
@@ -2012,6 +2012,8 @@ static struct xfrm_state *xfrm_state_clone_and_setup(struct xfrm_state *orig,
x->encap = kmemdup(encap, sizeof(*x->encap), GFP_KERNEL);
if (!x->encap)
goto error;
+ x->mapping_maxage = orig->mapping_maxage;
+ x->nat_keepalive_interval = orig->nat_keepalive_interval;
}
if (orig->security)
@@ -2046,7 +2048,6 @@ static struct xfrm_state *xfrm_state_clone_and_setup(struct xfrm_state *orig,
x->km.seq = orig->km.seq;
x->replay = orig->replay;
x->preplay = orig->preplay;
- x->mapping_maxage = orig->mapping_maxage;
x->lastused = orig->lastused;
x->new_mapping = 0;
x->new_mapping_sport = 0;
--
2.47.3
^ permalink raw reply related
* [PATCH ipsec-next v8 05/14] xfrm: rename reqid in xfrm_migrate
From: Antony Antony @ 2026-05-05 4:32 UTC (permalink / raw)
To: Antony Antony, Steffen Klassert, Herbert Xu, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Simon Horman,
David Ahern, Masahide NAKAMURA, Paul Moore, Stephen Smalley,
Ondrej Mosnacek, Jonathan Corbet, Shuah Khan
Cc: Sabrina Dubroca, netdev, linux-kernel, selinux, linux-doc,
Chiachang Wang, Yan Yan, devel
In-Reply-To: <migrate-state-v8-0-4578fb016965@secunet.com>
In preparation for a later patch in this series s/reqid/old_reqid/.
No functional change.
Signed-off-by: Antony Antony <antony.antony@secunet.com>
---
include/net/xfrm.h | 2 +-
net/key/af_key.c | 10 +++++-----
net/xfrm/xfrm_policy.c | 4 ++--
net/xfrm/xfrm_state.c | 6 +++---
net/xfrm/xfrm_user.c | 4 ++--
5 files changed, 13 insertions(+), 13 deletions(-)
diff --git a/include/net/xfrm.h b/include/net/xfrm.h
index 0c035955d87d..368b1dc22e5c 100644
--- a/include/net/xfrm.h
+++ b/include/net/xfrm.h
@@ -685,7 +685,7 @@ struct xfrm_migrate {
u8 proto;
u8 mode;
u16 reserved;
- u32 reqid;
+ u32 old_reqid;
u16 old_family;
u16 new_family;
};
diff --git a/net/key/af_key.c b/net/key/af_key.c
index 842bf5786e3f..1f0201d97b4f 100644
--- a/net/key/af_key.c
+++ b/net/key/af_key.c
@@ -2554,7 +2554,7 @@ static int ipsecrequests_to_migrate(struct sadb_x_ipsecrequest *rq1, int len,
if ((mode = pfkey_mode_to_xfrm(rq1->sadb_x_ipsecrequest_mode)) < 0)
return -EINVAL;
m->mode = mode;
- m->reqid = rq1->sadb_x_ipsecrequest_reqid;
+ m->old_reqid = rq1->sadb_x_ipsecrequest_reqid;
return ((int)(rq1->sadb_x_ipsecrequest_len +
rq2->sadb_x_ipsecrequest_len));
@@ -3655,15 +3655,15 @@ static int pfkey_send_migrate(const struct xfrm_selector *sel, u8 dir, u8 type,
if (mode < 0)
goto err;
if (set_ipsecrequest(skb, mp->proto, mode,
- (mp->reqid ? IPSEC_LEVEL_UNIQUE : IPSEC_LEVEL_REQUIRE),
- mp->reqid, mp->old_family,
+ (mp->old_reqid ? IPSEC_LEVEL_UNIQUE : IPSEC_LEVEL_REQUIRE),
+ mp->old_reqid, mp->old_family,
&mp->old_saddr, &mp->old_daddr) < 0)
goto err;
/* new ipsecrequest */
if (set_ipsecrequest(skb, mp->proto, mode,
- (mp->reqid ? IPSEC_LEVEL_UNIQUE : IPSEC_LEVEL_REQUIRE),
- mp->reqid, mp->new_family,
+ (mp->old_reqid ? IPSEC_LEVEL_UNIQUE : IPSEC_LEVEL_REQUIRE),
+ mp->old_reqid, mp->new_family,
&mp->new_saddr, &mp->new_daddr) < 0)
goto err;
}
diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c
index c944327ce66c..fd505adf080e 100644
--- a/net/xfrm/xfrm_policy.c
+++ b/net/xfrm/xfrm_policy.c
@@ -4538,7 +4538,7 @@ static int migrate_tmpl_match(const struct xfrm_migrate *m, const struct xfrm_tm
int match = 0;
if (t->mode == m->mode && t->id.proto == m->proto &&
- (m->reqid == 0 || t->reqid == m->reqid)) {
+ (m->old_reqid == 0 || t->reqid == m->old_reqid)) {
switch (t->mode) {
case XFRM_MODE_TUNNEL:
case XFRM_MODE_BEET:
@@ -4632,7 +4632,7 @@ static int xfrm_migrate_check(const struct xfrm_migrate *m, int num_migrate,
sizeof(m[i].old_saddr)) &&
m[i].proto == m[j].proto &&
m[i].mode == m[j].mode &&
- m[i].reqid == m[j].reqid &&
+ m[i].old_reqid == m[j].old_reqid &&
m[i].old_family == m[j].old_family) {
NL_SET_ERR_MSG(extack, "Entries in the MIGRATE attribute's list must be unique");
return -EINVAL;
diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c
index b9de931d84c1..5424f2becbaf 100644
--- a/net/xfrm/xfrm_state.c
+++ b/net/xfrm/xfrm_state.c
@@ -2081,14 +2081,14 @@ struct xfrm_state *xfrm_migrate_state_find(struct xfrm_migrate *m, struct net *n
spin_lock_bh(&net->xfrm.xfrm_state_lock);
- if (m->reqid) {
+ if (m->old_reqid) {
h = xfrm_dst_hash(net, &m->old_daddr, &m->old_saddr,
- m->reqid, m->old_family);
+ m->old_reqid, m->old_family);
hlist_for_each_entry(x, xfrm_state_deref_prot(net->xfrm.state_bydst, net) + h, bydst) {
if (x->props.mode != m->mode ||
x->id.proto != m->proto)
continue;
- if (m->reqid && x->props.reqid != m->reqid)
+ if (m->old_reqid && x->props.reqid != m->old_reqid)
continue;
if (if_id != 0 && x->if_id != if_id)
continue;
diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c
index ae144d1e4a65..273ea6fdb8ad 100644
--- a/net/xfrm/xfrm_user.c
+++ b/net/xfrm/xfrm_user.c
@@ -3104,7 +3104,7 @@ static int copy_from_user_migrate(struct xfrm_migrate *ma,
ma->proto = um->proto;
ma->mode = um->mode;
- ma->reqid = um->reqid;
+ ma->old_reqid = um->reqid;
ma->old_family = um->old_family;
ma->new_family = um->new_family;
@@ -3187,7 +3187,7 @@ static int copy_to_user_migrate(const struct xfrm_migrate *m, struct sk_buff *sk
memset(&um, 0, sizeof(um));
um.proto = m->proto;
um.mode = m->mode;
- um.reqid = m->reqid;
+ um.reqid = m->old_reqid;
um.old_family = m->old_family;
memcpy(&um.old_daddr, &m->old_daddr, sizeof(um.old_daddr));
memcpy(&um.old_saddr, &m->old_saddr, sizeof(um.old_saddr));
--
2.47.3
^ permalink raw reply related
* [PATCH ipsec-next v8 06/14] xfrm: split xfrm_state_migrate into create and install functions
From: Antony Antony @ 2026-05-05 4:33 UTC (permalink / raw)
To: Antony Antony, Steffen Klassert, Herbert Xu, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Simon Horman,
David Ahern, Masahide NAKAMURA, Paul Moore, Stephen Smalley,
Ondrej Mosnacek, Jonathan Corbet, Shuah Khan
Cc: Sabrina Dubroca, netdev, linux-kernel, selinux, linux-doc,
Chiachang Wang, Yan Yan, devel
In-Reply-To: <migrate-state-v8-0-4578fb016965@secunet.com>
To prepare for subsequent patches, split
xfrm_state_migrate() into two functions:
- xfrm_state_migrate_create(): creates the migrated state
- xfrm_state_migrate_install(): installs it into the state table
splitting will help to avoid SN/IV reuse when migrating AEAD SA.
And add const whenever possible.
No functional change.
Signed-off-by: Antony Antony <antony.antony@secunet.com>
---
v4->v5: - added this patch
---
include/net/xfrm.h | 11 ++++++++
net/xfrm/xfrm_state.c | 73 +++++++++++++++++++++++++++++++++++++--------------
2 files changed, 64 insertions(+), 20 deletions(-)
diff --git a/include/net/xfrm.h b/include/net/xfrm.h
index 368b1dc22e5c..4137986f15e2 100644
--- a/include/net/xfrm.h
+++ b/include/net/xfrm.h
@@ -1895,6 +1895,17 @@ int km_migrate(const struct xfrm_selector *sel, u8 dir, u8 type,
const struct xfrm_encap_tmpl *encap);
struct xfrm_state *xfrm_migrate_state_find(struct xfrm_migrate *m, struct net *net,
u32 if_id);
+struct xfrm_state *xfrm_state_migrate_create(struct xfrm_state *x,
+ const struct xfrm_migrate *m,
+ const struct xfrm_encap_tmpl *encap,
+ struct net *net,
+ struct xfrm_user_offload *xuo,
+ struct netlink_ext_ack *extack);
+int xfrm_state_migrate_install(const struct xfrm_state *x,
+ struct xfrm_state *xc,
+ const struct xfrm_migrate *m,
+ struct xfrm_user_offload *xuo,
+ struct netlink_ext_ack *extack);
struct xfrm_state *xfrm_state_migrate(struct xfrm_state *x,
struct xfrm_migrate *m,
struct xfrm_encap_tmpl *encap,
diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c
index 5424f2becbaf..85fd80520184 100644
--- a/net/xfrm/xfrm_state.c
+++ b/net/xfrm/xfrm_state.c
@@ -1966,8 +1966,8 @@ static inline int clone_security(struct xfrm_state *x, struct xfrm_sec_ctx *secu
}
static struct xfrm_state *xfrm_state_clone_and_setup(struct xfrm_state *orig,
- struct xfrm_encap_tmpl *encap,
- struct xfrm_migrate *m)
+ const struct xfrm_encap_tmpl *encap,
+ const struct xfrm_migrate *m)
{
struct net *net = xs_net(orig);
struct xfrm_state *x = xfrm_state_alloc(net);
@@ -2125,12 +2125,12 @@ struct xfrm_state *xfrm_migrate_state_find(struct xfrm_migrate *m, struct net *n
}
EXPORT_SYMBOL(xfrm_migrate_state_find);
-struct xfrm_state *xfrm_state_migrate(struct xfrm_state *x,
- struct xfrm_migrate *m,
- struct xfrm_encap_tmpl *encap,
- struct net *net,
- struct xfrm_user_offload *xuo,
- struct netlink_ext_ack *extack)
+struct xfrm_state *xfrm_state_migrate_create(struct xfrm_state *x,
+ const struct xfrm_migrate *m,
+ const struct xfrm_encap_tmpl *encap,
+ struct net *net,
+ struct xfrm_user_offload *xuo,
+ struct netlink_ext_ack *extack)
{
struct xfrm_state *xc;
@@ -2145,24 +2145,57 @@ struct xfrm_state *xfrm_state_migrate(struct xfrm_state *x,
if (xuo && xfrm_dev_state_add(net, xc, xuo, extack))
goto error;
- /* add state */
+ return xc;
+error:
+ xc->km.state = XFRM_STATE_DEAD;
+ xfrm_state_put(xc);
+ return NULL;
+}
+EXPORT_SYMBOL(xfrm_state_migrate_create);
+
+int xfrm_state_migrate_install(const struct xfrm_state *x,
+ struct xfrm_state *xc,
+ const struct xfrm_migrate *m,
+ struct xfrm_user_offload *xuo,
+ struct netlink_ext_ack *extack)
+{
if (xfrm_addr_equal(&x->id.daddr, &m->new_daddr, m->new_family)) {
- /* a care is needed when the destination address of the
- state is to be updated as it is a part of triplet */
+ /*
+ * Care is needed when the destination address
+ * of the state is to be updated as it is a part of triplet.
+ */
xfrm_state_insert(xc);
} else {
- if (xfrm_state_add(xc) < 0)
- goto error_add;
+ if (xfrm_state_add(xc) < 0) {
+ if (xuo)
+ xfrm_dev_state_delete(xc);
+ xc->km.state = XFRM_STATE_DEAD;
+ xfrm_state_put(xc);
+ return -EEXIST;
+ }
}
+ return 0;
+}
+EXPORT_SYMBOL(xfrm_state_migrate_install);
+
+struct xfrm_state *xfrm_state_migrate(struct xfrm_state *x,
+ struct xfrm_migrate *m,
+ struct xfrm_encap_tmpl *encap,
+ struct net *net,
+ struct xfrm_user_offload *xuo,
+ struct netlink_ext_ack *extack)
+{
+ struct xfrm_state *xc;
+
+ xc = xfrm_state_migrate_create(x, m, encap, net, xuo, extack);
+ if (!xc)
+ return NULL;
+
+ if (xfrm_state_migrate_install(x, xc, m, xuo, extack) < 0)
+ return NULL;
+
return xc;
-error_add:
- if (xuo)
- xfrm_dev_state_delete(xc);
-error:
- xc->km.state = XFRM_STATE_DEAD;
- xfrm_state_put(xc);
- return NULL;
}
EXPORT_SYMBOL(xfrm_state_migrate);
#endif
--
2.47.3
^ permalink raw reply related
* [PATCH ipsec-next v8 07/14] xfrm: check family before comparing addresses in migrate
From: Antony Antony @ 2026-05-05 4:33 UTC (permalink / raw)
To: Antony Antony, Steffen Klassert, Herbert Xu, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Simon Horman,
David Ahern, Masahide NAKAMURA, Paul Moore, Stephen Smalley,
Ondrej Mosnacek, Jonathan Corbet, Shuah Khan
Cc: Sabrina Dubroca, netdev, linux-kernel, selinux, linux-doc,
Chiachang Wang, Yan Yan, devel
In-Reply-To: <migrate-state-v8-0-4578fb016965@secunet.com>
When migrating between different address families, xfrm_addr_equal()
cannot meaningfully compare addresses, different lengths.
Only call xfrm_addr_equal() when families match, and take
the xfrm_state_insert() path when addresses are equal.
Fixes: 80c9abaabf42 ("[XFRM]: Extension for dynamic update of endpoint address(es)")
Signed-off-by: Antony Antony <antony.antony@secunet.com>
---
v5->v6: added this patch
---
net/xfrm/xfrm_state.c | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c
index 85fd80520184..327a855253e6 100644
--- a/net/xfrm/xfrm_state.c
+++ b/net/xfrm/xfrm_state.c
@@ -2159,10 +2159,11 @@ int xfrm_state_migrate_install(const struct xfrm_state *x,
struct xfrm_user_offload *xuo,
struct netlink_ext_ack *extack)
{
- if (xfrm_addr_equal(&x->id.daddr, &m->new_daddr, m->new_family)) {
+ if (m->new_family == m->old_family &&
+ xfrm_addr_equal(&x->id.daddr, &m->new_daddr, m->new_family)) {
/*
- * Care is needed when the destination address
- * of the state is to be updated as it is a part of triplet.
+ * Care is needed when the destination address of the state is
+ * to be updated as it is a part of triplet.
*/
xfrm_state_insert(xc);
} else {
--
2.47.3
^ permalink raw reply related
* [PATCH ipsec-next v8 08/14] xfrm: add state synchronization after migration
From: Antony Antony @ 2026-05-05 4:33 UTC (permalink / raw)
To: Antony Antony, Steffen Klassert, Herbert Xu, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Simon Horman,
David Ahern, Masahide NAKAMURA, Paul Moore, Stephen Smalley,
Ondrej Mosnacek, Jonathan Corbet, Shuah Khan
Cc: Sabrina Dubroca, netdev, linux-kernel, selinux, linux-doc,
Chiachang Wang, Yan Yan, devel
In-Reply-To: <migrate-state-v8-0-4578fb016965@secunet.com>
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>
---
v6->v7: - rephrase commit message
v5->v6: - move the sync before install to avoid overwriting
v4->v5: - added this patch
---
include/net/xfrm.h | 46 +++++++++++++++++++++++++++++++++++++---------
net/xfrm/xfrm_state.c | 11 ++++-------
2 files changed, 41 insertions(+), 16 deletions(-)
diff --git a/include/net/xfrm.h b/include/net/xfrm.h
index 4137986f15e2..be22c26e4661 100644
--- a/include/net/xfrm.h
+++ b/include/net/xfrm.h
@@ -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);
diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c
index 327a855253e6..fcf6f0c6400d 100644
--- a/net/xfrm/xfrm_state.c
+++ b/net/xfrm/xfrm_state.c
@@ -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;
--
2.47.3
^ permalink raw reply related
* [PATCH ipsec-next v8 09/14] xfrm: add error messages to state migration
From: Antony Antony @ 2026-05-05 4:33 UTC (permalink / raw)
To: Antony Antony, Steffen Klassert, Herbert Xu, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Simon Horman,
David Ahern, Masahide NAKAMURA, Paul Moore, Stephen Smalley,
Ondrej Mosnacek, Jonathan Corbet, Shuah Khan
Cc: Sabrina Dubroca, netdev, linux-kernel, selinux, linux-doc,
Chiachang Wang, Yan Yan, devel
In-Reply-To: <migrate-state-v8-0-4578fb016965@secunet.com>
Add descriptive(extack) error messages for all error paths
in state migration. This improves diagnostics by
providing clear feedback when migration fails.
After xfrm_init_state() use NL_SET_ERR_MSG_WEAK() as fallback for
error paths not yet propagating extack e.g. mode_cbs->init_state()
No functional change.
Signed-off-by: Antony Antony <antony.antony@secunet.com>
---
v5->v6: - in case dev_state_add() extack already set
- after xfrm_init_state() use NL_SET_ERR_MSG_WEAK() as fallback
v4->v5: - added this patch
---
net/xfrm/xfrm_state.c | 9 +++++++--
1 file changed, 7 insertions(+), 2 deletions(-)
diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c
index fcf6f0c6400d..1db48ecda80d 100644
--- a/net/xfrm/xfrm_state.c
+++ b/net/xfrm/xfrm_state.c
@@ -2130,11 +2130,15 @@ struct xfrm_state *xfrm_state_migrate_create(struct xfrm_state *x,
struct xfrm_state *xc;
xc = xfrm_state_clone_and_setup(x, encap, m);
- if (!xc)
+ if (!xc) {
+ NL_SET_ERR_MSG(extack, "Failed to clone and setup state");
return NULL;
+ }
- if (xfrm_init_state(xc, extack) < 0)
+ if (xfrm_init_state(xc, extack) < 0) {
+ NL_SET_ERR_MSG_WEAK(extack, "Failed to initialize migrated state");
goto error;
+ }
/* configure the hardware if offload is requested */
if (xuo && xfrm_dev_state_add(net, xc, xuo, extack))
@@ -2163,6 +2167,7 @@ int xfrm_state_migrate_install(const struct xfrm_state *x,
xfrm_state_insert(xc);
} else {
if (xfrm_state_add(xc) < 0) {
+ NL_SET_ERR_MSG(extack, "Failed to add migrated state");
if (xuo)
xfrm_dev_state_delete(xc);
xc->km.state = XFRM_STATE_DEAD;
--
2.47.3
^ permalink raw reply related
* [PATCH ipsec-next v8 10/14] xfrm: move encap and xuo into struct xfrm_migrate
From: Antony Antony @ 2026-05-05 4:34 UTC (permalink / raw)
To: Antony Antony, Steffen Klassert, Herbert Xu, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Simon Horman,
David Ahern, Masahide NAKAMURA, Paul Moore, Stephen Smalley,
Ondrej Mosnacek, Jonathan Corbet, Shuah Khan
Cc: Sabrina Dubroca, netdev, linux-kernel, selinux, linux-doc,
Chiachang Wang, Yan Yan, devel
In-Reply-To: <migrate-state-v8-0-4578fb016965@secunet.com>
In preparation for an upcoming patch, move the xfrm_encap_tmpl and
xfrm_user_offload pointers from separate parameters into struct
xfrm_migrate, reducing the parameter count of
xfrm_state_migrate_create(), xfrm_state_migrate_install(), and
xfrm_state_migrate().
The fields are placed after the four xfrm_address_t members where
the struct is naturally 8-byte aligned, avoiding padding.
No functional change.
Signed-off-by: Antony Antony <antony.antony@secunet.com>
---
v5->v6: added this patch.
---
include/net/xfrm.h | 7 ++-----
net/xfrm/xfrm_policy.c | 4 +++-
net/xfrm/xfrm_state.c | 20 +++++++-------------
3 files changed, 12 insertions(+), 19 deletions(-)
diff --git a/include/net/xfrm.h b/include/net/xfrm.h
index be22c26e4661..4b29ab92c2a7 100644
--- a/include/net/xfrm.h
+++ b/include/net/xfrm.h
@@ -682,6 +682,8 @@ struct xfrm_migrate {
xfrm_address_t old_saddr;
xfrm_address_t new_daddr;
xfrm_address_t new_saddr;
+ struct xfrm_encap_tmpl *encap;
+ struct xfrm_user_offload *xuo;
u8 proto;
u8 mode;
u16 reserved;
@@ -1897,20 +1899,15 @@ struct xfrm_state *xfrm_migrate_state_find(struct xfrm_migrate *m, struct net *n
u32 if_id);
struct xfrm_state *xfrm_state_migrate_create(struct xfrm_state *x,
const struct xfrm_migrate *m,
- const struct xfrm_encap_tmpl *encap,
struct net *net,
- struct xfrm_user_offload *xuo,
struct netlink_ext_ack *extack);
int xfrm_state_migrate_install(const struct xfrm_state *x,
struct xfrm_state *xc,
const struct xfrm_migrate *m,
- struct xfrm_user_offload *xuo,
struct netlink_ext_ack *extack);
struct xfrm_state *xfrm_state_migrate(struct xfrm_state *x,
struct xfrm_migrate *m,
- struct xfrm_encap_tmpl *encap,
struct net *net,
- struct xfrm_user_offload *xuo,
struct netlink_ext_ack *extack);
int xfrm_migrate(const struct xfrm_selector *sel, u8 dir, u8 type,
struct xfrm_migrate *m, int num_bundles,
diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c
index fd505adf080e..cf05d778e2dd 100644
--- a/net/xfrm/xfrm_policy.c
+++ b/net/xfrm/xfrm_policy.c
@@ -4680,7 +4680,9 @@ int xfrm_migrate(const struct xfrm_selector *sel, u8 dir, u8 type,
if ((x = xfrm_migrate_state_find(mp, net, if_id))) {
x_cur[nx_cur] = x;
nx_cur++;
- xc = xfrm_state_migrate(x, mp, encap, net, xuo, extack);
+ mp->encap = encap;
+ mp->xuo = xuo;
+ xc = xfrm_state_migrate(x, mp, net, extack);
if (xc) {
x_new[nx_new] = xc;
nx_new++;
diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c
index 1db48ecda80d..043e573c4f32 100644
--- a/net/xfrm/xfrm_state.c
+++ b/net/xfrm/xfrm_state.c
@@ -1966,7 +1966,6 @@ static inline int clone_security(struct xfrm_state *x, struct xfrm_sec_ctx *secu
}
static struct xfrm_state *xfrm_state_clone_and_setup(struct xfrm_state *orig,
- const struct xfrm_encap_tmpl *encap,
const struct xfrm_migrate *m)
{
struct net *net = xs_net(orig);
@@ -2008,8 +2007,8 @@ static struct xfrm_state *xfrm_state_clone_and_setup(struct xfrm_state *orig,
}
x->props.calgo = orig->props.calgo;
- if (encap) {
- x->encap = kmemdup(encap, sizeof(*x->encap), GFP_KERNEL);
+ if (m->encap) {
+ x->encap = kmemdup(m->encap, sizeof(*x->encap), GFP_KERNEL);
if (!x->encap)
goto error;
x->mapping_maxage = orig->mapping_maxage;
@@ -2122,14 +2121,12 @@ EXPORT_SYMBOL(xfrm_migrate_state_find);
struct xfrm_state *xfrm_state_migrate_create(struct xfrm_state *x,
const struct xfrm_migrate *m,
- const struct xfrm_encap_tmpl *encap,
struct net *net,
- struct xfrm_user_offload *xuo,
struct netlink_ext_ack *extack)
{
struct xfrm_state *xc;
- xc = xfrm_state_clone_and_setup(x, encap, m);
+ xc = xfrm_state_clone_and_setup(x, m);
if (!xc) {
NL_SET_ERR_MSG(extack, "Failed to clone and setup state");
return NULL;
@@ -2141,7 +2138,7 @@ struct xfrm_state *xfrm_state_migrate_create(struct xfrm_state *x,
}
/* configure the hardware if offload is requested */
- if (xuo && xfrm_dev_state_add(net, xc, xuo, extack))
+ if (m->xuo && xfrm_dev_state_add(net, xc, m->xuo, extack))
goto error;
return xc;
@@ -2155,7 +2152,6 @@ EXPORT_SYMBOL(xfrm_state_migrate_create);
int xfrm_state_migrate_install(const struct xfrm_state *x,
struct xfrm_state *xc,
const struct xfrm_migrate *m,
- struct xfrm_user_offload *xuo,
struct netlink_ext_ack *extack)
{
if (m->new_family == m->old_family &&
@@ -2168,7 +2164,7 @@ int xfrm_state_migrate_install(const struct xfrm_state *x,
} else {
if (xfrm_state_add(xc) < 0) {
NL_SET_ERR_MSG(extack, "Failed to add migrated state");
- if (xuo)
+ if (m->xuo)
xfrm_dev_state_delete(xc);
xc->km.state = XFRM_STATE_DEAD;
xfrm_state_put(xc);
@@ -2182,20 +2178,18 @@ EXPORT_SYMBOL(xfrm_state_migrate_install);
struct xfrm_state *xfrm_state_migrate(struct xfrm_state *x,
struct xfrm_migrate *m,
- struct xfrm_encap_tmpl *encap,
struct net *net,
- struct xfrm_user_offload *xuo,
struct netlink_ext_ack *extack)
{
struct xfrm_state *xc;
- xc = xfrm_state_migrate_create(x, m, encap, net, xuo, extack);
+ xc = xfrm_state_migrate_create(x, m, net, extack);
if (!xc)
return NULL;
xfrm_migrate_sync(xc, x);
- if (xfrm_state_migrate_install(x, xc, m, xuo, extack) < 0)
+ if (xfrm_state_migrate_install(x, xc, m, extack) < 0)
return NULL;
return xc;
--
2.47.3
^ permalink raw reply related
* [PATCH ipsec-next v8 11/14] xfrm: refactor XFRMA_MTIMER_THRESH validation into a helper
From: Antony Antony @ 2026-05-05 4:34 UTC (permalink / raw)
To: Antony Antony, Steffen Klassert, Herbert Xu, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Simon Horman,
David Ahern, Masahide NAKAMURA, Paul Moore, Stephen Smalley,
Ondrej Mosnacek, Jonathan Corbet, Shuah Khan
Cc: Sabrina Dubroca, netdev, linux-kernel, selinux, linux-doc,
Chiachang Wang, Yan Yan, devel
In-Reply-To: <migrate-state-v8-0-4578fb016965@secunet.com>
Extract verify_mtimer_thresh() to consolidate the XFRMA_MTIMER_THRESH
validation logic shared between the add_sa and upcoming patch.
Signed-off-by: Antony Antony <antony.antony@secunet.com>
---
v5->v6: added this patch
---
net/xfrm/xfrm_user.c | 29 ++++++++++++++++++-----------
1 file changed, 18 insertions(+), 11 deletions(-)
diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c
index 273ea6fdb8ad..03fa4cabf601 100644
--- a/net/xfrm/xfrm_user.c
+++ b/net/xfrm/xfrm_user.c
@@ -248,6 +248,22 @@ static inline int verify_replay(struct xfrm_usersa_info *p,
return 0;
}
+static int verify_mtimer_thresh(bool has_encap, u8 dir,
+ struct netlink_ext_ack *extack)
+{
+ if (!has_encap) {
+ NL_SET_ERR_MSG(extack,
+ "MTIMER_THRESH requires encapsulation");
+ return -EINVAL;
+ }
+ if (dir == XFRM_SA_DIR_OUT) {
+ NL_SET_ERR_MSG(extack,
+ "MTIMER_THRESH should not be set on output SA");
+ return -EINVAL;
+ }
+ return 0;
+}
+
static int verify_newsa_info(struct xfrm_usersa_info *p,
struct nlattr **attrs,
struct netlink_ext_ack *extack)
@@ -455,18 +471,9 @@ static int verify_newsa_info(struct xfrm_usersa_info *p,
err = 0;
if (attrs[XFRMA_MTIMER_THRESH]) {
- if (!attrs[XFRMA_ENCAP]) {
- NL_SET_ERR_MSG(extack, "MTIMER_THRESH attribute can only be set on ENCAP states");
- err = -EINVAL;
- goto out;
- }
-
- if (sa_dir == XFRM_SA_DIR_OUT) {
- NL_SET_ERR_MSG(extack,
- "MTIMER_THRESH attribute should not be set on output SA");
- err = -EINVAL;
+ err = verify_mtimer_thresh(!!attrs[XFRMA_ENCAP], sa_dir, extack);
+ if (err)
goto out;
- }
}
if (sa_dir == XFRM_SA_DIR_OUT) {
--
2.47.3
^ permalink raw reply related
* [PATCH ipsec-next v8 12/14] xfrm: add XFRM_MSG_MIGRATE_STATE for single SA migration
From: Antony Antony @ 2026-05-05 4:34 UTC (permalink / raw)
To: Antony Antony, Steffen Klassert, Herbert Xu, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Simon Horman,
David Ahern, Masahide NAKAMURA, Paul Moore, Stephen Smalley,
Ondrej Mosnacek, Jonathan Corbet, Shuah Khan
Cc: Sabrina Dubroca, netdev, linux-kernel, selinux, linux-doc,
Chiachang Wang, Yan Yan, devel
In-Reply-To: <migrate-state-v8-0-4578fb016965@secunet.com>
Add a new netlink method to migrate a single xfrm_state.
Unlike the existing migration mechanism (SA + policy), this
supports migrating only the SA and allows changing the reqid.
The SA is looked up via xfrm_usersa_id, which uniquely
identifies it, so old_saddr is not needed. old_daddr is carried in
xfrm_usersa_id.daddr.
The reqid is invariant in the old migration.
Signed-off-by: Antony Antony <antony.antony@secunet.com>
---
v7->v8: - removed the unknown-flags validation block
v6->v7: - add flags field to xfrm_user_migrate_state (based on Sabrina's feedback)
- add XFRM_MIGRATE_STATE_NO_OFFLOAD (bit 0): suppresses offload
- omit-to-inherit; mutually exclusive with XFRMA_OFFLOAD_DEV
- zero-initialize struct xfrm_migrate m[XFRM_MAX_DEPTH]
- add struct xfrm_selector new_sel to xfrm_user_migrate_state
- add XFRM_MIGRATE_STATE_UPDATE_SEL: derive new selector
from SA addresses when old selector is a single-host match
v5->v6: - (Feedback from Sabrina's review)
- reqid change: use xfrm_state_add, not xfrm_state_insert
- encap and xuo: use nla_data() directly, no kmemdup needed
- notification failure is non-fatal: set extack warning, return 0
- drop state direction, x->dir, check, not required
- reverse xmas tree local variable ordering
- use NL_SET_ERR_MSG_WEAK for clone failure message
- fix implicit padding in xfrm_user_migrate_state uapi struct
- support XFRMA_SET_MARK/XFRMA_SET_MARK_MASK in XFRM_MSG_MIGRATE_STATE
v4->v5: - set portid, seq in XFRM_MSG_MIGRATE_STATE netlink notification
- rename error label to out for clarity
- add locking and synchronize after cloning
- change some if(x) to if(!x) for clarity
- call __xfrm_state_delete() inside the lock
- return error from xfrm_send_migrate_state() instead of always returning 0
v3->v4: preserve reqid invariant for each state migrated
v2->v3: free the skb on the error path
v1->v2: merged next patch here to fix use uninitialized value
- removed unnecessary inline
- added const when possible
---
include/net/xfrm.h | 16 ++-
include/uapi/linux/xfrm.h | 21 ++++
net/xfrm/xfrm_device.c | 2 +-
net/xfrm/xfrm_policy.c | 19 +++
net/xfrm/xfrm_state.c | 29 +++--
net/xfrm/xfrm_user.c | 281 +++++++++++++++++++++++++++++++++++++++++++-
security/selinux/nlmsgtab.c | 3 +-
7 files changed, 357 insertions(+), 14 deletions(-)
diff --git a/include/net/xfrm.h b/include/net/xfrm.h
index 4b29ab92c2a7..e33e524cd909 100644
--- a/include/net/xfrm.h
+++ b/include/net/xfrm.h
@@ -684,12 +684,20 @@ struct xfrm_migrate {
xfrm_address_t new_saddr;
struct xfrm_encap_tmpl *encap;
struct xfrm_user_offload *xuo;
+ struct xfrm_mark old_mark;
+ struct xfrm_mark *new_mark;
+ struct xfrm_mark smark;
u8 proto;
u8 mode;
- u16 reserved;
+ u16 msg_type; /* XFRM_MSG_MIGRATE or XFRM_MSG_MIGRATE_STATE */
+ u32 flags;
u32 old_reqid;
+ u32 new_reqid;
+ u32 nat_keepalive_interval;
+ u32 mapping_maxage;
u16 old_family;
u16 new_family;
+ const struct xfrm_selector *new_sel;
};
#define XFRM_KM_TIMEOUT 30
@@ -2104,7 +2112,7 @@ void xfrm_dev_resume(struct sk_buff *skb);
void xfrm_dev_backlog(struct softnet_data *sd);
struct sk_buff *validate_xmit_xfrm(struct sk_buff *skb, netdev_features_t features, bool *again);
int xfrm_dev_state_add(struct net *net, struct xfrm_state *x,
- struct xfrm_user_offload *xuo,
+ const struct xfrm_user_offload *xuo,
struct netlink_ext_ack *extack);
int xfrm_dev_policy_add(struct net *net, struct xfrm_policy *xp,
struct xfrm_user_offload *xuo, u8 dir,
@@ -2175,7 +2183,9 @@ static inline struct sk_buff *validate_xmit_xfrm(struct sk_buff *skb, netdev_fea
return skb;
}
-static inline int xfrm_dev_state_add(struct net *net, struct xfrm_state *x, struct xfrm_user_offload *xuo, struct netlink_ext_ack *extack)
+static inline int xfrm_dev_state_add(struct net *net, struct xfrm_state *x,
+ const struct xfrm_user_offload *xuo,
+ struct netlink_ext_ack *extack)
{
return 0;
}
diff --git a/include/uapi/linux/xfrm.h b/include/uapi/linux/xfrm.h
index a23495c0e0a1..34d8ad5c4818 100644
--- a/include/uapi/linux/xfrm.h
+++ b/include/uapi/linux/xfrm.h
@@ -227,6 +227,9 @@ enum {
#define XFRM_MSG_SETDEFAULT XFRM_MSG_SETDEFAULT
XFRM_MSG_GETDEFAULT,
#define XFRM_MSG_GETDEFAULT XFRM_MSG_GETDEFAULT
+
+ XFRM_MSG_MIGRATE_STATE,
+#define XFRM_MSG_MIGRATE_STATE XFRM_MSG_MIGRATE_STATE
__XFRM_MSG_MAX
};
#define XFRM_MSG_MAX (__XFRM_MSG_MAX - 1)
@@ -507,6 +510,24 @@ struct xfrm_user_migrate {
__u16 new_family;
};
+struct xfrm_user_migrate_state {
+ struct xfrm_usersa_id id;
+ xfrm_address_t new_daddr;
+ xfrm_address_t new_saddr;
+ struct xfrm_mark old_mark;
+ struct xfrm_selector new_sel;
+ __u32 new_reqid;
+ __u32 flags;
+ __u16 new_family;
+ __u16 reserved;
+};
+
+/* Flags for xfrm_user_migrate_state.flags */
+enum xfrm_migrate_state_flags {
+ XFRM_MIGRATE_STATE_NO_OFFLOAD = 1, /* do not inherit offload from existing SA */
+ XFRM_MIGRATE_STATE_UPDATE_SEL = 2, /* update host-to-host selector from saddr and daddr */
+};
+
struct xfrm_user_mapping {
struct xfrm_usersa_id id;
__u32 reqid;
diff --git a/net/xfrm/xfrm_device.c b/net/xfrm/xfrm_device.c
index 550457e4c4f0..630f3dd31cc5 100644
--- a/net/xfrm/xfrm_device.c
+++ b/net/xfrm/xfrm_device.c
@@ -229,7 +229,7 @@ struct sk_buff *validate_xmit_xfrm(struct sk_buff *skb, netdev_features_t featur
EXPORT_SYMBOL_GPL(validate_xmit_xfrm);
int xfrm_dev_state_add(struct net *net, struct xfrm_state *x,
- struct xfrm_user_offload *xuo,
+ const struct xfrm_user_offload *xuo,
struct netlink_ext_ack *extack)
{
int err;
diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c
index cf05d778e2dd..9ecc4c8ba693 100644
--- a/net/xfrm/xfrm_policy.c
+++ b/net/xfrm/xfrm_policy.c
@@ -4643,6 +4643,22 @@ static int xfrm_migrate_check(const struct xfrm_migrate *m, int num_migrate,
return 0;
}
+/*
+ * Fill migrate fields that are invariant in XFRM_MSG_MIGRATE: inherited
+ * from the existing SA unchanged. XFRM_MSG_MIGRATE_STATE can update these.
+ */
+static void xfrm_migrate_copy_old(struct xfrm_migrate *mp,
+ const struct xfrm_state *x,
+ struct xfrm_mark *new_mark_buf)
+{
+ mp->smark = x->props.smark;
+ mp->new_reqid = x->props.reqid;
+ mp->nat_keepalive_interval = x->nat_keepalive_interval;
+ mp->mapping_maxage = x->mapping_maxage;
+ *new_mark_buf = x->mark;
+ mp->new_mark = new_mark_buf;
+}
+
int xfrm_migrate(const struct xfrm_selector *sel, u8 dir, u8 type,
struct xfrm_migrate *m, int num_migrate,
struct xfrm_kmaddress *k, struct net *net,
@@ -4650,6 +4666,7 @@ int xfrm_migrate(const struct xfrm_selector *sel, u8 dir, u8 type,
struct netlink_ext_ack *extack, struct xfrm_user_offload *xuo)
{
int i, err, nx_cur = 0, nx_new = 0;
+ struct xfrm_mark new_marks[XFRM_MAX_DEPTH] = {};
struct xfrm_policy *pol = NULL;
struct xfrm_state *x, *xc;
struct xfrm_state *x_cur[XFRM_MAX_DEPTH];
@@ -4682,6 +4699,8 @@ int xfrm_migrate(const struct xfrm_selector *sel, u8 dir, u8 type,
nx_cur++;
mp->encap = encap;
mp->xuo = xuo;
+ xfrm_migrate_copy_old(mp, x, &new_marks[i]);
+
xc = xfrm_state_migrate(x, mp, net, extack);
if (xc) {
x_new[nx_new] = xc;
diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c
index 043e573c4f32..44244bd323ea 100644
--- a/net/xfrm/xfrm_state.c
+++ b/net/xfrm/xfrm_state.c
@@ -1974,11 +1974,25 @@ static struct xfrm_state *xfrm_state_clone_and_setup(struct xfrm_state *orig,
goto out;
memcpy(&x->id, &orig->id, sizeof(x->id));
- memcpy(&x->sel, &orig->sel, sizeof(x->sel));
+ if (m->msg_type == XFRM_MSG_MIGRATE_STATE) {
+ if (m->flags & XFRM_MIGRATE_STATE_UPDATE_SEL) {
+ u8 prefixlen = (m->new_family == AF_INET6) ? 128 : 32;
+
+ memcpy(&x->sel, &orig->sel, sizeof(x->sel));
+ x->sel.family = m->new_family;
+ x->sel.prefixlen_d = prefixlen;
+ x->sel.prefixlen_s = prefixlen;
+ memcpy(&x->sel.daddr, &m->new_daddr, sizeof(x->sel.daddr));
+ memcpy(&x->sel.saddr, &m->new_saddr, sizeof(x->sel.saddr));
+ } else {
+ x->sel = *m->new_sel;
+ }
+ } else {
+ memcpy(&x->sel, &orig->sel, sizeof(x->sel));
+ }
memcpy(&x->lft, &orig->lft, sizeof(x->lft));
x->props.mode = orig->props.mode;
x->props.replay_window = orig->props.replay_window;
- x->props.reqid = orig->props.reqid;
if (orig->aalg) {
x->aalg = xfrm_algo_auth_clone(orig->aalg);
@@ -2011,8 +2025,8 @@ static struct xfrm_state *xfrm_state_clone_and_setup(struct xfrm_state *orig,
x->encap = kmemdup(m->encap, sizeof(*x->encap), GFP_KERNEL);
if (!x->encap)
goto error;
- x->mapping_maxage = orig->mapping_maxage;
- x->nat_keepalive_interval = orig->nat_keepalive_interval;
+ x->mapping_maxage = m->mapping_maxage;
+ x->nat_keepalive_interval = m->nat_keepalive_interval;
}
if (orig->security)
@@ -2029,8 +2043,9 @@ static struct xfrm_state *xfrm_state_clone_and_setup(struct xfrm_state *orig,
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));
+ x->mark = m->new_mark ? *m->new_mark : m->old_mark;
+
+ x->props.smark = m->smark;
x->props.flags = orig->props.flags;
x->props.extra_flags = orig->props.extra_flags;
@@ -2053,7 +2068,7 @@ static struct xfrm_state *xfrm_state_clone_and_setup(struct xfrm_state *orig,
goto error;
}
-
+ x->props.reqid = m->new_reqid;
x->props.family = m->new_family;
memcpy(&x->id.daddr, &m->new_daddr, sizeof(x->id.daddr));
memcpy(&x->props.saddr, &m->new_saddr, sizeof(x->props.saddr));
diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c
index 03fa4cabf601..a49edf7d6f78 100644
--- a/net/xfrm/xfrm_user.c
+++ b/net/xfrm/xfrm_user.c
@@ -1327,7 +1327,7 @@ static int copy_to_user_encap(struct xfrm_encap_tmpl *ep, struct sk_buff *skb)
return 0;
}
-static int xfrm_smark_put(struct sk_buff *skb, struct xfrm_mark *m)
+static int xfrm_smark_put(struct sk_buff *skb, const struct xfrm_mark *m)
{
int ret = 0;
@@ -3076,6 +3076,25 @@ static int xfrm_add_acquire(struct sk_buff *skb, struct nlmsghdr *nlh,
}
#ifdef CONFIG_XFRM_MIGRATE
+static void copy_from_user_migrate_state(struct xfrm_migrate *ma,
+ const struct xfrm_user_migrate_state *um)
+{
+ memcpy(&ma->old_daddr, &um->id.daddr, sizeof(ma->old_daddr));
+ memcpy(&ma->new_daddr, &um->new_daddr, sizeof(ma->new_daddr));
+ memcpy(&ma->new_saddr, &um->new_saddr, sizeof(ma->new_saddr));
+
+ ma->proto = um->id.proto;
+ ma->new_reqid = um->new_reqid;
+
+ ma->old_family = um->id.family;
+ ma->new_family = um->new_family;
+
+ ma->old_mark = um->old_mark;
+ ma->flags = um->flags;
+ ma->new_sel = &um->new_sel;
+ ma->msg_type = XFRM_MSG_MIGRATE_STATE;
+}
+
static int copy_from_user_migrate(struct xfrm_migrate *ma,
struct xfrm_kmaddress *k,
struct nlattr **attrs, int *num,
@@ -3115,6 +3134,7 @@ static int copy_from_user_migrate(struct xfrm_migrate *ma,
ma->old_family = um->old_family;
ma->new_family = um->new_family;
+ ma->msg_type = XFRM_MSG_MIGRATE;
}
*num = i;
@@ -3125,7 +3145,7 @@ static int xfrm_do_migrate(struct sk_buff *skb, struct nlmsghdr *nlh,
struct nlattr **attrs, struct netlink_ext_ack *extack)
{
struct xfrm_userpolicy_id *pi = nlmsg_data(nlh);
- struct xfrm_migrate m[XFRM_MAX_DEPTH];
+ struct xfrm_migrate m[XFRM_MAX_DEPTH] = {};
struct xfrm_kmaddress km, *kmp;
u8 type;
int err;
@@ -3178,7 +3198,262 @@ static int xfrm_do_migrate(struct sk_buff *skb, struct nlmsghdr *nlh,
kfree(xuo);
return err;
}
+
+static int build_migrate_state(struct sk_buff *skb,
+ const struct xfrm_user_migrate_state *um,
+ const struct xfrm_migrate *m,
+ u8 dir, u32 portid, u32 seq)
+{
+ int err;
+ struct nlmsghdr *nlh;
+ struct xfrm_user_migrate_state *hdr;
+
+ nlh = nlmsg_put(skb, portid, seq, XFRM_MSG_MIGRATE_STATE,
+ sizeof(struct xfrm_user_migrate_state), 0);
+ if (!nlh)
+ return -EMSGSIZE;
+
+ hdr = nlmsg_data(nlh);
+ *hdr = *um;
+ hdr->new_sel = *m->new_sel;
+
+ if (m->encap) {
+ err = nla_put(skb, XFRMA_ENCAP, sizeof(*m->encap), m->encap);
+ if (err)
+ goto out_cancel;
+ }
+
+ if (m->xuo) {
+ err = nla_put(skb, XFRMA_OFFLOAD_DEV, sizeof(*m->xuo), m->xuo);
+ if (err)
+ goto out_cancel;
+ }
+
+ if (m->new_mark) {
+ err = nla_put(skb, XFRMA_MARK, sizeof(*m->new_mark),
+ m->new_mark);
+ if (err)
+ goto out_cancel;
+ }
+
+ err = xfrm_smark_put(skb, &m->smark);
+ if (err)
+ goto out_cancel;
+
+ if (m->mapping_maxage) {
+ err = nla_put_u32(skb, XFRMA_MTIMER_THRESH, m->mapping_maxage);
+ if (err)
+ goto out_cancel;
+ }
+
+ if (m->nat_keepalive_interval) {
+ err = nla_put_u32(skb, XFRMA_NAT_KEEPALIVE_INTERVAL,
+ m->nat_keepalive_interval);
+ if (err)
+ goto out_cancel;
+ }
+
+ if (dir) {
+ err = nla_put_u8(skb, XFRMA_SA_DIR, dir);
+ if (err)
+ goto out_cancel;
+ }
+
+ nlmsg_end(skb, nlh);
+ return 0;
+
+out_cancel:
+ nlmsg_cancel(skb, nlh);
+ return err;
+}
+
+static unsigned int xfrm_migrate_state_msgsize(const struct xfrm_migrate *m,
+ u8 dir)
+{
+ return NLMSG_ALIGN(sizeof(struct xfrm_user_migrate_state)) +
+ (m->encap ? nla_total_size(sizeof(struct xfrm_encap_tmpl)) : 0) +
+ (m->xuo ? nla_total_size(sizeof(struct xfrm_user_offload)) : 0) +
+ (m->new_mark ? nla_total_size(sizeof(struct xfrm_mark)) : 0) +
+ (m->smark.v ? nla_total_size(sizeof(u32)) * 2 : 0) + /* SET_MARK + SET_MARK_MASK */
+ (m->mapping_maxage ? nla_total_size(sizeof(u32)) : 0) +
+ (m->nat_keepalive_interval ? nla_total_size(sizeof(u32)) : 0) +
+ (dir ? nla_total_size(sizeof(u8)) : 0); /* XFRMA_SA_DIR */
+}
+
+static int xfrm_send_migrate_state(const struct xfrm_user_migrate_state *um,
+ const struct xfrm_migrate *m,
+ u8 dir, u32 portid, u32 seq)
+{
+ int err;
+ struct sk_buff *skb;
+ struct net *net = &init_net;
+
+ skb = nlmsg_new(xfrm_migrate_state_msgsize(m, dir), GFP_ATOMIC);
+ if (!skb)
+ return -ENOMEM;
+
+ err = build_migrate_state(skb, um, m, dir, portid, seq);
+ if (err < 0) {
+ kfree_skb(skb);
+ return err;
+ }
+
+ return xfrm_nlmsg_multicast(net, skb, 0, XFRMNLGRP_MIGRATE);
+}
+
+static int xfrm_do_migrate_state(struct sk_buff *skb, struct nlmsghdr *nlh,
+ struct nlattr **attrs, struct netlink_ext_ack *extack)
+{
+ struct xfrm_user_migrate_state *um = nlmsg_data(nlh);
+ struct net *net = sock_net(skb->sk);
+ struct xfrm_user_offload xuo = {};
+ struct xfrm_migrate m = {};
+ struct xfrm_state *xc;
+ struct xfrm_state *x;
+ int err;
+
+ if (!um->id.spi) {
+ NL_SET_ERR_MSG(extack, "Invalid SPI 0x0");
+ return -EINVAL;
+ }
+
+ if (um->reserved) {
+ NL_SET_ERR_MSG(extack, "Reserved field must be zero");
+ return -EINVAL;
+ }
+
+ if ((um->flags & XFRM_MIGRATE_STATE_NO_OFFLOAD) &&
+ attrs[XFRMA_OFFLOAD_DEV]) {
+ NL_SET_ERR_MSG(extack,
+ "XFRM_MIGRATE_STATE_NO_OFFLOAD and XFRMA_OFFLOAD_DEV are mutually exclusive");
+ return -EINVAL;
+ }
+
+ copy_from_user_migrate_state(&m, um);
+
+ x = xfrm_state_lookup(net, m.old_mark.v & m.old_mark.m,
+ &um->id.daddr, um->id.spi,
+ um->id.proto, um->id.family);
+ if (!x) {
+ NL_SET_ERR_MSG(extack, "Can not find state");
+ return -ESRCH;
+ }
+
+ if (um->flags & XFRM_MIGRATE_STATE_UPDATE_SEL) {
+ u8 prefixlen = (x->sel.family == AF_INET6) ? 128 : 32;
+
+ if (x->sel.prefixlen_s != x->sel.prefixlen_d ||
+ x->sel.prefixlen_d != prefixlen ||
+ !xfrm_addr_equal(&x->sel.daddr, &x->id.daddr, x->sel.family) ||
+ !xfrm_addr_equal(&x->sel.saddr, &x->props.saddr, x->sel.family)) {
+ NL_SET_ERR_MSG(extack,
+ "SA selector is not a single-host match for SA addresses");
+ err = -EINVAL;
+ goto out;
+ }
+ }
+
+ if (attrs[XFRMA_ENCAP]) {
+ m.encap = nla_data(attrs[XFRMA_ENCAP]);
+ if (m.encap->encap_type == 0) {
+ m.encap = NULL; /* sentinel: remove encap */
+ } else if (m.encap->encap_type != UDP_ENCAP_ESPINUDP) {
+ NL_SET_ERR_MSG(extack, "Unsupported encapsulation type");
+ err = -EINVAL;
+ goto out;
+ }
+ } else {
+ m.encap = x->encap; /* omit-to-inherit */
+ }
+
+ if (attrs[XFRMA_MTIMER_THRESH]) {
+ err = verify_mtimer_thresh(!!m.encap, x->dir, extack);
+ if (err)
+ goto out;
+ }
+
+ if (attrs[XFRMA_NAT_KEEPALIVE_INTERVAL] &&
+ nla_get_u32(attrs[XFRMA_NAT_KEEPALIVE_INTERVAL]) && !m.encap) {
+ NL_SET_ERR_MSG(extack,
+ "NAT_KEEPALIVE_INTERVAL requires encapsulation");
+ err = -EINVAL;
+ goto out;
+ }
+
+ if (attrs[XFRMA_OFFLOAD_DEV]) {
+ m.xuo = nla_data(attrs[XFRMA_OFFLOAD_DEV]);
+ } else if (!(um->flags & XFRM_MIGRATE_STATE_NO_OFFLOAD) && x->xso.dev) {
+ xuo.ifindex = x->xso.dev->ifindex;
+ if (x->xso.dir == XFRM_DEV_OFFLOAD_IN)
+ xuo.flags = XFRM_OFFLOAD_INBOUND;
+ if (x->xso.type == XFRM_DEV_OFFLOAD_PACKET)
+ xuo.flags |= XFRM_OFFLOAD_PACKET;
+ m.xuo = &xuo;
+ }
+
+ if (attrs[XFRMA_MARK])
+ m.new_mark = nla_data(attrs[XFRMA_MARK]);
+
+ if (attrs[XFRMA_SET_MARK])
+ xfrm_smark_init(attrs, &m.smark);
+ else
+ m.smark = x->props.smark;
+
+ m.mapping_maxage = attrs[XFRMA_MTIMER_THRESH] ?
+ nla_get_u32(attrs[XFRMA_MTIMER_THRESH]) : x->mapping_maxage;
+ m.nat_keepalive_interval = attrs[XFRMA_NAT_KEEPALIVE_INTERVAL] ?
+ nla_get_u32(attrs[XFRMA_NAT_KEEPALIVE_INTERVAL]) :
+ x->nat_keepalive_interval;
+
+ xc = xfrm_state_migrate_create(x, &m, net, extack);
+ if (!xc) {
+ NL_SET_ERR_MSG_WEAK(extack, "State migration clone failed");
+ err = -EINVAL;
+ goto out;
+ }
+
+ spin_lock_bh(&x->lock);
+ xfrm_migrate_sync(xc, x); /* to prevent SN/IV reuse */
+ __xfrm_state_delete(x);
+ spin_unlock_bh(&x->lock);
+
+ err = xfrm_state_migrate_install(x, xc, &m, extack);
+ if (err < 0) {
+ /*
+ * In this rare case both the old SA and the new SA
+ * will disappear.
+ * Alternatives risk duplicate SN/IV usage which must not occur.
+ * Userspace must handle this error, -EEXIST.
+ */
+ goto out;
+ }
+
+ /* Restore encap cleared by sentinel (type=0) during migration. */
+ if (attrs[XFRMA_ENCAP])
+ m.encap = nla_data(attrs[XFRMA_ENCAP]);
+
+ m.new_sel = &xc->sel;
+
+ err = xfrm_send_migrate_state(um, &m, xc->dir,
+ nlh->nlmsg_pid, nlh->nlmsg_seq);
+ if (err < 0) {
+ NL_SET_ERR_MSG(extack, "Failed to send migration notification");
+ err = 0;
+ }
+
+out:
+ xfrm_state_put(x);
+ return err;
+}
+
#else
+static int xfrm_do_migrate_state(struct sk_buff *skb, struct nlmsghdr *nlh,
+ struct nlattr **attrs, struct netlink_ext_ack *extack)
+{
+ NL_SET_ERR_MSG(extack, "XFRM_MSG_MIGRATE_STATE is not supported");
+ return -ENOPROTOOPT;
+}
+
static int xfrm_do_migrate(struct sk_buff *skb, struct nlmsghdr *nlh,
struct nlattr **attrs, struct netlink_ext_ack *extack)
{
@@ -3331,6 +3606,7 @@ const int xfrm_msg_min[XFRM_NR_MSGTYPES] = {
[XFRM_MSG_GETSPDINFO - XFRM_MSG_BASE] = sizeof(u32),
[XFRM_MSG_SETDEFAULT - XFRM_MSG_BASE] = XMSGSIZE(xfrm_userpolicy_default),
[XFRM_MSG_GETDEFAULT - XFRM_MSG_BASE] = XMSGSIZE(xfrm_userpolicy_default),
+ [XFRM_MSG_MIGRATE_STATE - XFRM_MSG_BASE] = XMSGSIZE(xfrm_user_migrate_state),
};
EXPORT_SYMBOL_GPL(xfrm_msg_min);
@@ -3424,6 +3700,7 @@ static const struct xfrm_link {
[XFRM_MSG_GETSPDINFO - XFRM_MSG_BASE] = { .doit = xfrm_get_spdinfo },
[XFRM_MSG_SETDEFAULT - XFRM_MSG_BASE] = { .doit = xfrm_set_default },
[XFRM_MSG_GETDEFAULT - XFRM_MSG_BASE] = { .doit = xfrm_get_default },
+ [XFRM_MSG_MIGRATE_STATE - XFRM_MSG_BASE] = { .doit = xfrm_do_migrate_state },
};
static int xfrm_reject_unused_attr(int type, struct nlattr **attrs,
diff --git a/security/selinux/nlmsgtab.c b/security/selinux/nlmsgtab.c
index 2c0b07f9fbbd..655d2616c9d2 100644
--- a/security/selinux/nlmsgtab.c
+++ b/security/selinux/nlmsgtab.c
@@ -128,6 +128,7 @@ static const struct nlmsg_perm nlmsg_xfrm_perms[] = {
{ XFRM_MSG_MAPPING, NETLINK_XFRM_SOCKET__NLMSG_READ },
{ XFRM_MSG_SETDEFAULT, NETLINK_XFRM_SOCKET__NLMSG_WRITE },
{ XFRM_MSG_GETDEFAULT, NETLINK_XFRM_SOCKET__NLMSG_READ },
+ { XFRM_MSG_MIGRATE_STATE, NETLINK_XFRM_SOCKET__NLMSG_WRITE },
};
static const struct nlmsg_perm nlmsg_audit_perms[] = {
@@ -203,7 +204,7 @@ int selinux_nlmsg_lookup(u16 sclass, u16 nlmsg_type, u32 *perm)
* structures at the top of this file with the new mappings
* before updating the BUILD_BUG_ON() macro!
*/
- BUILD_BUG_ON(XFRM_MSG_MAX != XFRM_MSG_GETDEFAULT);
+ BUILD_BUG_ON(XFRM_MSG_MAX != XFRM_MSG_MIGRATE_STATE);
if (selinux_policycap_netlink_xperm()) {
*perm = NETLINK_XFRM_SOCKET__NLMSG;
--
2.47.3
^ permalink raw reply related
* [PATCH ipsec-next v8 13/14] xfrm: restrict netlink attributes for XFRM_MSG_MIGRATE_STATE
From: Antony Antony @ 2026-05-05 4:34 UTC (permalink / raw)
To: Antony Antony, Steffen Klassert, Herbert Xu, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Simon Horman,
David Ahern, Masahide NAKAMURA, Paul Moore, Stephen Smalley,
Ondrej Mosnacek, Jonathan Corbet, Shuah Khan
Cc: Sabrina Dubroca, netdev, linux-kernel, selinux, linux-doc,
Chiachang Wang, Yan Yan, devel
In-Reply-To: <migrate-state-v8-0-4578fb016965@secunet.com>
Only accept XFRMA used in this method, reject the rest.
Signed-off-by: Antony Antony <antony.antony@secunet.com>
---
v5->v6: added this patch
---
net/xfrm/xfrm_user.c | 24 ++++++++++++++++++++++++
1 file changed, 24 insertions(+)
diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c
index a49edf7d6f78..c435b38f25bd 100644
--- a/net/xfrm/xfrm_user.c
+++ b/net/xfrm/xfrm_user.c
@@ -3732,6 +3732,30 @@ static int xfrm_reject_unused_attr(int type, struct nlattr **attrs,
}
}
+ if (type == XFRM_MSG_MIGRATE_STATE) {
+ int i;
+
+ for (i = 0; i <= XFRMA_MAX; i++) {
+ if (!attrs[i])
+ continue;
+
+ switch (i) {
+ case XFRMA_MARK:
+ case XFRMA_ENCAP:
+ case XFRMA_OFFLOAD_DEV:
+ case XFRMA_SET_MARK:
+ case XFRMA_SET_MARK_MASK:
+ case XFRMA_MTIMER_THRESH:
+ case XFRMA_NAT_KEEPALIVE_INTERVAL:
+ break;
+ default:
+ NL_SET_ERR_MSG_ATTR(extack, attrs[i],
+ "Unsupported attribute in XFRM_MSG_MIGRATE_STATE");
+ return -EINVAL;
+ }
+ }
+ }
+
return 0;
}
--
2.47.3
^ permalink raw reply related
* [PATCH ipsec-next v8 14/14] xfrm: add documentation for XFRM_MSG_MIGRATE_STATE
From: Antony Antony @ 2026-05-05 4:34 UTC (permalink / raw)
To: Antony Antony, Steffen Klassert, Herbert Xu, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Simon Horman,
David Ahern, Masahide NAKAMURA, Paul Moore, Stephen Smalley,
Ondrej Mosnacek, Jonathan Corbet, Shuah Khan
Cc: Sabrina Dubroca, netdev, linux-kernel, selinux, linux-doc,
Chiachang Wang, Yan Yan, devel
In-Reply-To: <migrate-state-v8-0-4578fb016965@secunet.com>
Add documentation for the new XFRM_MSG_MIGRATE_STATE netlink message,
which migrates a single SA identified by SPI and mark without involving
policies.
The document covers the motivation and design differences from the
existing XFRM_MSG_MIGRATE, the SA lookup mechanism, supported attributes
with their omit-to-inherit semantics, and usage examples.
Signed-off-by: Antony Antony <antony.antony@secunet.com>
---
v7->v8: unknown flags ignored
v6->v7: update docs to reflect the flags
v5->v6: added this patch
---
Documentation/networking/xfrm/index.rst | 1 +
.../networking/xfrm/xfrm_migrate_state.rst | 231 +++++++++++++++++++++
2 files changed, 232 insertions(+)
diff --git a/Documentation/networking/xfrm/index.rst b/Documentation/networking/xfrm/index.rst
index 7d866da836fe..90191848f8db 100644
--- a/Documentation/networking/xfrm/index.rst
+++ b/Documentation/networking/xfrm/index.rst
@@ -9,5 +9,6 @@ XFRM Framework
xfrm_device
xfrm_proc
+ xfrm_migrate_state
xfrm_sync
xfrm_sysctl
diff --git a/Documentation/networking/xfrm/xfrm_migrate_state.rst b/Documentation/networking/xfrm/xfrm_migrate_state.rst
new file mode 100644
index 000000000000..66e7f2e729d8
--- /dev/null
+++ b/Documentation/networking/xfrm/xfrm_migrate_state.rst
@@ -0,0 +1,231 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+=====================
+XFRM SA Migrate State
+=====================
+
+Overview
+========
+
+``XFRM_MSG_MIGRATE_STATE`` migrates a single SA, looked up using SPI and
+mark, without involving policies. Unlike ``XFRM_MSG_MIGRATE``, which couples
+SA and policy migration and allows migrating multiple SAs in one call, this
+interface identifies the SA unambiguously via SPI and supports changing
+the reqid, addresses, encapsulation, selector, and offload.
+
+Because IKE daemons such as *wan manage policies independently of
+the kernel, this interface allows precise per-SA migration without
+requiring policy involvement. Optional netlink attributes follow an
+omit-to-inherit model: omitting an attribute preserves the value from
+the old SA. The ``flags`` field controls two exceptions: hardware offload
+is inherited by default and can be suppressed with
+``XFRM_MIGRATE_STATE_NO_OFFLOAD`` or overridden with ``XFRMA_OFFLOAD_DEV``;
+the new selector is taken from ``new_sel`` by default and can instead be
+derived from the new addresses with ``XFRM_MIGRATE_STATE_UPDATE_SEL``.
+
+SA Identification
+=================
+
+The struct is defined in ``include/uapi/linux/xfrm.h``. The SA is looked
+up using ``xfrm_state_lookup()`` with ``id.spi``,
+``id.daddr``, ``id.proto``, ``id.family``, and
+``old_mark.v & old_mark.m`` as the mark key::
+
+ struct xfrm_user_migrate_state {
+ struct xfrm_usersa_id id; /* spi, daddr, proto, family */
+ xfrm_address_t new_daddr;
+ xfrm_address_t new_saddr;
+ struct xfrm_mark old_mark; /* SA lookup: key = v & m */
+ struct xfrm_selector new_sel; /* new selector (see Flags) */
+ __u32 new_reqid;
+ __u32 flags; /* XFRM_MIGRATE_STATE_* */
+ __u16 new_family;
+ __u16 reserved;
+ };
+
+Supported Attributes
+====================
+
+The following fields in ``xfrm_user_migrate_state`` are always explicit
+and are not inherited from the existing SA. Passing zero is not equivalent
+to "keep unchanged" — zero is used as-is:
+
+- ``new_daddr`` - new destination address
+- ``new_saddr`` - new source address
+- ``new_family`` - new address family
+- ``new_reqid`` - new reqid (0 = no reqid)
+- ``new_sel`` - new selector; used when ``XFRM_MIGRATE_STATE_UPDATE_SEL`` is
+ not set (see `Flags`_ below)
+- ``flags`` - bitmask of ``XFRM_MIGRATE_STATE_*`` flags (see `Flags`_ below)
+
+The following netlink attributes are also accepted. Omitting an attribute
+inherits the value from the existing SA (omit-to-inherit).
+
+.. list-table::
+ :widths: 30 70
+ :header-rows: 1
+
+ * - Attribute
+ - Description
+ * - ``XFRMA_MARK``
+ - Mark on the migrated SA (``struct xfrm_mark``). Absent inherits
+ ``old_mark``. To use no mark on the new SA, send ``XFRMA_MARK``
+ with ``{0, 0}``.
+ * - ``XFRMA_ENCAP``
+ - UDP encapsulation template; only ``UDP_ENCAP_ESPINUDP`` is supported.
+ Set ``encap_type=0`` to remove encap.
+ * - ``XFRMA_OFFLOAD_DEV``
+ - Hardware offload configuration (``struct xfrm_user_offload``). Absent
+ copies offload from the existing SA. When
+ ``XFRM_MIGRATE_STATE_NO_OFFLOAD`` is set in ``flags``, the new SA has
+ no offload; this flag is mutually exclusive with ``XFRMA_OFFLOAD_DEV``
+ and sending both returns ``-EINVAL``.
+ * - ``XFRMA_SET_MARK``
+ - Output mark on the migrated SA; pair with ``XFRMA_SET_MARK_MASK``.
+ Send 0 to clear.
+ * - ``XFRMA_NAT_KEEPALIVE_INTERVAL``
+ - NAT keepalive interval in seconds. Requires encap. Send 0 to clear.
+ Automatically cleared when encap is removed; setting a non-zero
+ value without encap returns ``-EINVAL``.
+ * - ``XFRMA_MTIMER_THRESH``
+ - Mapping maxage threshold. Requires encap. Send 0 to clear.
+ Automatically cleared when encap is removed; setting a non-zero
+ value without encap returns ``-EINVAL``.
+
+The following SA properties are immutable and cannot be changed via
+``XFRM_MSG_MIGRATE_STATE``: algorithms (``XFRMA_ALG_*``), replay state,
+direction (``XFRMA_SA_DIR``), and security context (``XFRMA_SEC_CTX``).
+
+Flags
+=====
+
+The ``flags`` field in ``xfrm_user_migrate_state`` controls optional
+migration behaviour. Unknown flag bits are ignored.
+
+.. list-table::
+ :widths: 40 60
+ :header-rows: 1
+
+ * - Flag
+ - Description
+ * - ``XFRM_MIGRATE_STATE_NO_OFFLOAD``
+ - When set, the new SA has no hardware offload even when
+ ``XFRMA_OFFLOAD_DEV`` is absent. Without this flag, omitting
+ ``XFRMA_OFFLOAD_DEV`` copies the existing offload to the new SA.
+ Mutually exclusive with ``XFRMA_OFFLOAD_DEV``; sending both
+ returns ``-EINVAL``.
+ * - ``XFRM_MIGRATE_STATE_UPDATE_SEL``
+ - When set, the kernel validates that the existing SA selector is a
+ single-host entry matching the SA addresses (``prefixlen_s ==
+ prefixlen_d`` equal to 32 for IPv4 or 128 for IPv6, and addresses
+ matching ``id.daddr`` and ``props.saddr``). If the check passes,
+ the new selector is derived from ``new_daddr`` and ``new_saddr``
+ with the single-host mask for ``new_family``. A mismatch returns
+ ``-EINVAL``. When this flag is not set, ``new_sel`` is used as-is
+ for the migrated SA.
+
+Migration Steps
+===============
+
+#. Install a block policy to drop traffic on the affected selector.
+#. Remove the old policy.
+#. Call ``XFRM_MSG_MIGRATE_STATE`` for each SA.
+#. Reinstall the policies.
+#. Remove the block policy.
+
+Block Policy and IV Safety
+--------------------------
+
+Installing a block policy before migration is required to prevent
+traffic leaks and IV reuse in counter mode.
+
+AES-GCM IV uniqueness is critical: reusing a (key, IV) pair allows
+an attacker to recover the authentication subkey and forge
+authentication tags, breaking both confidentiality and integrity.
+
+``XFRM_MSG_MIGRATE_STATE`` atomically copies the sequence number and
+replay window from the old SA to the new SA and deletes the old SA.
+The block policy ensures no outgoing packets are sent in the migration
+window, preventing IV reuse under the same key.
+
+Feature Detection
+=================
+
+Userspace can probe for kernel support by sending a minimal
+``XFRM_MSG_MIGRATE_STATE`` message with a non-existent SPI:
+
+- ``-ENOPROTOOPT``: not supported (``CONFIG_XFRM_MIGRATE`` not enabled)
+- any other error: supported
+
+Userspace Notification on Success
+=================================
+
+On successful migration the kernel multicasts an
+``XFRM_MSG_MIGRATE_STATE`` message to the ``XFRMNLGRP_MIGRATE`` group.
+The fixed header is ``struct xfrm_user_migrate_state`` copied from the
+request, followed by the same set of netlink attributes that are
+accepted as input, with the differences noted below.
+
+Differences from the request
+-----------------------------
+
+.. list-table::
+ :widths: 25 75
+ :header-rows: 1
+
+ * - Field / Attribute
+ - Difference
+ * - ``new_sel``
+ - Contains the actual selector of the newly installed SA, not the
+ ``new_sel`` from the request. When
+ ``XFRM_MIGRATE_STATE_UPDATE_SEL`` is set the kernel derives the
+ selector from ``new_daddr`` / ``new_saddr``; the caller's
+ ``new_sel`` field is ignored in that case. The notification
+ always carries the real selector of the new SA.
+ * - ``XFRMA_SA_DIR``
+ - Present in the notification (set from the direction of the new
+ SA) but **not accepted as input** — direction is immutable.
+ * - ``flags``
+ - Echoed back as-is. ``XFRM_MIGRATE_STATE_NO_OFFLOAD`` and
+ ``XFRM_MIGRATE_STATE_UPDATE_SEL`` describe the request that was
+ made, not a property of the resulting SA.
+
+Attributes in the notification
+-------------------------------
+
+.. list-table::
+ :widths: 30 70
+ :header-rows: 1
+
+ * - Attribute
+ - Description
+ * - ``XFRMA_ENCAP``
+ - UDP encapsulation template, if configured on the new SA.
+ * - ``XFRMA_OFFLOAD_DEV``
+ - Hardware offload configuration, if active on the new SA.
+ * - ``XFRMA_MARK``
+ - Mark on the new SA, if set.
+ * - ``XFRMA_SET_MARK``
+ - Output mark on the new SA, if set.
+ * - ``XFRMA_SET_MARK_MASK``
+ - Output mark mask, present together with ``XFRMA_SET_MARK``.
+ * - ``XFRMA_MTIMER_THRESH``
+ - Mapping maxage threshold, if non-zero.
+ * - ``XFRMA_NAT_KEEPALIVE_INTERVAL``
+ - NAT keepalive interval, if non-zero.
+ * - ``XFRMA_SA_DIR``
+ - Direction of the new SA.
+
+Error Handling
+==============
+
+If the target SA tuple (daddr, SPI, proto, family) is occupied by an existing
+unrelated SA, the operation returns ``-EEXIST``. In this case both the old and
+the new SA are gone. The old SA cannot be restored as doing so would risk
+duplicate sequence number and IV reuse, which must not occur. Userspace should
+handle ``-EEXIST``, for example by re-establishing the SA at the IKE level.
+
+If the multicast notification (``XFRMNLGRP_MIGRATE``) fails to send,
+the migration itself has already completed successfully and the new SA
+is installed. The operation returns success, 0, with an extack warning,
+but listeners will not receive the migration event.
--
2.47.3
^ permalink raw reply related
* Re: [Intel-wired-lan] [PATCH iwl-net v1 2/2] ice: use NETIF_F_HW_CSUM instead of IP/IPV6
From: Michal Swiatkowski @ 2026-05-05 4:35 UTC (permalink / raw)
To: Jacob Keller
Cc: Michal Swiatkowski, intel-wired-lan, netdev, jramaseu,
anthony.l.nguyen, przemyslaw.kitszel, aleksandr.loktionov
In-Reply-To: <44623db1-8b86-4e8e-82a3-46d65b055ebc@intel.com>
On Mon, May 04, 2026 at 04:53:12PM -0700, Jacob Keller wrote:
> On 4/28/2026 12:06 AM, Michal Swiatkowski wrote:
> > The hardware is capable of calculating checksum for IPV6 packets with
> > extension header. To not drop such packets switch from IP/IPV6 checksum
> > to HW_CSUM.
> >
> > HW_CSUM is also used in previous generation (i40e).
> >
> > Previously HW_CSUM was used to indicate that hardware supports general
> > checksum. Drop it assuming that if the hardware supports it, it is used.
> >
> > Disabling offload for E830 in case of TSO isn't needed anymore as the
> > check for TSO is done in Tx path just before preparation of the special
> > GCS descriptor.
> >
> > The commit from Fixes didn't introduce a bug, it just shown that the
> > driver is doing sth wrong with the checksum features.
> >
> > Suggested-by: Jakub Ramaseuski <jramaseu@redhat.com>
> > Reviewed-by: Przemek Kitszel <przemyslaw.kitszel@intel.com>
> > Fixes: 04c20a9356f2 ("net: skip offload for NETIF_F_IPV6_CSUM if ipv6 header contains extension")
> > Signed-off-by: Michal Swiatkowski <michal.swiatkowski@linux.intel.com>
> > ---
> Am I correct in thinking that this supersedes (really, properly fixes)
> the patch "ice: enable NETIF_F_HW_CSUM for GSO packets" at
> https://patchwork.ozlabs.org/project/intel-wired-lan/patch/20260310150557.1138437-1-jramaseu@redhat.com/
> ?
>
> Thanks,
> Jake
Yes, exactly. I think I linked it in cover letter, but maybe I should do
it also here.
Thanks
^ permalink raw reply
* Re: [PATCH batadv 0/8] batman-adv: follow up fixes
From: Sven Eckelmann @ 2026-05-05 4:46 UTC (permalink / raw)
To: Jakub Kicinski
Cc: Marek Lindner, Simon Wunderlich, Antonio Quartulli,
David S. Miller, Eric Dumazet, Paolo Abeni, Simon Horman,
b.a.t.m.a.n, netdev, linux-kernel, Ao Zhou, Haoze Xie,
Jiexun Wang, Juefei Pu, Luxing Yin, Ruide Cao, Xin Liu, Yifan Wu,
Yuan Tan, stable
In-Reply-To: <20260504171051.2b3e47ef@kernel.org>
[-- Attachment #1: Type: text/plain, Size: 563 bytes --]
On Tuesday, 5 May 2026 02:10:51 CEST Jakub Kicinski wrote:
> On Sun, 03 May 2026 14:22:33 +0200 Sven Eckelmann wrote:
> > While reviewing the fixes submitted to batman-adv in the recent weeks,
> > further problems in similar or adjecent code was identified. This was either
> > noticed in the manual review or reported by sashiko.dev.
>
> Are you CCing netdev to get this reviewed by Sashiko?
> Please don't..
> We delegate code to sub-sub-systems to lower the patch volume :(
>
Because of `b4 prep --auto-to-cc`. Will now manually remove you.
Regards,
Sven
[-- Attachment #2: This is a digitally signed message part. --]
[-- Type: application/pgp-signature, Size: 228 bytes --]
^ permalink raw reply
* Re: [PATCH RFC net-next 0/4] net: pse-pd: decouple controller lookup from MDIO probe
From: Corey Leavitt @ 2026-05-05 4:36 UTC (permalink / raw)
To: Carlo Szelinsky
Cc: Kory Maincent, Oleksij Rempel, Andrew Lunn, Andrew Lunn,
Heiner Kallweit, Russell King, Jakub Kicinski, David S . Miller,
Eric Dumazet, Paolo Abeni, Simon Horman, netdev, linux-kernel
In-Reply-To: <20260502201016.2215520-1-github@szelinsky.de>
Hi Carlo,
Thanks for testing, and for the clear writeup of the rmmod path.
Will add your Tested-by to v2.
On the rmmod / module_get question: I think you are right that the
per-consumer try_module_get is structurally redundant once the
notifier exists, since PSE_UNREGISTERED already gives us a
synchronous invalidation point at unregister time. The reason I
did not strip it in v1 was caution: I want to walk every
pse_control_get / pse_control_put consumer and confirm no path
holds a transient reference outside of rtnl-lock (or some
equivalent serialization) such that an in-flight use could race
the unregister notifier.
That audit is on my plate now. I will share findings on this
thread before v2, and let the result drive whether the removal
lands in v2 or as a separate discussion.
Glad to hear the probe loop is gone on your bench and that ethtool
plus PD power are behaving. I expect to have an SFP-capable board
on my own bench later this week, so I will cover the sfp.c path
change before sending v2; a retest from you on the SFP chassis
whenever it is convenient is still very welcome as independent
confirmation.
Kory, Jakub, Russell, others: input welcome on whether dropping
try_module_get in favor of pure notifier-based invalidation is the
direction you would prefer here, or whether the belt-and-suspenders
refcount is worth keeping.
Thanks,
Corey
^ permalink raw reply
* Re: [PATCH net-next v1 0/5] net: mdiobus: HIde ACPI implementation
From: Andy Shevchenko @ 2026-05-05 4:54 UTC (permalink / raw)
To: Andrew Lunn
Cc: linux-kernel, netdev, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Heiner Kallweit, Russell King
In-Reply-To: <28a4ebd7-2670-439d-836b-14559a916ed0@lunn.ch>
On Mon, May 04, 2026 at 05:16:57PM +0200, Andrew Lunn wrote:
> On Mon, May 04, 2026 at 04:49:35PM +0300, Andy Shevchenko wrote:
> > On Mon, May 04, 2026 at 03:02:54PM +0200, Andrew Lunn wrote:
> > > On Mon, May 04, 2026 at 09:29:51AM +0200, Andy Shevchenko wrote:
> > > > This mini-series is dedicated to hiding ACPI implementation details
> > > > from the wider users as they (as of today) do not need to know that.
> > >
> > > Please could you expand on that. ACPI != OF. They have different
> > > bindings, so you need different implementations.
> >
> > As of today the users that want ACPI also have the OF support. Even without
> > that if the device is pure ACPI supported one (and somehow never going to DT)
> > the proposed API (see the first patch) will be no-op in case when CONFIG_ACPI=n
> > or when it's a non-ACPI platform with no support of the device. Hence, the
> > pure ACPI (and actually OF as well) do not need to be exposed. The decision is
> > made based on the type of firmware node.
>
> I see two different things here:
>
> You are refactoring code into a helper, so reducing the amount of
> duplicated code. That in itself is good.
>
> However, we have the problem in general that developers think that OF
> and ACPI do the same thing. And that is not true. OF MDIO busses have
> support for a GPIO used as a reset. In retrospect, that was a bad
> idea. The documented ACPI binding does not have this, and if anybody
> was to suggest adding it, i want to NACK it.
>
> Having OF code and ACPI code to instantiate the MDIO bus makes it
> clearer they are different things. fwnode gives the impression they
> are the same, which is not true. Hence i don't like fwnode at this
> level.
>
> Where fwnode makes sense is deeper down in the implementation of the
> bindings, for properties which are the same in both bindings. Then
> you can use fwnode_ for the shared properties, of_ for the OF only
> properties, and acpi_ for the ACPI only properties.
>
> So although i like the reduction in duplicated code, i think overall
> it makes the code worse because developers are more likely to wrongly
> understand it.
OK.
Thanks for review.
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply
* Re: [PATCH batadv 0/8] batman-adv: follow up fixes
From: Sven Eckelmann @ 2026-05-05 5:00 UTC (permalink / raw)
To: Jakub Kicinski, Konstantin Ryabitsev
Cc: Marek Lindner, Simon Wunderlich, Antonio Quartulli,
David S. Miller, Eric Dumazet, Paolo Abeni, Simon Horman,
b.a.t.m.a.n, netdev, linux-kernel, Ao Zhou, Haoze Xie,
Jiexun Wang, Juefei Pu, Luxing Yin, Ruide Cao, Xin Liu, Yifan Wu,
Yuan Tan, stable, Kernel.org Tools
In-Reply-To: <5990011.DvuYhMxLoT@sven-l14>
[-- Attachment #1: Type: text/plain, Size: 870 bytes --]
On Tuesday, 5 May 2026 06:46:11 CEST Sven Eckelmann wrote:
> On Tuesday, 5 May 2026 02:10:51 CEST Jakub Kicinski wrote:
> > On Sun, 03 May 2026 14:22:33 +0200 Sven Eckelmann wrote:
> > > While reviewing the fixes submitted to batman-adv in the recent weeks,
> > > further problems in similar or adjecent code was identified. This was either
> > > noticed in the manual review or reported by sashiko.dev.
> >
> > Are you CCing netdev to get this reviewed by Sashiko?
> > Please don't..
> > We delegate code to sub-sub-systems to lower the patch volume :(
> >
>
> Because of `b4 prep --auto-to-cc`. Will now manually remove you.
To speed up the discussion: @Konstantin, is there a way in b4 to say "stop at
the sub-sub-systems" when doing `b4 prep --auto-to-cc`? I am just trying to get the
`b4` workflow somehow working with the netdev requirements.
Regards,
Sven
[-- Attachment #2: This is a digitally signed message part. --]
[-- Type: application/pgp-signature, Size: 228 bytes --]
^ permalink raw reply
* RE: [Intel-wired-lan] [PATCH net] ice: fix setting promisc mode while adding VID filter
From: Rinitha, SX @ 2026-05-05 5:01 UTC (permalink / raw)
To: Loktionov, Aleksandr, intel-wired-lan@lists.osuosl.org,
Nguyen, Anthony L, Loktionov, Aleksandr
Cc: netdev@vger.kernel.org, Szycik, Marcin
In-Reply-To: <20260327072332.130320-5-aleksandr.loktionov@intel.com>
> -----Original Message-----
> From: Intel-wired-lan <intel-wired-lan-bounces@osuosl.org> On Behalf Of Aleksandr Loktionov
> Sent: 27 March 2026 12:53
> To: intel-wired-lan@lists.osuosl.org; Nguyen, Anthony L <anthony.l.nguyen@intel.com>; Loktionov, Aleksandr <aleksandr.loktionov@intel.com>
> Cc: netdev@vger.kernel.org; Szycik, Marcin <marcin.szycik@intel.com>
> Subject: [Intel-wired-lan] [PATCH net] ice: fix setting promisc mode while adding VID filter
>
> From: Marcin Szycik <marcin.szycik@intel.com>
>
> There are at least two paths through which VSI promiscuous mode can be independently configured via ice_fltr_set_vsi_promisc():
> - ice_vlan_rx_add_vid() (netdev op)
> - ice_service_task() -> ... -> ice_set_promisc()
>
> Both paths may try to program promiscuous mode concurrently. One such scenario is:
>
> 1. Add ice netdev to bond
> 2. Add the bond netdev to bridge
> 3. ice netdev enters allmulticast mode (IFF_ALLMULTI) 4. Service task programs promisc mode filter 5. Bridge -> bond calls ice_vlan_rx_add_vid()
>
> Crucially, ice_vlan_rx_add_vid() fails if ice_fltr_set_vsi_promisc() returns any error, including -EEXIST. This causes VLAN filtering setup to fail on the bond interface. ice_set_promisc() already handles -EEXIST correctly.
>
> Fix by adding the same -EEXIST check to ice_vlan_rx_add_vid(): if the promisc filter is already programmed, continue without returning error.
>
> Fixes: 1273f89578f2 ("ice: Fix broken IFF_ALLMULTI handling")
> Cc: stable@vger.kernel.org
> Signed-off-by: Marcin Szycik <marcin.szycik@intel.com>
> Signed-off-by: Aleksandr Loktionov <aleksandr.loktionov@intel.com>
> ---
>
> drivers/net/ethernet/intel/ice/ice_main.c | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
Tested-by: Rinitha S <sx.rinitha@intel.com> (A Contingent worker at Intel)
^ permalink raw reply
* RE: [Intel-wired-lan] [PATCH net] ice: fix null-ptr dereference on false-positive tx timeout
From: Rinitha, SX @ 2026-05-05 5:08 UTC (permalink / raw)
To: Loktionov, Aleksandr, intel-wired-lan@lists.osuosl.org,
Nguyen, Anthony L, Loktionov, Aleksandr
Cc: netdev@vger.kernel.org
In-Reply-To: <20260327072332.130320-1-aleksandr.loktionov@intel.com>
> -----Original Message-----
> From: Intel-wired-lan <intel-wired-lan-bounces@osuosl.org> On Behalf Of Aleksandr Loktionov
> Sent: 27 March 2026 12:53
> To: intel-wired-lan@lists.osuosl.org; Nguyen, Anthony L <anthony.l.nguyen@intel.com>; Loktionov, Aleksandr <aleksandr.loktionov@intel.com>
> Cc: netdev@vger.kernel.org
> Subject: [Intel-wired-lan] [PATCH net] ice: fix null-ptr dereference on false-positive tx timeout
>
> From: Michal Kubiak <michal.kubiak@intel.com>
>
> Due to some kernel bugs the Tx timeout event may be false-positive.
> For example, the kernel commit 95ecba62e2fd ("net: fix races in
> netdev_tx_sent_queue()/dev_watchdog()") fixes race conditions that can also occur during the ice driver initialization.
>
> In case of such false-positive Tx timeouts there can be no real Tx transaction started, so the SKB pointer can be NULL. Therefore, the call
> devlink_fmsg_dump_skb() can crash because of NULL-ptr dereference.
>
> Fix that by checking the SKB pointer before dereferencing it.
>
> Fixes: 2a82874a3b7b ("ice: add Tx hang devlink health reporter")
> Cc: stable@vger.kernel.org
> Signed-off-by: Michal Kubiak <michal.kubiak@intel.com>
> Signed-off-by: Aleksandr Loktionov <aleksandr.loktionov@intel.com>
> ---
>
> drivers/net/ethernet/intel/ice/devlink/health.c | 3 ++-
> 1 file changed, 2 insertions(+), 1 deletion(-)
>
Tested-by: Rinitha S <sx.rinitha@intel.com> (A Contingent worker at Intel)
^ permalink raw reply
* [PATCH net] netfilter: ctnetlink: use nf_ct_exp_net() in expectation dump
From: Pratham Gupta @ 2026-05-05 5:11 UTC (permalink / raw)
To: netfilter-devel
Cc: pablo, fw, phil, davem, edumazet, kuba, pabeni, horms, coreteam,
netdev, linux-kernel, stable, Pratham Gupta
Commit 02a3231b6d82 ("netfilter: nf_conntrack_expect: store netns and zone in expectation")
introduced exp->net so RCU-only expectation paths no longer need to
dereference exp->master for netns lookups.
Commit 3db5647984de ("netfilter: nf_conntrack_expect: skip expectations in other netns via proc")
updated the proc path accordingly, but ctnetlink_exp_dump_table() still
compares against nf_ct_net(exp->master).
Use nf_ct_exp_net(exp) here as well so the netlink dump path matches
the rest of the March 2026 expectation netns/RCU cleanup.
Fixes: 02a3231b6d82 ("netfilter: nf_conntrack_expect: store netns and zone in expectation")
Cc: stable@vger.kernel.org
Signed-off-by: Pratham Gupta <pratham36gupta@gmail.com>
---
Tested expectation create/dump/delete on the host and in fresh Ubuntu 24.04
Docker userspace. Concurrent namespace churn/dump testing did not reproduce
a cross-netns leak.
net/netfilter/nf_conntrack_netlink.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c
index eda5fe4a75c8..8ae3f6acc2d2 100644
--- a/net/netfilter/nf_conntrack_netlink.c
+++ b/net/netfilter/nf_conntrack_netlink.c
@@ -3158,7 +3158,7 @@ ctnetlink_exp_dump_table(struct sk_buff *skb, struct netlink_callback *cb)
if (l3proto && exp->tuple.src.l3num != l3proto)
continue;
- if (!net_eq(nf_ct_net(exp->master), net))
+ if (!net_eq(nf_ct_exp_net(exp), net))
continue;
if (cb->args[1]) {
--
2.43.0
^ permalink raw reply related
* [PATCH net 00/13] Intel Wired LAN Driver Updates 2026-05-04 (i40e, ice, idpf)
From: Jacob Keller @ 2026-05-05 5:14 UTC (permalink / raw)
To: Przemek Kitszel, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Piotr Kwapulinski,
Aleksandr Loktionov, Arkadiusz Kubalewski, Maciej Fijalkowski,
Michal Kubiak, Joshua Hay, Madhu Chittim, Willem de Bruijn,
Dave Ertman, Ivan Vecera, Grzegorz Nitka
Cc: netdev, stable, Jacob Keller, Matt Vollrath, Sunitha Mekala,
Kohei Enju, Paul Menzel, Simon Horman, Emil Tantilov,
Samuel Salin, Patryk Holda, Greg Kroah-Hartman, Tony Nguyen,
stable, Marcin Szycik, Bart Van Assche, intel-wired-lan,
Arpana Arland, Rinitha S
Matt Volrath fixes two issues with the i40e driver probe routine, ensuring
that PTP is properly cleaned up if the probe fails.
Maciej fixes the i40e driver logic to keep the q_vectors array in sync with
changes to the channel count via ethtool.
Emil corrects the initialization of the read_dev_clk_lock spinlock in
idpf_ptp_init, ensuring it is initialized prior to when the
ptp_schedule_worker() is called.
Josh fixes the idpf driver to prevent enabling XDP if the queue based
scheduling is not supported by the firmware.
Josh fixes the idpf skb data path for handling queue based scheduling.
Josh fixes an XDP crash in the soft reset error path, restoring the
original configuration if idpf_xdp_setup_prog() fails.
Greg KH fixes a double free and use-after free in the idpf auxiliary device
error paths.
Marcin fixes ice_set_rss_hfunc() to use the correct q_opt_flags field,
correcting the assignment and preventing submission of invalid data to the
firmware.
Bart corrects the locking in ice_dcb_rebuild(), ensuring that the tc_mutex
is held over the entire operation.
Grzegorz fixes the ordering of ice_ptp_link_change() in ice_up_complete()
ensuring that the PTP timestamps will not be enabled before the PTP timer
is actually re-initialized.
Ivan fixes the rclk pin state get for E810 devices, ensuring the index is
properly offset by the base_rclk_idx value. This ensures that the correct
pin index is used to look up recovered clock state. He additionally adds
bounds checking to prevent attempting to access pins outside of the pin
state array.
Ivan also moves the CGU register macros to the top of ice_dpll.h, inside
the header guard to avoid duplicate macro definitions should the ice_dpll.h
header is included multiple times.
Signed-off-by: Jacob Keller <jacob.e.keller@intel.com>
---
Bart Van Assche (1):
ice: fix locking in ice_dcb_rebuild()
Emil Tantilov (2):
idpf: fix read_dev_clk_lock spinlock init in idpf_ptp_init()
idpf: fix xdp crash in soft reset error path
Greg Kroah-Hartman (1):
idpf: fix double free and use-after-free in aux device error paths
Grzegorz Nitka (1):
ice: fix PTP hang for E825C devices
Ivan Vecera (2):
ice: dpll: fix rclk pin state get for E810
ice: dpll: fix misplaced header macros
Joshua Hay (2):
idpf: do not enable XDP if queue based scheduling is not supported
idpf: fix skb datapath queue based scheduling crashes and timeouts
Maciej Fijalkowski (1):
i40e: keep q_vectors array in sync with channel count changes
Marcin Szycik (1):
ice: fix setting RSS VSI hash for E830
Matt Vollrath (2):
i40e: Cleanup PTP registration on probe failure
i40e: Cleanup PTP pins on probe failure
drivers/net/ethernet/intel/i40e/i40e.h | 1 +
drivers/net/ethernet/intel/ice/ice_dpll.h | 32 ++++++-------
drivers/net/ethernet/intel/idpf/idpf_txrx.h | 12 +++--
drivers/net/ethernet/intel/idpf/idpf_virtchnl.h | 4 +-
drivers/net/ethernet/intel/i40e/i40e_main.c | 36 ++++++++++++---
drivers/net/ethernet/intel/i40e/i40e_ptp.c | 3 +-
drivers/net/ethernet/intel/ice/ice_dcb_lib.c | 4 +-
drivers/net/ethernet/intel/ice/ice_dpll.c | 5 ++
drivers/net/ethernet/intel/ice/ice_main.c | 6 +--
drivers/net/ethernet/intel/idpf/idpf_idc.c | 6 +++
drivers/net/ethernet/intel/idpf/idpf_lib.c | 4 +-
drivers/net/ethernet/intel/idpf/idpf_ptp.c | 4 +-
drivers/net/ethernet/intel/idpf/idpf_txrx.c | 61 ++++++++++++++-----------
drivers/net/ethernet/intel/idpf/idpf_virtchnl.c | 19 ++------
drivers/net/ethernet/intel/idpf/xdp.c | 15 ++++--
drivers/net/ethernet/intel/idpf/xsk.c | 4 +-
16 files changed, 132 insertions(+), 84 deletions(-)
---
base-commit: bd3a4795d5744f59a1f485379f1303e5e606f377
change-id: 20260504-jk-iwl-net-2026-05-04-f9526823577f
Best regards,
--
Jacob Keller <jacob.e.keller@intel.com>
^ permalink raw reply
* [PATCH net 01/13] i40e: Cleanup PTP registration on probe failure
From: Jacob Keller @ 2026-05-05 5:14 UTC (permalink / raw)
To: Przemek Kitszel, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Piotr Kwapulinski,
Aleksandr Loktionov, Arkadiusz Kubalewski, Maciej Fijalkowski,
Michal Kubiak, Joshua Hay, Madhu Chittim, Willem de Bruijn,
Dave Ertman, Ivan Vecera, Grzegorz Nitka
Cc: netdev, stable, Jacob Keller, Matt Vollrath, Sunitha Mekala
In-Reply-To: <20260504-jk-iwl-net-2026-05-04-v1-0-a222a88bd962@intel.com>
From: Matt Vollrath <tactii@gmail.com>
Fix two conditions which would leak PTP registration on probe failure:
1. i40e_setup_pf_switch can encounter an error in
i40e_setup_pf_filter_control, call i40e_ptp_init, then return
non-zero, sending i40e_probe to err_vsis.
2. i40e_setup_misc_vector can return non-zero, sending i40e_probe to
err_vsis.
Both of these conditions have been present since PTP was introduced in
this driver.
Found with coccinelle.
Fixes: beb0dff1251db ("i40e: enable PTP")
Signed-off-by: Matt Vollrath <tactii@gmail.com>
Tested-by: Sunitha Mekala <sunithax.d.mekala@intel.com>
Reviewed-by: Aleksandr Loktionov <aleksandr.loktionov@intel.com>
Signed-off-by: Jacob Keller <jacob.e.keller@intel.com>
---
drivers/net/ethernet/intel/i40e/i40e_main.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c
index 028bd500603a..f06fcef644e5 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_main.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_main.c
@@ -16108,6 +16108,7 @@ static int i40e_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
/* Unwind what we've done if something failed in the setup */
err_vsis:
set_bit(__I40E_DOWN, pf->state);
+ i40e_ptp_stop(pf);
i40e_clear_interrupt_scheme(pf);
kfree(pf->vsi);
err_switch_setup:
--
2.54.0.rc2.531.gaf818d63126a
^ permalink raw reply related
* [PATCH net 02/13] i40e: Cleanup PTP pins on probe failure
From: Jacob Keller @ 2026-05-05 5:14 UTC (permalink / raw)
To: Przemek Kitszel, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Piotr Kwapulinski,
Aleksandr Loktionov, Arkadiusz Kubalewski, Maciej Fijalkowski,
Michal Kubiak, Joshua Hay, Madhu Chittim, Willem de Bruijn,
Dave Ertman, Ivan Vecera, Grzegorz Nitka
Cc: netdev, stable, Jacob Keller, Matt Vollrath, Kohei Enju,
Paul Menzel, Sunitha Mekala
In-Reply-To: <20260504-jk-iwl-net-2026-05-04-v1-0-a222a88bd962@intel.com>
From: Matt Vollrath <tactii@gmail.com>
PTP pin structs are allocated early in probe, but never cleaned up.
Fix this by calling i40e_ptp_free_pins in the error path.
To support this, i40e_ptp_free_pins is added to the header and
pin_config is correctly nullified after being freed.
This has been an issue since i40e_ptp_alloc_pins was introduced.
Fixes: 1050713026a08 ("i40e: add support for PTP external synchronization clock")
Reported-by: Kohei Enju <kohei@enjuk.jp>
Cc: stable@vger.kernel.org
Signed-off-by: Matt Vollrath <tactii@gmail.com>
Reviewed-by: Paul Menzel <pmenzel@molgen.mpg.de>
Reviewed-by: Aleksandr Loktionov <aleksandr.loktionov@intel.com>
Reviewed-by: Kohei Enju <kohei@enjuk.jp>
Tested-by: Sunitha Mekala <sunithax.d.mekala@intel.com>
Signed-off-by: Jacob Keller <jacob.e.keller@intel.com>
---
drivers/net/ethernet/intel/i40e/i40e.h | 1 +
drivers/net/ethernet/intel/i40e/i40e_main.c | 1 +
drivers/net/ethernet/intel/i40e/i40e_ptp.c | 3 ++-
3 files changed, 4 insertions(+), 1 deletion(-)
diff --git a/drivers/net/ethernet/intel/i40e/i40e.h b/drivers/net/ethernet/intel/i40e/i40e.h
index dcb50c2e1aa2..83e780919ac9 100644
--- a/drivers/net/ethernet/intel/i40e/i40e.h
+++ b/drivers/net/ethernet/intel/i40e/i40e.h
@@ -1318,6 +1318,7 @@ void i40e_ptp_restore_hw_time(struct i40e_pf *pf);
void i40e_ptp_init(struct i40e_pf *pf);
void i40e_ptp_stop(struct i40e_pf *pf);
int i40e_ptp_alloc_pins(struct i40e_pf *pf);
+void i40e_ptp_free_pins(struct i40e_pf *pf);
int i40e_update_adq_vsi_queues(struct i40e_vsi *vsi, int vsi_offset);
int i40e_is_vsi_uplink_mode_veb(struct i40e_vsi *vsi);
int i40e_get_partition_bw_setting(struct i40e_pf *pf);
diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c
index f06fcef644e5..6d4f9218dc68 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_main.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_main.c
@@ -16112,6 +16112,7 @@ static int i40e_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
i40e_clear_interrupt_scheme(pf);
kfree(pf->vsi);
err_switch_setup:
+ i40e_ptp_free_pins(pf);
i40e_reset_interrupt_capability(pf);
timer_shutdown_sync(&pf->service_timer);
err_mac_addr:
diff --git a/drivers/net/ethernet/intel/i40e/i40e_ptp.c b/drivers/net/ethernet/intel/i40e/i40e_ptp.c
index 404a716db8da..7d07c389bb23 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_ptp.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_ptp.c
@@ -940,12 +940,13 @@ int i40e_ptp_hwtstamp_get(struct net_device *netdev,
*
* Release memory allocated for PTP pins.
**/
-static void i40e_ptp_free_pins(struct i40e_pf *pf)
+void i40e_ptp_free_pins(struct i40e_pf *pf)
{
if (i40e_is_ptp_pin_dev(&pf->hw)) {
kfree(pf->ptp_pins);
kfree(pf->ptp_caps.pin_config);
pf->ptp_pins = NULL;
+ pf->ptp_caps.pin_config = NULL;
}
}
--
2.54.0.rc2.531.gaf818d63126a
^ permalink raw reply related
* [PATCH net 04/13] idpf: fix read_dev_clk_lock spinlock init in idpf_ptp_init()
From: Jacob Keller @ 2026-05-05 5:14 UTC (permalink / raw)
To: Przemek Kitszel, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Piotr Kwapulinski,
Aleksandr Loktionov, Arkadiusz Kubalewski, Maciej Fijalkowski,
Michal Kubiak, Joshua Hay, Madhu Chittim, Willem de Bruijn,
Dave Ertman, Ivan Vecera, Grzegorz Nitka
Cc: netdev, stable, Jacob Keller, Emil Tantilov, Simon Horman,
Samuel Salin
In-Reply-To: <20260504-jk-iwl-net-2026-05-04-v1-0-a222a88bd962@intel.com>
From: Emil Tantilov <emil.s.tantilov@intel.com>
In idpf_ptp_init(), read_dev_clk_lock is initialized after
ptp_schedule_worker() had already been called (and after
idpf_ptp_settime64() could reach the lock). The PTP aux worker
fires immediately upon scheduling and can call into
idpf_ptp_read_src_clk_reg_direct(), which takes
spin_lock(&ptp->read_dev_clk_lock) on an uninitialized lock, triggering
the lockdep "non-static key" warning:
[12973.796587] idpf 0000:83:00.0: Device HW Reset initiated
[12974.094507] INFO: trying to register non-static key.
...
[12974.097208] Call Trace:
[12974.097213] <TASK>
[12974.097218] dump_stack_lvl+0x93/0xe0
[12974.097234] register_lock_class+0x4c4/0x4e0
[12974.097249] ? __lock_acquire+0x427/0x2290
[12974.097259] __lock_acquire+0x98/0x2290
[12974.097272] lock_acquire+0xc6/0x310
[12974.097281] ? idpf_ptp_read_src_clk_reg+0xb7/0x150 [idpf]
[12974.097311] ? lockdep_hardirqs_on_prepare+0xde/0x190
[12974.097318] ? finish_task_switch.isra.0+0xd2/0x350
[12974.097330] ? __pfx_ptp_aux_kworker+0x10/0x10 [ptp]
[12974.097343] _raw_spin_lock+0x30/0x40
[12974.097353] ? idpf_ptp_read_src_clk_reg+0xb7/0x150 [idpf]
[12974.097373] idpf_ptp_read_src_clk_reg+0xb7/0x150 [idpf]
[12974.097391] ? kthread_worker_fn+0x88/0x3d0
[12974.097404] ? kthread_worker_fn+0x4e/0x3d0
[12974.097411] idpf_ptp_update_cached_phctime+0x26/0x120 [idpf]
[12974.097428] ? _raw_spin_unlock_irq+0x28/0x50
[12974.097436] idpf_ptp_do_aux_work+0x15/0x20 [idpf]
[12974.097454] ptp_aux_kworker+0x20/0x40 [ptp]
[12974.097464] kthread_worker_fn+0xd5/0x3d0
[12974.097474] ? __pfx_kthread_worker_fn+0x10/0x10
[12974.097482] kthread+0xf4/0x130
[12974.097489] ? __pfx_kthread+0x10/0x10
[12974.097498] ret_from_fork+0x32c/0x410
[12974.097512] ? __pfx_kthread+0x10/0x10
[12974.097519] ret_from_fork_asm+0x1a/0x30
[12974.097540] </TASK>
Move the call to spin_lock_init() up a bit to make sure read_dev_clk_lock
is not touched before it's been initialized.
Fixes: 5cb8805d2366 ("idpf: negotiate PTP capabilities and get PTP clock")
Signed-off-by: Emil Tantilov <emil.s.tantilov@intel.com>
Reviewed-by: Madhu Chittim <madhu.chittim@intel.com>
Reviewed-by: Aleksandr Loktionov <aleksandr.loktionov@intel.com>
Reviewed-by: Simon Horman <horms@kernel.org>
Tested-by: Samuel Salin <Samuel.salin@intel.com>
Signed-off-by: Jacob Keller <jacob.e.keller@intel.com>
---
drivers/net/ethernet/intel/idpf/idpf_ptp.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/net/ethernet/intel/idpf/idpf_ptp.c b/drivers/net/ethernet/intel/idpf/idpf_ptp.c
index eec91c4f0a75..4a51d2727547 100644
--- a/drivers/net/ethernet/intel/idpf/idpf_ptp.c
+++ b/drivers/net/ethernet/intel/idpf/idpf_ptp.c
@@ -952,6 +952,8 @@ int idpf_ptp_init(struct idpf_adapter *adapter)
goto free_ptp;
}
+ spin_lock_init(&adapter->ptp->read_dev_clk_lock);
+
err = idpf_ptp_create_clock(adapter);
if (err)
goto free_ptp;
@@ -977,8 +979,6 @@ int idpf_ptp_init(struct idpf_adapter *adapter)
goto remove_clock;
}
- spin_lock_init(&adapter->ptp->read_dev_clk_lock);
-
pci_dbg(adapter->pdev, "PTP init successful\n");
return 0;
--
2.54.0.rc2.531.gaf818d63126a
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox