1c398230bSWarner Losh /*- 2e79adb8eSGarrett Wollman * Copyright (c) 1982, 1986, 1988, 1990, 1993, 1995 3df8bae1dSRodney W. Grimes * The Regents of the University of California. All rights reserved. 4df8bae1dSRodney W. Grimes * 5df8bae1dSRodney W. Grimes * Redistribution and use in source and binary forms, with or without 6df8bae1dSRodney W. Grimes * modification, are permitted provided that the following conditions 7df8bae1dSRodney W. Grimes * are met: 8df8bae1dSRodney W. Grimes * 1. Redistributions of source code must retain the above copyright 9df8bae1dSRodney W. Grimes * notice, this list of conditions and the following disclaimer. 10df8bae1dSRodney W. Grimes * 2. Redistributions in binary form must reproduce the above copyright 11df8bae1dSRodney W. Grimes * notice, this list of conditions and the following disclaimer in the 12df8bae1dSRodney W. Grimes * documentation and/or other materials provided with the distribution. 13df8bae1dSRodney W. Grimes * 4. Neither the name of the University nor the names of its contributors 14df8bae1dSRodney W. Grimes * may be used to endorse or promote products derived from this software 15df8bae1dSRodney W. Grimes * without specific prior written permission. 16df8bae1dSRodney W. Grimes * 17df8bae1dSRodney W. Grimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 18df8bae1dSRodney W. Grimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19df8bae1dSRodney W. Grimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20df8bae1dSRodney W. Grimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 21df8bae1dSRodney W. Grimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22df8bae1dSRodney W. Grimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23df8bae1dSRodney W. Grimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24df8bae1dSRodney W. Grimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25df8bae1dSRodney W. Grimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26df8bae1dSRodney W. Grimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27df8bae1dSRodney W. Grimes * SUCH DAMAGE. 28df8bae1dSRodney W. Grimes * 29e79adb8eSGarrett Wollman * @(#)tcp_timer.c 8.2 (Berkeley) 5/24/95 30df8bae1dSRodney W. Grimes */ 31df8bae1dSRodney W. Grimes 324b421e2dSMike Silbersack #include <sys/cdefs.h> 334b421e2dSMike Silbersack __FBSDID("$FreeBSD$"); 344b421e2dSMike Silbersack 35fb59c426SYoshinobu Inoue #include "opt_inet6.h" 360cc12cc5SJoerg Wunsch #include "opt_tcpdebug.h" 370cc12cc5SJoerg Wunsch 38df8bae1dSRodney W. Grimes #include <sys/param.h> 3998163b98SPoul-Henning Kamp #include <sys/kernel.h> 40c74af4faSBruce Evans #include <sys/lock.h> 4108517d53SMike Silbersack #include <sys/mbuf.h> 42c74af4faSBruce Evans #include <sys/mutex.h> 43c74af4faSBruce Evans #include <sys/protosw.h> 4487aedea4SKip Macy #include <sys/smp.h> 45df8bae1dSRodney W. Grimes #include <sys/socket.h> 46df8bae1dSRodney W. Grimes #include <sys/socketvar.h> 47c74af4faSBruce Evans #include <sys/sysctl.h> 48c74af4faSBruce Evans #include <sys/systm.h> 49e79adb8eSGarrett Wollman 504b79449eSBjoern A. Zeeb #include <net/if.h> 51df8bae1dSRodney W. Grimes #include <net/route.h> 52530c0060SRobert Watson #include <net/vnet.h> 53df8bae1dSRodney W. Grimes 54dbc42409SLawrence Stewart #include <netinet/cc.h> 55df8bae1dSRodney W. Grimes #include <netinet/in.h> 56df8bae1dSRodney W. Grimes #include <netinet/in_pcb.h> 57c74af4faSBruce Evans #include <netinet/in_systm.h> 58fb59c426SYoshinobu Inoue #ifdef INET6 59fb59c426SYoshinobu Inoue #include <netinet6/in6_pcb.h> 60fb59c426SYoshinobu Inoue #endif 61df8bae1dSRodney W. Grimes #include <netinet/ip_var.h> 62df8bae1dSRodney W. Grimes #include <netinet/tcp_fsm.h> 63df8bae1dSRodney W. Grimes #include <netinet/tcp_timer.h> 64df8bae1dSRodney W. Grimes #include <netinet/tcp_var.h> 65df8bae1dSRodney W. Grimes #include <netinet/tcpip.h> 66af7a2999SDavid Greenman #ifdef TCPDEBUG 67af7a2999SDavid Greenman #include <netinet/tcp_debug.h> 68af7a2999SDavid Greenman #endif 69df8bae1dSRodney W. Grimes 709b8b58e0SJonathan Lemon int tcp_keepinit; 71ccb4d0c6SJonathan Lemon SYSCTL_PROC(_net_inet_tcp, TCPCTL_KEEPINIT, keepinit, CTLTYPE_INT|CTLFLAG_RW, 7241698ebfSTom Rhodes &tcp_keepinit, 0, sysctl_msec_to_ticks, "I", "time to establish connection"); 737b40aa32SPaul Traina 749b8b58e0SJonathan Lemon int tcp_keepidle; 75ccb4d0c6SJonathan Lemon SYSCTL_PROC(_net_inet_tcp, TCPCTL_KEEPIDLE, keepidle, CTLTYPE_INT|CTLFLAG_RW, 7641698ebfSTom Rhodes &tcp_keepidle, 0, sysctl_msec_to_ticks, "I", "time before keepalive probes begin"); 7798163b98SPoul-Henning Kamp 789b8b58e0SJonathan Lemon int tcp_keepintvl; 79ccb4d0c6SJonathan Lemon SYSCTL_PROC(_net_inet_tcp, TCPCTL_KEEPINTVL, keepintvl, CTLTYPE_INT|CTLFLAG_RW, 8041698ebfSTom Rhodes &tcp_keepintvl, 0, sysctl_msec_to_ticks, "I", "time between keepalive probes"); 8198163b98SPoul-Henning Kamp 829b8b58e0SJonathan Lemon int tcp_delacktime; 836489fe65SAndre Oppermann SYSCTL_PROC(_net_inet_tcp, TCPCTL_DELACKTIME, delacktime, CTLTYPE_INT|CTLFLAG_RW, 846489fe65SAndre Oppermann &tcp_delacktime, 0, sysctl_msec_to_ticks, "I", 85ccb4d0c6SJonathan Lemon "Time before a delayed ACK is sent"); 869b8b58e0SJonathan Lemon 879b8b58e0SJonathan Lemon int tcp_msl; 88ccb4d0c6SJonathan Lemon SYSCTL_PROC(_net_inet_tcp, OID_AUTO, msl, CTLTYPE_INT|CTLFLAG_RW, 89ccb4d0c6SJonathan Lemon &tcp_msl, 0, sysctl_msec_to_ticks, "I", "Maximum segment lifetime"); 909b8b58e0SJonathan Lemon 91701bec5aSMatthew Dillon int tcp_rexmit_min; 92701bec5aSMatthew Dillon SYSCTL_PROC(_net_inet_tcp, OID_AUTO, rexmit_min, CTLTYPE_INT|CTLFLAG_RW, 936489fe65SAndre Oppermann &tcp_rexmit_min, 0, sysctl_msec_to_ticks, "I", 946489fe65SAndre Oppermann "Minimum Retransmission Timeout"); 95701bec5aSMatthew Dillon 96701bec5aSMatthew Dillon int tcp_rexmit_slop; 97701bec5aSMatthew Dillon SYSCTL_PROC(_net_inet_tcp, OID_AUTO, rexmit_slop, CTLTYPE_INT|CTLFLAG_RW, 986489fe65SAndre Oppermann &tcp_rexmit_slop, 0, sysctl_msec_to_ticks, "I", 996489fe65SAndre Oppermann "Retransmission Timer Slop"); 100701bec5aSMatthew Dillon 101c39a614eSRobert Watson static int always_keepalive = 1; 1023d177f46SBill Fumerola SYSCTL_INT(_net_inet_tcp, OID_AUTO, always_keepalive, CTLFLAG_RW, 1033d177f46SBill Fumerola &always_keepalive , 0, "Assume SO_KEEPALIVE on all TCP connections"); 10434be9bf3SPoul-Henning Kamp 1057c72af87SMohan Srinivasan int tcp_fast_finwait2_recycle = 0; 1067c72af87SMohan Srinivasan SYSCTL_INT(_net_inet_tcp, OID_AUTO, fast_finwait2_recycle, CTLFLAG_RW, 1076489fe65SAndre Oppermann &tcp_fast_finwait2_recycle, 0, 1086489fe65SAndre Oppermann "Recycle closed FIN_WAIT_2 connections faster"); 1097c72af87SMohan Srinivasan 1107c72af87SMohan Srinivasan int tcp_finwait2_timeout; 1117c72af87SMohan Srinivasan SYSCTL_PROC(_net_inet_tcp, OID_AUTO, finwait2_timeout, CTLTYPE_INT|CTLFLAG_RW, 1126489fe65SAndre Oppermann &tcp_finwait2_timeout, 0, sysctl_msec_to_ticks, "I", "FIN-WAIT2 timeout"); 1137c72af87SMohan Srinivasan 1149077f387SGleb Smirnoff int tcp_keepcnt = TCPTV_KEEPCNT; 1159077f387SGleb Smirnoff SYSCTL_INT(_net_inet_tcp, OID_AUTO, keepcnt, CTLFLAG_RW, &tcp_keepcnt, 0, 1169077f387SGleb Smirnoff "Number of keepalive probes to send"); 1177c72af87SMohan Srinivasan 1180312fbe9SPoul-Henning Kamp /* max idle probes */ 1199b8b58e0SJonathan Lemon int tcp_maxpersistidle; 120e79adb8eSGarrett Wollman 12187aedea4SKip Macy static int per_cpu_timers = 0; 12287aedea4SKip Macy SYSCTL_INT(_net_inet_tcp, OID_AUTO, per_cpu_timers, CTLFLAG_RW, 12387aedea4SKip Macy &per_cpu_timers , 0, "run tcp timers on all cpus"); 12487aedea4SKip Macy 12587aedea4SKip Macy #define INP_CPU(inp) (per_cpu_timers ? (!CPU_ABSENT(((inp)->inp_flowid % (mp_maxid+1))) ? \ 12687aedea4SKip Macy ((inp)->inp_flowid % (mp_maxid+1)) : curcpu) : 0) 12787aedea4SKip Macy 128df8bae1dSRodney W. Grimes /* 129df8bae1dSRodney W. Grimes * Tcp protocol timeout routine called every 500 ms. 1309b8b58e0SJonathan Lemon * Updates timestamps used for TCP 131df8bae1dSRodney W. Grimes * causes finite state machine actions if timers expire. 132df8bae1dSRodney W. Grimes */ 133df8bae1dSRodney W. Grimes void 134e2f2059fSMike Silbersack tcp_slowtimo(void) 135df8bae1dSRodney W. Grimes { 1368b615593SMarko Zec VNET_ITERATOR_DECL(vnet_iter); 13715bd2b43SDavid Greenman 1385ee847d3SRobert Watson VNET_LIST_RLOCK_NOSLEEP(); 1398b615593SMarko Zec VNET_FOREACH(vnet_iter) { 1408b615593SMarko Zec CURVNET_SET(vnet_iter); 141603724d3SBjoern A. Zeeb INP_INFO_WLOCK(&V_tcbinfo); 1422104448fSAndre Oppermann (void) tcp_tw_2msl_scan(0); 143603724d3SBjoern A. Zeeb INP_INFO_WUNLOCK(&V_tcbinfo); 1448b615593SMarko Zec CURVNET_RESTORE(); 1458b615593SMarko Zec } 1465ee847d3SRobert Watson VNET_LIST_RUNLOCK_NOSLEEP(); 147df8bae1dSRodney W. Grimes } 148df8bae1dSRodney W. Grimes 1497d42e30cSJonathan Lemon int tcp_syn_backoff[TCP_MAXRXTSHIFT + 1] = 1507d42e30cSJonathan Lemon { 1, 1, 1, 1, 1, 2, 4, 8, 16, 32, 64, 64, 64 }; 1517d42e30cSJonathan Lemon 152df8bae1dSRodney W. Grimes int tcp_backoff[TCP_MAXRXTSHIFT + 1] = 153f058535dSJeffrey Hsu { 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 512, 512, 512 }; 154df8bae1dSRodney W. Grimes 155f058535dSJeffrey Hsu static int tcp_totbackoff = 2559; /* sum of tcp_backoff[] */ 156e79adb8eSGarrett Wollman 157623dce13SRobert Watson static int tcp_timer_race; 158623dce13SRobert Watson SYSCTL_INT(_net_inet_tcp, OID_AUTO, timer_race, CTLFLAG_RD, &tcp_timer_race, 159623dce13SRobert Watson 0, "Count of t_inpcb races on tcp_discardcb"); 160623dce13SRobert Watson 161df8bae1dSRodney W. Grimes /* 162df8bae1dSRodney W. Grimes * TCP timer processing. 163df8bae1dSRodney W. Grimes */ 16485d94372SRobert Watson 16585d94372SRobert Watson void 16685d94372SRobert Watson tcp_timer_delack(void *xtp) 167df8bae1dSRodney W. Grimes { 16885d94372SRobert Watson struct tcpcb *tp = xtp; 16985d94372SRobert Watson struct inpcb *inp; 1708b615593SMarko Zec CURVNET_SET(tp->t_vnet); 17185d94372SRobert Watson 17285d94372SRobert Watson inp = tp->t_inpcb; 17385d94372SRobert Watson /* 17485d94372SRobert Watson * XXXRW: While this assert is in fact correct, bugs in the tcpcb 17585d94372SRobert Watson * tear-down mean we need it as a work-around for races between 17685d94372SRobert Watson * timers and tcp_discardcb(). 17785d94372SRobert Watson * 17885d94372SRobert Watson * KASSERT(inp != NULL, ("tcp_timer_delack: inp == NULL")); 17985d94372SRobert Watson */ 18085d94372SRobert Watson if (inp == NULL) { 18185d94372SRobert Watson tcp_timer_race++; 1828b615593SMarko Zec CURVNET_RESTORE(); 18385d94372SRobert Watson return; 18485d94372SRobert Watson } 1858501a69cSRobert Watson INP_WLOCK(inp); 186655f934bSMikolaj Golub if (callout_pending(&tp->t_timers->tt_delack) || 187655f934bSMikolaj Golub !callout_active(&tp->t_timers->tt_delack)) { 1888501a69cSRobert Watson INP_WUNLOCK(inp); 1898b615593SMarko Zec CURVNET_RESTORE(); 19085d94372SRobert Watson return; 19185d94372SRobert Watson } 192e2f2059fSMike Silbersack callout_deactivate(&tp->t_timers->tt_delack); 193655f934bSMikolaj Golub if ((inp->inp_flags & INP_DROPPED) != 0) { 194655f934bSMikolaj Golub INP_WUNLOCK(inp); 195655f934bSMikolaj Golub CURVNET_RESTORE(); 196655f934bSMikolaj Golub return; 197655f934bSMikolaj Golub } 198df8bae1dSRodney W. Grimes 1999b8b58e0SJonathan Lemon tp->t_flags |= TF_ACKNOW; 20078b50714SRobert Watson TCPSTAT_INC(tcps_delack); 2019b8b58e0SJonathan Lemon (void) tcp_output(tp); 2028501a69cSRobert Watson INP_WUNLOCK(inp); 2038b615593SMarko Zec CURVNET_RESTORE(); 2049b8b58e0SJonathan Lemon } 2059b8b58e0SJonathan Lemon 20685d94372SRobert Watson void 20785d94372SRobert Watson tcp_timer_2msl(void *xtp) 2089b8b58e0SJonathan Lemon { 20985d94372SRobert Watson struct tcpcb *tp = xtp; 21085d94372SRobert Watson struct inpcb *inp; 2118b615593SMarko Zec CURVNET_SET(tp->t_vnet); 2129b8b58e0SJonathan Lemon #ifdef TCPDEBUG 2139b8b58e0SJonathan Lemon int ostate; 2149b8b58e0SJonathan Lemon 2159b8b58e0SJonathan Lemon ostate = tp->t_state; 2169b8b58e0SJonathan Lemon #endif 217623dce13SRobert Watson /* 21885d94372SRobert Watson * XXXRW: Does this actually happen? 21985d94372SRobert Watson */ 220603724d3SBjoern A. Zeeb INP_INFO_WLOCK(&V_tcbinfo); 22185d94372SRobert Watson inp = tp->t_inpcb; 22285d94372SRobert Watson /* 22385d94372SRobert Watson * XXXRW: While this assert is in fact correct, bugs in the tcpcb 22485d94372SRobert Watson * tear-down mean we need it as a work-around for races between 22585d94372SRobert Watson * timers and tcp_discardcb(). 22685d94372SRobert Watson * 22785d94372SRobert Watson * KASSERT(inp != NULL, ("tcp_timer_2msl: inp == NULL")); 22885d94372SRobert Watson */ 22985d94372SRobert Watson if (inp == NULL) { 23085d94372SRobert Watson tcp_timer_race++; 231603724d3SBjoern A. Zeeb INP_INFO_WUNLOCK(&V_tcbinfo); 2328b615593SMarko Zec CURVNET_RESTORE(); 23385d94372SRobert Watson return; 23485d94372SRobert Watson } 2358501a69cSRobert Watson INP_WLOCK(inp); 23685d94372SRobert Watson tcp_free_sackholes(tp); 237655f934bSMikolaj Golub if (callout_pending(&tp->t_timers->tt_2msl) || 238e2f2059fSMike Silbersack !callout_active(&tp->t_timers->tt_2msl)) { 2398501a69cSRobert Watson INP_WUNLOCK(tp->t_inpcb); 240603724d3SBjoern A. Zeeb INP_INFO_WUNLOCK(&V_tcbinfo); 2418b615593SMarko Zec CURVNET_RESTORE(); 24285d94372SRobert Watson return; 24385d94372SRobert Watson } 244e2f2059fSMike Silbersack callout_deactivate(&tp->t_timers->tt_2msl); 245655f934bSMikolaj Golub if ((inp->inp_flags & INP_DROPPED) != 0) { 246655f934bSMikolaj Golub INP_WUNLOCK(inp); 247655f934bSMikolaj Golub INP_INFO_WUNLOCK(&V_tcbinfo); 248655f934bSMikolaj Golub CURVNET_RESTORE(); 249655f934bSMikolaj Golub return; 250655f934bSMikolaj Golub } 25185d94372SRobert Watson /* 252df8bae1dSRodney W. Grimes * 2 MSL timeout in shutdown went off. If we're closed but 253df8bae1dSRodney W. Grimes * still waiting for peer to close and connection has been idle 254df8bae1dSRodney W. Grimes * too long, or if 2MSL time is up from TIME_WAIT, delete connection 255df8bae1dSRodney W. Grimes * control block. Otherwise, check again in a bit. 2567c72af87SMohan Srinivasan * 2577c72af87SMohan Srinivasan * If fastrecycle of FIN_WAIT_2, in FIN_WAIT_2 and receiver has closed, 2587c72af87SMohan Srinivasan * there's no point in hanging onto FIN_WAIT_2 socket. Just close it. 2597c72af87SMohan Srinivasan * Ignore fact that there were recent incoming segments. 260df8bae1dSRodney W. Grimes */ 2617c72af87SMohan Srinivasan if (tcp_fast_finwait2_recycle && tp->t_state == TCPS_FIN_WAIT_2 && 26285d94372SRobert Watson tp->t_inpcb && tp->t_inpcb->inp_socket && 2637c72af87SMohan Srinivasan (tp->t_inpcb->inp_socket->so_rcv.sb_state & SBS_CANTRCVMORE)) { 26478b50714SRobert Watson TCPSTAT_INC(tcps_finwait2_drops); 26585d94372SRobert Watson tp = tcp_close(tp); 2667c72af87SMohan Srinivasan } else { 267df8bae1dSRodney W. Grimes if (tp->t_state != TCPS_TIME_WAIT && 2689077f387SGleb Smirnoff ticks - tp->t_rcvtime <= TP_MAXIDLE(tp)) 2699077f387SGleb Smirnoff callout_reset_on(&tp->t_timers->tt_2msl, 2709077f387SGleb Smirnoff TP_KEEPINTVL(tp), tcp_timer_2msl, tp, INP_CPU(inp)); 271df8bae1dSRodney W. Grimes else 27285d94372SRobert Watson tp = tcp_close(tp); 2737c72af87SMohan Srinivasan } 274df8bae1dSRodney W. Grimes 2759b8b58e0SJonathan Lemon #ifdef TCPDEBUG 276586b4a0eSKonstantin Belousov if (tp != NULL && (tp->t_inpcb->inp_socket->so_options & SO_DEBUG)) 277fb59c426SYoshinobu Inoue tcp_trace(TA_USER, ostate, tp, (void *)0, (struct tcphdr *)0, 2789b8b58e0SJonathan Lemon PRU_SLOWTIMO); 2799b8b58e0SJonathan Lemon #endif 28085d94372SRobert Watson if (tp != NULL) 2818501a69cSRobert Watson INP_WUNLOCK(inp); 282603724d3SBjoern A. Zeeb INP_INFO_WUNLOCK(&V_tcbinfo); 2838b615593SMarko Zec CURVNET_RESTORE(); 2849b8b58e0SJonathan Lemon } 2859b8b58e0SJonathan Lemon 28685d94372SRobert Watson void 28785d94372SRobert Watson tcp_timer_keep(void *xtp) 2889b8b58e0SJonathan Lemon { 28985d94372SRobert Watson struct tcpcb *tp = xtp; 29008517d53SMike Silbersack struct tcptemp *t_template; 29185d94372SRobert Watson struct inpcb *inp; 2928b615593SMarko Zec CURVNET_SET(tp->t_vnet); 2939b8b58e0SJonathan Lemon #ifdef TCPDEBUG 2949b8b58e0SJonathan Lemon int ostate; 2959b8b58e0SJonathan Lemon 2969b8b58e0SJonathan Lemon ostate = tp->t_state; 2979b8b58e0SJonathan Lemon #endif 298603724d3SBjoern A. Zeeb INP_INFO_WLOCK(&V_tcbinfo); 29985d94372SRobert Watson inp = tp->t_inpcb; 30085d94372SRobert Watson /* 30185d94372SRobert Watson * XXXRW: While this assert is in fact correct, bugs in the tcpcb 30285d94372SRobert Watson * tear-down mean we need it as a work-around for races between 30385d94372SRobert Watson * timers and tcp_discardcb(). 30485d94372SRobert Watson * 30585d94372SRobert Watson * KASSERT(inp != NULL, ("tcp_timer_keep: inp == NULL")); 30685d94372SRobert Watson */ 30785d94372SRobert Watson if (inp == NULL) { 30885d94372SRobert Watson tcp_timer_race++; 309603724d3SBjoern A. Zeeb INP_INFO_WUNLOCK(&V_tcbinfo); 3108b615593SMarko Zec CURVNET_RESTORE(); 31185d94372SRobert Watson return; 31285d94372SRobert Watson } 3138501a69cSRobert Watson INP_WLOCK(inp); 314655f934bSMikolaj Golub if (callout_pending(&tp->t_timers->tt_keep) || 315655f934bSMikolaj Golub !callout_active(&tp->t_timers->tt_keep)) { 3168501a69cSRobert Watson INP_WUNLOCK(inp); 317603724d3SBjoern A. Zeeb INP_INFO_WUNLOCK(&V_tcbinfo); 3188b615593SMarko Zec CURVNET_RESTORE(); 31985d94372SRobert Watson return; 32085d94372SRobert Watson } 321e2f2059fSMike Silbersack callout_deactivate(&tp->t_timers->tt_keep); 322655f934bSMikolaj Golub if ((inp->inp_flags & INP_DROPPED) != 0) { 323655f934bSMikolaj Golub INP_WUNLOCK(inp); 324655f934bSMikolaj Golub INP_INFO_WUNLOCK(&V_tcbinfo); 325655f934bSMikolaj Golub CURVNET_RESTORE(); 326655f934bSMikolaj Golub return; 327655f934bSMikolaj Golub } 3289b8b58e0SJonathan Lemon /* 3299b8b58e0SJonathan Lemon * Keep-alive timer went off; send something 3309b8b58e0SJonathan Lemon * or drop connection if idle for too long. 3319b8b58e0SJonathan Lemon */ 33278b50714SRobert Watson TCPSTAT_INC(tcps_keeptimeo); 3339b8b58e0SJonathan Lemon if (tp->t_state < TCPS_ESTABLISHED) 3349b8b58e0SJonathan Lemon goto dropit; 3352a074620SSam Leffler if ((always_keepalive || inp->inp_socket->so_options & SO_KEEPALIVE) && 3369b8b58e0SJonathan Lemon tp->t_state <= TCPS_CLOSING) { 3379077f387SGleb Smirnoff if (ticks - tp->t_rcvtime >= TP_KEEPIDLE(tp) + TP_MAXIDLE(tp)) 3389b8b58e0SJonathan Lemon goto dropit; 3399b8b58e0SJonathan Lemon /* 3409b8b58e0SJonathan Lemon * Send a packet designed to force a response 3419b8b58e0SJonathan Lemon * if the peer is up and reachable: 3429b8b58e0SJonathan Lemon * either an ACK if the connection is still alive, 3439b8b58e0SJonathan Lemon * or an RST if the peer has closed the connection 3449b8b58e0SJonathan Lemon * due to timeout or reboot. 3459b8b58e0SJonathan Lemon * Using sequence number tp->snd_una-1 3469b8b58e0SJonathan Lemon * causes the transmitted zero-length segment 3479b8b58e0SJonathan Lemon * to lie outside the receive window; 3489b8b58e0SJonathan Lemon * by the protocol spec, this requires the 3499b8b58e0SJonathan Lemon * correspondent TCP to respond. 3509b8b58e0SJonathan Lemon */ 35178b50714SRobert Watson TCPSTAT_INC(tcps_keepprobe); 35279909384SJonathan Lemon t_template = tcpip_maketemplate(inp); 35308517d53SMike Silbersack if (t_template) { 35408517d53SMike Silbersack tcp_respond(tp, t_template->tt_ipgen, 35508517d53SMike Silbersack &t_template->tt_t, (struct mbuf *)NULL, 3569b8b58e0SJonathan Lemon tp->rcv_nxt, tp->snd_una - 1, 0); 35753640b0eSRobert Watson free(t_template, M_TEMP); 35808517d53SMike Silbersack } 3599077f387SGleb Smirnoff callout_reset_on(&tp->t_timers->tt_keep, TP_KEEPINTVL(tp), 3609077f387SGleb Smirnoff tcp_timer_keep, tp, INP_CPU(inp)); 3614cc20ab1SSeigo Tanimura } else 3629077f387SGleb Smirnoff callout_reset_on(&tp->t_timers->tt_keep, TP_KEEPIDLE(tp), 3639077f387SGleb Smirnoff tcp_timer_keep, tp, INP_CPU(inp)); 3649b8b58e0SJonathan Lemon 3659b8b58e0SJonathan Lemon #ifdef TCPDEBUG 3662a074620SSam Leffler if (inp->inp_socket->so_options & SO_DEBUG) 367fb59c426SYoshinobu Inoue tcp_trace(TA_USER, ostate, tp, (void *)0, (struct tcphdr *)0, 3689b8b58e0SJonathan Lemon PRU_SLOWTIMO); 3699b8b58e0SJonathan Lemon #endif 3708501a69cSRobert Watson INP_WUNLOCK(inp); 371603724d3SBjoern A. Zeeb INP_INFO_WUNLOCK(&V_tcbinfo); 3728b615593SMarko Zec CURVNET_RESTORE(); 37385d94372SRobert Watson return; 3749b8b58e0SJonathan Lemon 3759b8b58e0SJonathan Lemon dropit: 37678b50714SRobert Watson TCPSTAT_INC(tcps_keepdrops); 37785d94372SRobert Watson tp = tcp_drop(tp, ETIMEDOUT); 37885d94372SRobert Watson 37985d94372SRobert Watson #ifdef TCPDEBUG 38085d94372SRobert Watson if (tp != NULL && (tp->t_inpcb->inp_socket->so_options & SO_DEBUG)) 38185d94372SRobert Watson tcp_trace(TA_USER, ostate, tp, (void *)0, (struct tcphdr *)0, 38285d94372SRobert Watson PRU_SLOWTIMO); 38385d94372SRobert Watson #endif 38485d94372SRobert Watson if (tp != NULL) 3858501a69cSRobert Watson INP_WUNLOCK(tp->t_inpcb); 386603724d3SBjoern A. Zeeb INP_INFO_WUNLOCK(&V_tcbinfo); 3878b615593SMarko Zec CURVNET_RESTORE(); 3889b8b58e0SJonathan Lemon } 3899b8b58e0SJonathan Lemon 39085d94372SRobert Watson void 39185d94372SRobert Watson tcp_timer_persist(void *xtp) 3929b8b58e0SJonathan Lemon { 39385d94372SRobert Watson struct tcpcb *tp = xtp; 39485d94372SRobert Watson struct inpcb *inp; 3958b615593SMarko Zec CURVNET_SET(tp->t_vnet); 3969b8b58e0SJonathan Lemon #ifdef TCPDEBUG 3979b8b58e0SJonathan Lemon int ostate; 3989b8b58e0SJonathan Lemon 3999b8b58e0SJonathan Lemon ostate = tp->t_state; 4009b8b58e0SJonathan Lemon #endif 401603724d3SBjoern A. Zeeb INP_INFO_WLOCK(&V_tcbinfo); 40285d94372SRobert Watson inp = tp->t_inpcb; 40385d94372SRobert Watson /* 40485d94372SRobert Watson * XXXRW: While this assert is in fact correct, bugs in the tcpcb 40585d94372SRobert Watson * tear-down mean we need it as a work-around for races between 40685d94372SRobert Watson * timers and tcp_discardcb(). 40785d94372SRobert Watson * 40885d94372SRobert Watson * KASSERT(inp != NULL, ("tcp_timer_persist: inp == NULL")); 40985d94372SRobert Watson */ 41085d94372SRobert Watson if (inp == NULL) { 41185d94372SRobert Watson tcp_timer_race++; 412603724d3SBjoern A. Zeeb INP_INFO_WUNLOCK(&V_tcbinfo); 4138b615593SMarko Zec CURVNET_RESTORE(); 41485d94372SRobert Watson return; 41585d94372SRobert Watson } 4168501a69cSRobert Watson INP_WLOCK(inp); 417655f934bSMikolaj Golub if (callout_pending(&tp->t_timers->tt_persist) || 418655f934bSMikolaj Golub !callout_active(&tp->t_timers->tt_persist)) { 4198501a69cSRobert Watson INP_WUNLOCK(inp); 420603724d3SBjoern A. Zeeb INP_INFO_WUNLOCK(&V_tcbinfo); 4218b615593SMarko Zec CURVNET_RESTORE(); 42285d94372SRobert Watson return; 42385d94372SRobert Watson } 424e2f2059fSMike Silbersack callout_deactivate(&tp->t_timers->tt_persist); 425655f934bSMikolaj Golub if ((inp->inp_flags & INP_DROPPED) != 0) { 426655f934bSMikolaj Golub INP_WUNLOCK(inp); 427655f934bSMikolaj Golub INP_INFO_WUNLOCK(&V_tcbinfo); 428655f934bSMikolaj Golub CURVNET_RESTORE(); 429655f934bSMikolaj Golub return; 430655f934bSMikolaj Golub } 4319b8b58e0SJonathan Lemon /* 4329b8b58e0SJonathan Lemon * Persistance timer into zero window. 4339b8b58e0SJonathan Lemon * Force a byte to be output, if possible. 4349b8b58e0SJonathan Lemon */ 43578b50714SRobert Watson TCPSTAT_INC(tcps_persisttimeo); 4369b8b58e0SJonathan Lemon /* 4379b8b58e0SJonathan Lemon * Hack: if the peer is dead/unreachable, we do not 4389b8b58e0SJonathan Lemon * time out if the window is closed. After a full 4399b8b58e0SJonathan Lemon * backoff, drop the connection if the idle time 4409b8b58e0SJonathan Lemon * (no responses to probes) reaches the maximum 4419b8b58e0SJonathan Lemon * backoff that we would use if retransmitting. 4429b8b58e0SJonathan Lemon */ 4439b8b58e0SJonathan Lemon if (tp->t_rxtshift == TCP_MAXRXTSHIFT && 4446b0c5521SJohn Baldwin (ticks - tp->t_rcvtime >= tcp_maxpersistidle || 4456b0c5521SJohn Baldwin ticks - tp->t_rcvtime >= TCP_REXMTVAL(tp) * tcp_totbackoff)) { 44678b50714SRobert Watson TCPSTAT_INC(tcps_persistdrop); 44785d94372SRobert Watson tp = tcp_drop(tp, ETIMEDOUT); 44885d94372SRobert Watson goto out; 4499b8b58e0SJonathan Lemon } 450*322181c9SAndre Oppermann /* 451*322181c9SAndre Oppermann * If the user has closed the socket then drop a persisting 452*322181c9SAndre Oppermann * connection after a much reduced timeout. 453*322181c9SAndre Oppermann */ 454*322181c9SAndre Oppermann if (tp->t_state > TCPS_CLOSE_WAIT && 455*322181c9SAndre Oppermann (ticks - tp->t_rcvtime) >= TCPTV_PERSMAX) { 456*322181c9SAndre Oppermann TCPSTAT_INC(tcps_persistdrop); 457*322181c9SAndre Oppermann tp = tcp_drop(tp, ETIMEDOUT); 458*322181c9SAndre Oppermann goto out; 459*322181c9SAndre Oppermann } 4609b8b58e0SJonathan Lemon tcp_setpersist(tp); 4612cdbfa66SPaul Saab tp->t_flags |= TF_FORCEDATA; 4629b8b58e0SJonathan Lemon (void) tcp_output(tp); 4632cdbfa66SPaul Saab tp->t_flags &= ~TF_FORCEDATA; 4649b8b58e0SJonathan Lemon 46585d94372SRobert Watson out: 4669b8b58e0SJonathan Lemon #ifdef TCPDEBUG 467ffb761f6SGleb Smirnoff if (tp != NULL && tp->t_inpcb->inp_socket->so_options & SO_DEBUG) 468ffb761f6SGleb Smirnoff tcp_trace(TA_USER, ostate, tp, NULL, NULL, PRU_SLOWTIMO); 4699b8b58e0SJonathan Lemon #endif 47085d94372SRobert Watson if (tp != NULL) 4718501a69cSRobert Watson INP_WUNLOCK(inp); 472603724d3SBjoern A. Zeeb INP_INFO_WUNLOCK(&V_tcbinfo); 4738b615593SMarko Zec CURVNET_RESTORE(); 4749b8b58e0SJonathan Lemon } 4759b8b58e0SJonathan Lemon 47685d94372SRobert Watson void 47785d94372SRobert Watson tcp_timer_rexmt(void * xtp) 4789b8b58e0SJonathan Lemon { 47985d94372SRobert Watson struct tcpcb *tp = xtp; 4808b615593SMarko Zec CURVNET_SET(tp->t_vnet); 4819b8b58e0SJonathan Lemon int rexmt; 48285d94372SRobert Watson int headlocked; 48385d94372SRobert Watson struct inpcb *inp; 4849b8b58e0SJonathan Lemon #ifdef TCPDEBUG 4859b8b58e0SJonathan Lemon int ostate; 4869b8b58e0SJonathan Lemon 4879b8b58e0SJonathan Lemon ostate = tp->t_state; 4889b8b58e0SJonathan Lemon #endif 48987aedea4SKip Macy INP_INFO_RLOCK(&V_tcbinfo); 49085d94372SRobert Watson inp = tp->t_inpcb; 49185d94372SRobert Watson /* 49285d94372SRobert Watson * XXXRW: While this assert is in fact correct, bugs in the tcpcb 49385d94372SRobert Watson * tear-down mean we need it as a work-around for races between 49485d94372SRobert Watson * timers and tcp_discardcb(). 49585d94372SRobert Watson * 49685d94372SRobert Watson * KASSERT(inp != NULL, ("tcp_timer_rexmt: inp == NULL")); 49785d94372SRobert Watson */ 49885d94372SRobert Watson if (inp == NULL) { 49985d94372SRobert Watson tcp_timer_race++; 50087aedea4SKip Macy INP_INFO_RUNLOCK(&V_tcbinfo); 5018b615593SMarko Zec CURVNET_RESTORE(); 50285d94372SRobert Watson return; 50385d94372SRobert Watson } 5048501a69cSRobert Watson INP_WLOCK(inp); 505655f934bSMikolaj Golub if (callout_pending(&tp->t_timers->tt_rexmt) || 506655f934bSMikolaj Golub !callout_active(&tp->t_timers->tt_rexmt)) { 5078501a69cSRobert Watson INP_WUNLOCK(inp); 50887aedea4SKip Macy INP_INFO_RUNLOCK(&V_tcbinfo); 5098b615593SMarko Zec CURVNET_RESTORE(); 51085d94372SRobert Watson return; 51185d94372SRobert Watson } 512e2f2059fSMike Silbersack callout_deactivate(&tp->t_timers->tt_rexmt); 513655f934bSMikolaj Golub if ((inp->inp_flags & INP_DROPPED) != 0) { 514655f934bSMikolaj Golub INP_WUNLOCK(inp); 515655f934bSMikolaj Golub INP_INFO_RUNLOCK(&V_tcbinfo); 516655f934bSMikolaj Golub CURVNET_RESTORE(); 517655f934bSMikolaj Golub return; 518655f934bSMikolaj Golub } 5196d90faf3SPaul Saab tcp_free_sackholes(tp); 520df8bae1dSRodney W. Grimes /* 521df8bae1dSRodney W. Grimes * Retransmission timer went off. Message has not 522df8bae1dSRodney W. Grimes * been acked within retransmit interval. Back off 523df8bae1dSRodney W. Grimes * to a longer retransmit interval and retransmit one segment. 524df8bae1dSRodney W. Grimes */ 525df8bae1dSRodney W. Grimes if (++tp->t_rxtshift > TCP_MAXRXTSHIFT) { 526df8bae1dSRodney W. Grimes tp->t_rxtshift = TCP_MAXRXTSHIFT; 52778b50714SRobert Watson TCPSTAT_INC(tcps_timeoutdrop); 52887aedea4SKip Macy in_pcbref(inp); 52987aedea4SKip Macy INP_INFO_RUNLOCK(&V_tcbinfo); 53087aedea4SKip Macy INP_WUNLOCK(inp); 53187aedea4SKip Macy INP_INFO_WLOCK(&V_tcbinfo); 53287aedea4SKip Macy INP_WLOCK(inp); 533fa046d87SRobert Watson if (in_pcbrele_wlocked(inp)) { 53487aedea4SKip Macy INP_INFO_WUNLOCK(&V_tcbinfo); 53587aedea4SKip Macy CURVNET_RESTORE(); 53687aedea4SKip Macy return; 53787aedea4SKip Macy } 538aa4b09c5SNavdeep Parhar if (inp->inp_flags & INP_DROPPED) { 539aa4b09c5SNavdeep Parhar INP_WUNLOCK(inp); 540aa4b09c5SNavdeep Parhar INP_INFO_WUNLOCK(&V_tcbinfo); 541aa4b09c5SNavdeep Parhar CURVNET_RESTORE(); 542aa4b09c5SNavdeep Parhar return; 543aa4b09c5SNavdeep Parhar } 544aa4b09c5SNavdeep Parhar 54585d94372SRobert Watson tp = tcp_drop(tp, tp->t_softerror ? 54685d94372SRobert Watson tp->t_softerror : ETIMEDOUT); 54787aedea4SKip Macy headlocked = 1; 54885d94372SRobert Watson goto out; 5499b8b58e0SJonathan Lemon } 55087aedea4SKip Macy INP_INFO_RUNLOCK(&V_tcbinfo); 55185d94372SRobert Watson headlocked = 0; 552cf8f04f4SAndre Oppermann if (tp->t_state == TCPS_SYN_SENT) { 553cf8f04f4SAndre Oppermann /* 554cf8f04f4SAndre Oppermann * If the SYN was retransmitted, indicate CWND to be 555cf8f04f4SAndre Oppermann * limited to 1 segment in cc_conn_init(). 556cf8f04f4SAndre Oppermann */ 557cf8f04f4SAndre Oppermann tp->snd_cwnd = 1; 558cf8f04f4SAndre Oppermann } else if (tp->t_rxtshift == 1) { 5599b8b58e0SJonathan Lemon /* 5609b8b58e0SJonathan Lemon * first retransmit; record ssthresh and cwnd so they can 5619b8b58e0SJonathan Lemon * be recovered if this turns out to be a "bad" retransmit. 5629b8b58e0SJonathan Lemon * A retransmit is considered "bad" if an ACK for this 5639b8b58e0SJonathan Lemon * segment is received within RTT/2 interval; the assumption 5649b8b58e0SJonathan Lemon * here is that the ACK was already in flight. See 5659b8b58e0SJonathan Lemon * "On Estimating End-to-End Network Path Properties" by 5669b8b58e0SJonathan Lemon * Allman and Paxson for more details. 5679b8b58e0SJonathan Lemon */ 5689b8b58e0SJonathan Lemon tp->snd_cwnd_prev = tp->snd_cwnd; 5699b8b58e0SJonathan Lemon tp->snd_ssthresh_prev = tp->snd_ssthresh; 5709d11646dSJeffrey Hsu tp->snd_recover_prev = tp->snd_recover; 571dbc42409SLawrence Stewart if (IN_FASTRECOVERY(tp->t_flags)) 5729d11646dSJeffrey Hsu tp->t_flags |= TF_WASFRECOVERY; 5739d11646dSJeffrey Hsu else 5749d11646dSJeffrey Hsu tp->t_flags &= ~TF_WASFRECOVERY; 575dbc42409SLawrence Stewart if (IN_CONGRECOVERY(tp->t_flags)) 576dbc42409SLawrence Stewart tp->t_flags |= TF_WASCRECOVERY; 577dbc42409SLawrence Stewart else 578dbc42409SLawrence Stewart tp->t_flags &= ~TF_WASCRECOVERY; 5799b8b58e0SJonathan Lemon tp->t_badrxtwin = ticks + (tp->t_srtt >> (TCP_RTT_SHIFT + 1)); 580672dc4aeSJohn Baldwin tp->t_flags |= TF_PREVVALID; 581672dc4aeSJohn Baldwin } else 582672dc4aeSJohn Baldwin tp->t_flags &= ~TF_PREVVALID; 58378b50714SRobert Watson TCPSTAT_INC(tcps_rexmttimeo); 5847d42e30cSJonathan Lemon if (tp->t_state == TCPS_SYN_SENT) 585f4748ef5SAndre Oppermann rexmt = TCPTV_RTOBASE * tcp_syn_backoff[tp->t_rxtshift]; 5867d42e30cSJonathan Lemon else 587df8bae1dSRodney W. Grimes rexmt = TCP_REXMTVAL(tp) * tcp_backoff[tp->t_rxtshift]; 588df8bae1dSRodney W. Grimes TCPT_RANGESET(tp->t_rxtcur, rexmt, 589df8bae1dSRodney W. Grimes tp->t_rttmin, TCPTV_REXMTMAX); 590df8bae1dSRodney W. Grimes /* 59177339e1cSAndre Oppermann * Disable RFC1323 and SACK if we haven't got any response to 5927ceb7783SJesper Skriver * our third SYN to work-around some broken terminal servers 5937ceb7783SJesper Skriver * (most of which have hopefully been retired) that have bad VJ 5947ceb7783SJesper Skriver * header compression code which trashes TCP segments containing 5957ceb7783SJesper Skriver * unknown-to-them TCP options. 5967ceb7783SJesper Skriver */ 5977ceb7783SJesper Skriver if ((tp->t_state == TCPS_SYN_SENT) && (tp->t_rxtshift == 3)) 598c4ab59c1SAndre Oppermann tp->t_flags &= ~(TF_REQ_SCALE|TF_REQ_TSTMP|TF_SACK_PERMIT); 5997ceb7783SJesper Skriver /* 60097d8d152SAndre Oppermann * If we backed off this far, our srtt estimate is probably bogus. 60197d8d152SAndre Oppermann * Clobber it so we'll take the next rtt measurement as our srtt; 602df8bae1dSRodney W. Grimes * move the current srtt into rttvar to keep the current 603df8bae1dSRodney W. Grimes * retransmit times until then. 604df8bae1dSRodney W. Grimes */ 605df8bae1dSRodney W. Grimes if (tp->t_rxtshift > TCP_MAXRXTSHIFT / 4) { 606fb59c426SYoshinobu Inoue #ifdef INET6 607fb59c426SYoshinobu Inoue if ((tp->t_inpcb->inp_vflag & INP_IPV6) != 0) 608fb59c426SYoshinobu Inoue in6_losing(tp->t_inpcb); 609fb59c426SYoshinobu Inoue #endif 610df8bae1dSRodney W. Grimes tp->t_rttvar += (tp->t_srtt >> TCP_RTT_SHIFT); 611df8bae1dSRodney W. Grimes tp->t_srtt = 0; 612df8bae1dSRodney W. Grimes } 613df8bae1dSRodney W. Grimes tp->snd_nxt = tp->snd_una; 6149d11646dSJeffrey Hsu tp->snd_recover = tp->snd_max; 61546f58482SJonathan Lemon /* 61674b48c1dSAndras Olah * Force a segment to be sent. 61774b48c1dSAndras Olah */ 61874b48c1dSAndras Olah tp->t_flags |= TF_ACKNOW; 61974b48c1dSAndras Olah /* 620df8bae1dSRodney W. Grimes * If timing a segment in this window, stop the timer. 621df8bae1dSRodney W. Grimes */ 6229b8b58e0SJonathan Lemon tp->t_rtttime = 0; 623dbc42409SLawrence Stewart 624b5af1b88SLawrence Stewart cc_cong_signal(tp, NULL, CC_RTO); 625dbc42409SLawrence Stewart 626df8bae1dSRodney W. Grimes (void) tcp_output(tp); 627df8bae1dSRodney W. Grimes 62885d94372SRobert Watson out: 6299b8b58e0SJonathan Lemon #ifdef TCPDEBUG 6301c53f806SRobert Watson if (tp != NULL && (tp->t_inpcb->inp_socket->so_options & SO_DEBUG)) 631fb59c426SYoshinobu Inoue tcp_trace(TA_USER, ostate, tp, (void *)0, (struct tcphdr *)0, 6329b8b58e0SJonathan Lemon PRU_SLOWTIMO); 633df8bae1dSRodney W. Grimes #endif 63485d94372SRobert Watson if (tp != NULL) 6358501a69cSRobert Watson INP_WUNLOCK(inp); 63685d94372SRobert Watson if (headlocked) 637603724d3SBjoern A. Zeeb INP_INFO_WUNLOCK(&V_tcbinfo); 6388b615593SMarko Zec CURVNET_RESTORE(); 63985d94372SRobert Watson } 64085d94372SRobert Watson 64185d94372SRobert Watson void 64285d94372SRobert Watson tcp_timer_activate(struct tcpcb *tp, int timer_type, u_int delta) 64385d94372SRobert Watson { 64485d94372SRobert Watson struct callout *t_callout; 64585d94372SRobert Watson void *f_callout; 64687aedea4SKip Macy struct inpcb *inp = tp->t_inpcb; 64787aedea4SKip Macy int cpu = INP_CPU(inp); 64885d94372SRobert Watson 64909fe6320SNavdeep Parhar #ifdef TCP_OFFLOAD 65009fe6320SNavdeep Parhar if (tp->t_flags & TF_TOE) 65109fe6320SNavdeep Parhar return; 65209fe6320SNavdeep Parhar #endif 65309fe6320SNavdeep Parhar 65485d94372SRobert Watson switch (timer_type) { 65585d94372SRobert Watson case TT_DELACK: 656e2f2059fSMike Silbersack t_callout = &tp->t_timers->tt_delack; 65785d94372SRobert Watson f_callout = tcp_timer_delack; 65885d94372SRobert Watson break; 65985d94372SRobert Watson case TT_REXMT: 660e2f2059fSMike Silbersack t_callout = &tp->t_timers->tt_rexmt; 66185d94372SRobert Watson f_callout = tcp_timer_rexmt; 66285d94372SRobert Watson break; 66385d94372SRobert Watson case TT_PERSIST: 664e2f2059fSMike Silbersack t_callout = &tp->t_timers->tt_persist; 66585d94372SRobert Watson f_callout = tcp_timer_persist; 66685d94372SRobert Watson break; 66785d94372SRobert Watson case TT_KEEP: 668e2f2059fSMike Silbersack t_callout = &tp->t_timers->tt_keep; 66985d94372SRobert Watson f_callout = tcp_timer_keep; 67085d94372SRobert Watson break; 67185d94372SRobert Watson case TT_2MSL: 672e2f2059fSMike Silbersack t_callout = &tp->t_timers->tt_2msl; 67385d94372SRobert Watson f_callout = tcp_timer_2msl; 67485d94372SRobert Watson break; 67585d94372SRobert Watson default: 67685d94372SRobert Watson panic("bad timer_type"); 67785d94372SRobert Watson } 67885d94372SRobert Watson if (delta == 0) { 67985d94372SRobert Watson callout_stop(t_callout); 68085d94372SRobert Watson } else { 68187aedea4SKip Macy callout_reset_on(t_callout, delta, f_callout, tp, cpu); 68285d94372SRobert Watson } 68385d94372SRobert Watson } 68485d94372SRobert Watson 68585d94372SRobert Watson int 68685d94372SRobert Watson tcp_timer_active(struct tcpcb *tp, int timer_type) 68785d94372SRobert Watson { 68885d94372SRobert Watson struct callout *t_callout; 68985d94372SRobert Watson 69085d94372SRobert Watson switch (timer_type) { 69185d94372SRobert Watson case TT_DELACK: 692e2f2059fSMike Silbersack t_callout = &tp->t_timers->tt_delack; 69385d94372SRobert Watson break; 69485d94372SRobert Watson case TT_REXMT: 695e2f2059fSMike Silbersack t_callout = &tp->t_timers->tt_rexmt; 69685d94372SRobert Watson break; 69785d94372SRobert Watson case TT_PERSIST: 698e2f2059fSMike Silbersack t_callout = &tp->t_timers->tt_persist; 69985d94372SRobert Watson break; 70085d94372SRobert Watson case TT_KEEP: 701e2f2059fSMike Silbersack t_callout = &tp->t_timers->tt_keep; 70285d94372SRobert Watson break; 70385d94372SRobert Watson case TT_2MSL: 704e2f2059fSMike Silbersack t_callout = &tp->t_timers->tt_2msl; 70585d94372SRobert Watson break; 70685d94372SRobert Watson default: 70785d94372SRobert Watson panic("bad timer_type"); 70885d94372SRobert Watson } 70985d94372SRobert Watson return callout_active(t_callout); 710df8bae1dSRodney W. Grimes } 711b8614722SMike Silbersack 712b8614722SMike Silbersack #define ticks_to_msecs(t) (1000*(t) / hz) 713b8614722SMike Silbersack 714b8614722SMike Silbersack void 715b8614722SMike Silbersack tcp_timer_to_xtimer(struct tcpcb *tp, struct tcp_timer *timer, struct xtcp_timer *xtimer) 716b8614722SMike Silbersack { 717b8614722SMike Silbersack bzero(xtimer, sizeof(struct xtcp_timer)); 718b8614722SMike Silbersack if (timer == NULL) 719b8614722SMike Silbersack return; 720b8614722SMike Silbersack if (callout_active(&timer->tt_delack)) 721b8614722SMike Silbersack xtimer->tt_delack = ticks_to_msecs(timer->tt_delack.c_time - ticks); 722b8614722SMike Silbersack if (callout_active(&timer->tt_rexmt)) 723b8614722SMike Silbersack xtimer->tt_rexmt = ticks_to_msecs(timer->tt_rexmt.c_time - ticks); 724b8614722SMike Silbersack if (callout_active(&timer->tt_persist)) 725b8614722SMike Silbersack xtimer->tt_persist = ticks_to_msecs(timer->tt_persist.c_time - ticks); 726b8614722SMike Silbersack if (callout_active(&timer->tt_keep)) 727b8614722SMike Silbersack xtimer->tt_keep = ticks_to_msecs(timer->tt_keep.c_time - ticks); 728b8614722SMike Silbersack if (callout_active(&timer->tt_2msl)) 729b8614722SMike Silbersack xtimer->tt_2msl = ticks_to_msecs(timer->tt_2msl.c_time - ticks); 730b8614722SMike Silbersack xtimer->t_rcvtime = ticks_to_msecs(ticks - tp->t_rcvtime); 731b8614722SMike Silbersack } 732