Commit 4d9f62e9 authored by Herbert Xu's avatar Herbert Xu Committed by David S. Miller

[IPSEC]: Find larval SAs by sequence number

When larval states are generated along with ACQUIRE messages, we should
use the sequence to find the corresponding larval state when creating
states with ADD_SA or ALLOC_SPI.

If we don't do that, then it may take down an unrelated larval state
with the same parameters (think different TCP sessions).  This not only
leaves behind a larval state that shouldn't be there, it may also cause
another ACQUIRE message to be sent unnecessarily.
Signed-off-by: default avatarHerbert Xu <herbert@gondor.apana.org.au>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 52eb5487
...@@ -896,4 +896,17 @@ typedef void (icv_update_fn_t)(struct crypto_tfm *, struct scatterlist *, unsign ...@@ -896,4 +896,17 @@ typedef void (icv_update_fn_t)(struct crypto_tfm *, struct scatterlist *, unsign
extern void skb_icv_walk(const struct sk_buff *skb, struct crypto_tfm *tfm, extern void skb_icv_walk(const struct sk_buff *skb, struct crypto_tfm *tfm,
int offset, int len, icv_update_fn_t icv_update); int offset, int len, icv_update_fn_t icv_update);
static inline int xfrm_addr_cmp(xfrm_address_t *a, xfrm_address_t *b,
int family)
{
switch (family) {
default:
case AF_INET:
return a->a4 - b->a4;
case AF_INET6:
return ipv6_addr_cmp((struct in6_addr *)a,
(struct in6_addr *)b);
}
}
#endif /* _NET_XFRM_H */ #endif /* _NET_XFRM_H */
...@@ -1156,7 +1156,16 @@ static int pfkey_getspi(struct sock *sk, struct sk_buff *skb, struct sadb_msg *h ...@@ -1156,7 +1156,16 @@ static int pfkey_getspi(struct sock *sk, struct sk_buff *skb, struct sadb_msg *h
break; break;
#endif #endif
} }
if (xdaddr)
if (hdr->sadb_msg_seq) {
x = xfrm_find_acq_byseq(hdr->sadb_msg_seq);
if (x && xfrm_addr_cmp(&x->id.daddr, xdaddr, family)) {
xfrm_state_put(x);
x = NULL;
}
}
if (!x)
x = xfrm_find_acq(mode, reqid, proto, xdaddr, xsaddr, 1, family); x = xfrm_find_acq(mode, reqid, proto, xdaddr, xsaddr, 1, family);
if (x == NULL) if (x == NULL)
......
...@@ -387,13 +387,17 @@ void xfrm_state_insert(struct xfrm_state *x) ...@@ -387,13 +387,17 @@ void xfrm_state_insert(struct xfrm_state *x)
spin_unlock_bh(&xfrm_state_lock); spin_unlock_bh(&xfrm_state_lock);
} }
static struct xfrm_state *__xfrm_find_acq_byseq(u32 seq);
int xfrm_state_add(struct xfrm_state *x) int xfrm_state_add(struct xfrm_state *x)
{ {
struct xfrm_state_afinfo *afinfo; struct xfrm_state_afinfo *afinfo;
struct xfrm_state *x1; struct xfrm_state *x1;
int family;
int err; int err;
afinfo = xfrm_state_get_afinfo(x->props.family); family = x->props.family;
afinfo = xfrm_state_get_afinfo(family);
if (unlikely(afinfo == NULL)) if (unlikely(afinfo == NULL))
return -EAFNOSUPPORT; return -EAFNOSUPPORT;
...@@ -407,9 +411,18 @@ int xfrm_state_add(struct xfrm_state *x) ...@@ -407,9 +411,18 @@ int xfrm_state_add(struct xfrm_state *x)
goto out; goto out;
} }
x1 = afinfo->find_acq( if (x->km.seq) {
x->props.mode, x->props.reqid, x->id.proto, x1 = __xfrm_find_acq_byseq(x->km.seq);
&x->id.daddr, &x->props.saddr, 0); if (x1 && xfrm_addr_cmp(&x1->id.daddr, &x->id.daddr, family)) {
xfrm_state_put(x1);
x1 = NULL;
}
}
if (!x1)
x1 = afinfo->find_acq(
x->props.mode, x->props.reqid, x->id.proto,
&x->id.daddr, &x->props.saddr, 0);
__xfrm_state_insert(x); __xfrm_state_insert(x);
err = 0; err = 0;
...@@ -570,12 +583,11 @@ xfrm_find_acq(u8 mode, u32 reqid, u8 proto, ...@@ -570,12 +583,11 @@ xfrm_find_acq(u8 mode, u32 reqid, u8 proto,
/* Silly enough, but I'm lazy to build resolution list */ /* Silly enough, but I'm lazy to build resolution list */
struct xfrm_state * xfrm_find_acq_byseq(u32 seq) static struct xfrm_state *__xfrm_find_acq_byseq(u32 seq)
{ {
int i; int i;
struct xfrm_state *x; struct xfrm_state *x;
spin_lock_bh(&xfrm_state_lock);
for (i = 0; i < XFRM_DST_HSIZE; i++) { for (i = 0; i < XFRM_DST_HSIZE; i++) {
list_for_each_entry(x, xfrm_state_bydst+i, bydst) { list_for_each_entry(x, xfrm_state_bydst+i, bydst) {
if (x->km.seq == seq) { if (x->km.seq == seq) {
...@@ -585,9 +597,18 @@ struct xfrm_state * xfrm_find_acq_byseq(u32 seq) ...@@ -585,9 +597,18 @@ struct xfrm_state * xfrm_find_acq_byseq(u32 seq)
} }
} }
} }
spin_unlock_bh(&xfrm_state_lock);
return NULL; return NULL;
} }
struct xfrm_state *xfrm_find_acq_byseq(u32 seq)
{
struct xfrm_state *x;
spin_lock_bh(&xfrm_state_lock);
x = __xfrm_find_acq_byseq(seq);
spin_unlock_bh(&xfrm_state_lock);
return x;
}
u32 xfrm_get_acqseq(void) u32 xfrm_get_acqseq(void)
{ {
......
...@@ -470,16 +470,32 @@ static int xfrm_alloc_userspi(struct sk_buff *skb, struct nlmsghdr *nlh, void ** ...@@ -470,16 +470,32 @@ static int xfrm_alloc_userspi(struct sk_buff *skb, struct nlmsghdr *nlh, void **
struct xfrm_state *x; struct xfrm_state *x;
struct xfrm_userspi_info *p; struct xfrm_userspi_info *p;
struct sk_buff *resp_skb; struct sk_buff *resp_skb;
xfrm_address_t *daddr;
int family;
int err; int err;
p = NLMSG_DATA(nlh); p = NLMSG_DATA(nlh);
err = verify_userspi_info(p); err = verify_userspi_info(p);
if (err) if (err)
goto out_noput; goto out_noput;
x = xfrm_find_acq(p->info.mode, p->info.reqid, p->info.id.proto,
&p->info.id.daddr, family = p->info.family;
&p->info.saddr, 1, daddr = &p->info.id.daddr;
p->info.family);
x = NULL;
if (p->info.seq) {
x = xfrm_find_acq_byseq(p->info.seq);
if (x && xfrm_addr_cmp(&x->id.daddr, daddr, family)) {
xfrm_state_put(x);
x = NULL;
}
}
if (!x)
x = xfrm_find_acq(p->info.mode, p->info.reqid,
p->info.id.proto, daddr,
&p->info.saddr, 1,
family);
err = -ENOENT; err = -ENOENT;
if (x == NULL) if (x == NULL)
goto out_noput; goto out_noput;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment