The existing code does not allow migration from UDP encapsulation to non-encapsulation (ESP). This is useful when migrating from behind a NAT to no NAT, or from IPv4 with NAT to IPv6 without NAT. With this fix, while migrating state, the existing encap will be copied only if the migrate call includes the encap attribute. Which fixes tag should I add? Fixes: 80c9abaabf42 ("[XFRM]: Extension for dynamic update of endpoint address(es)") ? or Fixes: 4ab47d47af20 ("xfrm: extend MIGRATE with UDP encapsulation port") ? Signed-off-by: Antony Antony --- 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 7f70ea7f4d46..1770b56c8587 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.39.5 In prepreation for the following patch rename s/reqid/old_reqid/. Signed-off-by: Antony Antony --- 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 f3014e4f54fc..ae35a0499168 100644 --- a/include/net/xfrm.h +++ b/include/net/xfrm.h @@ -684,7 +684,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 571200433aa9..7d5cf386654c 100644 --- a/net/key/af_key.c +++ b/net/key/af_key.c @@ -2538,7 +2538,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)); @@ -3634,15 +3634,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 62486f866975..854dfc16ed55 100644 --- a/net/xfrm/xfrm_policy.c +++ b/net/xfrm/xfrm_policy.c @@ -4530,7 +4530,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: @@ -4624,7 +4624,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 1770b56c8587..62ccdf35cd0e 100644 --- a/net/xfrm/xfrm_state.c +++ b/net/xfrm/xfrm_state.c @@ -2079,14 +2079,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, net->xfrm.state_bydst+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 010c9e6638c0..027e9ad10b45 100644 --- a/net/xfrm/xfrm_user.c +++ b/net/xfrm/xfrm_user.c @@ -3081,7 +3081,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; @@ -3164,7 +3164,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.39.5 new method to support single xfrm_state migration. Besides exiting migration (SA + policy), this also support changing reqid. Signed-off-by: Antony Antony --- include/net/xfrm.h | 5 +- include/uapi/linux/xfrm.h | 10 +++ net/xfrm/xfrm_replay.c | 16 ++++ net/xfrm/xfrm_state.c | 18 ++-- net/xfrm/xfrm_user.c | 161 +++++++++++++++++++++++++++++++++++- security/selinux/nlmsgtab.c | 3 +- 6 files changed, 202 insertions(+), 11 deletions(-) diff --git a/include/net/xfrm.h b/include/net/xfrm.h index ae35a0499168..6a6babcdcd09 100644 --- a/include/net/xfrm.h +++ b/include/net/xfrm.h @@ -685,6 +685,7 @@ struct xfrm_migrate { u8 mode; u16 reserved; u32 old_reqid; + u32 new_reqid; u16 old_family; u16 new_family; }; @@ -1906,6 +1907,7 @@ int xfrm_migrate(const struct xfrm_selector *sel, u8 dir, u8 type, struct xfrm_encap_tmpl *encap, u32 if_id, struct netlink_ext_ack *extack, struct xfrm_user_offload *xuo); +void xfrm_sync_oseq(struct xfrm_state *x, struct xfrm_state *orig); #endif int km_new_mapping(struct xfrm_state *x, xfrm_address_t *ipaddr, __be16 sport); @@ -2011,8 +2013,7 @@ 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) +static inline int xfrm_replay_clone(struct xfrm_state *x, struct xfrm_state *orig) { x->replay_esn = kmemdup(orig->replay_esn, diff --git a/include/uapi/linux/xfrm.h b/include/uapi/linux/xfrm.h index a23495c0e0a1..df75f7c2b7f7 100644 --- a/include/uapi/linux/xfrm.h +++ b/include/uapi/linux/xfrm.h @@ -227,6 +227,8 @@ 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 +509,14 @@ struct xfrm_user_migrate { __u16 new_family; }; +struct xfrm_user_migrate_state { + struct xfrm_usersa_id id; + xfrm_address_t new_saddr; + xfrm_address_t new_daddr; + __u16 new_family; + __u32 new_reqid; +}; + struct xfrm_user_mapping { struct xfrm_usersa_id id; __u32 reqid; diff --git a/net/xfrm/xfrm_replay.c b/net/xfrm/xfrm_replay.c index dbdf8a39dffe..3404c03a8590 100644 --- a/net/xfrm/xfrm_replay.c +++ b/net/xfrm/xfrm_replay.c @@ -797,3 +797,19 @@ int xfrm_init_replay(struct xfrm_state *x, struct netlink_ext_ack *extack) return 0; } EXPORT_SYMBOL(xfrm_init_replay); + +void xfrm_sync_oseq(struct xfrm_state *x, struct xfrm_state *orig) +{ + switch (x->repl_mode) { + case XFRM_REPLAY_MODE_LEGACY: + x->replay.oseq = orig->replay.oseq; + break; + + case XFRM_REPLAY_MODE_BMP: + case XFRM_REPLAY_MODE_ESN: + x->replay_esn->oseq = orig->replay_esn->oseq; + x->replay_esn->oseq_hi = orig->replay_esn->oseq_hi; + break; + } +} +EXPORT_SYMBOL(xfrm_sync_oseq); diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c index 62ccdf35cd0e..17c3de65fb00 100644 --- a/net/xfrm/xfrm_state.c +++ b/net/xfrm/xfrm_state.c @@ -1966,7 +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) + struct xfrm_migrate *m, + struct netlink_ext_ack *extack) { struct net *net = xs_net(orig); struct xfrm_state *x = xfrm_state_alloc(net); @@ -1978,7 +1979,6 @@ static struct xfrm_state *xfrm_state_clone_and_setup(struct xfrm_state *orig, 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; x->props.saddr = orig->props.saddr; if (orig->aalg) { @@ -2058,7 +2058,10 @@ static struct xfrm_state *xfrm_state_clone_and_setup(struct xfrm_state *orig, goto error; } - + if (orig->props.reqid != m->new_reqid) + x->props.reqid = m->new_reqid; + else + x->props.reqid = orig->props.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)); @@ -2132,7 +2135,7 @@ struct xfrm_state *xfrm_state_migrate(struct xfrm_state *x, { struct xfrm_state *xc; - xc = xfrm_state_clone_and_setup(x, encap, m); + xc = xfrm_state_clone_and_setup(x, encap, m, extack); if (!xc) return NULL; @@ -2144,9 +2147,10 @@ struct xfrm_state *xfrm_state_migrate(struct xfrm_state *x, goto error; /* add state */ - 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 */ + if (xfrm_addr_equal(&x->id.daddr, &m->new_daddr, m->new_family) || + x->props.reqid != xc->props.reqid) { + /* a care is needed when the destination address or the reqid + * 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) diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c index 027e9ad10b45..cc5c816f01ed 100644 --- a/net/xfrm/xfrm_user.c +++ b/net/xfrm/xfrm_user.c @@ -3046,6 +3046,22 @@ static int xfrm_add_acquire(struct sk_buff *skb, struct nlmsghdr *nlh, } #ifdef CONFIG_XFRM_MIGRATE +static int copy_from_user_migrate_state(struct xfrm_migrate *ma, + 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; + + return 0; +} + static int copy_from_user_migrate(struct xfrm_migrate *ma, struct xfrm_kmaddress *k, struct nlattr **attrs, int *num, @@ -3148,7 +3164,149 @@ 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 *m, + const struct xfrm_encap_tmpl *encap, + const struct xfrm_user_offload *xuo) +{ + int err; + struct nlmsghdr *nlh; + struct xfrm_user_migrate_state *um; + + nlh = nlmsg_put(skb, 0, 0, XFRM_MSG_MIGRATE_STATE, + sizeof(struct xfrm_user_migrate_state), 0); + if (!nlh) + return -EMSGSIZE; + + um = nlmsg_data(nlh); + memset(um, 0, sizeof(*um)); + memcpy(um, m, sizeof(*um)); + + if (encap) { + err = nla_put(skb, XFRMA_ENCAP, sizeof(*encap), encap); + if (err) + goto out_cancel; + } + + if (xuo) { + err = nla_put(skb, XFRMA_OFFLOAD_DEV, sizeof(*xuo), xuo); + if (err) + goto out_cancel; + } + + nlmsg_end(skb, nlh); + return 0; + +out_cancel: + nlmsg_cancel(skb, nlh); + return err; +} + +static inline unsigned int xfrm_migrate_state_msgsize(bool with_encp) +{ + return NLMSG_ALIGN(sizeof(struct xfrm_user_migrate_state)) + + (with_encp ? nla_total_size(sizeof(struct xfrm_encap_tmpl)) : 0); +} + +static int xfrm_send_migrate_state(const struct xfrm_user_migrate_state *um, + const struct xfrm_encap_tmpl *encap, + const struct xfrm_user_offload *xuo) +{ + int err; + struct sk_buff *skb; + struct net *net = &init_net; + + skb = nlmsg_new(xfrm_migrate_state_msgsize(!!encap), GFP_ATOMIC); + if (!skb) + return -ENOMEM; + + err = build_migrate_state(skb, um, encap, xuo); + if (err < 0) { + WARN_ON(1); + 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) +{ + int err = -ESRCH; + struct xfrm_state *x; + struct xfrm_encap_tmpl *encap = NULL; + struct xfrm_user_offload *xuo = NULL; + struct xfrm_migrate m = { .old_saddr.a4 = 0,}; + struct net *net = sock_net(skb->sk); + struct xfrm_user_migrate_state *um = nlmsg_data(nlh); + + if (!um->id.spi) { + NL_SET_ERR_MSG(extack, "Invalid SPI 0x0"); + return -EINVAL; + } + + err = copy_from_user_migrate_state(&m, um); + if (err) + return err; + + x = xfrm_user_state_lookup(net, &um->id, attrs, &err); + + if (x) { + struct xfrm_state *xc; + + if (!x->dir) { + NL_SET_ERR_MSG(extack, "State direction is invalid"); + err = -EINVAL; + goto error; + } + + if (attrs[XFRMA_ENCAP]) { + encap = kmemdup(nla_data(attrs[XFRMA_ENCAP]), + sizeof(*encap), GFP_KERNEL); + if (!encap) { + err = -ENOMEM; + goto error; + } + } + if (attrs[XFRMA_OFFLOAD_DEV]) { + xuo = kmemdup(nla_data(attrs[XFRMA_OFFLOAD_DEV]), + sizeof(*xuo), GFP_KERNEL); + if (!xuo) { + err = -ENOMEM; + goto error; + } + } + xc = xfrm_state_migrate(x, &m, encap, net, xuo, extack); + if (xc) { + xfrm_state_delete(x); + if (x->dir == XFRM_SA_DIR_OUT) + xfrm_sync_oseq(xc, x); + xfrm_send_migrate_state(um, encap, xuo); + } else { + if (extack && !extack->_msg) + NL_SET_ERR_MSG(extack, "State migration clone failed"); + err = -EINVAL; + } + } else { + NL_SET_ERR_MSG(extack, "Can not find state"); + return err; + } +error: + xfrm_state_put(x); + kfree(encap); + kfree(xuo); + 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) { @@ -3213,7 +3371,6 @@ static int build_migrate(struct sk_buff *skb, const struct xfrm_migrate *m, return -EMSGSIZE; pol_id = nlmsg_data(nlh); - /* copy data from selector, dir, and type to the pol_id */ memset(pol_id, 0, sizeof(*pol_id)); memcpy(&pol_id->sel, sel, sizeof(pol_id->sel)); pol_id->dir = dir; @@ -3301,6 +3458,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); @@ -3394,6 +3552,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..9cec74c317f0 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.39.5 During the XFRM_MSG_MIGRATE the reqid remains invariant. Signed-off-by: Antony Antony --- net/xfrm/xfrm_state.c | 2 +- net/xfrm/xfrm_user.c | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c index 17c3de65fb00..91e0898c458f 100644 --- a/net/xfrm/xfrm_state.c +++ b/net/xfrm/xfrm_state.c @@ -2148,7 +2148,7 @@ struct xfrm_state *xfrm_state_migrate(struct xfrm_state *x, /* add state */ if (xfrm_addr_equal(&x->id.daddr, &m->new_daddr, m->new_family) || - x->props.reqid != xc->props.reqid) { + x->props.reqid != xc->props.reqid) { /* a care is needed when the destination address or the reqid * of the state is to be updated as it is a part of triplet */ xfrm_state_insert(xc); diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c index cc5c816f01ed..b14a11b74788 100644 --- a/net/xfrm/xfrm_user.c +++ b/net/xfrm/xfrm_user.c @@ -3098,6 +3098,7 @@ static int copy_from_user_migrate(struct xfrm_migrate *ma, ma->proto = um->proto; ma->mode = um->mode; ma->old_reqid = um->reqid; + ma->new_reqid = um->reqid; /* reqid is invariant in XFRM_MSG_MIGRATE */ ma->old_family = um->old_family; ma->new_family = um->new_family; @@ -3285,7 +3286,7 @@ static int xfrm_do_migrate_state(struct sk_buff *skb, struct nlmsghdr *nlh, xfrm_send_migrate_state(um, encap, xuo); } else { if (extack && !extack->_msg) - NL_SET_ERR_MSG(extack, "State migration clone failed"); + NL_SET_ERR_MSG(extack, "State migration clone failed"); err = -EINVAL; } } else { -- 2.39.5 During migration, a state/SA may have been deleted or become invalid. Since skb(s) may still hold a reference to this deleted state, it could be reused inadvertently. Using a migrated SA could result in reusing the same IV for AES-GCM, which must be avoided for an output SA. Add a check to ensure the state is in XFRM_STATE_VALID before use. This check is useful for both output and input data paths. Call chain: xfrm_output_one() xfrm_state_check_expire() if (x->km.state != XFRM_STATE_VALID) ---> new check return -EINVAL; encapsulate the packet Signed-off-by: Antony Antony --- net/xfrm/xfrm_state.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c index 91e0898c458f..c9c5a2daa86f 100644 --- a/net/xfrm/xfrm_state.c +++ b/net/xfrm/xfrm_state.c @@ -2265,6 +2265,9 @@ EXPORT_SYMBOL(xfrm_state_update); int xfrm_state_check_expire(struct xfrm_state *x) { + if (x->km.state != XFRM_STATE_VALID) + return -EINVAL; + /* All counters which are needed to decide if state is expired * are handled by SW for non-packet offload modes. Simply skip * the following update and save extra boilerplate in drivers. -- 2.39.5