summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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);