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> 44df8bae1dSRodney W. Grimes #include <sys/socket.h> 45df8bae1dSRodney W. Grimes #include <sys/socketvar.h> 46c74af4faSBruce Evans #include <sys/sysctl.h> 47c74af4faSBruce Evans #include <sys/systm.h> 48603724d3SBjoern A. Zeeb #include <sys/vimage.h> 49e79adb8eSGarrett Wollman 504b79449eSBjoern A. Zeeb #include <net/if.h> 51df8bae1dSRodney W. Grimes #include <net/route.h> 52df8bae1dSRodney W. Grimes 53df8bae1dSRodney W. Grimes #include <netinet/in.h> 54df8bae1dSRodney W. Grimes #include <netinet/in_pcb.h> 55c74af4faSBruce Evans #include <netinet/in_systm.h> 56fb59c426SYoshinobu Inoue #ifdef INET6 57fb59c426SYoshinobu Inoue #include <netinet6/in6_pcb.h> 58fb59c426SYoshinobu Inoue #endif 59df8bae1dSRodney W. Grimes #include <netinet/ip_var.h> 60df8bae1dSRodney W. Grimes #include <netinet/tcp.h> 61df8bae1dSRodney W. Grimes #include <netinet/tcp_fsm.h> 62df8bae1dSRodney W. Grimes #include <netinet/tcp_timer.h> 63df8bae1dSRodney W. Grimes #include <netinet/tcp_var.h> 64df8bae1dSRodney W. Grimes #include <netinet/tcpip.h> 65af7a2999SDavid Greenman #ifdef TCPDEBUG 66af7a2999SDavid Greenman #include <netinet/tcp_debug.h> 67af7a2999SDavid Greenman #endif 684b79449eSBjoern A. Zeeb #include <netinet/vinet.h> 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 1147c72af87SMohan Srinivasan 1150312fbe9SPoul-Henning Kamp static int tcp_keepcnt = TCPTV_KEEPCNT; 1160312fbe9SPoul-Henning Kamp /* max idle probes */ 1179b8b58e0SJonathan Lemon int tcp_maxpersistidle; 1180312fbe9SPoul-Henning Kamp /* max idle time in persist */ 119df8bae1dSRodney W. Grimes int tcp_maxidle; 120e79adb8eSGarrett Wollman 121df8bae1dSRodney W. Grimes /* 122df8bae1dSRodney W. Grimes * Tcp protocol timeout routine called every 500 ms. 1239b8b58e0SJonathan Lemon * Updates timestamps used for TCP 124df8bae1dSRodney W. Grimes * causes finite state machine actions if timers expire. 125df8bae1dSRodney W. Grimes */ 126df8bae1dSRodney W. Grimes void 127e2f2059fSMike Silbersack tcp_slowtimo(void) 128df8bae1dSRodney W. Grimes { 1298b615593SMarko Zec VNET_ITERATOR_DECL(vnet_iter); 13015bd2b43SDavid Greenman 1318b615593SMarko Zec VNET_LIST_RLOCK(); 1328b615593SMarko Zec VNET_FOREACH(vnet_iter) { 1338b615593SMarko Zec CURVNET_SET(vnet_iter); 1348b615593SMarko Zec INIT_VNET_INET(vnet_iter); 135e79adb8eSGarrett Wollman tcp_maxidle = tcp_keepcnt * tcp_keepintvl; 136603724d3SBjoern A. Zeeb INP_INFO_WLOCK(&V_tcbinfo); 1372104448fSAndre Oppermann (void) tcp_tw_2msl_scan(0); 138603724d3SBjoern A. Zeeb INP_INFO_WUNLOCK(&V_tcbinfo); 1398b615593SMarko Zec CURVNET_RESTORE(); 1408b615593SMarko Zec } 1418b615593SMarko Zec VNET_LIST_RUNLOCK(); 142df8bae1dSRodney W. Grimes } 143df8bae1dSRodney W. Grimes 1447d42e30cSJonathan Lemon int tcp_syn_backoff[TCP_MAXRXTSHIFT + 1] = 1457d42e30cSJonathan Lemon { 1, 1, 1, 1, 1, 2, 4, 8, 16, 32, 64, 64, 64 }; 1467d42e30cSJonathan Lemon 147df8bae1dSRodney W. Grimes int tcp_backoff[TCP_MAXRXTSHIFT + 1] = 148f058535dSJeffrey Hsu { 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 512, 512, 512 }; 149df8bae1dSRodney W. Grimes 150f058535dSJeffrey Hsu static int tcp_totbackoff = 2559; /* sum of tcp_backoff[] */ 151e79adb8eSGarrett Wollman 152623dce13SRobert Watson static int tcp_timer_race; 153623dce13SRobert Watson SYSCTL_INT(_net_inet_tcp, OID_AUTO, timer_race, CTLFLAG_RD, &tcp_timer_race, 154623dce13SRobert Watson 0, "Count of t_inpcb races on tcp_discardcb"); 155623dce13SRobert Watson 156df8bae1dSRodney W. Grimes /* 157df8bae1dSRodney W. Grimes * TCP timer processing. 158df8bae1dSRodney W. Grimes */ 15985d94372SRobert Watson 16085d94372SRobert Watson void 16185d94372SRobert Watson tcp_timer_delack(void *xtp) 162df8bae1dSRodney W. Grimes { 16385d94372SRobert Watson struct tcpcb *tp = xtp; 16485d94372SRobert Watson struct inpcb *inp; 1658b615593SMarko Zec CURVNET_SET(tp->t_vnet); 1668b615593SMarko Zec INIT_VNET_INET(tp->t_vnet); 16785d94372SRobert Watson 168603724d3SBjoern A. Zeeb INP_INFO_RLOCK(&V_tcbinfo); 16985d94372SRobert Watson inp = tp->t_inpcb; 17085d94372SRobert Watson /* 17185d94372SRobert Watson * XXXRW: While this assert is in fact correct, bugs in the tcpcb 17285d94372SRobert Watson * tear-down mean we need it as a work-around for races between 17385d94372SRobert Watson * timers and tcp_discardcb(). 17485d94372SRobert Watson * 17585d94372SRobert Watson * KASSERT(inp != NULL, ("tcp_timer_delack: inp == NULL")); 17685d94372SRobert Watson */ 17785d94372SRobert Watson if (inp == NULL) { 17885d94372SRobert Watson tcp_timer_race++; 179603724d3SBjoern A. Zeeb INP_INFO_RUNLOCK(&V_tcbinfo); 1808b615593SMarko Zec CURVNET_RESTORE(); 18185d94372SRobert Watson return; 18285d94372SRobert Watson } 1838501a69cSRobert Watson INP_WLOCK(inp); 184603724d3SBjoern A. Zeeb INP_INFO_RUNLOCK(&V_tcbinfo); 185e2f2059fSMike Silbersack if ((inp->inp_vflag & INP_DROPPED) || callout_pending(&tp->t_timers->tt_delack) 186e2f2059fSMike Silbersack || !callout_active(&tp->t_timers->tt_delack)) { 1878501a69cSRobert Watson INP_WUNLOCK(inp); 1888b615593SMarko Zec CURVNET_RESTORE(); 18985d94372SRobert Watson return; 19085d94372SRobert Watson } 191e2f2059fSMike Silbersack callout_deactivate(&tp->t_timers->tt_delack); 192df8bae1dSRodney W. Grimes 1939b8b58e0SJonathan Lemon tp->t_flags |= TF_ACKNOW; 194603724d3SBjoern A. Zeeb V_tcpstat.tcps_delack++; 1959b8b58e0SJonathan Lemon (void) tcp_output(tp); 1968501a69cSRobert Watson INP_WUNLOCK(inp); 1978b615593SMarko Zec CURVNET_RESTORE(); 1989b8b58e0SJonathan Lemon } 1999b8b58e0SJonathan Lemon 20085d94372SRobert Watson void 20185d94372SRobert Watson tcp_timer_2msl(void *xtp) 2029b8b58e0SJonathan Lemon { 20385d94372SRobert Watson struct tcpcb *tp = xtp; 20485d94372SRobert Watson struct inpcb *inp; 2058b615593SMarko Zec CURVNET_SET(tp->t_vnet); 2068b615593SMarko Zec INIT_VNET_INET(tp->t_vnet); 2079b8b58e0SJonathan Lemon #ifdef TCPDEBUG 2089b8b58e0SJonathan Lemon int ostate; 2099b8b58e0SJonathan Lemon 2109b8b58e0SJonathan Lemon ostate = tp->t_state; 2119b8b58e0SJonathan Lemon #endif 212623dce13SRobert Watson /* 21385d94372SRobert Watson * XXXRW: Does this actually happen? 21485d94372SRobert Watson */ 215603724d3SBjoern A. Zeeb INP_INFO_WLOCK(&V_tcbinfo); 21685d94372SRobert Watson inp = tp->t_inpcb; 21785d94372SRobert Watson /* 21885d94372SRobert Watson * XXXRW: While this assert is in fact correct, bugs in the tcpcb 21985d94372SRobert Watson * tear-down mean we need it as a work-around for races between 22085d94372SRobert Watson * timers and tcp_discardcb(). 22185d94372SRobert Watson * 22285d94372SRobert Watson * KASSERT(inp != NULL, ("tcp_timer_2msl: inp == NULL")); 22385d94372SRobert Watson */ 22485d94372SRobert Watson if (inp == NULL) { 22585d94372SRobert Watson tcp_timer_race++; 226603724d3SBjoern A. Zeeb INP_INFO_WUNLOCK(&V_tcbinfo); 2278b615593SMarko Zec CURVNET_RESTORE(); 22885d94372SRobert Watson return; 22985d94372SRobert Watson } 2308501a69cSRobert Watson INP_WLOCK(inp); 23185d94372SRobert Watson tcp_free_sackholes(tp); 232e2f2059fSMike Silbersack if ((inp->inp_vflag & INP_DROPPED) || callout_pending(&tp->t_timers->tt_2msl) || 233e2f2059fSMike Silbersack !callout_active(&tp->t_timers->tt_2msl)) { 2348501a69cSRobert Watson INP_WUNLOCK(tp->t_inpcb); 235603724d3SBjoern A. Zeeb INP_INFO_WUNLOCK(&V_tcbinfo); 2368b615593SMarko Zec CURVNET_RESTORE(); 23785d94372SRobert Watson return; 23885d94372SRobert Watson } 239e2f2059fSMike Silbersack callout_deactivate(&tp->t_timers->tt_2msl); 24085d94372SRobert Watson /* 241df8bae1dSRodney W. Grimes * 2 MSL timeout in shutdown went off. If we're closed but 242df8bae1dSRodney W. Grimes * still waiting for peer to close and connection has been idle 243df8bae1dSRodney W. Grimes * too long, or if 2MSL time is up from TIME_WAIT, delete connection 244df8bae1dSRodney W. Grimes * control block. Otherwise, check again in a bit. 2457c72af87SMohan Srinivasan * 2467c72af87SMohan Srinivasan * If fastrecycle of FIN_WAIT_2, in FIN_WAIT_2 and receiver has closed, 2477c72af87SMohan Srinivasan * there's no point in hanging onto FIN_WAIT_2 socket. Just close it. 2487c72af87SMohan Srinivasan * Ignore fact that there were recent incoming segments. 249df8bae1dSRodney W. Grimes */ 2507c72af87SMohan Srinivasan if (tcp_fast_finwait2_recycle && tp->t_state == TCPS_FIN_WAIT_2 && 25185d94372SRobert Watson tp->t_inpcb && tp->t_inpcb->inp_socket && 2527c72af87SMohan Srinivasan (tp->t_inpcb->inp_socket->so_rcv.sb_state & SBS_CANTRCVMORE)) { 253603724d3SBjoern A. Zeeb V_tcpstat.tcps_finwait2_drops++; 25485d94372SRobert Watson tp = tcp_close(tp); 2557c72af87SMohan Srinivasan } else { 256df8bae1dSRodney W. Grimes if (tp->t_state != TCPS_TIME_WAIT && 2579b8b58e0SJonathan Lemon (ticks - tp->t_rcvtime) <= tcp_maxidle) 258e2f2059fSMike Silbersack callout_reset(&tp->t_timers->tt_2msl, tcp_keepintvl, 25985d94372SRobert Watson tcp_timer_2msl, tp); 260df8bae1dSRodney W. Grimes else 26185d94372SRobert Watson tp = tcp_close(tp); 2627c72af87SMohan Srinivasan } 263df8bae1dSRodney W. Grimes 2649b8b58e0SJonathan Lemon #ifdef TCPDEBUG 265586b4a0eSKonstantin Belousov if (tp != NULL && (tp->t_inpcb->inp_socket->so_options & SO_DEBUG)) 266fb59c426SYoshinobu Inoue tcp_trace(TA_USER, ostate, tp, (void *)0, (struct tcphdr *)0, 2679b8b58e0SJonathan Lemon PRU_SLOWTIMO); 2689b8b58e0SJonathan Lemon #endif 26985d94372SRobert Watson if (tp != NULL) 2708501a69cSRobert Watson INP_WUNLOCK(inp); 271603724d3SBjoern A. Zeeb INP_INFO_WUNLOCK(&V_tcbinfo); 2728b615593SMarko Zec CURVNET_RESTORE(); 2739b8b58e0SJonathan Lemon } 2749b8b58e0SJonathan Lemon 27585d94372SRobert Watson void 27685d94372SRobert Watson tcp_timer_keep(void *xtp) 2779b8b58e0SJonathan Lemon { 27885d94372SRobert Watson struct tcpcb *tp = xtp; 27908517d53SMike Silbersack struct tcptemp *t_template; 28085d94372SRobert Watson struct inpcb *inp; 2818b615593SMarko Zec CURVNET_SET(tp->t_vnet); 2828b615593SMarko Zec INIT_VNET_INET(tp->t_vnet); 2839b8b58e0SJonathan Lemon #ifdef TCPDEBUG 2849b8b58e0SJonathan Lemon int ostate; 2859b8b58e0SJonathan Lemon 2869b8b58e0SJonathan Lemon ostate = tp->t_state; 2879b8b58e0SJonathan Lemon #endif 288603724d3SBjoern A. Zeeb INP_INFO_WLOCK(&V_tcbinfo); 28985d94372SRobert Watson inp = tp->t_inpcb; 29085d94372SRobert Watson /* 29185d94372SRobert Watson * XXXRW: While this assert is in fact correct, bugs in the tcpcb 29285d94372SRobert Watson * tear-down mean we need it as a work-around for races between 29385d94372SRobert Watson * timers and tcp_discardcb(). 29485d94372SRobert Watson * 29585d94372SRobert Watson * KASSERT(inp != NULL, ("tcp_timer_keep: inp == NULL")); 29685d94372SRobert Watson */ 29785d94372SRobert Watson if (inp == NULL) { 29885d94372SRobert Watson tcp_timer_race++; 299603724d3SBjoern A. Zeeb INP_INFO_WUNLOCK(&V_tcbinfo); 3008b615593SMarko Zec CURVNET_RESTORE(); 30185d94372SRobert Watson return; 30285d94372SRobert Watson } 3038501a69cSRobert Watson INP_WLOCK(inp); 304e2f2059fSMike Silbersack if ((inp->inp_vflag & INP_DROPPED) || callout_pending(&tp->t_timers->tt_keep) 305e2f2059fSMike Silbersack || !callout_active(&tp->t_timers->tt_keep)) { 3068501a69cSRobert Watson INP_WUNLOCK(inp); 307603724d3SBjoern A. Zeeb INP_INFO_WUNLOCK(&V_tcbinfo); 3088b615593SMarko Zec CURVNET_RESTORE(); 30985d94372SRobert Watson return; 31085d94372SRobert Watson } 311e2f2059fSMike Silbersack callout_deactivate(&tp->t_timers->tt_keep); 3129b8b58e0SJonathan Lemon /* 3139b8b58e0SJonathan Lemon * Keep-alive timer went off; send something 3149b8b58e0SJonathan Lemon * or drop connection if idle for too long. 3159b8b58e0SJonathan Lemon */ 316603724d3SBjoern A. Zeeb V_tcpstat.tcps_keeptimeo++; 3179b8b58e0SJonathan Lemon if (tp->t_state < TCPS_ESTABLISHED) 3189b8b58e0SJonathan Lemon goto dropit; 3192a074620SSam Leffler if ((always_keepalive || inp->inp_socket->so_options & SO_KEEPALIVE) && 3209b8b58e0SJonathan Lemon tp->t_state <= TCPS_CLOSING) { 3219b8b58e0SJonathan Lemon if ((ticks - tp->t_rcvtime) >= tcp_keepidle + tcp_maxidle) 3229b8b58e0SJonathan Lemon goto dropit; 3239b8b58e0SJonathan Lemon /* 3249b8b58e0SJonathan Lemon * Send a packet designed to force a response 3259b8b58e0SJonathan Lemon * if the peer is up and reachable: 3269b8b58e0SJonathan Lemon * either an ACK if the connection is still alive, 3279b8b58e0SJonathan Lemon * or an RST if the peer has closed the connection 3289b8b58e0SJonathan Lemon * due to timeout or reboot. 3299b8b58e0SJonathan Lemon * Using sequence number tp->snd_una-1 3309b8b58e0SJonathan Lemon * causes the transmitted zero-length segment 3319b8b58e0SJonathan Lemon * to lie outside the receive window; 3329b8b58e0SJonathan Lemon * by the protocol spec, this requires the 3339b8b58e0SJonathan Lemon * correspondent TCP to respond. 3349b8b58e0SJonathan Lemon */ 335603724d3SBjoern A. Zeeb V_tcpstat.tcps_keepprobe++; 33679909384SJonathan Lemon t_template = tcpip_maketemplate(inp); 33708517d53SMike Silbersack if (t_template) { 33808517d53SMike Silbersack tcp_respond(tp, t_template->tt_ipgen, 33908517d53SMike Silbersack &t_template->tt_t, (struct mbuf *)NULL, 3409b8b58e0SJonathan Lemon tp->rcv_nxt, tp->snd_una - 1, 0); 34153640b0eSRobert Watson free(t_template, M_TEMP); 34208517d53SMike Silbersack } 343e2f2059fSMike Silbersack callout_reset(&tp->t_timers->tt_keep, tcp_keepintvl, tcp_timer_keep, tp); 3444cc20ab1SSeigo Tanimura } else 345e2f2059fSMike Silbersack callout_reset(&tp->t_timers->tt_keep, tcp_keepidle, tcp_timer_keep, tp); 3469b8b58e0SJonathan Lemon 3479b8b58e0SJonathan Lemon #ifdef TCPDEBUG 3482a074620SSam Leffler if (inp->inp_socket->so_options & SO_DEBUG) 349fb59c426SYoshinobu Inoue tcp_trace(TA_USER, ostate, tp, (void *)0, (struct tcphdr *)0, 3509b8b58e0SJonathan Lemon PRU_SLOWTIMO); 3519b8b58e0SJonathan Lemon #endif 3528501a69cSRobert Watson INP_WUNLOCK(inp); 353603724d3SBjoern A. Zeeb INP_INFO_WUNLOCK(&V_tcbinfo); 3548b615593SMarko Zec CURVNET_RESTORE(); 35585d94372SRobert Watson return; 3569b8b58e0SJonathan Lemon 3579b8b58e0SJonathan Lemon dropit: 358603724d3SBjoern A. Zeeb V_tcpstat.tcps_keepdrops++; 35985d94372SRobert Watson tp = tcp_drop(tp, ETIMEDOUT); 36085d94372SRobert Watson 36185d94372SRobert Watson #ifdef TCPDEBUG 36285d94372SRobert Watson if (tp != NULL && (tp->t_inpcb->inp_socket->so_options & SO_DEBUG)) 36385d94372SRobert Watson tcp_trace(TA_USER, ostate, tp, (void *)0, (struct tcphdr *)0, 36485d94372SRobert Watson PRU_SLOWTIMO); 36585d94372SRobert Watson #endif 36685d94372SRobert Watson if (tp != NULL) 3678501a69cSRobert Watson INP_WUNLOCK(tp->t_inpcb); 368603724d3SBjoern A. Zeeb INP_INFO_WUNLOCK(&V_tcbinfo); 3698b615593SMarko Zec CURVNET_RESTORE(); 3709b8b58e0SJonathan Lemon } 3719b8b58e0SJonathan Lemon 37285d94372SRobert Watson void 37385d94372SRobert Watson tcp_timer_persist(void *xtp) 3749b8b58e0SJonathan Lemon { 37585d94372SRobert Watson struct tcpcb *tp = xtp; 37685d94372SRobert Watson struct inpcb *inp; 3778b615593SMarko Zec CURVNET_SET(tp->t_vnet); 3788b615593SMarko Zec INIT_VNET_INET(tp->t_vnet); 3799b8b58e0SJonathan Lemon #ifdef TCPDEBUG 3809b8b58e0SJonathan Lemon int ostate; 3819b8b58e0SJonathan Lemon 3829b8b58e0SJonathan Lemon ostate = tp->t_state; 3839b8b58e0SJonathan Lemon #endif 384603724d3SBjoern A. Zeeb INP_INFO_WLOCK(&V_tcbinfo); 38585d94372SRobert Watson inp = tp->t_inpcb; 38685d94372SRobert Watson /* 38785d94372SRobert Watson * XXXRW: While this assert is in fact correct, bugs in the tcpcb 38885d94372SRobert Watson * tear-down mean we need it as a work-around for races between 38985d94372SRobert Watson * timers and tcp_discardcb(). 39085d94372SRobert Watson * 39185d94372SRobert Watson * KASSERT(inp != NULL, ("tcp_timer_persist: inp == NULL")); 39285d94372SRobert Watson */ 39385d94372SRobert Watson if (inp == NULL) { 39485d94372SRobert Watson tcp_timer_race++; 395603724d3SBjoern A. Zeeb INP_INFO_WUNLOCK(&V_tcbinfo); 3968b615593SMarko Zec CURVNET_RESTORE(); 39785d94372SRobert Watson return; 39885d94372SRobert Watson } 3998501a69cSRobert Watson INP_WLOCK(inp); 400e2f2059fSMike Silbersack if ((inp->inp_vflag & INP_DROPPED) || callout_pending(&tp->t_timers->tt_persist) 401e2f2059fSMike Silbersack || !callout_active(&tp->t_timers->tt_persist)) { 4028501a69cSRobert Watson INP_WUNLOCK(inp); 403603724d3SBjoern A. Zeeb INP_INFO_WUNLOCK(&V_tcbinfo); 4048b615593SMarko Zec CURVNET_RESTORE(); 40585d94372SRobert Watson return; 40685d94372SRobert Watson } 407e2f2059fSMike Silbersack callout_deactivate(&tp->t_timers->tt_persist); 4089b8b58e0SJonathan Lemon /* 4099b8b58e0SJonathan Lemon * Persistance timer into zero window. 4109b8b58e0SJonathan Lemon * Force a byte to be output, if possible. 4119b8b58e0SJonathan Lemon */ 412603724d3SBjoern A. Zeeb V_tcpstat.tcps_persisttimeo++; 4139b8b58e0SJonathan Lemon /* 4149b8b58e0SJonathan Lemon * Hack: if the peer is dead/unreachable, we do not 4159b8b58e0SJonathan Lemon * time out if the window is closed. After a full 4169b8b58e0SJonathan Lemon * backoff, drop the connection if the idle time 4179b8b58e0SJonathan Lemon * (no responses to probes) reaches the maximum 4189b8b58e0SJonathan Lemon * backoff that we would use if retransmitting. 4199b8b58e0SJonathan Lemon */ 4209b8b58e0SJonathan Lemon if (tp->t_rxtshift == TCP_MAXRXTSHIFT && 4219b8b58e0SJonathan Lemon ((ticks - tp->t_rcvtime) >= tcp_maxpersistidle || 4229b8b58e0SJonathan Lemon (ticks - tp->t_rcvtime) >= TCP_REXMTVAL(tp) * tcp_totbackoff)) { 423603724d3SBjoern A. Zeeb V_tcpstat.tcps_persistdrop++; 42485d94372SRobert Watson tp = tcp_drop(tp, ETIMEDOUT); 42585d94372SRobert Watson goto out; 4269b8b58e0SJonathan Lemon } 4279b8b58e0SJonathan Lemon tcp_setpersist(tp); 4282cdbfa66SPaul Saab tp->t_flags |= TF_FORCEDATA; 4299b8b58e0SJonathan Lemon (void) tcp_output(tp); 4302cdbfa66SPaul Saab tp->t_flags &= ~TF_FORCEDATA; 4319b8b58e0SJonathan Lemon 43285d94372SRobert Watson out: 4339b8b58e0SJonathan Lemon #ifdef TCPDEBUG 434ffb761f6SGleb Smirnoff if (tp != NULL && tp->t_inpcb->inp_socket->so_options & SO_DEBUG) 435ffb761f6SGleb Smirnoff tcp_trace(TA_USER, ostate, tp, NULL, NULL, PRU_SLOWTIMO); 4369b8b58e0SJonathan Lemon #endif 43785d94372SRobert Watson if (tp != NULL) 4388501a69cSRobert Watson INP_WUNLOCK(inp); 439603724d3SBjoern A. Zeeb INP_INFO_WUNLOCK(&V_tcbinfo); 4408b615593SMarko Zec CURVNET_RESTORE(); 4419b8b58e0SJonathan Lemon } 4429b8b58e0SJonathan Lemon 44385d94372SRobert Watson void 44485d94372SRobert Watson tcp_timer_rexmt(void * xtp) 4459b8b58e0SJonathan Lemon { 44685d94372SRobert Watson struct tcpcb *tp = xtp; 4478b615593SMarko Zec CURVNET_SET(tp->t_vnet); 4488b615593SMarko Zec INIT_VNET_INET(tp->t_vnet); 4499b8b58e0SJonathan Lemon int rexmt; 45085d94372SRobert Watson int headlocked; 45185d94372SRobert Watson struct inpcb *inp; 4529b8b58e0SJonathan Lemon #ifdef TCPDEBUG 4539b8b58e0SJonathan Lemon int ostate; 4549b8b58e0SJonathan Lemon 4559b8b58e0SJonathan Lemon ostate = tp->t_state; 4569b8b58e0SJonathan Lemon #endif 457603724d3SBjoern A. Zeeb INP_INFO_WLOCK(&V_tcbinfo); 45885d94372SRobert Watson headlocked = 1; 45985d94372SRobert Watson inp = tp->t_inpcb; 46085d94372SRobert Watson /* 46185d94372SRobert Watson * XXXRW: While this assert is in fact correct, bugs in the tcpcb 46285d94372SRobert Watson * tear-down mean we need it as a work-around for races between 46385d94372SRobert Watson * timers and tcp_discardcb(). 46485d94372SRobert Watson * 46585d94372SRobert Watson * KASSERT(inp != NULL, ("tcp_timer_rexmt: inp == NULL")); 46685d94372SRobert Watson */ 46785d94372SRobert Watson if (inp == NULL) { 46885d94372SRobert Watson tcp_timer_race++; 469603724d3SBjoern A. Zeeb INP_INFO_WUNLOCK(&V_tcbinfo); 4708b615593SMarko Zec CURVNET_RESTORE(); 47185d94372SRobert Watson return; 47285d94372SRobert Watson } 4738501a69cSRobert Watson INP_WLOCK(inp); 474e2f2059fSMike Silbersack if ((inp->inp_vflag & INP_DROPPED) || callout_pending(&tp->t_timers->tt_rexmt) 475e2f2059fSMike Silbersack || !callout_active(&tp->t_timers->tt_rexmt)) { 4768501a69cSRobert Watson INP_WUNLOCK(inp); 477603724d3SBjoern A. Zeeb INP_INFO_WUNLOCK(&V_tcbinfo); 4788b615593SMarko Zec CURVNET_RESTORE(); 47985d94372SRobert Watson return; 48085d94372SRobert Watson } 481e2f2059fSMike Silbersack callout_deactivate(&tp->t_timers->tt_rexmt); 4826d90faf3SPaul Saab tcp_free_sackholes(tp); 483df8bae1dSRodney W. Grimes /* 484df8bae1dSRodney W. Grimes * Retransmission timer went off. Message has not 485df8bae1dSRodney W. Grimes * been acked within retransmit interval. Back off 486df8bae1dSRodney W. Grimes * to a longer retransmit interval and retransmit one segment. 487df8bae1dSRodney W. Grimes */ 488df8bae1dSRodney W. Grimes if (++tp->t_rxtshift > TCP_MAXRXTSHIFT) { 489df8bae1dSRodney W. Grimes tp->t_rxtshift = TCP_MAXRXTSHIFT; 490603724d3SBjoern A. Zeeb V_tcpstat.tcps_timeoutdrop++; 49185d94372SRobert Watson tp = tcp_drop(tp, tp->t_softerror ? 49285d94372SRobert Watson tp->t_softerror : ETIMEDOUT); 49385d94372SRobert Watson goto out; 4949b8b58e0SJonathan Lemon } 495603724d3SBjoern A. Zeeb INP_INFO_WUNLOCK(&V_tcbinfo); 49685d94372SRobert Watson headlocked = 0; 4979b8b58e0SJonathan Lemon if (tp->t_rxtshift == 1) { 4989b8b58e0SJonathan Lemon /* 4999b8b58e0SJonathan Lemon * first retransmit; record ssthresh and cwnd so they can 5009b8b58e0SJonathan Lemon * be recovered if this turns out to be a "bad" retransmit. 5019b8b58e0SJonathan Lemon * A retransmit is considered "bad" if an ACK for this 5029b8b58e0SJonathan Lemon * segment is received within RTT/2 interval; the assumption 5039b8b58e0SJonathan Lemon * here is that the ACK was already in flight. See 5049b8b58e0SJonathan Lemon * "On Estimating End-to-End Network Path Properties" by 5059b8b58e0SJonathan Lemon * Allman and Paxson for more details. 5069b8b58e0SJonathan Lemon */ 5079b8b58e0SJonathan Lemon tp->snd_cwnd_prev = tp->snd_cwnd; 5089b8b58e0SJonathan Lemon tp->snd_ssthresh_prev = tp->snd_ssthresh; 5099d11646dSJeffrey Hsu tp->snd_recover_prev = tp->snd_recover; 5109d11646dSJeffrey Hsu if (IN_FASTRECOVERY(tp)) 5119d11646dSJeffrey Hsu tp->t_flags |= TF_WASFRECOVERY; 5129d11646dSJeffrey Hsu else 5139d11646dSJeffrey Hsu tp->t_flags &= ~TF_WASFRECOVERY; 5149b8b58e0SJonathan Lemon tp->t_badrxtwin = ticks + (tp->t_srtt >> (TCP_RTT_SHIFT + 1)); 515df8bae1dSRodney W. Grimes } 516603724d3SBjoern A. Zeeb V_tcpstat.tcps_rexmttimeo++; 5177d42e30cSJonathan Lemon if (tp->t_state == TCPS_SYN_SENT) 5187d42e30cSJonathan Lemon rexmt = TCP_REXMTVAL(tp) * tcp_syn_backoff[tp->t_rxtshift]; 5197d42e30cSJonathan Lemon else 520df8bae1dSRodney W. Grimes rexmt = TCP_REXMTVAL(tp) * tcp_backoff[tp->t_rxtshift]; 521df8bae1dSRodney W. Grimes TCPT_RANGESET(tp->t_rxtcur, rexmt, 522df8bae1dSRodney W. Grimes tp->t_rttmin, TCPTV_REXMTMAX); 523df8bae1dSRodney W. Grimes /* 524c94c54e4SAndre Oppermann * Disable rfc1323 if we havn't got any response to 5257ceb7783SJesper Skriver * our third SYN to work-around some broken terminal servers 5267ceb7783SJesper Skriver * (most of which have hopefully been retired) that have bad VJ 5277ceb7783SJesper Skriver * header compression code which trashes TCP segments containing 5287ceb7783SJesper Skriver * unknown-to-them TCP options. 5297ceb7783SJesper Skriver */ 5307ceb7783SJesper Skriver if ((tp->t_state == TCPS_SYN_SENT) && (tp->t_rxtshift == 3)) 531c94c54e4SAndre Oppermann tp->t_flags &= ~(TF_REQ_SCALE|TF_REQ_TSTMP); 5327ceb7783SJesper Skriver /* 53397d8d152SAndre Oppermann * If we backed off this far, our srtt estimate is probably bogus. 53497d8d152SAndre Oppermann * Clobber it so we'll take the next rtt measurement as our srtt; 535df8bae1dSRodney W. Grimes * move the current srtt into rttvar to keep the current 536df8bae1dSRodney W. Grimes * retransmit times until then. 537df8bae1dSRodney W. Grimes */ 538df8bae1dSRodney W. Grimes if (tp->t_rxtshift > TCP_MAXRXTSHIFT / 4) { 539fb59c426SYoshinobu Inoue #ifdef INET6 540fb59c426SYoshinobu Inoue if ((tp->t_inpcb->inp_vflag & INP_IPV6) != 0) 541fb59c426SYoshinobu Inoue in6_losing(tp->t_inpcb); 542fb59c426SYoshinobu Inoue else 543fb59c426SYoshinobu Inoue #endif 544df8bae1dSRodney W. Grimes tp->t_rttvar += (tp->t_srtt >> TCP_RTT_SHIFT); 545df8bae1dSRodney W. Grimes tp->t_srtt = 0; 546df8bae1dSRodney W. Grimes } 547df8bae1dSRodney W. Grimes tp->snd_nxt = tp->snd_una; 5489d11646dSJeffrey Hsu tp->snd_recover = tp->snd_max; 54946f58482SJonathan Lemon /* 55074b48c1dSAndras Olah * Force a segment to be sent. 55174b48c1dSAndras Olah */ 55274b48c1dSAndras Olah tp->t_flags |= TF_ACKNOW; 55374b48c1dSAndras Olah /* 554df8bae1dSRodney W. Grimes * If timing a segment in this window, stop the timer. 555df8bae1dSRodney W. Grimes */ 5569b8b58e0SJonathan Lemon tp->t_rtttime = 0; 557df8bae1dSRodney W. Grimes /* 558df8bae1dSRodney W. Grimes * Close the congestion window down to one segment 559df8bae1dSRodney W. Grimes * (we'll open it by one segment for each ack we get). 560df8bae1dSRodney W. Grimes * Since we probably have a window's worth of unacked 561df8bae1dSRodney W. Grimes * data accumulated, this "slow start" keeps us from 562df8bae1dSRodney W. Grimes * dumping all that data as back-to-back packets (which 563df8bae1dSRodney W. Grimes * might overwhelm an intermediate gateway). 564df8bae1dSRodney W. Grimes * 565df8bae1dSRodney W. Grimes * There are two phases to the opening: Initially we 566df8bae1dSRodney W. Grimes * open by one mss on each ack. This makes the window 567df8bae1dSRodney W. Grimes * size increase exponentially with time. If the 568df8bae1dSRodney W. Grimes * window is larger than the path can handle, this 569df8bae1dSRodney W. Grimes * exponential growth results in dropped packet(s) 570df8bae1dSRodney W. Grimes * almost immediately. To get more time between 571df8bae1dSRodney W. Grimes * drops but still "push" the network to take advantage 572df8bae1dSRodney W. Grimes * of improving conditions, we switch from exponential 573df8bae1dSRodney W. Grimes * to linear window opening at some threshhold size. 574df8bae1dSRodney W. Grimes * For a threshhold, we use half the current window 575df8bae1dSRodney W. Grimes * size, truncated to a multiple of the mss. 576df8bae1dSRodney W. Grimes * 577df8bae1dSRodney W. Grimes * (the minimum cwnd that will give us exponential 578df8bae1dSRodney W. Grimes * growth is 2 mss. We don't allow the threshhold 579df8bae1dSRodney W. Grimes * to go below this.) 580df8bae1dSRodney W. Grimes */ 581df8bae1dSRodney W. Grimes { 582df8bae1dSRodney W. Grimes u_int win = min(tp->snd_wnd, tp->snd_cwnd) / 2 / tp->t_maxseg; 583df8bae1dSRodney W. Grimes if (win < 2) 584df8bae1dSRodney W. Grimes win = 2; 585df8bae1dSRodney W. Grimes tp->snd_cwnd = tp->t_maxseg; 586df8bae1dSRodney W. Grimes tp->snd_ssthresh = win * tp->t_maxseg; 587df8bae1dSRodney W. Grimes tp->t_dupacks = 0; 588df8bae1dSRodney W. Grimes } 5899d11646dSJeffrey Hsu EXIT_FASTRECOVERY(tp); 590df8bae1dSRodney W. Grimes (void) tcp_output(tp); 591df8bae1dSRodney W. Grimes 59285d94372SRobert Watson out: 5939b8b58e0SJonathan Lemon #ifdef TCPDEBUG 5941c53f806SRobert Watson if (tp != NULL && (tp->t_inpcb->inp_socket->so_options & SO_DEBUG)) 595fb59c426SYoshinobu Inoue tcp_trace(TA_USER, ostate, tp, (void *)0, (struct tcphdr *)0, 5969b8b58e0SJonathan Lemon PRU_SLOWTIMO); 597df8bae1dSRodney W. Grimes #endif 59885d94372SRobert Watson if (tp != NULL) 5998501a69cSRobert Watson INP_WUNLOCK(inp); 60085d94372SRobert Watson if (headlocked) 601603724d3SBjoern A. Zeeb INP_INFO_WUNLOCK(&V_tcbinfo); 6028b615593SMarko Zec CURVNET_RESTORE(); 60385d94372SRobert Watson } 60485d94372SRobert Watson 60585d94372SRobert Watson void 60685d94372SRobert Watson tcp_timer_activate(struct tcpcb *tp, int timer_type, u_int delta) 60785d94372SRobert Watson { 60885d94372SRobert Watson struct callout *t_callout; 60985d94372SRobert Watson void *f_callout; 61085d94372SRobert Watson 61185d94372SRobert Watson switch (timer_type) { 61285d94372SRobert Watson case TT_DELACK: 613e2f2059fSMike Silbersack t_callout = &tp->t_timers->tt_delack; 61485d94372SRobert Watson f_callout = tcp_timer_delack; 61585d94372SRobert Watson break; 61685d94372SRobert Watson case TT_REXMT: 617e2f2059fSMike Silbersack t_callout = &tp->t_timers->tt_rexmt; 61885d94372SRobert Watson f_callout = tcp_timer_rexmt; 61985d94372SRobert Watson break; 62085d94372SRobert Watson case TT_PERSIST: 621e2f2059fSMike Silbersack t_callout = &tp->t_timers->tt_persist; 62285d94372SRobert Watson f_callout = tcp_timer_persist; 62385d94372SRobert Watson break; 62485d94372SRobert Watson case TT_KEEP: 625e2f2059fSMike Silbersack t_callout = &tp->t_timers->tt_keep; 62685d94372SRobert Watson f_callout = tcp_timer_keep; 62785d94372SRobert Watson break; 62885d94372SRobert Watson case TT_2MSL: 629e2f2059fSMike Silbersack t_callout = &tp->t_timers->tt_2msl; 63085d94372SRobert Watson f_callout = tcp_timer_2msl; 63185d94372SRobert Watson break; 63285d94372SRobert Watson default: 63385d94372SRobert Watson panic("bad timer_type"); 63485d94372SRobert Watson } 63585d94372SRobert Watson if (delta == 0) { 63685d94372SRobert Watson callout_stop(t_callout); 63785d94372SRobert Watson } else { 63885d94372SRobert Watson callout_reset(t_callout, delta, f_callout, tp); 63985d94372SRobert Watson } 64085d94372SRobert Watson } 64185d94372SRobert Watson 64285d94372SRobert Watson int 64385d94372SRobert Watson tcp_timer_active(struct tcpcb *tp, int timer_type) 64485d94372SRobert Watson { 64585d94372SRobert Watson struct callout *t_callout; 64685d94372SRobert Watson 64785d94372SRobert Watson switch (timer_type) { 64885d94372SRobert Watson case TT_DELACK: 649e2f2059fSMike Silbersack t_callout = &tp->t_timers->tt_delack; 65085d94372SRobert Watson break; 65185d94372SRobert Watson case TT_REXMT: 652e2f2059fSMike Silbersack t_callout = &tp->t_timers->tt_rexmt; 65385d94372SRobert Watson break; 65485d94372SRobert Watson case TT_PERSIST: 655e2f2059fSMike Silbersack t_callout = &tp->t_timers->tt_persist; 65685d94372SRobert Watson break; 65785d94372SRobert Watson case TT_KEEP: 658e2f2059fSMike Silbersack t_callout = &tp->t_timers->tt_keep; 65985d94372SRobert Watson break; 66085d94372SRobert Watson case TT_2MSL: 661e2f2059fSMike Silbersack t_callout = &tp->t_timers->tt_2msl; 66285d94372SRobert Watson break; 66385d94372SRobert Watson default: 66485d94372SRobert Watson panic("bad timer_type"); 66585d94372SRobert Watson } 66685d94372SRobert Watson return callout_active(t_callout); 667df8bae1dSRodney W. Grimes } 668