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 has no mark (mask=0), so exact-match UPDSA
can't find it and the ADD fallback trips -EEXIST via xfrm_state_add().
Fixes: 3d6acfa7641f ("xfrm: SA lookups with mark")
Signed-off-by: Antony Antony <[email protected]>
---
v1->v2: UPDSA keep the wildcard lookup
---
include/net/xfrm.h | 3 +++
net/xfrm/xfrm_state.c | 56 ++++++++++++++++++++++++++++++++++++++++++---------
net/xfrm/xfrm_user.c | 29 +++++++++++++-------------
3 files changed, 64 insertions(+), 24 deletions(-)
diff --git a/include/net/xfrm.h b/include/net/xfrm.h
index a6d69aaa6cd2..9e053388aa4b 100644
--- a/include/net/xfrm.h
+++ b/include/net/xfrm.h
@@ -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);
diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c
index e45aa1ed5b96..b7ea1060dac9 100644
--- a/net/xfrm/xfrm_state.c
+++ b/net/xfrm/xfrm_state.c
@@ -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 & mask) && 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,
@@ -2383,7 +2402,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;
}
@@ -2407,6 +2426,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,
diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c
index a2587c7e796b..fffce22389e8 100644
--- a/net/xfrm/xfrm_user.c
+++ b/net/xfrm/xfrm_user.c
@@ -1089,11 +1089,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;
@@ -1104,7 +1105,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);
}
@@ -2795,14 +2796,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;
@@ -2843,7 +2843,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];
@@ -2863,9 +2862,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;
@@ -2999,9 +2999,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)
@@ -3372,9 +3373,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;
--
2.47.3