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

[IPSEC] split xfrm_state_replace + fixes

Split xfrm_state_replace into xfrm_state_add and xfrm_state_replace.

Fixes:
1. Only update update lifetime and encap options if the state is valid.
2. Disallow updates to states that do not exist.
3. Bail if afinfo cannot be found.
 
This brings SADB_UPDATE in line with what is required by RFC2367.
It is also needed by SFS NAT-T support as it needs to update valid
states when the encap ports move.

I've tweaked the logic slightly so that SADB_UPDATE will fail on a
larval state that hasn't undergone SADB_GETSPI.  This is what RFC2367
calls for and it simplifies the code in that we don't have to call
find_acq for SADB_UPDATE.

This doesn't affect any of the three KMs as they either don't use
SADB_UPDATE or call SADB_GETSPI before doing an update.
parent df7c718d
......@@ -767,7 +767,8 @@ extern struct xfrm_state *xfrm_state_find(xfrm_address_t *daddr, xfrm_address_t
unsigned short family);
extern int xfrm_state_check_expire(struct xfrm_state *x);
extern void xfrm_state_insert(struct xfrm_state *x);
extern int xfrm_state_replace(struct xfrm_state *x, int excl);
extern int xfrm_state_add(struct xfrm_state *x);
extern int xfrm_state_update(struct xfrm_state *x);
extern int xfrm_state_check_space(struct xfrm_state *x, struct sk_buff *skb);
extern struct xfrm_state *xfrm_state_lookup(xfrm_address_t *daddr, u32 spi, u8 proto, unsigned short family);
extern struct xfrm_state *xfrm_find_acq_byseq(u32 seq);
......
......@@ -1221,7 +1221,11 @@ static int pfkey_add(struct sock *sk, struct sk_buff *skb, struct sadb_msg *hdr,
if (IS_ERR(x))
return PTR_ERR(x);
err = xfrm_state_replace(x, hdr->sadb_msg_type == SADB_ADD);
if (hdr->sadb_msg_type == SADB_ADD)
err = xfrm_state_add(x);
else
err = xfrm_state_update(x);
if (err < 0) {
x->km.state = XFRM_STATE_DEAD;
xfrm_state_put(x);
......
......@@ -306,7 +306,8 @@ EXPORT_SYMBOL(xfrm_state_alloc);
EXPORT_SYMBOL(__xfrm_state_destroy);
EXPORT_SYMBOL(xfrm_state_find);
EXPORT_SYMBOL(xfrm_state_insert);
EXPORT_SYMBOL(xfrm_state_replace);
EXPORT_SYMBOL(xfrm_state_add);
EXPORT_SYMBOL(xfrm_state_update);
EXPORT_SYMBOL(xfrm_state_check_expire);
EXPORT_SYMBOL(xfrm_state_check_space);
EXPORT_SYMBOL(xfrm_state_lookup);
......
......@@ -395,49 +395,110 @@ void xfrm_state_insert(struct xfrm_state *x)
spin_unlock_bh(&xfrm_state_lock);
}
int xfrm_state_replace(struct xfrm_state *x, int excl)
int xfrm_state_add(struct xfrm_state *x)
{
struct xfrm_state_afinfo *afinfo;
struct xfrm_state *x1;
int err;
afinfo = xfrm_state_get_afinfo(x->props.family);
x1 = NULL;
if (unlikely(afinfo == NULL))
return -EAFNOSUPPORT;
spin_lock_bh(&xfrm_state_lock);
if (afinfo) {
x1 = afinfo->state_lookup(&x->id.daddr, x->id.spi, x->id.proto);
if (!x1) {
x1 = afinfo->find_acq(
x->props.mode, x->props.reqid, x->id.proto,
&x->id.daddr, &x->props.saddr, 0);
if (x1 && x1->id.spi != x->id.spi && x1->id.spi) {
xfrm_state_put(x1);
x1 = NULL;
}
}
if (x1 && (excl ? x1->id.spi : xfrm_state_kern(x1))) {
x1 = afinfo->state_lookup(&x->id.daddr, x->id.spi, x->id.proto);
if (!x1) {
x1 = afinfo->find_acq(
x->props.mode, x->props.reqid, x->id.proto,
&x->id.daddr, &x->props.saddr, 0);
if (x1 && x1->id.spi != x->id.spi && x1->id.spi) {
xfrm_state_put(x1);
x1 = NULL;
err = -EEXIST;
goto out;
}
}
if (x1 && x1->id.spi) {
xfrm_state_put(x1);
x1 = NULL;
err = -EEXIST;
goto out;
}
__xfrm_state_insert(x);
err = 0;
out:
spin_unlock_bh(&xfrm_state_lock);
xfrm_state_put_afinfo(afinfo);
if (x1) {
xfrm_state_delete(x1);
xfrm_state_put(x1);
}
return err;
}
int xfrm_state_update(struct xfrm_state *x)
{
struct xfrm_state_afinfo *afinfo;
struct xfrm_state *x1;
int err;
afinfo = xfrm_state_get_afinfo(x->props.family);
if (unlikely(afinfo == NULL))
return -EAFNOSUPPORT;
spin_lock_bh(&xfrm_state_lock);
x1 = afinfo->state_lookup(&x->id.daddr, x->id.spi, x->id.proto);
err = -ESRCH;
if (!x1)
goto out;
if (xfrm_state_kern(x1)) {
xfrm_state_put(x1);
err = -EEXIST;
goto out;
}
if (x1->km.state == XFRM_STATE_ACQ) {
__xfrm_state_insert(x);
x = NULL;
}
err = 0;
out:
spin_unlock_bh(&xfrm_state_lock);
xfrm_state_put_afinfo(afinfo);
if (err)
return err;
if (!x) {
xfrm_state_delete(x1);
xfrm_state_put(x1);
return 0;
}
err = -EINVAL;
spin_lock_bh(&x1->lock);
if (likely(x1->km.state == XFRM_STATE_VALID)) {
memcpy(x1->encap, x->encap, sizeof(*x1->encap));
memcpy(&x1->lft, &x->lft, sizeof(x1->lft));
x1->km.dying = 0;
err = 0;
}
spin_unlock_bh(&x1->lock);
if (!mod_timer(&x1->timer, jiffies + HZ))
xfrm_state_hold(x1);
if (x1->curlft.use_time)
xfrm_state_check_expire(x1);
xfrm_state_put(x1);
return err;
}
......
......@@ -260,7 +260,11 @@ static int xfrm_add_sa(struct sk_buff *skb, struct nlmsghdr *nlh, void **xfrma)
if (!x)
return err;
err = xfrm_state_replace(x, nlh->nlmsg_type == XFRM_MSG_NEWSA);
if (nlh->nlmsg_type == XFRM_MSG_NEWSA)
err = xfrm_state_add(x);
else
err = xfrm_state_update(x);
if (err < 0) {
x->km.state = XFRM_STATE_DEAD;
xfrm_state_put(x);
......
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