diff options
author | Markus Friedl <markus@cvs.openbsd.org> | 2004-11-25 15:32:09 +0000 |
---|---|---|
committer | Markus Friedl <markus@cvs.openbsd.org> | 2004-11-25 15:32:09 +0000 |
commit | f7a08eb995954e1589ac898355fdd7c49742f0a8 (patch) | |
tree | d44ae27476029ee8d3d991f37c34322339a97014 /sys/netinet | |
parent | 7c9e4c2086c4479dbec08dd4d1e199a9864ee907 (diff) |
fix for race between invocation for timer and network input
1) add a reaper for TCP and SYN cache states (cf. netbsd pr 20390)
2) additional check for TCP_TIMER_ISARMED(TCPT_REXMT) in tcp_timer_persist()
with mickey@; ok deraadt@
Diffstat (limited to 'sys/netinet')
-rw-r--r-- | sys/netinet/tcp_input.c | 22 | ||||
-rw-r--r-- | sys/netinet/tcp_subr.c | 21 | ||||
-rw-r--r-- | sys/netinet/tcp_timer.c | 23 | ||||
-rw-r--r-- | sys/netinet/tcp_var.h | 7 |
4 files changed, 66 insertions, 7 deletions
diff --git a/sys/netinet/tcp_input.c b/sys/netinet/tcp_input.c index 57fc27065f6..83acaa05bed 100644 --- a/sys/netinet/tcp_input.c +++ b/sys/netinet/tcp_input.c @@ -1,4 +1,4 @@ -/* $OpenBSD: tcp_input.c,v 1.177 2004/10/28 19:22:52 mcbride Exp $ */ +/* $OpenBSD: tcp_input.c,v 1.178 2004/11/25 15:32:08 markus Exp $ */ /* $NetBSD: tcp_input.c,v 1.23 1996/02/13 23:43:44 christos Exp $ */ /* @@ -3205,6 +3205,7 @@ do { \ #define SYN_CACHE_RM(sc) \ do { \ + (sc)->sc_flags |= SCF_DEAD; \ TAILQ_REMOVE(&tcp_syn_cache[(sc)->sc_bucketidx].sch_bucket, \ (sc), sc_bucketq); \ (sc)->sc_tp = NULL; \ @@ -3220,7 +3221,8 @@ do { \ (void) m_free((sc)->sc_ipopts); \ if ((sc)->sc_route4.ro_rt != NULL) \ RTFREE((sc)->sc_route4.ro_rt); \ - pool_put(&syn_cache_pool, (sc)); \ + timeout_set(&(sc)->sc_timer, syn_cache_reaper, (sc)); \ + timeout_add(&(sc)->sc_timer, 0); \ } while (/*CONSTCOND*/0) struct pool syn_cache_pool; @@ -3366,6 +3368,10 @@ syn_cache_timer(void *arg) int s; s = splsoftnet(); + if (sc->sc_flags & SCF_DEAD) { + splx(s); + return; + } if (__predict_false(sc->sc_rxtshift == TCP_MAXRXTSHIFT)) { /* Drop it -- too many retransmissions. */ @@ -3398,6 +3404,18 @@ syn_cache_timer(void *arg) splx(s); } +void +syn_cache_reaper(void *arg) +{ + struct syn_cache *sc = arg; + int s; + + s = splsoftnet(); + pool_put(&syn_cache_pool, (sc)); + splx(s); + return; +} + /* * Remove syn cache created by the specified tcb entry, * because this does not make sense to keep them diff --git a/sys/netinet/tcp_subr.c b/sys/netinet/tcp_subr.c index 0070c1824e0..5a9945f3fb7 100644 --- a/sys/netinet/tcp_subr.c +++ b/sys/netinet/tcp_subr.c @@ -1,4 +1,4 @@ -/* $OpenBSD: tcp_subr.c,v 1.84 2004/10/28 19:22:52 mcbride Exp $ */ +/* $OpenBSD: tcp_subr.c,v 1.85 2004/11/25 15:32:08 markus Exp $ */ /* $NetBSD: tcp_subr.c,v 1.22 1996/02/13 23:44:00 christos Exp $ */ /* @@ -495,6 +495,7 @@ tcp_newtcpcb(struct inpcb *inp) TCP_INIT_DELACK(tp); for (i = 0; i < TCPT_NTIMERS; i++) TCP_TIMER_INIT(tp, i); + timeout_set(&tp->t_reap_to, tcp_reaper, tp); #ifdef TCP_SACK tp->sack_enable = tcp_do_sack; @@ -593,14 +594,28 @@ tcp_close(struct tcpcb *tp) #endif if (tp->t_template) (void) m_free(tp->t_template); - pool_put(&tcpcb_pool, tp); + + tp->t_flags |= TF_DEAD; + timeout_add(&tp->t_reap_to, 0); + inp->inp_ppcb = 0; soisdisconnected(so); in_pcbdetach(inp); - tcpstat.tcps_closed++; return ((struct tcpcb *)0); } +void +tcp_reaper(void *arg) +{ + struct tcpcb *tp = arg; + int s; + + s = splsoftnet(); + pool_put(&tcpcb_pool, tp); + splx(s); + tcpstat.tcps_closed++; +} + int tcp_freeq(struct tcpcb *tp) { diff --git a/sys/netinet/tcp_timer.c b/sys/netinet/tcp_timer.c index 60e310cf02c..1a5acbb3e78 100644 --- a/sys/netinet/tcp_timer.c +++ b/sys/netinet/tcp_timer.c @@ -1,4 +1,4 @@ -/* $OpenBSD: tcp_timer.c,v 1.34 2003/12/10 07:22:43 itojun Exp $ */ +/* $OpenBSD: tcp_timer.c,v 1.35 2004/11/25 15:32:08 markus Exp $ */ /* $NetBSD: tcp_timer.c,v 1.14 1996/02/13 23:44:09 christos Exp $ */ /* @@ -112,6 +112,10 @@ tcp_delack(void *arg) */ s = splsoftnet(); + if (tp->t_flags & TF_DEAD) { + splx(s); + return; + } tp->t_flags |= TF_ACKNOW; (void) tcp_output(tp); splx(s); @@ -193,6 +197,10 @@ tcp_timer_rexmt(void *arg) int s; s = splsoftnet(); + if (tp->t_flags & TF_DEAD) { + splx(s); + return; + } #ifdef TCP_SACK tcp_timer_freesack(tp); @@ -357,6 +365,11 @@ tcp_timer_persist(void *arg) int s; s = splsoftnet(); + if ((tp->t_flags & TF_DEAD) || + TCP_TIMER_ISARMED(tp, TCPT_REXMT)) { + splx(s); + return; + } tcpstat.tcps_persisttimeo++; /* * Hack: if the peer is dead/unreachable, we do not @@ -390,6 +403,10 @@ tcp_timer_keep(void *arg) int s; s = splsoftnet(); + if (tp->t_flags & TF_DEAD) { + splx(s); + return; + } tcpstat.tcps_keeptimeo++; if (TCPS_HAVEESTABLISHED(tp->t_state) == 0) @@ -444,6 +461,10 @@ tcp_timer_2msl(void *arg) int s; s = splsoftnet(); + if (tp->t_flags & TF_DEAD) { + splx(s); + return; + } #ifdef TCP_SACK tcp_timer_freesack(tp); diff --git a/sys/netinet/tcp_var.h b/sys/netinet/tcp_var.h index 9eb1dc3c0e4..66999823ae4 100644 --- a/sys/netinet/tcp_var.h +++ b/sys/netinet/tcp_var.h @@ -1,4 +1,4 @@ -/* $OpenBSD: tcp_var.h,v 1.67 2004/10/28 19:22:52 mcbride Exp $ */ +/* $OpenBSD: tcp_var.h,v 1.68 2004/11/25 15:32:08 markus Exp $ */ /* $NetBSD: tcp_var.h,v 1.17 1996/02/13 23:44:24 christos Exp $ */ /* @@ -84,6 +84,7 @@ struct tcpcb { #endif #define TF_REASSLOCK 0x00080000 /* reassembling or draining */ #define TF_LASTIDLE 0x00100000 /* no outstanding ACK on last send */ +#define TF_DEAD 0x00200000 /* dead and to-be-released */ struct mbuf *t_template; /* skeletal packet for transmit */ struct inpcb *t_inpcb; /* back pointer to internet pcb */ @@ -181,6 +182,8 @@ struct tcpcb { caddr_t t_tuba_pcb; /* next level down pcb for TCP over z */ int pf; + + struct timeout t_reap_to; /* delayed cleanup timeout */ }; #define intotcpcb(ip) ((struct tcpcb *)(ip)->inp_ppcb) @@ -550,6 +553,7 @@ int tcp_attach(struct socket *); void tcp_canceltimers(struct tcpcb *); struct tcpcb * tcp_close(struct tcpcb *); +void tcp_reaper(void *); int tcp_freeq(struct tcpcb *); #if defined(INET6) && !defined(TCP6) void tcp6_ctlinput(int, struct sockaddr *, void *); @@ -646,6 +650,7 @@ void syn_cache_reset(struct sockaddr *, struct sockaddr *, int syn_cache_respond(struct syn_cache *, struct mbuf *); void syn_cache_timer(void *); void syn_cache_cleanup(struct tcpcb *); +void syn_cache_reaper(void *); #endif /* _KERNEL */ #endif /* _NETINET_TCP_VAR_H_ */ |