f641bea548
Long ago, the storage representations of srtt and rttvar were changed from the 4.4BSD scheme, and the comments are out of sync with the code. This commit rewrites most of the comments that explain the RTO calculations, and points out some issues in the code. Joint work with Bev Schwartz of BBN (original analysis and comments), but I have rewritten and extended them, so errors are mine. This material is based upon work supported by the Defense Advanced Research Projects Agency and Space and Naval Warfare Systems Center, Pacific, under Contract No. N66001-09-C-2073. Approved for Public Release, Distribution Unlimited
659 lines
18 KiB
C
659 lines
18 KiB
C
/* $NetBSD: tcp_timer.c,v 1.85 2011/04/20 13:35:52 gdt Exp $ */
|
|
|
|
/*
|
|
* Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* 3. Neither the name of the project nor the names of its contributors
|
|
* may be used to endorse or promote products derived from this software
|
|
* without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
* SUCH DAMAGE.
|
|
*/
|
|
|
|
/*-
|
|
* Copyright (c) 1997, 1998, 2001, 2005 The NetBSD Foundation, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This code is derived from software contributed to The NetBSD Foundation
|
|
* by Jason R. Thorpe and Kevin M. Lahey of the Numerical Aerospace Simulation
|
|
* Facility, NASA Ames Research Center.
|
|
* This code is derived from software contributed to The NetBSD Foundation
|
|
* by Charles M. Hannum.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
|
|
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
|
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
|
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
|
|
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
* POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
/*
|
|
* Copyright (c) 1982, 1986, 1988, 1990, 1993, 1995
|
|
* The Regents of the University of California. All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* 3. Neither the name of the University nor the names of its contributors
|
|
* may be used to endorse or promote products derived from this software
|
|
* without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
* SUCH DAMAGE.
|
|
*
|
|
* @(#)tcp_timer.c 8.2 (Berkeley) 5/24/95
|
|
*/
|
|
|
|
#include <sys/cdefs.h>
|
|
__KERNEL_RCSID(0, "$NetBSD: tcp_timer.c,v 1.85 2011/04/20 13:35:52 gdt Exp $");
|
|
|
|
#include "opt_inet.h"
|
|
#include "opt_tcp_debug.h"
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/systm.h>
|
|
#include <sys/malloc.h>
|
|
#include <sys/mbuf.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/socketvar.h>
|
|
#include <sys/protosw.h>
|
|
#include <sys/errno.h>
|
|
#include <sys/kernel.h>
|
|
|
|
#include <net/if.h>
|
|
#include <net/route.h>
|
|
|
|
#include <netinet/in.h>
|
|
#include <netinet/in_systm.h>
|
|
#include <netinet/ip.h>
|
|
#include <netinet/in_pcb.h>
|
|
#include <netinet/ip_var.h>
|
|
#include <netinet/ip_icmp.h>
|
|
|
|
#ifdef INET6
|
|
#ifndef INET
|
|
#include <netinet/in.h>
|
|
#endif
|
|
#include <netinet/ip6.h>
|
|
#include <netinet6/in6_pcb.h>
|
|
#endif
|
|
|
|
#include <netinet/tcp.h>
|
|
#include <netinet/tcp_fsm.h>
|
|
#include <netinet/tcp_seq.h>
|
|
#include <netinet/tcp_timer.h>
|
|
#include <netinet/tcp_var.h>
|
|
#include <netinet/tcp_private.h>
|
|
#include <netinet/tcp_congctl.h>
|
|
#include <netinet/tcpip.h>
|
|
#ifdef TCP_DEBUG
|
|
#include <netinet/tcp_debug.h>
|
|
#endif
|
|
|
|
/*
|
|
* Various tunable timer parameters. These are initialized in tcp_init(),
|
|
* unless they are patched.
|
|
*/
|
|
u_int tcp_keepinit = 0;
|
|
u_int tcp_keepidle = 0;
|
|
u_int tcp_keepintvl = 0;
|
|
u_int tcp_keepcnt = 0; /* max idle probes */
|
|
|
|
int tcp_maxpersistidle = 0; /* max idle time in persist */
|
|
|
|
/*
|
|
* Time to delay the ACK. This is initialized in tcp_init(), unless
|
|
* its patched.
|
|
*/
|
|
int tcp_delack_ticks = 0;
|
|
|
|
void tcp_timer_rexmt(void *);
|
|
void tcp_timer_persist(void *);
|
|
void tcp_timer_keep(void *);
|
|
void tcp_timer_2msl(void *);
|
|
|
|
const tcp_timer_func_t tcp_timer_funcs[TCPT_NTIMERS] = {
|
|
tcp_timer_rexmt,
|
|
tcp_timer_persist,
|
|
tcp_timer_keep,
|
|
tcp_timer_2msl,
|
|
};
|
|
|
|
/*
|
|
* Timer state initialization, called from tcp_init().
|
|
*/
|
|
void
|
|
tcp_timer_init(void)
|
|
{
|
|
|
|
if (tcp_keepinit == 0)
|
|
tcp_keepinit = TCPTV_KEEP_INIT;
|
|
|
|
if (tcp_keepidle == 0)
|
|
tcp_keepidle = TCPTV_KEEP_IDLE;
|
|
|
|
if (tcp_keepintvl == 0)
|
|
tcp_keepintvl = TCPTV_KEEPINTVL;
|
|
|
|
if (tcp_keepcnt == 0)
|
|
tcp_keepcnt = TCPTV_KEEPCNT;
|
|
|
|
if (tcp_maxpersistidle == 0)
|
|
tcp_maxpersistidle = TCPTV_KEEP_IDLE;
|
|
|
|
if (tcp_delack_ticks == 0)
|
|
tcp_delack_ticks = TCP_DELACK_TICKS;
|
|
}
|
|
|
|
/*
|
|
* Callout to process delayed ACKs for a TCPCB.
|
|
*/
|
|
void
|
|
tcp_delack(void *arg)
|
|
{
|
|
struct tcpcb *tp = arg;
|
|
|
|
/*
|
|
* If tcp_output() wasn't able to transmit the ACK
|
|
* for whatever reason, it will restart the delayed
|
|
* ACK callout.
|
|
*/
|
|
|
|
mutex_enter(softnet_lock);
|
|
if ((tp->t_flags & (TF_DEAD | TF_DELACK)) != TF_DELACK) {
|
|
mutex_exit(softnet_lock);
|
|
return;
|
|
}
|
|
if (!callout_expired(&tp->t_delack_ch)) {
|
|
mutex_exit(softnet_lock);
|
|
return;
|
|
}
|
|
|
|
tp->t_flags |= TF_ACKNOW;
|
|
KERNEL_LOCK(1, NULL);
|
|
(void) tcp_output(tp);
|
|
KERNEL_UNLOCK_ONE(NULL);
|
|
mutex_exit(softnet_lock);
|
|
}
|
|
|
|
/*
|
|
* Tcp protocol timeout routine called every 500 ms.
|
|
* Updates the timers in all active tcb's and
|
|
* causes finite state machine actions if timers expire.
|
|
*/
|
|
void
|
|
tcp_slowtimo(void)
|
|
{
|
|
|
|
mutex_enter(softnet_lock);
|
|
tcp_iss_seq += TCP_ISSINCR; /* increment iss */
|
|
tcp_now++; /* for timestamps */
|
|
mutex_exit(softnet_lock);
|
|
}
|
|
|
|
/*
|
|
* Cancel all timers for TCP tp.
|
|
*/
|
|
void
|
|
tcp_canceltimers(struct tcpcb *tp)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < TCPT_NTIMERS; i++)
|
|
TCP_TIMER_DISARM(tp, i);
|
|
}
|
|
|
|
const int tcp_backoff[TCP_MAXRXTSHIFT + 1] =
|
|
{ 1, 2, 4, 8, 16, 32, 64, 64, 64, 64, 64, 64, 64 };
|
|
|
|
const int tcp_totbackoff = 511; /* sum of tcp_backoff[] */
|
|
|
|
/*
|
|
* TCP timer processing.
|
|
*/
|
|
|
|
void
|
|
tcp_timer_rexmt(void *arg)
|
|
{
|
|
struct tcpcb *tp = arg;
|
|
uint32_t rto;
|
|
#ifdef TCP_DEBUG
|
|
struct socket *so = NULL;
|
|
short ostate;
|
|
#endif
|
|
|
|
mutex_enter(softnet_lock);
|
|
if ((tp->t_flags & TF_DEAD) != 0) {
|
|
mutex_exit(softnet_lock);
|
|
return;
|
|
}
|
|
if (!callout_expired(&tp->t_timer[TCPT_REXMT])) {
|
|
mutex_exit(softnet_lock);
|
|
return;
|
|
}
|
|
|
|
KERNEL_LOCK(1, NULL);
|
|
if ((tp->t_flags & TF_PMTUD_PEND) && tp->t_inpcb &&
|
|
SEQ_GEQ(tp->t_pmtud_th_seq, tp->snd_una) &&
|
|
SEQ_LT(tp->t_pmtud_th_seq, (int)(tp->snd_una + tp->t_ourmss))) {
|
|
extern struct sockaddr_in icmpsrc;
|
|
struct icmp icmp;
|
|
|
|
tp->t_flags &= ~TF_PMTUD_PEND;
|
|
|
|
/* XXX create fake icmp message with relevant entries */
|
|
icmp.icmp_nextmtu = tp->t_pmtud_nextmtu;
|
|
icmp.icmp_ip.ip_len = tp->t_pmtud_ip_len;
|
|
icmp.icmp_ip.ip_hl = tp->t_pmtud_ip_hl;
|
|
icmpsrc.sin_addr = tp->t_inpcb->inp_faddr;
|
|
icmp_mtudisc(&icmp, icmpsrc.sin_addr);
|
|
|
|
/*
|
|
* Notify all connections to the same peer about
|
|
* new mss and trigger retransmit.
|
|
*/
|
|
in_pcbnotifyall(&tcbtable, icmpsrc.sin_addr, EMSGSIZE,
|
|
tcp_mtudisc);
|
|
KERNEL_UNLOCK_ONE(NULL);
|
|
mutex_exit(softnet_lock);
|
|
return;
|
|
}
|
|
#ifdef TCP_DEBUG
|
|
#ifdef INET
|
|
if (tp->t_inpcb)
|
|
so = tp->t_inpcb->inp_socket;
|
|
#endif
|
|
#ifdef INET6
|
|
if (tp->t_in6pcb)
|
|
so = tp->t_in6pcb->in6p_socket;
|
|
#endif
|
|
ostate = tp->t_state;
|
|
#endif /* TCP_DEBUG */
|
|
|
|
/*
|
|
* Clear the SACK scoreboard, reset FACK estimate.
|
|
*/
|
|
tcp_free_sackholes(tp);
|
|
tp->snd_fack = tp->snd_una;
|
|
|
|
/*
|
|
* Retransmission timer went off. Message has not
|
|
* been acked within retransmit interval. Back off
|
|
* to a longer retransmit interval and retransmit one segment.
|
|
*/
|
|
|
|
if (++tp->t_rxtshift > TCP_MAXRXTSHIFT) {
|
|
tp->t_rxtshift = TCP_MAXRXTSHIFT;
|
|
TCP_STATINC(TCP_STAT_TIMEOUTDROP);
|
|
tp = tcp_drop(tp, tp->t_softerror ?
|
|
tp->t_softerror : ETIMEDOUT);
|
|
goto out;
|
|
}
|
|
TCP_STATINC(TCP_STAT_REXMTTIMEO);
|
|
rto = TCP_REXMTVAL(tp);
|
|
if (rto < tp->t_rttmin)
|
|
rto = tp->t_rttmin;
|
|
TCPT_RANGESET(tp->t_rxtcur, rto * tcp_backoff[tp->t_rxtshift],
|
|
tp->t_rttmin, TCPTV_REXMTMAX);
|
|
TCP_TIMER_ARM(tp, TCPT_REXMT, tp->t_rxtcur);
|
|
|
|
/*
|
|
* If we are losing and we are trying path MTU discovery,
|
|
* try turning it off. This will avoid black holes in
|
|
* the network which suppress or fail to send "packet
|
|
* too big" ICMP messages. We should ideally do
|
|
* lots more sophisticated searching to find the right
|
|
* value here...
|
|
*/
|
|
if (tp->t_mtudisc && tp->t_rxtshift > TCP_MAXRXTSHIFT / 6) {
|
|
TCP_STATINC(TCP_STAT_PMTUBLACKHOLE);
|
|
|
|
#ifdef INET
|
|
/* try turning PMTUD off */
|
|
if (tp->t_inpcb)
|
|
tp->t_mtudisc = 0;
|
|
#endif
|
|
#ifdef INET6
|
|
/* try using IPv6 minimum MTU */
|
|
if (tp->t_in6pcb)
|
|
tp->t_mtudisc = 0;
|
|
#endif
|
|
|
|
/* XXX: more sophisticated Black hole recovery code? */
|
|
}
|
|
|
|
/*
|
|
* If losing, let the lower level know and try for
|
|
* a better route. Also, if we backed off this far,
|
|
* our srtt estimate is probably bogus. Clobber it
|
|
* so we'll take the next rtt measurement as our srtt;
|
|
* move the current srtt into rttvar to keep the current
|
|
* retransmit times until then.
|
|
*/
|
|
if (tp->t_rxtshift > TCP_MAXRXTSHIFT / 4) {
|
|
#ifdef INET
|
|
if (tp->t_inpcb)
|
|
in_losing(tp->t_inpcb);
|
|
#endif
|
|
#ifdef INET6
|
|
if (tp->t_in6pcb)
|
|
in6_losing(tp->t_in6pcb);
|
|
#endif
|
|
/*
|
|
* This operation is not described in RFC2988. The
|
|
* point is to keep srtt+4*rttvar constant, so we
|
|
* should shift right 2 bits to divide by 4, and then
|
|
* shift right one bit because the storage
|
|
* representation of rttvar is 1/16s vs 1/32s for
|
|
* srtt.
|
|
*/
|
|
tp->t_rttvar += (tp->t_srtt >> TCP_RTT_SHIFT);
|
|
tp->t_srtt = 0;
|
|
}
|
|
tp->snd_nxt = tp->snd_una;
|
|
tp->snd_high = tp->snd_max;
|
|
/*
|
|
* If timing a segment in this window, stop the timer.
|
|
*/
|
|
tp->t_rtttime = 0;
|
|
/*
|
|
* Remember if we are retransmitting a SYN, because if
|
|
* we do, set the initial congestion window must be set
|
|
* to 1 segment.
|
|
*/
|
|
if (tp->t_state == TCPS_SYN_SENT)
|
|
tp->t_flags |= TF_SYN_REXMT;
|
|
|
|
/*
|
|
* Adjust congestion control parameters.
|
|
*/
|
|
tp->t_congctl->slow_retransmit(tp);
|
|
|
|
(void) tcp_output(tp);
|
|
|
|
out:
|
|
#ifdef TCP_DEBUG
|
|
if (tp && so->so_options & SO_DEBUG)
|
|
tcp_trace(TA_USER, ostate, tp, NULL,
|
|
PRU_SLOWTIMO | (TCPT_REXMT << 8));
|
|
#endif
|
|
KERNEL_UNLOCK_ONE(NULL);
|
|
mutex_exit(softnet_lock);
|
|
}
|
|
|
|
void
|
|
tcp_timer_persist(void *arg)
|
|
{
|
|
struct tcpcb *tp = arg;
|
|
uint32_t rto;
|
|
#ifdef TCP_DEBUG
|
|
struct socket *so = NULL;
|
|
short ostate;
|
|
#endif
|
|
|
|
mutex_enter(softnet_lock);
|
|
if ((tp->t_flags & TF_DEAD) != 0) {
|
|
mutex_exit(softnet_lock);
|
|
return;
|
|
}
|
|
if (!callout_expired(&tp->t_timer[TCPT_PERSIST])) {
|
|
mutex_exit(softnet_lock);
|
|
return;
|
|
}
|
|
|
|
KERNEL_LOCK(1, NULL);
|
|
#ifdef TCP_DEBUG
|
|
#ifdef INET
|
|
if (tp->t_inpcb)
|
|
so = tp->t_inpcb->inp_socket;
|
|
#endif
|
|
#ifdef INET6
|
|
if (tp->t_in6pcb)
|
|
so = tp->t_in6pcb->in6p_socket;
|
|
#endif
|
|
|
|
ostate = tp->t_state;
|
|
#endif /* TCP_DEBUG */
|
|
|
|
/*
|
|
* Persistance timer into zero window.
|
|
* Force a byte to be output, if possible.
|
|
*/
|
|
|
|
/*
|
|
* Hack: if the peer is dead/unreachable, we do not
|
|
* time out if the window is closed. After a full
|
|
* backoff, drop the connection if the idle time
|
|
* (no responses to probes) reaches the maximum
|
|
* backoff that we would use if retransmitting.
|
|
*/
|
|
rto = TCP_REXMTVAL(tp);
|
|
if (rto < tp->t_rttmin)
|
|
rto = tp->t_rttmin;
|
|
if (tp->t_rxtshift == TCP_MAXRXTSHIFT &&
|
|
((tcp_now - tp->t_rcvtime) >= tcp_maxpersistidle ||
|
|
(tcp_now - tp->t_rcvtime) >= rto * tcp_totbackoff)) {
|
|
TCP_STATINC(TCP_STAT_PERSISTDROPS);
|
|
tp = tcp_drop(tp, ETIMEDOUT);
|
|
goto out;
|
|
}
|
|
TCP_STATINC(TCP_STAT_PERSISTTIMEO);
|
|
tcp_setpersist(tp);
|
|
tp->t_force = 1;
|
|
(void) tcp_output(tp);
|
|
tp->t_force = 0;
|
|
|
|
out:
|
|
#ifdef TCP_DEBUG
|
|
if (tp && so->so_options & SO_DEBUG)
|
|
tcp_trace(TA_USER, ostate, tp, NULL,
|
|
PRU_SLOWTIMO | (TCPT_PERSIST << 8));
|
|
#endif
|
|
KERNEL_UNLOCK_ONE(NULL);
|
|
mutex_exit(softnet_lock);
|
|
}
|
|
|
|
void
|
|
tcp_timer_keep(void *arg)
|
|
{
|
|
struct tcpcb *tp = arg;
|
|
struct socket *so = NULL; /* Quell compiler warning */
|
|
#ifdef TCP_DEBUG
|
|
short ostate;
|
|
#endif
|
|
|
|
mutex_enter(softnet_lock);
|
|
if ((tp->t_flags & TF_DEAD) != 0) {
|
|
mutex_exit(softnet_lock);
|
|
return;
|
|
}
|
|
if (!callout_expired(&tp->t_timer[TCPT_KEEP])) {
|
|
mutex_exit(softnet_lock);
|
|
return;
|
|
}
|
|
|
|
KERNEL_LOCK(1, NULL);
|
|
|
|
#ifdef TCP_DEBUG
|
|
ostate = tp->t_state;
|
|
#endif /* TCP_DEBUG */
|
|
|
|
/*
|
|
* Keep-alive timer went off; send something
|
|
* or drop connection if idle for too long.
|
|
*/
|
|
|
|
TCP_STATINC(TCP_STAT_KEEPTIMEO);
|
|
if (TCPS_HAVEESTABLISHED(tp->t_state) == 0)
|
|
goto dropit;
|
|
#ifdef INET
|
|
if (tp->t_inpcb)
|
|
so = tp->t_inpcb->inp_socket;
|
|
#endif
|
|
#ifdef INET6
|
|
if (tp->t_in6pcb)
|
|
so = tp->t_in6pcb->in6p_socket;
|
|
#endif
|
|
KASSERT(so != NULL);
|
|
if (so->so_options & SO_KEEPALIVE &&
|
|
tp->t_state <= TCPS_CLOSE_WAIT) {
|
|
if ((tp->t_maxidle > 0) &&
|
|
((tcp_now - tp->t_rcvtime) >=
|
|
tp->t_keepidle + tp->t_maxidle))
|
|
goto dropit;
|
|
/*
|
|
* Send a packet designed to force a response
|
|
* if the peer is up and reachable:
|
|
* either an ACK if the connection is still alive,
|
|
* or an RST if the peer has closed the connection
|
|
* due to timeout or reboot.
|
|
* Using sequence number tp->snd_una-1
|
|
* causes the transmitted zero-length segment
|
|
* to lie outside the receive window;
|
|
* by the protocol spec, this requires the
|
|
* correspondent TCP to respond.
|
|
*/
|
|
TCP_STATINC(TCP_STAT_KEEPPROBE);
|
|
if (tcp_compat_42) {
|
|
/*
|
|
* The keepalive packet must have nonzero
|
|
* length to get a 4.2 host to respond.
|
|
*/
|
|
(void)tcp_respond(tp, tp->t_template,
|
|
(struct mbuf *)NULL, NULL, tp->rcv_nxt - 1,
|
|
tp->snd_una - 1, 0);
|
|
} else {
|
|
(void)tcp_respond(tp, tp->t_template,
|
|
(struct mbuf *)NULL, NULL, tp->rcv_nxt,
|
|
tp->snd_una - 1, 0);
|
|
}
|
|
TCP_TIMER_ARM(tp, TCPT_KEEP, tp->t_keepintvl);
|
|
} else
|
|
TCP_TIMER_ARM(tp, TCPT_KEEP, tp->t_keepidle);
|
|
|
|
#ifdef TCP_DEBUG
|
|
if (tp && so->so_options & SO_DEBUG)
|
|
tcp_trace(TA_USER, ostate, tp, NULL,
|
|
PRU_SLOWTIMO | (TCPT_KEEP << 8));
|
|
#endif
|
|
KERNEL_UNLOCK_ONE(NULL);
|
|
mutex_exit(softnet_lock);
|
|
return;
|
|
|
|
dropit:
|
|
TCP_STATINC(TCP_STAT_KEEPDROPS);
|
|
(void) tcp_drop(tp, ETIMEDOUT);
|
|
KERNEL_UNLOCK_ONE(NULL);
|
|
mutex_exit(softnet_lock);
|
|
}
|
|
|
|
void
|
|
tcp_timer_2msl(void *arg)
|
|
{
|
|
struct tcpcb *tp = arg;
|
|
#ifdef TCP_DEBUG
|
|
struct socket *so = NULL;
|
|
short ostate;
|
|
#endif
|
|
|
|
mutex_enter(softnet_lock);
|
|
if ((tp->t_flags & TF_DEAD) != 0) {
|
|
mutex_exit(softnet_lock);
|
|
return;
|
|
}
|
|
if (!callout_expired(&tp->t_timer[TCPT_2MSL])) {
|
|
mutex_exit(softnet_lock);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* 2 MSL timeout went off, clear the SACK scoreboard, reset
|
|
* the FACK estimate.
|
|
*/
|
|
KERNEL_LOCK(1, NULL);
|
|
tcp_free_sackholes(tp);
|
|
tp->snd_fack = tp->snd_una;
|
|
|
|
#ifdef TCP_DEBUG
|
|
#ifdef INET
|
|
if (tp->t_inpcb)
|
|
so = tp->t_inpcb->inp_socket;
|
|
#endif
|
|
#ifdef INET6
|
|
if (tp->t_in6pcb)
|
|
so = tp->t_in6pcb->in6p_socket;
|
|
#endif
|
|
|
|
ostate = tp->t_state;
|
|
#endif /* TCP_DEBUG */
|
|
|
|
/*
|
|
* 2 MSL timeout in shutdown went off. If we're closed but
|
|
* still waiting for peer to close and connection has been idle
|
|
* too long, or if 2MSL time is up from TIME_WAIT, delete connection
|
|
* control block. Otherwise, check again in a bit.
|
|
*/
|
|
if (tp->t_state != TCPS_TIME_WAIT &&
|
|
((tp->t_maxidle == 0) ||
|
|
((tcp_now - tp->t_rcvtime) <= tp->t_maxidle)))
|
|
TCP_TIMER_ARM(tp, TCPT_2MSL, tp->t_keepintvl);
|
|
else
|
|
tp = tcp_close(tp);
|
|
|
|
#ifdef TCP_DEBUG
|
|
if (tp && so->so_options & SO_DEBUG)
|
|
tcp_trace(TA_USER, ostate, tp, NULL,
|
|
PRU_SLOWTIMO | (TCPT_2MSL << 8));
|
|
#endif
|
|
mutex_exit(softnet_lock);
|
|
KERNEL_UNLOCK_ONE(NULL);
|
|
}
|