On Sun, Jun 29, 2003 at 10:47:45PM +1000, herbert wrote: > On Sun, Jun 29, 2003 at 10:42:54PM +1000, James Morris wrote: > > > > The logic in this function is starting to look pretty tortured. > > Indeed, perhaps we should split it into add and replace. Here is the patch to do that. 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. Cheers, -- Debian GNU/Linux 3.0 is out! ( http://www.debian.org/ ) Email: Herbert Xu ~{PmV>HI~} <herbert@gondor.apana.org.au> Home Page: http://gondor.apana.org.au/~herbert/ PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt
Index: kernel-source-2.5/include/net/xfrm.h =================================================================== RCS file: /home/gondolin/herbert/src/CVS/debian/kernel-source-2.5/include/net/xfrm.h,v retrieving revision 1.6 diff -u -r1.6 xfrm.h --- kernel-source-2.5/include/net/xfrm.h 28 Jun 2003 00:28:01 -0000 1.6 +++ kernel-source-2.5/include/net/xfrm.h 29 Jun 2003 12:57:53 -0000 @@ -767,7 +767,8 @@ 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); Index: kernel-source-2.5/net/netsyms.c =================================================================== RCS file: /home/gondolin/herbert/src/CVS/debian/kernel-source-2.5/net/netsyms.c,v retrieving revision 1.4 diff -u -r1.4 netsyms.c --- kernel-source-2.5/net/netsyms.c 28 Jun 2003 00:31:10 -0000 1.4 +++ kernel-source-2.5/net/netsyms.c 29 Jun 2003 12:58:10 -0000 @@ -306,7 +306,8 @@ 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); Index: kernel-source-2.5/net/key/af_key.c =================================================================== RCS file: /home/gondolin/herbert/src/CVS/debian/kernel-source-2.5/net/key/af_key.c,v retrieving revision 1.7 diff -u -r1.7 af_key.c --- kernel-source-2.5/net/key/af_key.c 28 Jun 2003 00:28:01 -0000 1.7 +++ kernel-source-2.5/net/key/af_key.c 29 Jun 2003 12:58:52 -0000 @@ -1223,7 +1223,11 @@ 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); Index: kernel-source-2.5/net/xfrm/xfrm_state.c =================================================================== RCS file: /home/gondolin/herbert/src/CVS/debian/kernel-source-2.5/net/xfrm/xfrm_state.c,v retrieving revision 1.2 diff -u -r1.2 xfrm_state.c --- kernel-source-2.5/net/xfrm/xfrm_state.c 28 Jun 2003 00:28:01 -0000 1.2 +++ kernel-source-2.5/net/xfrm/xfrm_state.c 29 Jun 2003 13:14:48 -0000 @@ -395,49 +395,110 @@ 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; } Index: kernel-source-2.5/net/xfrm/xfrm_user.c =================================================================== RCS file: /home/gondolin/herbert/src/CVS/debian/kernel-source-2.5/net/xfrm/xfrm_user.c,v retrieving revision 1.6 diff -u -r1.6 xfrm_user.c --- kernel-source-2.5/net/xfrm/xfrm_user.c 28 Jun 2003 00:28:01 -0000 1.6 +++ kernel-source-2.5/net/xfrm/xfrm_user.c 29 Jun 2003 12:59:17 -0000 @@ -260,7 +260,11 @@ 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);