Hi Dave: This patch closes the SADB_ADD race with xfrm_find_acq/xfrm_state_insert. It also realigns the netlink interface with the pfkey interface for this operation. This adds a new XFRM_MSG_UPDSA which does the same thing as SADB_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.5 diff -u -r1.5 xfrm.h --- kernel-source-2.5/include/net/xfrm.h 20 Jun 2003 11:49:07 -0000 1.5 +++ kernel-source-2.5/include/net/xfrm.h 22 Jun 2003 00:18:29 -0000 @@ -767,6 +767,7 @@ 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_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.3 diff -u -r1.3 netsyms.c --- kernel-source-2.5/net/netsyms.c 20 Jun 2003 11:49:08 -0000 1.3 +++ kernel-source-2.5/net/netsyms.c 22 Jun 2003 00:18:38 -0000 @@ -306,6 +306,7 @@ EXPORT_SYMBOL(__xfrm_state_destroy); EXPORT_SYMBOL(xfrm_state_find); EXPORT_SYMBOL(xfrm_state_insert); +EXPORT_SYMBOL(xfrm_state_replace); EXPORT_SYMBOL(xfrm_state_check_expire); EXPORT_SYMBOL(xfrm_state_check_space); EXPORT_SYMBOL(xfrm_state_lookup); 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.5 diff -u -r1.5 xfrm_user.c --- kernel-source-2.5/net/xfrm/xfrm_user.c 20 Jun 2003 11:49:08 -0000 1.5 +++ kernel-source-2.5/net/xfrm/xfrm_user.c 22 Jun 2003 00:23:44 -0000 @@ -249,7 +249,7 @@ static int xfrm_add_sa(struct sk_buff *skb, struct nlmsghdr *nlh, void **xfrma) { struct xfrm_usersa_info *p = NLMSG_DATA(nlh); - struct xfrm_state *x, *x1; + struct xfrm_state *x; int err; err = verify_newsa_info(p, (struct rtattr **) xfrma); @@ -260,16 +260,13 @@ if (!x) return err; - x1 = xfrm_state_lookup(&x->id.daddr, x->id.spi, x->id.proto, x->props.family); - if (x1) { + err = xfrm_state_replace(x, nlh->nlmsg_type == XFRM_MSG_NEWSA); + if (err < 0) { + x->km.state = XFRM_STATE_DEAD; xfrm_state_put(x); - xfrm_state_put(x1); - return -EEXIST; } - xfrm_state_insert(x); - - return 0; + return err; } static int xfrm_del_sa(struct sk_buff *skb, struct nlmsghdr *nlh, void **xfrma) @@ -801,6 +798,7 @@ NLMSG_LENGTH(sizeof(struct xfrm_user_acquire)), /* ACQUIRE */ NLMSG_LENGTH(sizeof(struct xfrm_user_expire)), /* EXPIRE */ NLMSG_LENGTH(sizeof(struct xfrm_userpolicy_info)),/* UPD POLICY */ + NLMSG_LENGTH(sizeof(struct xfrm_usersa_info)), /* UPD SA */ }; static struct xfrm_link { @@ -823,6 +821,7 @@ {}, {}, { .doit = xfrm_add_policy }, + { .doit = xfrm_add_sa, }, }; static int xfrm_done(struct netlink_callback *cb) Index: kernel-source-2.5/include/linux/xfrm.h =================================================================== RCS file: /home/gondolin/herbert/src/CVS/debian/kernel-source-2.5/include/linux/xfrm.h,v retrieving revision 1.1.1.6 diff -u -r1.1.1.6 xfrm.h --- kernel-source-2.5/include/linux/xfrm.h 17 Jun 2003 04:19:42 -0000 1.1.1.6 +++ kernel-source-2.5/include/linux/xfrm.h 21 Jun 2003 23:13:35 -0000 @@ -117,8 +117,9 @@ #define XFRM_MSG_EXPIRE (XFRM_MSG_BASE + 8) #define XFRM_MSG_UPDPOLICY (XFRM_MSG_BASE + 9) +#define XFRM_MSG_UPDSA (XFRM_MSG_BASE + 10) -#define XFRM_MSG_MAX (XFRM_MSG_UPDPOLICY+1) +#define XFRM_MSG_MAX (XFRM_MSG_UPDSA+1) struct xfrm_user_tmpl { struct xfrm_id id; 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.1.1.4 diff -u -r1.1.1.4 xfrm_state.c --- kernel-source-2.5/net/xfrm/xfrm_state.c 17 Jun 2003 04:20:19 -0000 1.1.1.4 +++ kernel-source-2.5/net/xfrm/xfrm_state.c 22 Jun 2003 00:31:09 -0000 @@ -370,11 +370,10 @@ return x; } -void xfrm_state_insert(struct xfrm_state *x) +static void __xfrm_state_insert(struct xfrm_state *x) { unsigned h = xfrm_dst_hash(&x->id.daddr, x->props.family); - spin_lock_bh(&xfrm_state_lock); list_add(&x->bydst, xfrm_state_bydst+h); xfrm_state_hold(x); @@ -386,8 +385,60 @@ if (!mod_timer(&x->timer, jiffies + HZ)) xfrm_state_hold(x); - spin_unlock_bh(&xfrm_state_lock); wake_up(&km_waitq); +} + +void xfrm_state_insert(struct xfrm_state *x) +{ + spin_lock_bh(&xfrm_state_lock); + __xfrm_state_insert(x); + spin_unlock_bh(&xfrm_state_lock); +} + +int xfrm_state_replace(struct xfrm_state *x, int excl) +{ + struct xfrm_state_afinfo *afinfo; + struct xfrm_state *x1; + int err; + + afinfo = xfrm_state_get_afinfo(x->props.family); + x1 = NULL; + + 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))) { + xfrm_state_put(x1); + x1 = NULL; + err = -EEXIST; + goto out; + } + } + + __xfrm_state_insert(x); + err = 0; + +out: + spin_unlock_bh(&xfrm_state_lock); + + if (x1) { + xfrm_state_delete(x1); + xfrm_state_put(x1); + } + + xfrm_state_put_afinfo(afinfo); + return err; } int xfrm_state_check_expire(struct xfrm_state *x) 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.6 diff -u -r1.6 af_key.c --- kernel-source-2.5/net/key/af_key.c 20 Jun 2003 11:49:08 -0000 1.6 +++ kernel-source-2.5/net/key/af_key.c 22 Jun 2003 00:32:48 -0000 @@ -1215,7 +1215,7 @@ struct sk_buff *out_skb; struct sadb_msg *out_hdr; struct xfrm_state *x; - struct xfrm_state *x1; + int err; xfrm_probe_algs(); @@ -1223,31 +1223,11 @@ if (IS_ERR(x)) return PTR_ERR(x); - /* XXX there is race condition */ - x1 = pfkey_xfrm_state_lookup(hdr, ext_hdrs); - if (!x1) { - x1 = xfrm_find_acq(x->props.mode, x->props.reqid, x->id.proto, - &x->id.daddr, - &x->props.saddr, 0, x->props.family); - if (x1 && x1->id.spi != x->id.spi && x1->id.spi) { - xfrm_state_put(x1); - x1 = NULL; - } - } - - if (x1 && ((x1->id.spi && hdr->sadb_msg_type == SADB_ADD) || - (hdr->sadb_msg_type == SADB_UPDATE && xfrm_state_kern(x1)))) { + err = xfrm_state_replace(x, hdr->sadb_msg_type == SADB_ADD); + if (err < 0) { x->km.state = XFRM_STATE_DEAD; xfrm_state_put(x); - xfrm_state_put(x1); - return -EEXIST; - } - - xfrm_state_insert(x); - - if (x1) { - xfrm_state_delete(x1); - xfrm_state_put(x1); + return err; } out_skb = pfkey_xfrm_state2msg(x, 0, 3);