123194Smckusick /* 223194Smckusick * Copyright (c) 1982 Regents of the University of California. 323194Smckusick * All rights reserved. The Berkeley software License Agreement 423194Smckusick * specifies the terms and conditions for redistribution. 523194Smckusick * 6*27059Skarels * @(#)tcp_timer.c 6.13 (Berkeley) 04/12/86 723194Smckusick */ 85069Swnj 917064Sbloom #include "param.h" 1017064Sbloom #include "systm.h" 1117064Sbloom #include "mbuf.h" 1217064Sbloom #include "socket.h" 1317064Sbloom #include "socketvar.h" 1417064Sbloom #include "protosw.h" 1517064Sbloom #include "errno.h" 168697Sroot 178697Sroot #include "../net/if.h" 188697Sroot #include "../net/route.h" 1910896Ssam 2017064Sbloom #include "in.h" 2117064Sbloom #include "in_pcb.h" 2217064Sbloom #include "in_systm.h" 2317064Sbloom #include "ip.h" 2417064Sbloom #include "ip_var.h" 2517064Sbloom #include "tcp.h" 2617064Sbloom #include "tcp_fsm.h" 2717064Sbloom #include "tcp_seq.h" 2817064Sbloom #include "tcp_timer.h" 2917064Sbloom #include "tcp_var.h" 3017064Sbloom #include "tcpip.h" 315069Swnj 325442Swnj int tcpnodelack = 0; 335069Swnj /* 345069Swnj * Fast timeout routine for processing delayed acks 355069Swnj */ 365069Swnj tcp_fasttimo() 375069Swnj { 385284Sroot register struct inpcb *inp; 395284Sroot register struct tcpcb *tp; 405284Sroot int s = splnet(); 415069Swnj 425691Swnj inp = tcb.inp_next; 435691Swnj if (inp) 445691Swnj for (; inp != &tcb; inp = inp->inp_next) 455284Sroot if ((tp = (struct tcpcb *)inp->inp_ppcb) && 465284Sroot (tp->t_flags & TF_DELACK)) { 475284Sroot tp->t_flags &= ~TF_DELACK; 485284Sroot tp->t_flags |= TF_ACKNOW; 495284Sroot (void) tcp_output(tp); 505284Sroot } 515284Sroot splx(s); 525069Swnj } 535069Swnj 545069Swnj /* 555069Swnj * Tcp protocol timeout routine called every 500 ms. 565069Swnj * Updates the timers in all active tcb's and 575069Swnj * causes finite state machine actions if timers expire. 585069Swnj */ 595069Swnj tcp_slowtimo() 605069Swnj { 616212Swnj register struct inpcb *ip, *ipnxt; 625069Swnj register struct tcpcb *tp; 635069Swnj int s = splnet(); 645069Swnj register int i; 655069Swnj 665069Swnj /* 675069Swnj * Search through tcb's and update active timers. 685069Swnj */ 695245Sroot ip = tcb.inp_next; 705245Sroot if (ip == 0) { 715245Sroot splx(s); 725245Sroot return; 735245Sroot } 7417315Skarels for (; ip != &tcb; ip = ipnxt) { 7517315Skarels ipnxt = ip->inp_next; 765069Swnj tp = intotcpcb(ip); 775260Swnj if (tp == 0) 785260Swnj continue; 795075Swnj for (i = 0; i < TCPT_NTIMERS; i++) { 806212Swnj if (tp->t_timer[i] && --tp->t_timer[i] == 0) { 815069Swnj (void) tcp_usrreq(tp->t_inpcb->inp_socket, 825069Swnj PRU_SLOWTIMO, (struct mbuf *)0, 8312764Ssam (struct mbuf *)i, (struct mbuf *)0); 846212Swnj if (ipnxt->inp_prev != ip) 856212Swnj goto tpgone; 866212Swnj } 875069Swnj } 885165Swnj tp->t_idle++; 895165Swnj if (tp->t_rtt) 905165Swnj tp->t_rtt++; 916212Swnj tpgone: 9217315Skarels ; 935069Swnj } 945075Swnj tcp_iss += TCP_ISSINCR/PR_SLOWHZ; /* increment iss */ 95*27059Skarels #ifdef TCP_COMPAT_42 9626118Skarels if ((int)tcp_iss < 0) 9726118Skarels tcp_iss = 0; /* XXX */ 9826118Skarels #endif 995069Swnj splx(s); 1005069Swnj } 1015069Swnj 1025069Swnj /* 1035075Swnj * Cancel all timers for TCP tp. 1045069Swnj */ 1055089Swnj tcp_canceltimers(tp) 1065069Swnj struct tcpcb *tp; 1075069Swnj { 1085069Swnj register int i; 1095069Swnj 1105075Swnj for (i = 0; i < TCPT_NTIMERS; i++) 1115075Swnj tp->t_timer[i] = 0; 1125069Swnj } 1135069Swnj 11425888Skarels int tcp_backoff[TCP_MAXRXTSHIFT+1] = 11525888Skarels { 1, 2, 4, 6, 8, 10, 15, 20, 30, 30, 30, 30, 30 }; 1165069Swnj /* 1175165Swnj * TCP timer processing. 1185069Swnj */ 11910396Ssam struct tcpcb * 1205075Swnj tcp_timers(tp, timer) 1215069Swnj register struct tcpcb *tp; 1225075Swnj int timer; 1235069Swnj { 12425888Skarels register int rexmt; 1255069Swnj 1265089Swnj switch (timer) { 1275069Swnj 1285165Swnj /* 12924819Skarels * 2 MSL timeout in shutdown went off. If we're closed but 13024819Skarels * still waiting for peer to close and connection has been idle 13124819Skarels * too long, or if 2MSL time is up from TIME_WAIT, delete connection 13224819Skarels * control block. Otherwise, check again in a bit. 1335165Swnj */ 1345075Swnj case TCPT_2MSL: 13524819Skarels if (tp->t_state != TCPS_TIME_WAIT && 13624819Skarels tp->t_idle <= TCPTV_MAXIDLE) 13724819Skarels tp->t_timer[TCPT_2MSL] = TCPTV_KEEP; 13824819Skarels else 13924819Skarels tp = tcp_close(tp); 14010396Ssam break; 1415069Swnj 1425165Swnj /* 1435165Swnj * Retransmission timer went off. Message has not 1445165Swnj * been acked within retransmit interval. Back off 14517362Skarels * to a longer retransmit interval and retransmit one segment. 1465165Swnj */ 1475075Swnj case TCPT_REXMT: 1485165Swnj tp->t_rxtshift++; 1495691Swnj if (tp->t_rxtshift > TCP_MAXRXTSHIFT) { 15010396Ssam tp = tcp_drop(tp, ETIMEDOUT); 15110396Ssam break; 1525691Swnj } 15325888Skarels if (tp->t_srtt == 0) 15425888Skarels rexmt = tcp_beta * TCPTV_SRTTDFLT; 15525888Skarels else 15625888Skarels rexmt = (int)(tcp_beta * tp->t_srtt); 15725888Skarels rexmt *= tcp_backoff[tp->t_rxtshift - 1]; 15825888Skarels TCPT_RANGESET(tp->t_timer[TCPT_REXMT], rexmt, 1596474Sroot TCPTV_MIN, TCPTV_MAX); 16026989Skarels /* 16126989Skarels * If losing, let the lower level know 16226989Skarels * and try for a better route. 16326989Skarels */ 16426989Skarels if (tp->t_rxtshift >= TCP_MAXRXTSHIFT / 4 || 16526989Skarels rexmt >= 10 * PR_SLOWHZ) 16626989Skarels in_losing(tp->t_inpcb); 1675165Swnj tp->snd_nxt = tp->snd_una; 16817314Skarels /* 16921120Skarels * If timing a segment in this window, 17021120Skarels * and we have already gotten some timing estimate, 17121120Skarels * stop the timer. 17217314Skarels */ 17321120Skarels if (tp->t_rtt && tp->t_srtt) 17417314Skarels tp->t_rtt = 0; 1755165Swnj (void) tcp_output(tp); 17610396Ssam break; 1775069Swnj 1785165Swnj /* 1795165Swnj * Persistance timer into zero window. 1805165Swnj * Force a byte to be output, if possible. 1815165Swnj */ 1825075Swnj case TCPT_PERSIST: 1837156Swnj tcp_setpersist(tp); 1845165Swnj tp->t_force = 1; 1855165Swnj (void) tcp_output(tp); 1865165Swnj tp->t_force = 0; 18710396Ssam break; 1885069Swnj 1895165Swnj /* 1905165Swnj * Keep-alive timer went off; send something 1915165Swnj * or drop connection if idle for too long. 1925165Swnj */ 1935075Swnj case TCPT_KEEP: 1946304Sroot if (tp->t_state < TCPS_ESTABLISHED) 1956304Sroot goto dropit; 1966267Sroot if (tp->t_inpcb->inp_socket->so_options & SO_KEEPALIVE) { 1976304Sroot if (tp->t_idle >= TCPTV_MAXIDLE) 1986304Sroot goto dropit; 1996267Sroot /* 2006267Sroot * Saying tp->rcv_nxt-1 lies about what 2016267Sroot * we have received, and by the protocol spec 2026267Sroot * requires the correspondent TCP to respond. 2036267Sroot * Saying tp->snd_una-1 causes the transmitted 2046267Sroot * byte to lie outside the receive window; this 2056267Sroot * is important because we don't necessarily 2066267Sroot * have a byte in the window to send (consider 2076267Sroot * a one-way stream!) 2086267Sroot */ 2095392Swnj tcp_respond(tp, 2106267Sroot tp->t_template, tp->rcv_nxt-1, tp->snd_una-1, 0); 2116267Sroot } else 2126212Swnj tp->t_idle = 0; 2135165Swnj tp->t_timer[TCPT_KEEP] = TCPTV_KEEP; 21410396Ssam break; 2156304Sroot dropit: 21610396Ssam tp = tcp_drop(tp, ETIMEDOUT); 21710396Ssam break; 2185069Swnj } 21910396Ssam return (tp); 2205069Swnj } 221