Hello! > The outgoing SA however is updated using pk_sendadd We traced all this today. It was not true reason of bad behaviour, real mistake was in absolutely different place. The patch (not incremental wrt patch of yesterday, so backout that one). Log message for Dave: - xfrm_state.c: never return mature SAs on getspi. - af_key.c: do not forget to delete dummy super-larvals when they are resolved - af_key.c: wow! specially for this case I added gfp argument to xfrm_alloc_policy() and forgot to use it really. You will see blames on failed add and it looks like expected behaviour of racoon, at least it makes the same thing working on freebsd. It may be a bug, I will look more, but now I think it is deliberate race breaker and failure of this add is right thing. > This brings us to the point that everything I try works. Key rollover is now > completely seamless. No, really. The trace showed another problem: one of them looks like a bug in racoon namely, after SA internal to IKE expires racoon does not initiate new connection to peer when some real kernel SA soft expires (well, it is right, soft expire does not mean that data flow is present), so it wait for hard expire and only after this reconnects to peer IKE. It results in short gap in service. I will analyze this, probably, it is due to the fact, that we store that thing which is required by rfc in use_time. It is almost harmells, rare enough even when lifetime is very short. Alexey ===== include/net/xfrm.h 1.5 vs edited ===== --- 1.5/include/net/xfrm.h Tue Nov 12 02:37:12 2002 +++ edited/include/net/xfrm.h Wed Nov 13 03:38:20 2002 @@ -473,7 +473,7 @@ struct xfrm_policy *xfrm_policy_byid(int dir, u32 id, int delete); void xfrm_policy_flush(void); void xfrm_alloc_spi(struct xfrm_state *x, u32 minspi, u32 maxspi); -struct xfrm_state * xfrm_find_acq(u8 mode, u16 reqid, u8 proto, u32 daddr, u32 saddr); +struct xfrm_state * xfrm_find_acq(u8 mode, u16 reqid, u8 proto, u32 daddr, u32 saddr, int create); extern void xfrm_policy_flush(void); extern void xfrm_policy_kill(struct xfrm_policy *); extern int xfrm_sk_policy_insert(struct sock *sk, int dir, struct xfrm_policy *pol); ===== net/ipv4/xfrm_state.c 1.6 vs edited ===== --- 1.6/net/ipv4/xfrm_state.c Tue Nov 12 02:37:12 2002 +++ edited/net/ipv4/xfrm_state.c Wed Nov 13 23:09:43 2002 @@ -386,7 +386,7 @@ } struct xfrm_state * -xfrm_find_acq(u8 mode, u16 reqid, u8 proto, u32 daddr, u32 saddr) +xfrm_find_acq(u8 mode, u16 reqid, u8 proto, u32 daddr, u32 saddr, int create) { struct xfrm_state *x, *x0; unsigned h = ntohl(daddr); @@ -400,10 +400,11 @@ mode == x->props.mode && proto == x->id.proto && saddr == x->props.saddr.xfrm4_addr && - reqid == x->props.reqid) { + reqid == x->props.reqid && + x->km.state == XFRM_STATE_ACQ) { if (!x0) x0 = x; - if (x->km.state != XFRM_STATE_ACQ) + if (x->id.spi) continue; x0 = x; break; @@ -411,7 +412,7 @@ } if (x0) { atomic_inc(&x0->refcnt); - } else if ((x0 = xfrm_state_alloc()) != NULL) { + } else if (create && (x0 = xfrm_state_alloc()) != NULL) { x0->sel.daddr.xfrm4_addr = daddr; x0->sel.daddr.xfrm4_mask = ~0; x0->sel.saddr.xfrm4_addr = saddr; ===== net/key/af_key.c 1.7 vs edited ===== --- 1.7/net/key/af_key.c Tue Nov 12 02:37:12 2002 +++ edited/net/key/af_key.c Wed Nov 13 23:09:44 2002 @@ -528,8 +528,7 @@ switch (((struct sockaddr *)(addr + 1))->sa_family) { case AF_INET: - x = xfrm_state_lookup( - ((struct sockaddr_in*)(addr + 1))->sin_addr.s_addr, + x = xfrm_state_lookup(((struct sockaddr_in*)(addr + 1))->sin_addr.s_addr, sa->sadb_sa_spi, proto); break; case AF_INET6: @@ -1043,7 +1042,7 @@ daddr = (struct sockaddr_in*)(addr + 1); x = xfrm_find_acq(mode, reqid, proto, daddr->sin_addr.s_addr, - saddr->sin_addr.s_addr); + saddr->sin_addr.s_addr, 1); if (x == NULL) return -ENOENT; @@ -1122,7 +1121,17 @@ /* XXX there is race condition */ x1 = pfkey_xfrm_state_lookup(hdr, ext_hdrs); - if (x1 && hdr->sadb_msg_type == SADB_ADD) { + if (!x1) { + x1 = xfrm_find_acq(x->props.mode, x->props.reqid, x->id.proto, + x->id.daddr.xfrm4_addr, + x->props.saddr.xfrm4_addr, 0); + 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) { x->km.state = XFRM_STATE_DEAD; xfrm_state_put(x); xfrm_state_put(x1); @@ -1131,7 +1140,7 @@ xfrm_state_insert(x); - if (x1 && hdr->sadb_msg_type != SADB_ADD) { + if (x1) { xfrm_state_delete(x1); xfrm_state_put(x1); } @@ -2156,7 +2165,7 @@ (!pol->sadb_x_policy_dir || pol->sadb_x_policy_dir > IPSEC_DIR_OUTBOUND)) return NULL; - xp = xfrm_policy_alloc(GFP_KERNEL); + xp = xfrm_policy_alloc(GFP_ATOMIC); if (xp == NULL) { *dir = -ENOBUFS; return NULL;