xfrm: state: exact mark/mask match for SPI-keyed control-plane SA lookups

Add __xfrm_state_lookup_exact(); wire it into DELSA/GETSA, GETAE/NEWAE,
EXPIRE, MIGRATE_STATE.  xfrm_state_update() (UPDSA) keeps wildcard match
because ALLOCSPI's larval state may be unmarked (mark 0/0), so an
exact-match UPDSA would not find it and the ADD fallback would then
fail with -EEXIST via xfrm_state_add().

Fixes: 3d6acfa764 ("xfrm: SA lookups with mark")
Signed-off-by: Antony Antony <antony.antony@secunet.com>
Signed-off-by: Steffen Klassert <steffen.klassert@secunet.com>
This commit is contained in:
Antony Antony 2026-10-07 11:35:09 +02:00 committed by Steffen Klassert
parent b3056e57b0
commit 6362f459bf
3 changed files with 64 additions and 24 deletions

View File

@ -1748,6 +1748,9 @@ struct xfrm_state *xfrm_state_lookup_byaddr(struct net *net, u32 mark,
const xfrm_address_t *saddr,
u8 proto,
unsigned short family);
struct xfrm_state *xfrm_state_lookup_exact(struct net *net, const struct xfrm_mark *mark,
const xfrm_address_t *daddr, __be32 spi,
u8 proto, unsigned short family);
#ifdef CONFIG_XFRM_SUB_POLICY
void xfrm_tmpl_sort(struct xfrm_tmpl **dst, struct xfrm_tmpl **src, int n,
unsigned short family);

View File

@ -1177,11 +1177,19 @@ static struct xfrm_state *__xfrm_state_lookup_all(const struct xfrm_hash_state_p
return NULL;
}
static struct xfrm_state *__xfrm_state_lookup(const struct xfrm_hash_state_ptrs *state_ptrs,
u32 mark,
const xfrm_address_t *daddr,
__be32 spi, u8 proto,
unsigned short family)
static bool xfrm_state_mark_matches(const struct xfrm_state *x, u32 mark, u32 mask, bool exact)
{
if (exact)
return x->mark.v == mark && x->mark.m == mask;
return (mark & x->mark.m) == x->mark.v;
}
static struct xfrm_state *
__xfrm_state_lookup(const struct xfrm_hash_state_ptrs *state_ptrs,
u32 mark, u32 mask, bool exact,
const xfrm_address_t *daddr,
__be32 spi, u8 proto,
unsigned short family)
{
unsigned int h = __xfrm_spi_hash(daddr, spi, proto, family, state_ptrs->hmask);
struct xfrm_state *x;
@ -1193,7 +1201,7 @@ static struct xfrm_state *__xfrm_state_lookup(const struct xfrm_hash_state_ptrs
!xfrm_addr_equal(&x->id.daddr, daddr, family))
continue;
if ((mark & x->mark.m) != x->mark.v)
if (!xfrm_state_mark_matches(x, mark, mask, exact))
continue;
if (!xfrm_state_hold_rcu(x))
continue;
@ -1203,6 +1211,17 @@ static struct xfrm_state *__xfrm_state_lookup(const struct xfrm_hash_state_ptrs
return NULL;
}
static struct xfrm_state *
__xfrm_state_lookup_exact(const struct xfrm_hash_state_ptrs *state_ptrs,
const struct xfrm_mark *mark,
const xfrm_address_t *daddr,
__be32 spi, u8 proto,
unsigned short family)
{
return __xfrm_state_lookup(state_ptrs, mark->v, mark->m, true,
daddr, spi, proto, family);
}
struct xfrm_state *xfrm_input_state_lookup(struct net *net, u32 mark,
const xfrm_address_t *daddr,
__be32 spi, u8 proto,
@ -1233,7 +1252,7 @@ struct xfrm_state *xfrm_input_state_lookup(struct net *net, u32 mark,
xfrm_hash_ptrs_get(net, &state_ptrs);
x = __xfrm_state_lookup(&state_ptrs, mark, daddr, spi, proto, family);
x = __xfrm_state_lookup(&state_ptrs, mark, 0, false, daddr, spi, proto, family);
if (x) {
spin_lock(&net->xfrm.xfrm_state_lock);
if (x->km.state != XFRM_STATE_VALID) {
@ -1283,7 +1302,7 @@ static struct xfrm_state *__xfrm_state_lookup_byaddr(const struct xfrm_hash_stat
return NULL;
}
static inline struct xfrm_state *
static struct xfrm_state *
__xfrm_state_locate(struct xfrm_state *x, int use_spi, int family)
{
struct xfrm_hash_state_ptrs state_ptrs;
@ -1293,7 +1312,7 @@ __xfrm_state_locate(struct xfrm_state *x, int use_spi, int family)
xfrm_hash_ptrs_get(net, &state_ptrs);
if (use_spi)
return __xfrm_state_lookup(&state_ptrs, mark, &x->id.daddr,
return __xfrm_state_lookup(&state_ptrs, mark, 0, false, &x->id.daddr,
x->id.spi, x->id.proto, family);
else
return __xfrm_state_lookup_byaddr(&state_ptrs, mark,
@ -2380,7 +2399,7 @@ xfrm_state_lookup(struct net *net, u32 mark, const xfrm_address_t *daddr, __be32
rcu_read_lock();
xfrm_hash_ptrs_get(net, &state_ptrs);
x = __xfrm_state_lookup(&state_ptrs, mark, daddr, spi, proto, family);
x = __xfrm_state_lookup(&state_ptrs, mark, 0, false, daddr, spi, proto, family);
rcu_read_unlock();
return x;
}
@ -2404,6 +2423,23 @@ xfrm_state_lookup_byaddr(struct net *net, u32 mark,
}
EXPORT_SYMBOL(xfrm_state_lookup_byaddr);
struct xfrm_state *
xfrm_state_lookup_exact(struct net *net, const struct xfrm_mark *mark,
const xfrm_address_t *daddr, __be32 spi,
u8 proto, unsigned short family)
{
struct xfrm_hash_state_ptrs state_ptrs;
struct xfrm_state *x;
rcu_read_lock();
xfrm_hash_ptrs_get(net, &state_ptrs);
x = __xfrm_state_lookup_exact(&state_ptrs, mark, daddr, spi, proto, family);
rcu_read_unlock();
return x;
}
EXPORT_SYMBOL(xfrm_state_lookup_exact);
struct xfrm_state *
xfrm_find_acq(struct net *net, const struct xfrm_mark *mark, u8 mode, u32 reqid,
u32 if_id, u32 pcpu_num, u8 proto, const xfrm_address_t *daddr,

View File

@ -1109,11 +1109,12 @@ static struct xfrm_state *xfrm_user_state_lookup(struct net *net,
struct xfrm_state *x = NULL;
struct xfrm_mark m;
int err;
u32 mark = xfrm_mark_get(attrs, &m);
xfrm_mark_get(attrs, &m);
if (xfrm_id_proto_match(p->proto, IPSEC_PROTO_ANY)) {
err = -ESRCH;
x = xfrm_state_lookup(net, mark, &p->daddr, p->spi, p->proto, p->family);
x = xfrm_state_lookup_exact(net, &m, &p->daddr, p->spi, p->proto, p->family);
} else {
xfrm_address_t *saddr = NULL;
@ -1124,7 +1125,7 @@ static struct xfrm_state *xfrm_user_state_lookup(struct net *net,
}
err = -ESRCH;
x = xfrm_state_lookup_byaddr(net, mark,
x = xfrm_state_lookup_byaddr(net, m.v & m.m,
&p->daddr, saddr,
p->proto, p->family);
}
@ -2821,14 +2822,13 @@ static int xfrm_get_ae(struct sk_buff *skb, struct nlmsghdr *nlh,
struct sk_buff *r_skb;
int err;
struct km_event c;
u32 mark;
struct xfrm_mark m;
struct xfrm_aevent_id *p = nlmsg_data(nlh);
struct xfrm_usersa_id *id = &p->sa_id;
mark = xfrm_mark_get(attrs, &m);
xfrm_mark_get(attrs, &m);
x = xfrm_state_lookup(net, mark, &id->daddr, id->spi, id->proto, id->family);
x = xfrm_state_lookup_exact(net, &m, &id->daddr, id->spi, id->proto, id->family);
if (x == NULL)
return -ESRCH;
@ -2869,7 +2869,6 @@ static int xfrm_new_ae(struct sk_buff *skb, struct nlmsghdr *nlh,
struct xfrm_state *x;
struct km_event c;
int err = -EINVAL;
u32 mark = 0;
struct xfrm_mark m;
struct xfrm_aevent_id *p = nlmsg_data(nlh);
struct nlattr *rp = attrs[XFRMA_REPLAY_VAL];
@ -2889,9 +2888,10 @@ static int xfrm_new_ae(struct sk_buff *skb, struct nlmsghdr *nlh,
return err;
}
mark = xfrm_mark_get(attrs, &m);
xfrm_mark_get(attrs, &m);
x = xfrm_state_lookup(net, mark, &p->sa_id.daddr, p->sa_id.spi, p->sa_id.proto, p->sa_id.family);
x = xfrm_state_lookup_exact(net, &m, &p->sa_id.daddr, p->sa_id.spi,
p->sa_id.proto, p->sa_id.family);
if (x == NULL)
return -ESRCH;
@ -3025,9 +3025,10 @@ static int xfrm_add_sa_expire(struct sk_buff *skb, struct nlmsghdr *nlh,
struct xfrm_user_expire *ue = nlmsg_data(nlh);
struct xfrm_usersa_info *p = &ue->state;
struct xfrm_mark m;
u32 mark = xfrm_mark_get(attrs, &m);
x = xfrm_state_lookup(net, mark, &p->id.daddr, p->id.spi, p->id.proto, p->family);
xfrm_mark_get(attrs, &m);
x = xfrm_state_lookup_exact(net, &m, &p->id.daddr, p->id.spi, p->id.proto, p->family);
err = -ENOENT;
if (x == NULL)
@ -3402,9 +3403,9 @@ static int xfrm_do_migrate_state(struct sk_buff *skb, struct nlmsghdr *nlh,
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);
x = xfrm_state_lookup_exact(net, &m.old_mark,
&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;