summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHerbert Xu <herbert@gondor.apana.org.au>2003-06-30 22:28:59 +1000
committerDavid S. Miller <davem@nuts.ninka.net>2003-06-30 22:28:59 +1000
commite821464cbc24ece210fc446deac090f8e4aeb220 (patch)
tree9705aec01f3ff74d08c644015738641cb073f77e
parentdf7c718d55a442da38bb0e30e38246178581efaa (diff)
[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.
-rw-r--r--include/net/xfrm.h3
-rw-r--r--net/key/af_key.c6
-rw-r--r--net/netsyms.c3
-rw-r--r--net/xfrm/xfrm_state.c95
-rw-r--r--net/xfrm/xfrm_user.c6
5 files changed, 92 insertions, 21 deletions
diff --git a/include/net/xfrm.h b/include/net/xfrm.h
index 46a7c3be09e8..515f88909601 100644
--- a/include/net/xfrm.h
+++ b/include/net/xfrm.h
@@ -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);
diff --git a/net/key/af_key.c b/net/key/af_key.c
index 7ce95138b67f..c14f2f1a18eb 100644
--- a/net/key/af_key.c
+++ b/net/key/af_key.c
@@ -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);
diff --git a/net/netsyms.c b/net/netsyms.c
index 1e00466b3ee4..aad457bd3d7c 100644
--- a/net/netsyms.c
+++ b/net/netsyms.c
@@ -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);
diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c
index 21846482f4bf..401fc996d9fb 100644
--- a/net/xfrm/xfrm_state.c
+++ b/net/xfrm/xfrm_state.c
@@ -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;
}
diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c
index 50d0eba6061c..70b9cc5d4ed9 100644
--- a/net/xfrm/xfrm_user.c
+++ b/net/xfrm/xfrm_user.c
@@ -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);