1c398230bSWarner Losh /*- 251369649SPedro F. Giffuni * SPDX-License-Identifier: BSD-3-Clause 351369649SPedro F. Giffuni * 4e79adb8eSGarrett Wollman * Copyright (c) 1982, 1986, 1988, 1990, 1993, 1995 5df8bae1dSRodney W. Grimes * The Regents of the University of California. All rights reserved. 6df8bae1dSRodney W. Grimes * 7df8bae1dSRodney W. Grimes * Redistribution and use in source and binary forms, with or without 8df8bae1dSRodney W. Grimes * modification, are permitted provided that the following conditions 9df8bae1dSRodney W. Grimes * are met: 10df8bae1dSRodney W. Grimes * 1. Redistributions of source code must retain the above copyright 11df8bae1dSRodney W. Grimes * notice, this list of conditions and the following disclaimer. 12df8bae1dSRodney W. Grimes * 2. Redistributions in binary form must reproduce the above copyright 13df8bae1dSRodney W. Grimes * notice, this list of conditions and the following disclaimer in the 14df8bae1dSRodney W. Grimes * documentation and/or other materials provided with the distribution. 15fbbd9655SWarner Losh * 3. Neither the name of the University nor the names of its contributors 16df8bae1dSRodney W. Grimes * may be used to endorse or promote products derived from this software 17df8bae1dSRodney W. Grimes * without specific prior written permission. 18df8bae1dSRodney W. Grimes * 19df8bae1dSRodney W. Grimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 20df8bae1dSRodney W. Grimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21df8bae1dSRodney W. Grimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22df8bae1dSRodney W. Grimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 23df8bae1dSRodney W. Grimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24df8bae1dSRodney W. Grimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25df8bae1dSRodney W. Grimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26df8bae1dSRodney W. Grimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27df8bae1dSRodney W. Grimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28df8bae1dSRodney W. Grimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29df8bae1dSRodney W. Grimes * SUCH DAMAGE. 30df8bae1dSRodney W. Grimes * 31e79adb8eSGarrett Wollman * @(#)tcp_timer.c 8.2 (Berkeley) 5/24/95 32df8bae1dSRodney W. Grimes */ 33df8bae1dSRodney W. Grimes 344b421e2dSMike Silbersack #include <sys/cdefs.h> 354b421e2dSMike Silbersack __FBSDID("$FreeBSD$"); 364b421e2dSMike Silbersack 37825fd1e4SNavdeep Parhar #include "opt_inet.h" 38fb59c426SYoshinobu Inoue #include "opt_inet6.h" 390cc12cc5SJoerg Wunsch #include "opt_tcpdebug.h" 40883831c6SAdrian Chadd #include "opt_rss.h" 410cc12cc5SJoerg Wunsch 42df8bae1dSRodney W. Grimes #include <sys/param.h> 4398163b98SPoul-Henning Kamp #include <sys/kernel.h> 44c74af4faSBruce Evans #include <sys/lock.h> 4508517d53SMike Silbersack #include <sys/mbuf.h> 46c74af4faSBruce Evans #include <sys/mutex.h> 47c74af4faSBruce Evans #include <sys/protosw.h> 4887aedea4SKip Macy #include <sys/smp.h> 49df8bae1dSRodney W. Grimes #include <sys/socket.h> 50df8bae1dSRodney W. Grimes #include <sys/socketvar.h> 51c74af4faSBruce Evans #include <sys/sysctl.h> 52c74af4faSBruce Evans #include <sys/systm.h> 53e79adb8eSGarrett Wollman 544b79449eSBjoern A. Zeeb #include <net/if.h> 55df8bae1dSRodney W. Grimes #include <net/route.h> 56b2bdc62aSAdrian Chadd #include <net/rss_config.h> 57530c0060SRobert Watson #include <net/vnet.h> 58883831c6SAdrian Chadd #include <net/netisr.h> 59df8bae1dSRodney W. Grimes 60df8bae1dSRodney W. Grimes #include <netinet/in.h> 615d06879aSGeorge V. Neville-Neil #include <netinet/in_kdtrace.h> 62df8bae1dSRodney W. Grimes #include <netinet/in_pcb.h> 63883831c6SAdrian Chadd #include <netinet/in_rss.h> 64c74af4faSBruce Evans #include <netinet/in_systm.h> 65fb59c426SYoshinobu Inoue #ifdef INET6 66fb59c426SYoshinobu Inoue #include <netinet6/in6_pcb.h> 67fb59c426SYoshinobu Inoue #endif 68df8bae1dSRodney W. Grimes #include <netinet/ip_var.h> 692de3e790SGleb Smirnoff #include <netinet/tcp.h> 70df8bae1dSRodney W. Grimes #include <netinet/tcp_fsm.h> 712529f56eSJonathan T. Looney #include <netinet/tcp_log_buf.h> 72df8bae1dSRodney W. Grimes #include <netinet/tcp_timer.h> 73df8bae1dSRodney W. Grimes #include <netinet/tcp_var.h> 7489e560f4SRandall Stewart #include <netinet/tcp_seq.h> 754644fda3SGleb Smirnoff #include <netinet/cc/cc.h> 76f6f6703fSSean Bruno #ifdef INET6 77f6f6703fSSean Bruno #include <netinet6/tcp6_var.h> 78f6f6703fSSean Bruno #endif 79df8bae1dSRodney W. Grimes #include <netinet/tcpip.h> 80af7a2999SDavid Greenman #include <netinet/tcp_debug.h> 81df8bae1dSRodney W. Grimes 820645c604SHiren Panchasara int tcp_persmin; 837029da5cSPawel Biernacki SYSCTL_PROC(_net_inet_tcp, OID_AUTO, persmin, 847029da5cSPawel Biernacki CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, 857029da5cSPawel Biernacki &tcp_persmin, 0, sysctl_msec_to_ticks, "I", 867029da5cSPawel Biernacki "minimum persistence interval"); 870645c604SHiren Panchasara 880645c604SHiren Panchasara int tcp_persmax; 897029da5cSPawel Biernacki SYSCTL_PROC(_net_inet_tcp, OID_AUTO, persmax, 907029da5cSPawel Biernacki CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, 917029da5cSPawel Biernacki &tcp_persmax, 0, sysctl_msec_to_ticks, "I", 927029da5cSPawel Biernacki "maximum persistence interval"); 930645c604SHiren Panchasara 949b8b58e0SJonathan Lemon int tcp_keepinit; 957029da5cSPawel Biernacki SYSCTL_PROC(_net_inet_tcp, TCPCTL_KEEPINIT, keepinit, 967029da5cSPawel Biernacki CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, 977029da5cSPawel Biernacki &tcp_keepinit, 0, sysctl_msec_to_ticks, "I", 987029da5cSPawel Biernacki "time to establish connection"); 997b40aa32SPaul Traina 1009b8b58e0SJonathan Lemon int tcp_keepidle; 1017029da5cSPawel Biernacki SYSCTL_PROC(_net_inet_tcp, TCPCTL_KEEPIDLE, keepidle, 1027029da5cSPawel Biernacki CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, 1037029da5cSPawel Biernacki &tcp_keepidle, 0, sysctl_msec_to_ticks, "I", 1047029da5cSPawel Biernacki "time before keepalive probes begin"); 10598163b98SPoul-Henning Kamp 1069b8b58e0SJonathan Lemon int tcp_keepintvl; 1077029da5cSPawel Biernacki SYSCTL_PROC(_net_inet_tcp, TCPCTL_KEEPINTVL, keepintvl, 1087029da5cSPawel Biernacki CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, 1097029da5cSPawel Biernacki &tcp_keepintvl, 0, sysctl_msec_to_ticks, "I", 1107029da5cSPawel Biernacki "time between keepalive probes"); 11198163b98SPoul-Henning Kamp 1129b8b58e0SJonathan Lemon int tcp_delacktime; 1137029da5cSPawel Biernacki SYSCTL_PROC(_net_inet_tcp, TCPCTL_DELACKTIME, delacktime, 1147029da5cSPawel Biernacki CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, 1156489fe65SAndre Oppermann &tcp_delacktime, 0, sysctl_msec_to_ticks, "I", 116ccb4d0c6SJonathan Lemon "Time before a delayed ACK is sent"); 1179b8b58e0SJonathan Lemon 118c2c8e360SAlexander V. Chernikov VNET_DEFINE(int, tcp_msl); 1197029da5cSPawel Biernacki SYSCTL_PROC(_net_inet_tcp, OID_AUTO, msl, 120c2c8e360SAlexander V. Chernikov CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_VNET, 121c2c8e360SAlexander V. Chernikov &VNET_NAME(tcp_msl), 0, sysctl_msec_to_ticks, "I", 1227029da5cSPawel Biernacki "Maximum segment lifetime"); 1239b8b58e0SJonathan Lemon 1240999766dSMichael Tuexen int tcp_rexmit_initial; 1257029da5cSPawel Biernacki SYSCTL_PROC(_net_inet_tcp, OID_AUTO, rexmit_initial, 1267029da5cSPawel Biernacki CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, 1270999766dSMichael Tuexen &tcp_rexmit_initial, 0, sysctl_msec_to_ticks, "I", 1280999766dSMichael Tuexen "Initial Retransmission Timeout"); 1290999766dSMichael Tuexen 130701bec5aSMatthew Dillon int tcp_rexmit_min; 1317029da5cSPawel Biernacki SYSCTL_PROC(_net_inet_tcp, OID_AUTO, rexmit_min, 1327029da5cSPawel Biernacki CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, 1336489fe65SAndre Oppermann &tcp_rexmit_min, 0, sysctl_msec_to_ticks, "I", 1346489fe65SAndre Oppermann "Minimum Retransmission Timeout"); 135701bec5aSMatthew Dillon 136701bec5aSMatthew Dillon int tcp_rexmit_slop; 1377029da5cSPawel Biernacki SYSCTL_PROC(_net_inet_tcp, OID_AUTO, rexmit_slop, 1387029da5cSPawel Biernacki CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, 1396489fe65SAndre Oppermann &tcp_rexmit_slop, 0, sysctl_msec_to_ticks, "I", 1406489fe65SAndre Oppermann "Retransmission Timer Slop"); 141701bec5aSMatthew Dillon 142334fc582SBjoern A. Zeeb VNET_DEFINE(int, tcp_always_keepalive) = 1; 143334fc582SBjoern A. Zeeb SYSCTL_INT(_net_inet_tcp, OID_AUTO, always_keepalive, CTLFLAG_VNET|CTLFLAG_RW, 144334fc582SBjoern A. Zeeb &VNET_NAME(tcp_always_keepalive) , 0, 145334fc582SBjoern A. Zeeb "Assume SO_KEEPALIVE on all TCP connections"); 14634be9bf3SPoul-Henning Kamp 1477c72af87SMohan Srinivasan int tcp_fast_finwait2_recycle = 0; 1487c72af87SMohan Srinivasan SYSCTL_INT(_net_inet_tcp, OID_AUTO, fast_finwait2_recycle, CTLFLAG_RW, 1496489fe65SAndre Oppermann &tcp_fast_finwait2_recycle, 0, 1506489fe65SAndre Oppermann "Recycle closed FIN_WAIT_2 connections faster"); 1517c72af87SMohan Srinivasan 1527c72af87SMohan Srinivasan int tcp_finwait2_timeout; 1537029da5cSPawel Biernacki SYSCTL_PROC(_net_inet_tcp, OID_AUTO, finwait2_timeout, 1547029da5cSPawel Biernacki CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, 1557029da5cSPawel Biernacki &tcp_finwait2_timeout, 0, sysctl_msec_to_ticks, "I", 1567029da5cSPawel Biernacki "FIN-WAIT2 timeout"); 1577c72af87SMohan Srinivasan 1589077f387SGleb Smirnoff int tcp_keepcnt = TCPTV_KEEPCNT; 1599077f387SGleb Smirnoff SYSCTL_INT(_net_inet_tcp, OID_AUTO, keepcnt, CTLFLAG_RW, &tcp_keepcnt, 0, 1609077f387SGleb Smirnoff "Number of keepalive probes to send"); 1617c72af87SMohan Srinivasan 1620312fbe9SPoul-Henning Kamp /* max idle probes */ 1639b8b58e0SJonathan Lemon int tcp_maxpersistidle; 164e79adb8eSGarrett Wollman 16589e560f4SRandall Stewart int tcp_rexmit_drop_options = 0; 1666c0ef895SJohn Baldwin SYSCTL_INT(_net_inet_tcp, OID_AUTO, rexmit_drop_options, CTLFLAG_RW, 1676c0ef895SJohn Baldwin &tcp_rexmit_drop_options, 0, 1686c0ef895SJohn Baldwin "Drop TCP options from 3rd and later retransmitted SYN"); 1696c0ef895SJohn Baldwin 17008af8aacSRandall Stewart int tcp_maxunacktime = TCPTV_MAXUNACKTIME; 17108af8aacSRandall Stewart SYSCTL_PROC(_net_inet_tcp, OID_AUTO, maxunacktime, 17208af8aacSRandall Stewart CTLTYPE_INT|CTLFLAG_RW | CTLFLAG_NEEDGIANT, 17308af8aacSRandall Stewart &tcp_maxunacktime, 0, sysctl_msec_to_ticks, "I", 17408af8aacSRandall Stewart "Maximum time (in ms) that a session can linger without making progress"); 17508af8aacSRandall Stewart 176e29c55e4SGleb Smirnoff VNET_DEFINE(int, tcp_pmtud_blackhole_detect); 177f6f6703fSSean Bruno SYSCTL_INT(_net_inet_tcp, OID_AUTO, pmtud_blackhole_detection, 178f0188618SHans Petter Selasky CTLFLAG_RW|CTLFLAG_VNET, 179f6f6703fSSean Bruno &VNET_NAME(tcp_pmtud_blackhole_detect), 0, 180f6f6703fSSean Bruno "Path MTU Discovery Black Hole Detection Enabled"); 181f6f6703fSSean Bruno 182f6f6703fSSean Bruno #ifdef INET 183e29c55e4SGleb Smirnoff VNET_DEFINE(int, tcp_pmtud_blackhole_mss) = 1200; 184f6f6703fSSean Bruno SYSCTL_INT(_net_inet_tcp, OID_AUTO, pmtud_blackhole_mss, 185f0188618SHans Petter Selasky CTLFLAG_RW|CTLFLAG_VNET, 186f6f6703fSSean Bruno &VNET_NAME(tcp_pmtud_blackhole_mss), 0, 187f6f6703fSSean Bruno "Path MTU Discovery Black Hole Detection lowered MSS"); 188f6f6703fSSean Bruno #endif 189f6f6703fSSean Bruno 190f6f6703fSSean Bruno #ifdef INET6 191e29c55e4SGleb Smirnoff VNET_DEFINE(int, tcp_v6pmtud_blackhole_mss) = 1220; 192f6f6703fSSean Bruno SYSCTL_INT(_net_inet_tcp, OID_AUTO, v6pmtud_blackhole_mss, 193f0188618SHans Petter Selasky CTLFLAG_RW|CTLFLAG_VNET, 194f6f6703fSSean Bruno &VNET_NAME(tcp_v6pmtud_blackhole_mss), 0, 195f6f6703fSSean Bruno "Path MTU Discovery IPv6 Black Hole Detection lowered MSS"); 196f6f6703fSSean Bruno #endif 197f6f6703fSSean Bruno 1988f7e75cbSAdrian Chadd #ifdef RSS 1998f7e75cbSAdrian Chadd static int per_cpu_timers = 1; 2008f7e75cbSAdrian Chadd #else 20187aedea4SKip Macy static int per_cpu_timers = 0; 2028f7e75cbSAdrian Chadd #endif 20387aedea4SKip Macy SYSCTL_INT(_net_inet_tcp, OID_AUTO, per_cpu_timers, CTLFLAG_RW, 20487aedea4SKip Macy &per_cpu_timers , 0, "run tcp timers on all cpus"); 20587aedea4SKip Macy 206883831c6SAdrian Chadd /* 207883831c6SAdrian Chadd * Map the given inp to a CPU id. 208883831c6SAdrian Chadd * 209883831c6SAdrian Chadd * This queries RSS if it's compiled in, else it defaults to the current 210883831c6SAdrian Chadd * CPU ID. 211883831c6SAdrian Chadd */ 21289e560f4SRandall Stewart inline int 213883831c6SAdrian Chadd inp_to_cpuid(struct inpcb *inp) 214883831c6SAdrian Chadd { 215883831c6SAdrian Chadd u_int cpuid; 216883831c6SAdrian Chadd 217883831c6SAdrian Chadd if (per_cpu_timers) { 21847ded797SFranco Fichtner #ifdef RSS 219883831c6SAdrian Chadd cpuid = rss_hash2cpuid(inp->inp_flowid, inp->inp_flowtype); 220883831c6SAdrian Chadd if (cpuid == NETISR_CPUID_NONE) 221883831c6SAdrian Chadd return (curcpu); /* XXX */ 222883831c6SAdrian Chadd else 223883831c6SAdrian Chadd return (cpuid); 22447ded797SFranco Fichtner #endif 225883831c6SAdrian Chadd /* 226883831c6SAdrian Chadd * We don't have a flowid -> cpuid mapping, so cheat and 227883831c6SAdrian Chadd * just map unknown cpuids to curcpu. Not the best, but 228883831c6SAdrian Chadd * apparently better than defaulting to swi 0. 229883831c6SAdrian Chadd */ 230883831c6SAdrian Chadd cpuid = inp->inp_flowid % (mp_maxid + 1); 231883831c6SAdrian Chadd if (! CPU_ABSENT(cpuid)) 232883831c6SAdrian Chadd return (cpuid); 233883831c6SAdrian Chadd return (curcpu); 23447ded797SFranco Fichtner } else { 235883831c6SAdrian Chadd return (0); 236883831c6SAdrian Chadd } 237883831c6SAdrian Chadd } 23887aedea4SKip Macy 239df8bae1dSRodney W. Grimes int tcp_backoff[TCP_MAXRXTSHIFT + 1] = 240f058535dSJeffrey Hsu { 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 512, 512, 512 }; 241df8bae1dSRodney W. Grimes 24289e560f4SRandall Stewart int tcp_totbackoff = 2559; /* sum of tcp_backoff[] */ 243e79adb8eSGarrett Wollman 244df8bae1dSRodney W. Grimes /* 245df8bae1dSRodney W. Grimes * TCP timer processing. 246df8bae1dSRodney W. Grimes */ 24785d94372SRobert Watson 24885d94372SRobert Watson void 24985d94372SRobert Watson tcp_timer_delack(void *xtp) 250df8bae1dSRodney W. Grimes { 251109eb549SGleb Smirnoff struct epoch_tracker et; 25285d94372SRobert Watson struct tcpcb *tp = xtp; 2539eb0e832SGleb Smirnoff #if defined(INVARIANTS) || defined(VIMAGE) 2549eb0e832SGleb Smirnoff struct inpcb *inp = tptoinpcb(tp); 2559eb0e832SGleb Smirnoff #endif 2569eb0e832SGleb Smirnoff 2578501a69cSRobert Watson INP_WLOCK(inp); 258*8840ae22SGleb Smirnoff CURVNET_SET(inp->inp_vnet); 259*8840ae22SGleb Smirnoff 260655f934bSMikolaj Golub if (callout_pending(&tp->t_timers->tt_delack) || 261655f934bSMikolaj Golub !callout_active(&tp->t_timers->tt_delack)) { 2628501a69cSRobert Watson INP_WUNLOCK(inp); 2638b615593SMarko Zec CURVNET_RESTORE(); 26485d94372SRobert Watson return; 26585d94372SRobert Watson } 266e2f2059fSMike Silbersack callout_deactivate(&tp->t_timers->tt_delack); 267655f934bSMikolaj Golub if ((inp->inp_flags & INP_DROPPED) != 0) { 268655f934bSMikolaj Golub INP_WUNLOCK(inp); 269655f934bSMikolaj Golub CURVNET_RESTORE(); 270655f934bSMikolaj Golub return; 271655f934bSMikolaj Golub } 2729b8b58e0SJonathan Lemon tp->t_flags |= TF_ACKNOW; 27378b50714SRobert Watson TCPSTAT_INC(tcps_delack); 274109eb549SGleb Smirnoff NET_EPOCH_ENTER(et); 275f64dc2abSGleb Smirnoff (void) tcp_output_unlock(tp); 276109eb549SGleb Smirnoff NET_EPOCH_EXIT(et); 2778b615593SMarko Zec CURVNET_RESTORE(); 2789b8b58e0SJonathan Lemon } 2799b8b58e0SJonathan Lemon 28077198a94SGleb Smirnoff /* 28177198a94SGleb Smirnoff * Call tcp_close() from a callout context. 28277198a94SGleb Smirnoff */ 28377198a94SGleb Smirnoff static void 28477198a94SGleb Smirnoff tcp_timer_close(struct tcpcb *tp) 285b07fef50SRandall Stewart { 28677198a94SGleb Smirnoff struct epoch_tracker et; 2879eb0e832SGleb Smirnoff struct inpcb *inp = tptoinpcb(tp); 28877198a94SGleb Smirnoff 28977198a94SGleb Smirnoff INP_WLOCK_ASSERT(inp); 29077198a94SGleb Smirnoff 29177198a94SGleb Smirnoff NET_EPOCH_ENTER(et); 29277198a94SGleb Smirnoff tp = tcp_close(tp); 29377198a94SGleb Smirnoff NET_EPOCH_EXIT(et); 29477198a94SGleb Smirnoff if (tp != NULL) 29577198a94SGleb Smirnoff INP_WUNLOCK(inp); 29677198a94SGleb Smirnoff } 29777198a94SGleb Smirnoff 29877198a94SGleb Smirnoff /* 29977198a94SGleb Smirnoff * Call tcp_drop() from a callout context. 30077198a94SGleb Smirnoff */ 30177198a94SGleb Smirnoff static void 30277198a94SGleb Smirnoff tcp_timer_drop(struct tcpcb *tp) 30377198a94SGleb Smirnoff { 30477198a94SGleb Smirnoff struct epoch_tracker et; 3059eb0e832SGleb Smirnoff struct inpcb *inp = tptoinpcb(tp); 30677198a94SGleb Smirnoff 30777198a94SGleb Smirnoff INP_WLOCK_ASSERT(inp); 30877198a94SGleb Smirnoff 30977198a94SGleb Smirnoff NET_EPOCH_ENTER(et); 31077198a94SGleb Smirnoff tp = tcp_drop(tp, ETIMEDOUT); 31177198a94SGleb Smirnoff NET_EPOCH_EXIT(et); 31277198a94SGleb Smirnoff if (tp != NULL) 313b07fef50SRandall Stewart INP_WUNLOCK(inp); 314b07fef50SRandall Stewart } 315b07fef50SRandall Stewart 31685d94372SRobert Watson void 31785d94372SRobert Watson tcp_timer_2msl(void *xtp) 3189b8b58e0SJonathan Lemon { 31985d94372SRobert Watson struct tcpcb *tp = xtp; 3209eb0e832SGleb Smirnoff struct inpcb *inp = tptoinpcb(tp); 3219b8b58e0SJonathan Lemon #ifdef TCPDEBUG 3229b8b58e0SJonathan Lemon int ostate; 3239b8b58e0SJonathan Lemon 3249b8b58e0SJonathan Lemon ostate = tp->t_state; 3259b8b58e0SJonathan Lemon #endif 3269eb0e832SGleb Smirnoff 3278501a69cSRobert Watson INP_WLOCK(inp); 328*8840ae22SGleb Smirnoff CURVNET_SET(inp->inp_vnet); 329*8840ae22SGleb Smirnoff 330d1b07f36SRandall Stewart tcp_log_end_status(tp, TCP_EI_STATUS_2MSL); 33185d94372SRobert Watson tcp_free_sackholes(tp); 332655f934bSMikolaj Golub if (callout_pending(&tp->t_timers->tt_2msl) || 333e2f2059fSMike Silbersack !callout_active(&tp->t_timers->tt_2msl)) { 3349eb0e832SGleb Smirnoff INP_WUNLOCK(inp); 3358b615593SMarko Zec CURVNET_RESTORE(); 33685d94372SRobert Watson return; 33785d94372SRobert Watson } 338e2f2059fSMike Silbersack callout_deactivate(&tp->t_timers->tt_2msl); 33953af6903SGleb Smirnoff if (inp->inp_flags & INP_DROPPED) { 340655f934bSMikolaj Golub INP_WUNLOCK(inp); 341655f934bSMikolaj Golub CURVNET_RESTORE(); 342655f934bSMikolaj Golub return; 343655f934bSMikolaj Golub } 3445571f9cfSJulien Charbon KASSERT((tp->t_timers->tt_flags & TT_STOPPED) == 0, 3455571f9cfSJulien Charbon ("%s: tp %p tcpcb can't be stopped here", __func__, tp)); 34685d94372SRobert Watson /* 347df8bae1dSRodney W. Grimes * 2 MSL timeout in shutdown went off. If we're closed but 348df8bae1dSRodney W. Grimes * still waiting for peer to close and connection has been idle 34931a7749dSJulien Charbon * too long delete connection control block. Otherwise, check 35031a7749dSJulien Charbon * again in a bit. 35131a7749dSJulien Charbon * 3527c72af87SMohan Srinivasan * If fastrecycle of FIN_WAIT_2, in FIN_WAIT_2 and receiver has closed, 3537c72af87SMohan Srinivasan * there's no point in hanging onto FIN_WAIT_2 socket. Just close it. 3547c72af87SMohan Srinivasan * Ignore fact that there were recent incoming segments. 355f71cb9f7SGleb Smirnoff * 356f71cb9f7SGleb Smirnoff * XXXGL: check if inp_socket shall always be !NULL here? 357df8bae1dSRodney W. Grimes */ 3580d744519SGleb Smirnoff if (tp->t_state == TCPS_TIME_WAIT) { 3590d744519SGleb Smirnoff tcp_timer_close(tp); 3600d744519SGleb Smirnoff CURVNET_RESTORE(); 3610d744519SGleb Smirnoff return; 3620d744519SGleb Smirnoff } else if (tp->t_state == TCPS_FIN_WAIT_2 && 3639eb0e832SGleb Smirnoff tcp_fast_finwait2_recycle && inp->inp_socket && 3649eb0e832SGleb Smirnoff (inp->inp_socket->so_rcv.sb_state & SBS_CANTRCVMORE)) { 36578b50714SRobert Watson TCPSTAT_INC(tcps_finwait2_drops); 36677198a94SGleb Smirnoff tcp_timer_close(tp); 36777198a94SGleb Smirnoff CURVNET_RESTORE(); 36877198a94SGleb Smirnoff return; 3697c72af87SMohan Srinivasan } else { 370d6de19acSJulien Charbon if (ticks - tp->t_rcvtime <= TP_MAXIDLE(tp)) { 371b07fef50SRandall Stewart callout_reset(&tp->t_timers->tt_2msl, 372b07fef50SRandall Stewart TP_KEEPINTVL(tp), tcp_timer_2msl, tp); 373b07fef50SRandall Stewart } else { 37477198a94SGleb Smirnoff tcp_timer_close(tp); 37577198a94SGleb Smirnoff CURVNET_RESTORE(); 37677198a94SGleb Smirnoff return; 377b07fef50SRandall Stewart } 3787c72af87SMohan Srinivasan } 379df8bae1dSRodney W. Grimes 3809b8b58e0SJonathan Lemon #ifdef TCPDEBUG 3819eb0e832SGleb Smirnoff if (tptosocket(tp)->so_options & SO_DEBUG) 382fb59c426SYoshinobu Inoue tcp_trace(TA_USER, ostate, tp, (void *)0, (struct tcphdr *)0, 3839b8b58e0SJonathan Lemon PRU_SLOWTIMO); 3849b8b58e0SJonathan Lemon #endif 3855d06879aSGeorge V. Neville-Neil TCP_PROBE2(debug__user, tp, PRU_SLOWTIMO); 3865d06879aSGeorge V. Neville-Neil 3878501a69cSRobert Watson INP_WUNLOCK(inp); 3888b615593SMarko Zec CURVNET_RESTORE(); 3899b8b58e0SJonathan Lemon } 3909b8b58e0SJonathan Lemon 39185d94372SRobert Watson void 39285d94372SRobert Watson tcp_timer_keep(void *xtp) 3939b8b58e0SJonathan Lemon { 3946573d758SMatt Macy struct epoch_tracker et; 3959eb0e832SGleb Smirnoff struct tcpcb *tp = xtp; 3969eb0e832SGleb Smirnoff struct inpcb *inp = tptoinpcb(tp); 3979eb0e832SGleb Smirnoff struct tcptemp *t_template; 3989b8b58e0SJonathan Lemon #ifdef TCPDEBUG 3999b8b58e0SJonathan Lemon int ostate; 4009b8b58e0SJonathan Lemon 4019b8b58e0SJonathan Lemon ostate = tp->t_state; 4029b8b58e0SJonathan Lemon #endif 4039eb0e832SGleb Smirnoff 4048501a69cSRobert Watson INP_WLOCK(inp); 405*8840ae22SGleb Smirnoff CURVNET_SET(inp->inp_vnet); 406*8840ae22SGleb Smirnoff 407655f934bSMikolaj Golub if (callout_pending(&tp->t_timers->tt_keep) || 408655f934bSMikolaj Golub !callout_active(&tp->t_timers->tt_keep)) { 4098501a69cSRobert Watson INP_WUNLOCK(inp); 4108b615593SMarko Zec CURVNET_RESTORE(); 41185d94372SRobert Watson return; 41285d94372SRobert Watson } 413e2f2059fSMike Silbersack callout_deactivate(&tp->t_timers->tt_keep); 41453af6903SGleb Smirnoff if (inp->inp_flags & INP_DROPPED) { 415655f934bSMikolaj Golub INP_WUNLOCK(inp); 416655f934bSMikolaj Golub CURVNET_RESTORE(); 417655f934bSMikolaj Golub return; 418655f934bSMikolaj Golub } 4195571f9cfSJulien Charbon KASSERT((tp->t_timers->tt_flags & TT_STOPPED) == 0, 4205571f9cfSJulien Charbon ("%s: tp %p tcpcb can't be stopped here", __func__, tp)); 4216d172f58SJonathan T. Looney 4226d172f58SJonathan T. Looney /* 4236d172f58SJonathan T. Looney * Because we don't regularly reset the keepalive callout in 4246d172f58SJonathan T. Looney * the ESTABLISHED state, it may be that we don't actually need 4256d172f58SJonathan T. Looney * to send a keepalive yet. If that occurs, schedule another 4266d172f58SJonathan T. Looney * call for the next time the keepalive timer might expire. 4276d172f58SJonathan T. Looney */ 4286d172f58SJonathan T. Looney if (TCPS_HAVEESTABLISHED(tp->t_state)) { 4296d172f58SJonathan T. Looney u_int idletime; 4306d172f58SJonathan T. Looney 4316d172f58SJonathan T. Looney idletime = ticks - tp->t_rcvtime; 4326d172f58SJonathan T. Looney if (idletime < TP_KEEPIDLE(tp)) { 4336d172f58SJonathan T. Looney callout_reset(&tp->t_timers->tt_keep, 4346d172f58SJonathan T. Looney TP_KEEPIDLE(tp) - idletime, tcp_timer_keep, tp); 4356d172f58SJonathan T. Looney INP_WUNLOCK(inp); 4366d172f58SJonathan T. Looney CURVNET_RESTORE(); 4376d172f58SJonathan T. Looney return; 4386d172f58SJonathan T. Looney } 4396d172f58SJonathan T. Looney } 4406d172f58SJonathan T. Looney 4419b8b58e0SJonathan Lemon /* 4429b8b58e0SJonathan Lemon * Keep-alive timer went off; send something 4439b8b58e0SJonathan Lemon * or drop connection if idle for too long. 4449b8b58e0SJonathan Lemon */ 44578b50714SRobert Watson TCPSTAT_INC(tcps_keeptimeo); 4469b8b58e0SJonathan Lemon if (tp->t_state < TCPS_ESTABLISHED) 4479b8b58e0SJonathan Lemon goto dropit; 448334fc582SBjoern A. Zeeb if ((V_tcp_always_keepalive || 449f1798531SJohn Baldwin inp->inp_socket->so_options & SO_KEEPALIVE) && 4509b8b58e0SJonathan Lemon tp->t_state <= TCPS_CLOSING) { 4519077f387SGleb Smirnoff if (ticks - tp->t_rcvtime >= TP_KEEPIDLE(tp) + TP_MAXIDLE(tp)) 4529b8b58e0SJonathan Lemon goto dropit; 4539b8b58e0SJonathan Lemon /* 4549b8b58e0SJonathan Lemon * Send a packet designed to force a response 4559b8b58e0SJonathan Lemon * if the peer is up and reachable: 4569b8b58e0SJonathan Lemon * either an ACK if the connection is still alive, 4579b8b58e0SJonathan Lemon * or an RST if the peer has closed the connection 4589b8b58e0SJonathan Lemon * due to timeout or reboot. 4599b8b58e0SJonathan Lemon * Using sequence number tp->snd_una-1 4609b8b58e0SJonathan Lemon * causes the transmitted zero-length segment 4619b8b58e0SJonathan Lemon * to lie outside the receive window; 4629b8b58e0SJonathan Lemon * by the protocol spec, this requires the 4639b8b58e0SJonathan Lemon * correspondent TCP to respond. 4649b8b58e0SJonathan Lemon */ 46578b50714SRobert Watson TCPSTAT_INC(tcps_keepprobe); 46679909384SJonathan Lemon t_template = tcpip_maketemplate(inp); 46708517d53SMike Silbersack if (t_template) { 468b9555453SGleb Smirnoff NET_EPOCH_ENTER(et); 46908517d53SMike Silbersack tcp_respond(tp, t_template->tt_ipgen, 47008517d53SMike Silbersack &t_template->tt_t, (struct mbuf *)NULL, 4719b8b58e0SJonathan Lemon tp->rcv_nxt, tp->snd_una - 1, 0); 472b9555453SGleb Smirnoff NET_EPOCH_EXIT(et); 47353640b0eSRobert Watson free(t_template, M_TEMP); 47408517d53SMike Silbersack } 475b07fef50SRandall Stewart callout_reset(&tp->t_timers->tt_keep, TP_KEEPINTVL(tp), 476b07fef50SRandall Stewart tcp_timer_keep, tp); 477b07fef50SRandall Stewart } else 478b07fef50SRandall Stewart callout_reset(&tp->t_timers->tt_keep, TP_KEEPIDLE(tp), 479b07fef50SRandall Stewart tcp_timer_keep, tp); 4809b8b58e0SJonathan Lemon 4819b8b58e0SJonathan Lemon #ifdef TCPDEBUG 4822a074620SSam Leffler if (inp->inp_socket->so_options & SO_DEBUG) 483fb59c426SYoshinobu Inoue tcp_trace(TA_USER, ostate, tp, (void *)0, (struct tcphdr *)0, 4849b8b58e0SJonathan Lemon PRU_SLOWTIMO); 4859b8b58e0SJonathan Lemon #endif 4865d06879aSGeorge V. Neville-Neil TCP_PROBE2(debug__user, tp, PRU_SLOWTIMO); 4878501a69cSRobert Watson INP_WUNLOCK(inp); 4888b615593SMarko Zec CURVNET_RESTORE(); 48985d94372SRobert Watson return; 4909b8b58e0SJonathan Lemon 4919b8b58e0SJonathan Lemon dropit: 49278b50714SRobert Watson TCPSTAT_INC(tcps_keepdrops); 49358d94bd0SGleb Smirnoff NET_EPOCH_ENTER(et); 494d1b07f36SRandall Stewart tcp_log_end_status(tp, TCP_EI_STATUS_KEEP_MAX); 49585d94372SRobert Watson tp = tcp_drop(tp, ETIMEDOUT); 49685d94372SRobert Watson 49785d94372SRobert Watson #ifdef TCPDEBUG 4989eb0e832SGleb Smirnoff if (tp != NULL && (tptosocket(tp)->so_options & SO_DEBUG)) 49985d94372SRobert Watson tcp_trace(TA_USER, ostate, tp, (void *)0, (struct tcphdr *)0, 50085d94372SRobert Watson PRU_SLOWTIMO); 50185d94372SRobert Watson #endif 5025d06879aSGeorge V. Neville-Neil TCP_PROBE2(debug__user, tp, PRU_SLOWTIMO); 50358d94bd0SGleb Smirnoff NET_EPOCH_EXIT(et); 50477198a94SGleb Smirnoff if (tp != NULL) 50577198a94SGleb Smirnoff INP_WUNLOCK(inp); 5068b615593SMarko Zec CURVNET_RESTORE(); 5079b8b58e0SJonathan Lemon } 5089b8b58e0SJonathan Lemon 50908af8aacSRandall Stewart /* 51008af8aacSRandall Stewart * Has this session exceeded the maximum time without seeing a substantive 51108af8aacSRandall Stewart * acknowledgement? If so, return true; otherwise false. 51208af8aacSRandall Stewart */ 51308af8aacSRandall Stewart static bool 51408af8aacSRandall Stewart tcp_maxunacktime_check(struct tcpcb *tp) 51508af8aacSRandall Stewart { 51608af8aacSRandall Stewart 51708af8aacSRandall Stewart /* Are we tracking this timer for this session? */ 51808af8aacSRandall Stewart if (TP_MAXUNACKTIME(tp) == 0) 51908af8aacSRandall Stewart return false; 52008af8aacSRandall Stewart 52108af8aacSRandall Stewart /* Do we have a current measurement. */ 52208af8aacSRandall Stewart if (tp->t_acktime == 0) 52308af8aacSRandall Stewart return false; 52408af8aacSRandall Stewart 52508af8aacSRandall Stewart /* Are we within the acceptable range? */ 52608af8aacSRandall Stewart if (TSTMP_GT(TP_MAXUNACKTIME(tp) + tp->t_acktime, (u_int)ticks)) 52708af8aacSRandall Stewart return false; 52808af8aacSRandall Stewart 52908af8aacSRandall Stewart /* We exceeded the timer. */ 53008af8aacSRandall Stewart TCPSTAT_INC(tcps_progdrops); 53108af8aacSRandall Stewart return true; 53208af8aacSRandall Stewart } 53308af8aacSRandall Stewart 53485d94372SRobert Watson void 53585d94372SRobert Watson tcp_timer_persist(void *xtp) 5369b8b58e0SJonathan Lemon { 5376573d758SMatt Macy struct epoch_tracker et; 5389eb0e832SGleb Smirnoff struct tcpcb *tp = xtp; 5399eb0e832SGleb Smirnoff #if defined(INVARIANTS) || defined(VIMAGE) 5409eb0e832SGleb Smirnoff struct inpcb *inp = tptoinpcb(tp); 5419eb0e832SGleb Smirnoff #endif 54208af8aacSRandall Stewart bool progdrop; 543f64dc2abSGleb Smirnoff int outrv; 5449b8b58e0SJonathan Lemon #ifdef TCPDEBUG 5459b8b58e0SJonathan Lemon int ostate; 5469b8b58e0SJonathan Lemon 5479b8b58e0SJonathan Lemon ostate = tp->t_state; 5489b8b58e0SJonathan Lemon #endif 5499eb0e832SGleb Smirnoff 5508501a69cSRobert Watson INP_WLOCK(inp); 551*8840ae22SGleb Smirnoff CURVNET_SET(inp->inp_vnet); 552*8840ae22SGleb Smirnoff 553655f934bSMikolaj Golub if (callout_pending(&tp->t_timers->tt_persist) || 554655f934bSMikolaj Golub !callout_active(&tp->t_timers->tt_persist)) { 5558501a69cSRobert Watson INP_WUNLOCK(inp); 5568b615593SMarko Zec CURVNET_RESTORE(); 55785d94372SRobert Watson return; 55885d94372SRobert Watson } 559e2f2059fSMike Silbersack callout_deactivate(&tp->t_timers->tt_persist); 56053af6903SGleb Smirnoff if (inp->inp_flags & INP_DROPPED) { 561655f934bSMikolaj Golub INP_WUNLOCK(inp); 562655f934bSMikolaj Golub CURVNET_RESTORE(); 563655f934bSMikolaj Golub return; 564655f934bSMikolaj Golub } 5655571f9cfSJulien Charbon KASSERT((tp->t_timers->tt_flags & TT_STOPPED) == 0, 5665571f9cfSJulien Charbon ("%s: tp %p tcpcb can't be stopped here", __func__, tp)); 5679b8b58e0SJonathan Lemon /* 568a4641f4eSPedro F. Giffuni * Persistence timer into zero window. 5699b8b58e0SJonathan Lemon * Force a byte to be output, if possible. 5709b8b58e0SJonathan Lemon */ 57178b50714SRobert Watson TCPSTAT_INC(tcps_persisttimeo); 5729b8b58e0SJonathan Lemon /* 5739b8b58e0SJonathan Lemon * Hack: if the peer is dead/unreachable, we do not 5749b8b58e0SJonathan Lemon * time out if the window is closed. After a full 5759b8b58e0SJonathan Lemon * backoff, drop the connection if the idle time 5769b8b58e0SJonathan Lemon * (no responses to probes) reaches the maximum 5779b8b58e0SJonathan Lemon * backoff that we would use if retransmitting. 57808af8aacSRandall Stewart * Also, drop the connection if we haven't been making 57908af8aacSRandall Stewart * progress. 5809b8b58e0SJonathan Lemon */ 58108af8aacSRandall Stewart progdrop = tcp_maxunacktime_check(tp); 58208af8aacSRandall Stewart if (progdrop || (tp->t_rxtshift == TCP_MAXRXTSHIFT && 5836b0c5521SJohn Baldwin (ticks - tp->t_rcvtime >= tcp_maxpersistidle || 58408af8aacSRandall Stewart ticks - tp->t_rcvtime >= TCP_REXMTVAL(tp) * tcp_totbackoff))) { 58508af8aacSRandall Stewart if (!progdrop) 58678b50714SRobert Watson TCPSTAT_INC(tcps_persistdrop); 587d1b07f36SRandall Stewart tcp_log_end_status(tp, TCP_EI_STATUS_PERSIST_MAX); 58877198a94SGleb Smirnoff tcp_timer_drop(tp); 58977198a94SGleb Smirnoff CURVNET_RESTORE(); 59077198a94SGleb Smirnoff return; 5919b8b58e0SJonathan Lemon } 592322181c9SAndre Oppermann /* 593322181c9SAndre Oppermann * If the user has closed the socket then drop a persisting 594322181c9SAndre Oppermann * connection after a much reduced timeout. 595322181c9SAndre Oppermann */ 596322181c9SAndre Oppermann if (tp->t_state > TCPS_CLOSE_WAIT && 597322181c9SAndre Oppermann (ticks - tp->t_rcvtime) >= TCPTV_PERSMAX) { 598322181c9SAndre Oppermann TCPSTAT_INC(tcps_persistdrop); 599d1b07f36SRandall Stewart tcp_log_end_status(tp, TCP_EI_STATUS_PERSIST_MAX); 60077198a94SGleb Smirnoff tcp_timer_drop(tp); 60177198a94SGleb Smirnoff CURVNET_RESTORE(); 60277198a94SGleb Smirnoff return; 603322181c9SAndre Oppermann } 6049b8b58e0SJonathan Lemon tcp_setpersist(tp); 6052cdbfa66SPaul Saab tp->t_flags |= TF_FORCEDATA; 606109eb549SGleb Smirnoff NET_EPOCH_ENTER(et); 607f64dc2abSGleb Smirnoff outrv = tcp_output_nodrop(tp); 6082cdbfa66SPaul Saab tp->t_flags &= ~TF_FORCEDATA; 6099b8b58e0SJonathan Lemon 6109b8b58e0SJonathan Lemon #ifdef TCPDEBUG 6119eb0e832SGleb Smirnoff if (tp != NULL && tptosocket(tp)->so_options & SO_DEBUG) 612ffb761f6SGleb Smirnoff tcp_trace(TA_USER, ostate, tp, NULL, NULL, PRU_SLOWTIMO); 6139b8b58e0SJonathan Lemon #endif 6145d06879aSGeorge V. Neville-Neil TCP_PROBE2(debug__user, tp, PRU_SLOWTIMO); 615f64dc2abSGleb Smirnoff (void) tcp_unlock_or_drop(tp, outrv); 616f64dc2abSGleb Smirnoff NET_EPOCH_EXIT(et); 6178b615593SMarko Zec CURVNET_RESTORE(); 6189b8b58e0SJonathan Lemon } 6199b8b58e0SJonathan Lemon 62085d94372SRobert Watson void 62185d94372SRobert Watson tcp_timer_rexmt(void * xtp) 6229b8b58e0SJonathan Lemon { 6239eb0e832SGleb Smirnoff struct epoch_tracker et; 62485d94372SRobert Watson struct tcpcb *tp = xtp; 6259eb0e832SGleb Smirnoff struct inpcb *inp = tptoinpcb(tp); 626f64dc2abSGleb Smirnoff int rexmt, outrv; 627413c3db1SMichael Tuexen bool isipv6; 6289b8b58e0SJonathan Lemon #ifdef TCPDEBUG 6299b8b58e0SJonathan Lemon int ostate; 6309b8b58e0SJonathan Lemon 6319b8b58e0SJonathan Lemon ostate = tp->t_state; 6329b8b58e0SJonathan Lemon #endif 6339eb0e832SGleb Smirnoff 6348501a69cSRobert Watson INP_WLOCK(inp); 635*8840ae22SGleb Smirnoff CURVNET_SET(inp->inp_vnet); 636*8840ae22SGleb Smirnoff 637655f934bSMikolaj Golub if (callout_pending(&tp->t_timers->tt_rexmt) || 638655f934bSMikolaj Golub !callout_active(&tp->t_timers->tt_rexmt)) { 6398501a69cSRobert Watson INP_WUNLOCK(inp); 6408b615593SMarko Zec CURVNET_RESTORE(); 64185d94372SRobert Watson return; 64285d94372SRobert Watson } 643e2f2059fSMike Silbersack callout_deactivate(&tp->t_timers->tt_rexmt); 64453af6903SGleb Smirnoff if (inp->inp_flags & INP_DROPPED) { 645655f934bSMikolaj Golub INP_WUNLOCK(inp); 646655f934bSMikolaj Golub CURVNET_RESTORE(); 647655f934bSMikolaj Golub return; 648655f934bSMikolaj Golub } 6495571f9cfSJulien Charbon KASSERT((tp->t_timers->tt_flags & TT_STOPPED) == 0, 6505571f9cfSJulien Charbon ("%s: tp %p tcpcb can't be stopped here", __func__, tp)); 6516d90faf3SPaul Saab tcp_free_sackholes(tp); 6522529f56eSJonathan T. Looney TCP_LOG_EVENT(tp, NULL, NULL, NULL, TCP_LOG_RTO, 0, 0, NULL, false); 6535105a92cSRandall Stewart if (tp->t_fb->tfb_tcp_rexmit_tmr) { 6545105a92cSRandall Stewart /* The stack has a timer action too. */ 6555105a92cSRandall Stewart (*tp->t_fb->tfb_tcp_rexmit_tmr)(tp); 6565105a92cSRandall Stewart } 657df8bae1dSRodney W. Grimes /* 658df8bae1dSRodney W. Grimes * Retransmission timer went off. Message has not 659df8bae1dSRodney W. Grimes * been acked within retransmit interval. Back off 660df8bae1dSRodney W. Grimes * to a longer retransmit interval and retransmit one segment. 66108af8aacSRandall Stewart * 66208af8aacSRandall Stewart * If we've either exceeded the maximum number of retransmissions, 66308af8aacSRandall Stewart * or we've gone long enough without making progress, then drop 66408af8aacSRandall Stewart * the session. 665df8bae1dSRodney W. Grimes */ 66608af8aacSRandall Stewart if (++tp->t_rxtshift > TCP_MAXRXTSHIFT || tcp_maxunacktime_check(tp)) { 66708af8aacSRandall Stewart if (tp->t_rxtshift > TCP_MAXRXTSHIFT) 66878b50714SRobert Watson TCPSTAT_INC(tcps_timeoutdrop); 66908af8aacSRandall Stewart tp->t_rxtshift = TCP_MAXRXTSHIFT; 670d1b07f36SRandall Stewart tcp_log_end_status(tp, TCP_EI_STATUS_RETRAN); 67177198a94SGleb Smirnoff tcp_timer_drop(tp); 67277198a94SGleb Smirnoff CURVNET_RESTORE(); 67377198a94SGleb Smirnoff return; 674b07fef50SRandall Stewart } 675cf8f04f4SAndre Oppermann if (tp->t_state == TCPS_SYN_SENT) { 676cf8f04f4SAndre Oppermann /* 677cf8f04f4SAndre Oppermann * If the SYN was retransmitted, indicate CWND to be 678cf8f04f4SAndre Oppermann * limited to 1 segment in cc_conn_init(). 679cf8f04f4SAndre Oppermann */ 680cf8f04f4SAndre Oppermann tp->snd_cwnd = 1; 681cf8f04f4SAndre Oppermann } else if (tp->t_rxtshift == 1) { 6829b8b58e0SJonathan Lemon /* 6839b8b58e0SJonathan Lemon * first retransmit; record ssthresh and cwnd so they can 6849b8b58e0SJonathan Lemon * be recovered if this turns out to be a "bad" retransmit. 6859b8b58e0SJonathan Lemon * A retransmit is considered "bad" if an ACK for this 6869b8b58e0SJonathan Lemon * segment is received within RTT/2 interval; the assumption 6879b8b58e0SJonathan Lemon * here is that the ACK was already in flight. See 6889b8b58e0SJonathan Lemon * "On Estimating End-to-End Network Path Properties" by 6899b8b58e0SJonathan Lemon * Allman and Paxson for more details. 6909b8b58e0SJonathan Lemon */ 6919b8b58e0SJonathan Lemon tp->snd_cwnd_prev = tp->snd_cwnd; 6929b8b58e0SJonathan Lemon tp->snd_ssthresh_prev = tp->snd_ssthresh; 6939d11646dSJeffrey Hsu tp->snd_recover_prev = tp->snd_recover; 694dbc42409SLawrence Stewart if (IN_FASTRECOVERY(tp->t_flags)) 6959d11646dSJeffrey Hsu tp->t_flags |= TF_WASFRECOVERY; 6969d11646dSJeffrey Hsu else 6979d11646dSJeffrey Hsu tp->t_flags &= ~TF_WASFRECOVERY; 698dbc42409SLawrence Stewart if (IN_CONGRECOVERY(tp->t_flags)) 699dbc42409SLawrence Stewart tp->t_flags |= TF_WASCRECOVERY; 700dbc42409SLawrence Stewart else 701dbc42409SLawrence Stewart tp->t_flags &= ~TF_WASCRECOVERY; 70210d20c84SMatt Macy if ((tp->t_flags & TF_RCVD_TSTMP) == 0) 7039b8b58e0SJonathan Lemon tp->t_badrxtwin = ticks + (tp->t_srtt >> (TCP_RTT_SHIFT + 1)); 70410d20c84SMatt Macy /* In the event that we've negotiated timestamps 70510d20c84SMatt Macy * badrxtwin will be set to the value that we set 70610d20c84SMatt Macy * the retransmitted packet's to_tsval to by tcp_output 70710d20c84SMatt Macy */ 708672dc4aeSJohn Baldwin tp->t_flags |= TF_PREVVALID; 709672dc4aeSJohn Baldwin } else 710672dc4aeSJohn Baldwin tp->t_flags &= ~TF_PREVVALID; 71178b50714SRobert Watson TCPSTAT_INC(tcps_rexmttimeo); 712281a0fd4SPatrick Kelsey if ((tp->t_state == TCPS_SYN_SENT) || 713281a0fd4SPatrick Kelsey (tp->t_state == TCPS_SYN_RECEIVED)) 7140999766dSMichael Tuexen rexmt = tcp_rexmit_initial * tcp_backoff[tp->t_rxtshift]; 7157d42e30cSJonathan Lemon else 716df8bae1dSRodney W. Grimes rexmt = TCP_REXMTVAL(tp) * tcp_backoff[tp->t_rxtshift]; 717df8bae1dSRodney W. Grimes TCPT_RANGESET(tp->t_rxtcur, rexmt, 718df8bae1dSRodney W. Grimes tp->t_rttmin, TCPTV_REXMTMAX); 719f6f6703fSSean Bruno 720882ac53eSSean Bruno /* 721882ac53eSSean Bruno * We enter the path for PLMTUD if connection is established or, if 722882ac53eSSean Bruno * connection is FIN_WAIT_1 status, reason for the last is that if 723882ac53eSSean Bruno * amount of data we send is very small, we could send it in couple of 724882ac53eSSean Bruno * packets and process straight to FIN. In that case we won't catch 725882ac53eSSean Bruno * ESTABLISHED state. 726882ac53eSSean Bruno */ 727f6f6703fSSean Bruno #ifdef INET6 7289eb0e832SGleb Smirnoff isipv6 = (inp->inp_vflag & INP_IPV6) ? true : false; 729413c3db1SMichael Tuexen #else 730413c3db1SMichael Tuexen isipv6 = false; 731f6f6703fSSean Bruno #endif 732413c3db1SMichael Tuexen if (((V_tcp_pmtud_blackhole_detect == 1) || 733413c3db1SMichael Tuexen (V_tcp_pmtud_blackhole_detect == 2 && !isipv6) || 734413c3db1SMichael Tuexen (V_tcp_pmtud_blackhole_detect == 3 && isipv6)) && 735413c3db1SMichael Tuexen ((tp->t_state == TCPS_ESTABLISHED) || 736413c3db1SMichael Tuexen (tp->t_state == TCPS_FIN_WAIT_1))) { 737b89af8e1SMichael Tuexen if (tp->t_rxtshift == 1) { 738adf43a92SHiren Panchasara /* 739b89af8e1SMichael Tuexen * We enter blackhole detection after the first 740b89af8e1SMichael Tuexen * unsuccessful timer based retransmission. 741b89af8e1SMichael Tuexen * Then we reduce up to two times the MSS, each 742b89af8e1SMichael Tuexen * candidate giving two tries of retransmissions. 743b89af8e1SMichael Tuexen * But we give a candidate only two tries, if it 744b89af8e1SMichael Tuexen * actually reduces the MSS. 745adf43a92SHiren Panchasara */ 746b89af8e1SMichael Tuexen tp->t_blackhole_enter = 2; 747b89af8e1SMichael Tuexen tp->t_blackhole_exit = tp->t_blackhole_enter; 748b89af8e1SMichael Tuexen if (isipv6) { 749b89af8e1SMichael Tuexen #ifdef INET6 750b89af8e1SMichael Tuexen if (tp->t_maxseg > V_tcp_v6pmtud_blackhole_mss) 751b89af8e1SMichael Tuexen tp->t_blackhole_exit += 2; 752b89af8e1SMichael Tuexen if (tp->t_maxseg > V_tcp_v6mssdflt && 753b89af8e1SMichael Tuexen V_tcp_v6pmtud_blackhole_mss > V_tcp_v6mssdflt) 754b89af8e1SMichael Tuexen tp->t_blackhole_exit += 2; 755b89af8e1SMichael Tuexen #endif 756b89af8e1SMichael Tuexen } else { 757b89af8e1SMichael Tuexen #ifdef INET 758b89af8e1SMichael Tuexen if (tp->t_maxseg > V_tcp_pmtud_blackhole_mss) 759b89af8e1SMichael Tuexen tp->t_blackhole_exit += 2; 760b89af8e1SMichael Tuexen if (tp->t_maxseg > V_tcp_mssdflt && 761b89af8e1SMichael Tuexen V_tcp_pmtud_blackhole_mss > V_tcp_mssdflt) 762b89af8e1SMichael Tuexen tp->t_blackhole_exit += 2; 763b89af8e1SMichael Tuexen #endif 764b89af8e1SMichael Tuexen } 765b89af8e1SMichael Tuexen } 766f6f6703fSSean Bruno if (((tp->t_flags2 & (TF2_PLPMTU_PMTUD|TF2_PLPMTU_MAXSEGSNT)) == 767f6f6703fSSean Bruno (TF2_PLPMTU_PMTUD|TF2_PLPMTU_MAXSEGSNT)) && 768b89af8e1SMichael Tuexen (tp->t_rxtshift >= tp->t_blackhole_enter && 769b89af8e1SMichael Tuexen tp->t_rxtshift < tp->t_blackhole_exit && 770b89af8e1SMichael Tuexen (tp->t_rxtshift - tp->t_blackhole_enter) % 2 == 0)) { 771f6f6703fSSean Bruno /* 772f6f6703fSSean Bruno * Enter Path MTU Black-hole Detection mechanism: 773f6f6703fSSean Bruno * - Disable Path MTU Discovery (IP "DF" bit). 774f6f6703fSSean Bruno * - Reduce MTU to lower value than what we 775f6f6703fSSean Bruno * negotiated with peer. 776f6f6703fSSean Bruno */ 7773d5af7a1SMichael Tuexen if ((tp->t_flags2 & TF2_PLPMTU_BLACKHOLE) == 0) { 778f6f6703fSSean Bruno /* Record that we may have found a black hole. */ 779f6f6703fSSean Bruno tp->t_flags2 |= TF2_PLPMTU_BLACKHOLE; 780f6f6703fSSean Bruno /* Keep track of previous MSS. */ 7810c39d38dSGleb Smirnoff tp->t_pmtud_saved_maxseg = tp->t_maxseg; 7823d5af7a1SMichael Tuexen } 783f6f6703fSSean Bruno 784f6f6703fSSean Bruno /* 785f6f6703fSSean Bruno * Reduce the MSS to blackhole value or to the default 786f6f6703fSSean Bruno * in an attempt to retransmit. 787f6f6703fSSean Bruno */ 788f6f6703fSSean Bruno #ifdef INET6 789f6f6703fSSean Bruno if (isipv6 && 790b89af8e1SMichael Tuexen tp->t_maxseg > V_tcp_v6pmtud_blackhole_mss && 791b89af8e1SMichael Tuexen V_tcp_v6pmtud_blackhole_mss > V_tcp_v6mssdflt) { 792f6f6703fSSean Bruno /* Use the sysctl tuneable blackhole MSS. */ 7930c39d38dSGleb Smirnoff tp->t_maxseg = V_tcp_v6pmtud_blackhole_mss; 79432a04bb8SSean Bruno TCPSTAT_INC(tcps_pmtud_blackhole_activated); 795f6f6703fSSean Bruno } else if (isipv6) { 796f6f6703fSSean Bruno /* Use the default MSS. */ 7970c39d38dSGleb Smirnoff tp->t_maxseg = V_tcp_v6mssdflt; 798f6f6703fSSean Bruno /* 799f6f6703fSSean Bruno * Disable Path MTU Discovery when we switch to 800f6f6703fSSean Bruno * minmss. 801f6f6703fSSean Bruno */ 802f6f6703fSSean Bruno tp->t_flags2 &= ~TF2_PLPMTU_PMTUD; 80332a04bb8SSean Bruno TCPSTAT_INC(tcps_pmtud_blackhole_activated_min_mss); 804f6f6703fSSean Bruno } 805f6f6703fSSean Bruno #endif 806f6f6703fSSean Bruno #if defined(INET6) && defined(INET) 807f6f6703fSSean Bruno else 808f6f6703fSSean Bruno #endif 809f6f6703fSSean Bruno #ifdef INET 810b89af8e1SMichael Tuexen if (tp->t_maxseg > V_tcp_pmtud_blackhole_mss && 811b89af8e1SMichael Tuexen V_tcp_pmtud_blackhole_mss > V_tcp_mssdflt) { 812f6f6703fSSean Bruno /* Use the sysctl tuneable blackhole MSS. */ 8130c39d38dSGleb Smirnoff tp->t_maxseg = V_tcp_pmtud_blackhole_mss; 81432a04bb8SSean Bruno TCPSTAT_INC(tcps_pmtud_blackhole_activated); 815f6f6703fSSean Bruno } else { 816f6f6703fSSean Bruno /* Use the default MSS. */ 8170c39d38dSGleb Smirnoff tp->t_maxseg = V_tcp_mssdflt; 818f6f6703fSSean Bruno /* 819f6f6703fSSean Bruno * Disable Path MTU Discovery when we switch to 820f6f6703fSSean Bruno * minmss. 821f6f6703fSSean Bruno */ 822f6f6703fSSean Bruno tp->t_flags2 &= ~TF2_PLPMTU_PMTUD; 82332a04bb8SSean Bruno TCPSTAT_INC(tcps_pmtud_blackhole_activated_min_mss); 824f6f6703fSSean Bruno } 825f6f6703fSSean Bruno #endif 826f6f6703fSSean Bruno /* 827f6f6703fSSean Bruno * Reset the slow-start flight size 828f6f6703fSSean Bruno * as it may depend on the new MSS. 829f6f6703fSSean Bruno */ 830f6f6703fSSean Bruno if (CC_ALGO(tp)->conn_init != NULL) 831f6f6703fSSean Bruno CC_ALGO(tp)->conn_init(tp->ccv); 832f6f6703fSSean Bruno } else { 833f6f6703fSSean Bruno /* 834f6f6703fSSean Bruno * If further retransmissions are still unsuccessful 835f6f6703fSSean Bruno * with a lowered MTU, maybe this isn't a blackhole and 836f6f6703fSSean Bruno * we restore the previous MSS and blackhole detection 837f6f6703fSSean Bruno * flags. 838f6f6703fSSean Bruno */ 839f6f6703fSSean Bruno if ((tp->t_flags2 & TF2_PLPMTU_BLACKHOLE) && 840b89af8e1SMichael Tuexen (tp->t_rxtshift >= tp->t_blackhole_exit)) { 841f6f6703fSSean Bruno tp->t_flags2 |= TF2_PLPMTU_PMTUD; 842f6f6703fSSean Bruno tp->t_flags2 &= ~TF2_PLPMTU_BLACKHOLE; 8430c39d38dSGleb Smirnoff tp->t_maxseg = tp->t_pmtud_saved_maxseg; 84432a04bb8SSean Bruno TCPSTAT_INC(tcps_pmtud_blackhole_failed); 845f6f6703fSSean Bruno /* 846f6f6703fSSean Bruno * Reset the slow-start flight size as it 847f6f6703fSSean Bruno * may depend on the new MSS. 848f6f6703fSSean Bruno */ 849f6f6703fSSean Bruno if (CC_ALGO(tp)->conn_init != NULL) 850f6f6703fSSean Bruno CC_ALGO(tp)->conn_init(tp->ccv); 851f6f6703fSSean Bruno } 852f6f6703fSSean Bruno } 853f6f6703fSSean Bruno } 854f6f6703fSSean Bruno 855df8bae1dSRodney W. Grimes /* 85677339e1cSAndre Oppermann * Disable RFC1323 and SACK if we haven't got any response to 8577ceb7783SJesper Skriver * our third SYN to work-around some broken terminal servers 8587ceb7783SJesper Skriver * (most of which have hopefully been retired) that have bad VJ 8597ceb7783SJesper Skriver * header compression code which trashes TCP segments containing 8607ceb7783SJesper Skriver * unknown-to-them TCP options. 8617ceb7783SJesper Skriver */ 8626c0ef895SJohn Baldwin if (tcp_rexmit_drop_options && (tp->t_state == TCPS_SYN_SENT) && 8636c0ef895SJohn Baldwin (tp->t_rxtshift == 3)) 864c4ab59c1SAndre Oppermann tp->t_flags &= ~(TF_REQ_SCALE|TF_REQ_TSTMP|TF_SACK_PERMIT); 8657ceb7783SJesper Skriver /* 8665ede40dcSRyan Stone * If we backed off this far, notify the L3 protocol that we're having 8675ede40dcSRyan Stone * connection problems. 868df8bae1dSRodney W. Grimes */ 8695ede40dcSRyan Stone if (tp->t_rxtshift > TCP_RTT_INVALIDATE) { 870fb59c426SYoshinobu Inoue #ifdef INET6 8719eb0e832SGleb Smirnoff if ((inp->inp_vflag & INP_IPV6) != 0) 8729eb0e832SGleb Smirnoff in6_losing(inp); 87384cc0778SGeorge V. Neville-Neil else 874fb59c426SYoshinobu Inoue #endif 8759eb0e832SGleb Smirnoff in_losing(inp); 876df8bae1dSRodney W. Grimes } 877df8bae1dSRodney W. Grimes tp->snd_nxt = tp->snd_una; 8789d11646dSJeffrey Hsu tp->snd_recover = tp->snd_max; 87946f58482SJonathan Lemon /* 88074b48c1dSAndras Olah * Force a segment to be sent. 88174b48c1dSAndras Olah */ 88274b48c1dSAndras Olah tp->t_flags |= TF_ACKNOW; 88374b48c1dSAndras Olah /* 884df8bae1dSRodney W. Grimes * If timing a segment in this window, stop the timer. 885df8bae1dSRodney W. Grimes */ 8869b8b58e0SJonathan Lemon tp->t_rtttime = 0; 887dbc42409SLawrence Stewart 888b5af1b88SLawrence Stewart cc_cong_signal(tp, NULL, CC_RTO); 889109eb549SGleb Smirnoff NET_EPOCH_ENTER(et); 890f64dc2abSGleb Smirnoff outrv = tcp_output_nodrop(tp); 8919b8b58e0SJonathan Lemon #ifdef TCPDEBUG 8929eb0e832SGleb Smirnoff if (tp != NULL && (tptosocket(tp)->so_options & SO_DEBUG)) 893fb59c426SYoshinobu Inoue tcp_trace(TA_USER, ostate, tp, (void *)0, (struct tcphdr *)0, 8949b8b58e0SJonathan Lemon PRU_SLOWTIMO); 895df8bae1dSRodney W. Grimes #endif 8965d06879aSGeorge V. Neville-Neil TCP_PROBE2(debug__user, tp, PRU_SLOWTIMO); 897f64dc2abSGleb Smirnoff (void) tcp_unlock_or_drop(tp, outrv); 898f64dc2abSGleb Smirnoff NET_EPOCH_EXIT(et); 8998b615593SMarko Zec CURVNET_RESTORE(); 90085d94372SRobert Watson } 90185d94372SRobert Watson 90285d94372SRobert Watson void 9035571f9cfSJulien Charbon tcp_timer_activate(struct tcpcb *tp, uint32_t timer_type, u_int delta) 90485d94372SRobert Watson { 90585d94372SRobert Watson struct callout *t_callout; 9065773ac11SJohn Baldwin callout_func_t *f_callout; 9079eb0e832SGleb Smirnoff struct inpcb *inp = tptoinpcb(tp); 908883831c6SAdrian Chadd int cpu = inp_to_cpuid(inp); 90985d94372SRobert Watson 91009fe6320SNavdeep Parhar #ifdef TCP_OFFLOAD 91109fe6320SNavdeep Parhar if (tp->t_flags & TF_TOE) 91209fe6320SNavdeep Parhar return; 91309fe6320SNavdeep Parhar #endif 91409fe6320SNavdeep Parhar 9155571f9cfSJulien Charbon if (tp->t_timers->tt_flags & TT_STOPPED) 9165571f9cfSJulien Charbon return; 9175571f9cfSJulien Charbon 91885d94372SRobert Watson switch (timer_type) { 91985d94372SRobert Watson case TT_DELACK: 920e2f2059fSMike Silbersack t_callout = &tp->t_timers->tt_delack; 92185d94372SRobert Watson f_callout = tcp_timer_delack; 92285d94372SRobert Watson break; 92385d94372SRobert Watson case TT_REXMT: 924e2f2059fSMike Silbersack t_callout = &tp->t_timers->tt_rexmt; 92585d94372SRobert Watson f_callout = tcp_timer_rexmt; 92685d94372SRobert Watson break; 92785d94372SRobert Watson case TT_PERSIST: 928e2f2059fSMike Silbersack t_callout = &tp->t_timers->tt_persist; 92985d94372SRobert Watson f_callout = tcp_timer_persist; 93085d94372SRobert Watson break; 93185d94372SRobert Watson case TT_KEEP: 932e2f2059fSMike Silbersack t_callout = &tp->t_timers->tt_keep; 93385d94372SRobert Watson f_callout = tcp_timer_keep; 93485d94372SRobert Watson break; 93585d94372SRobert Watson case TT_2MSL: 936e2f2059fSMike Silbersack t_callout = &tp->t_timers->tt_2msl; 93785d94372SRobert Watson f_callout = tcp_timer_2msl; 93885d94372SRobert Watson break; 93985d94372SRobert Watson default: 94055bceb1eSRandall Stewart if (tp->t_fb->tfb_tcp_timer_activate) { 94155bceb1eSRandall Stewart tp->t_fb->tfb_tcp_timer_activate(tp, timer_type, delta); 94255bceb1eSRandall Stewart return; 94355bceb1eSRandall Stewart } 94403374917SJulien Charbon panic("tp %p bad timer_type %#x", tp, timer_type); 94585d94372SRobert Watson } 94685d94372SRobert Watson if (delta == 0) { 947b07fef50SRandall Stewart callout_stop(t_callout); 94885d94372SRobert Watson } else { 94987aedea4SKip Macy callout_reset_on(t_callout, delta, f_callout, tp, cpu); 95085d94372SRobert Watson } 95185d94372SRobert Watson } 95285d94372SRobert Watson 95385d94372SRobert Watson int 9545571f9cfSJulien Charbon tcp_timer_active(struct tcpcb *tp, uint32_t timer_type) 95585d94372SRobert Watson { 95685d94372SRobert Watson struct callout *t_callout; 95785d94372SRobert Watson 95885d94372SRobert Watson switch (timer_type) { 95985d94372SRobert Watson case TT_DELACK: 960e2f2059fSMike Silbersack t_callout = &tp->t_timers->tt_delack; 96185d94372SRobert Watson break; 96285d94372SRobert Watson case TT_REXMT: 963e2f2059fSMike Silbersack t_callout = &tp->t_timers->tt_rexmt; 96485d94372SRobert Watson break; 96585d94372SRobert Watson case TT_PERSIST: 966e2f2059fSMike Silbersack t_callout = &tp->t_timers->tt_persist; 96785d94372SRobert Watson break; 96885d94372SRobert Watson case TT_KEEP: 969e2f2059fSMike Silbersack t_callout = &tp->t_timers->tt_keep; 97085d94372SRobert Watson break; 97185d94372SRobert Watson case TT_2MSL: 972e2f2059fSMike Silbersack t_callout = &tp->t_timers->tt_2msl; 97385d94372SRobert Watson break; 97485d94372SRobert Watson default: 97555bceb1eSRandall Stewart if (tp->t_fb->tfb_tcp_timer_active) { 97655bceb1eSRandall Stewart return(tp->t_fb->tfb_tcp_timer_active(tp, timer_type)); 97755bceb1eSRandall Stewart } 97803374917SJulien Charbon panic("tp %p bad timer_type %#x", tp, timer_type); 97985d94372SRobert Watson } 98085d94372SRobert Watson return callout_active(t_callout); 981df8bae1dSRodney W. Grimes } 982b8614722SMike Silbersack 98389e560f4SRandall Stewart /* 98489e560f4SRandall Stewart * Stop the timer from running, and apply a flag 98589e560f4SRandall Stewart * against the timer_flags that will force the 98689e560f4SRandall Stewart * timer never to run. The flag is needed to assure 98789e560f4SRandall Stewart * a race does not leave it running and cause 98889e560f4SRandall Stewart * the timer to possibly restart itself (keep and persist 98989e560f4SRandall Stewart * especially do this). 99089e560f4SRandall Stewart */ 99189e560f4SRandall Stewart int 99289e560f4SRandall Stewart tcp_timer_suspend(struct tcpcb *tp, uint32_t timer_type) 99389e560f4SRandall Stewart { 99489e560f4SRandall Stewart struct callout *t_callout; 99589e560f4SRandall Stewart uint32_t t_flags; 99689e560f4SRandall Stewart 99789e560f4SRandall Stewart switch (timer_type) { 99889e560f4SRandall Stewart case TT_DELACK: 99989e560f4SRandall Stewart t_flags = TT_DELACK_SUS; 100089e560f4SRandall Stewart t_callout = &tp->t_timers->tt_delack; 100189e560f4SRandall Stewart break; 100289e560f4SRandall Stewart case TT_REXMT: 100389e560f4SRandall Stewart t_flags = TT_REXMT_SUS; 100489e560f4SRandall Stewart t_callout = &tp->t_timers->tt_rexmt; 100589e560f4SRandall Stewart break; 100689e560f4SRandall Stewart case TT_PERSIST: 100789e560f4SRandall Stewart t_flags = TT_PERSIST_SUS; 100889e560f4SRandall Stewart t_callout = &tp->t_timers->tt_persist; 100989e560f4SRandall Stewart break; 101089e560f4SRandall Stewart case TT_KEEP: 101189e560f4SRandall Stewart t_flags = TT_KEEP_SUS; 101289e560f4SRandall Stewart t_callout = &tp->t_timers->tt_keep; 101389e560f4SRandall Stewart break; 101489e560f4SRandall Stewart case TT_2MSL: 101589e560f4SRandall Stewart t_flags = TT_2MSL_SUS; 101689e560f4SRandall Stewart t_callout = &tp->t_timers->tt_2msl; 101789e560f4SRandall Stewart break; 101889e560f4SRandall Stewart default: 101989e560f4SRandall Stewart panic("tp:%p bad timer_type 0x%x", tp, timer_type); 102089e560f4SRandall Stewart } 102189e560f4SRandall Stewart tp->t_timers->tt_flags |= t_flags; 102289e560f4SRandall Stewart return (callout_stop(t_callout)); 102389e560f4SRandall Stewart } 102489e560f4SRandall Stewart 102589e560f4SRandall Stewart void 102689e560f4SRandall Stewart tcp_timers_unsuspend(struct tcpcb *tp, uint32_t timer_type) 102789e560f4SRandall Stewart { 102889e560f4SRandall Stewart switch (timer_type) { 102989e560f4SRandall Stewart case TT_DELACK: 103089e560f4SRandall Stewart if (tp->t_timers->tt_flags & TT_DELACK_SUS) { 103189e560f4SRandall Stewart tp->t_timers->tt_flags &= ~TT_DELACK_SUS; 103289e560f4SRandall Stewart if (tp->t_flags & TF_DELACK) { 103389e560f4SRandall Stewart /* Delayed ack timer should be up activate a timer */ 103489e560f4SRandall Stewart tp->t_flags &= ~TF_DELACK; 103589e560f4SRandall Stewart tcp_timer_activate(tp, TT_DELACK, 103689e560f4SRandall Stewart tcp_delacktime); 103789e560f4SRandall Stewart } 103889e560f4SRandall Stewart } 103989e560f4SRandall Stewart break; 104089e560f4SRandall Stewart case TT_REXMT: 104189e560f4SRandall Stewart if (tp->t_timers->tt_flags & TT_REXMT_SUS) { 104289e560f4SRandall Stewart tp->t_timers->tt_flags &= ~TT_REXMT_SUS; 104389e560f4SRandall Stewart if (SEQ_GT(tp->snd_max, tp->snd_una) && 104489e560f4SRandall Stewart (tcp_timer_active((tp), TT_PERSIST) == 0) && 104589e560f4SRandall Stewart tp->snd_wnd) { 104689e560f4SRandall Stewart /* We have outstanding data activate a timer */ 104789e560f4SRandall Stewart tcp_timer_activate(tp, TT_REXMT, 104889e560f4SRandall Stewart tp->t_rxtcur); 104989e560f4SRandall Stewart } 105089e560f4SRandall Stewart } 105189e560f4SRandall Stewart break; 105289e560f4SRandall Stewart case TT_PERSIST: 105389e560f4SRandall Stewart if (tp->t_timers->tt_flags & TT_PERSIST_SUS) { 105489e560f4SRandall Stewart tp->t_timers->tt_flags &= ~TT_PERSIST_SUS; 105589e560f4SRandall Stewart if (tp->snd_wnd == 0) { 105689e560f4SRandall Stewart /* Activate the persists timer */ 105789e560f4SRandall Stewart tp->t_rxtshift = 0; 105889e560f4SRandall Stewart tcp_setpersist(tp); 105989e560f4SRandall Stewart } 106089e560f4SRandall Stewart } 106189e560f4SRandall Stewart break; 106289e560f4SRandall Stewart case TT_KEEP: 106389e560f4SRandall Stewart if (tp->t_timers->tt_flags & TT_KEEP_SUS) { 106489e560f4SRandall Stewart tp->t_timers->tt_flags &= ~TT_KEEP_SUS; 106589e560f4SRandall Stewart tcp_timer_activate(tp, TT_KEEP, 106689e560f4SRandall Stewart TCPS_HAVEESTABLISHED(tp->t_state) ? 106789e560f4SRandall Stewart TP_KEEPIDLE(tp) : TP_KEEPINIT(tp)); 106889e560f4SRandall Stewart } 106989e560f4SRandall Stewart break; 107089e560f4SRandall Stewart case TT_2MSL: 107189e560f4SRandall Stewart if (tp->t_timers->tt_flags &= TT_2MSL_SUS) { 10729eb0e832SGleb Smirnoff struct socket *so = tptosocket(tp); 10739eb0e832SGleb Smirnoff 107489e560f4SRandall Stewart tp->t_timers->tt_flags &= ~TT_2MSL_SUS; 107589e560f4SRandall Stewart if ((tp->t_state == TCPS_FIN_WAIT_2) && 10769eb0e832SGleb Smirnoff (so == NULL || /* XXXGL: needed? */ 10779eb0e832SGleb Smirnoff (so->so_rcv.sb_state & SBS_CANTRCVMORE))) { 107889e560f4SRandall Stewart /* Star the 2MSL timer */ 107989e560f4SRandall Stewart tcp_timer_activate(tp, TT_2MSL, 108089e560f4SRandall Stewart (tcp_fast_finwait2_recycle) ? 108189e560f4SRandall Stewart tcp_finwait2_timeout : TP_MAXIDLE(tp)); 108289e560f4SRandall Stewart } 108389e560f4SRandall Stewart } 108489e560f4SRandall Stewart break; 108589e560f4SRandall Stewart default: 108689e560f4SRandall Stewart panic("tp:%p bad timer_type 0x%x", tp, timer_type); 108789e560f4SRandall Stewart } 108889e560f4SRandall Stewart } 108989e560f4SRandall Stewart 1090ff945008SGleb Smirnoff static void 1091ff945008SGleb Smirnoff tcp_timer_discard(void *ptp) 1092ff945008SGleb Smirnoff { 1093ff945008SGleb Smirnoff struct epoch_tracker et; 10949eb0e832SGleb Smirnoff struct tcpcb *tp = (struct tcpcb *)ptp; 10959eb0e832SGleb Smirnoff struct inpcb *inp = tptoinpcb(tp); 1096ff945008SGleb Smirnoff 1097ff945008SGleb Smirnoff INP_WLOCK(inp); 1098*8840ae22SGleb Smirnoff CURVNET_SET(inp->inp_vnet); 10999eb0e832SGleb Smirnoff NET_EPOCH_ENTER(et); 11009eb0e832SGleb Smirnoff 1101ff945008SGleb Smirnoff KASSERT((tp->t_timers->tt_flags & TT_STOPPED) != 0, 1102ff945008SGleb Smirnoff ("%s: tcpcb has to be stopped here", __func__)); 1103ff945008SGleb Smirnoff if (--tp->t_timers->tt_draincnt > 0 || 1104ff945008SGleb Smirnoff tcp_freecb(tp) == false) 1105ff945008SGleb Smirnoff INP_WUNLOCK(inp); 1106ff945008SGleb Smirnoff NET_EPOCH_EXIT(et); 1107ff945008SGleb Smirnoff CURVNET_RESTORE(); 1108ff945008SGleb Smirnoff } 1109ff945008SGleb Smirnoff 11105571f9cfSJulien Charbon void 11115571f9cfSJulien Charbon tcp_timer_stop(struct tcpcb *tp, uint32_t timer_type) 11125571f9cfSJulien Charbon { 11135571f9cfSJulien Charbon struct callout *t_callout; 11145571f9cfSJulien Charbon 11155571f9cfSJulien Charbon tp->t_timers->tt_flags |= TT_STOPPED; 11165571f9cfSJulien Charbon switch (timer_type) { 11175571f9cfSJulien Charbon case TT_DELACK: 11185571f9cfSJulien Charbon t_callout = &tp->t_timers->tt_delack; 11195571f9cfSJulien Charbon break; 11205571f9cfSJulien Charbon case TT_REXMT: 11215571f9cfSJulien Charbon t_callout = &tp->t_timers->tt_rexmt; 11225571f9cfSJulien Charbon break; 11235571f9cfSJulien Charbon case TT_PERSIST: 11245571f9cfSJulien Charbon t_callout = &tp->t_timers->tt_persist; 11255571f9cfSJulien Charbon break; 11265571f9cfSJulien Charbon case TT_KEEP: 11275571f9cfSJulien Charbon t_callout = &tp->t_timers->tt_keep; 11285571f9cfSJulien Charbon break; 11295571f9cfSJulien Charbon case TT_2MSL: 11305571f9cfSJulien Charbon t_callout = &tp->t_timers->tt_2msl; 11315571f9cfSJulien Charbon break; 11325571f9cfSJulien Charbon default: 113355bceb1eSRandall Stewart if (tp->t_fb->tfb_tcp_timer_stop) { 113455bceb1eSRandall Stewart /* 113555bceb1eSRandall Stewart * XXXrrs we need to look at this with the 113655bceb1eSRandall Stewart * stop case below (flags). 113755bceb1eSRandall Stewart */ 113855bceb1eSRandall Stewart tp->t_fb->tfb_tcp_timer_stop(tp, timer_type); 113955bceb1eSRandall Stewart return; 114055bceb1eSRandall Stewart } 11415571f9cfSJulien Charbon panic("tp %p bad timer_type %#x", tp, timer_type); 11425571f9cfSJulien Charbon } 11435571f9cfSJulien Charbon 1144e5ad6456SRandall Stewart if (callout_async_drain(t_callout, tcp_timer_discard) == 0) { 11455571f9cfSJulien Charbon /* 11465571f9cfSJulien Charbon * Can't stop the callout, defer tcpcb actual deletion 1147e5ad6456SRandall Stewart * to the last one. We do this using the async drain 1148e5ad6456SRandall Stewart * function and incrementing the count in 11495571f9cfSJulien Charbon */ 1150e5ad6456SRandall Stewart tp->t_timers->tt_draincnt++; 11515571f9cfSJulien Charbon } 11525571f9cfSJulien Charbon } 1153