1*23194Smckusick /* 2*23194Smckusick * Copyright (c) 1982 Regents of the University of California. 3*23194Smckusick * All rights reserved. The Berkeley software License Agreement 4*23194Smckusick * specifies the terms and conditions for redistribution. 5*23194Smckusick * 6*23194Smckusick * @(#)tcp_timer.c 6.7 (Berkeley) 06/08/85 7*23194Smckusick */ 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 */ 955069Swnj splx(s); 965069Swnj } 975069Swnj 985069Swnj /* 995075Swnj * Cancel all timers for TCP tp. 1005069Swnj */ 1015089Swnj tcp_canceltimers(tp) 1025069Swnj struct tcpcb *tp; 1035069Swnj { 1045069Swnj register int i; 1055069Swnj 1065075Swnj for (i = 0; i < TCPT_NTIMERS; i++) 1075075Swnj tp->t_timer[i] = 0; 1085069Swnj } 1095069Swnj 1106474Sroot float tcp_backoff[TCP_MAXRXTSHIFT] = 1116474Sroot { 1.0, 1.2, 1.4, 1.7, 2.0, 3.0, 5.0, 8.0, 16.0, 32.0 }; 1126474Sroot int tcpexprexmtbackoff = 0; 1135069Swnj /* 1145165Swnj * TCP timer processing. 1155069Swnj */ 11610396Ssam struct tcpcb * 1175075Swnj tcp_timers(tp, timer) 1185069Swnj register struct tcpcb *tp; 1195075Swnj int timer; 1205069Swnj { 1215069Swnj 1225089Swnj switch (timer) { 1235069Swnj 1245165Swnj /* 1255165Swnj * 2 MSL timeout in shutdown went off. Delete connection 1265165Swnj * control block. 1275165Swnj */ 1285075Swnj case TCPT_2MSL: 12910396Ssam tp = tcp_close(tp); 13010396Ssam break; 1315069Swnj 1325165Swnj /* 1335165Swnj * Retransmission timer went off. Message has not 1345165Swnj * been acked within retransmit interval. Back off 13517362Skarels * to a longer retransmit interval and retransmit one segment. 1365165Swnj */ 1375075Swnj case TCPT_REXMT: 1385165Swnj tp->t_rxtshift++; 1395691Swnj if (tp->t_rxtshift > TCP_MAXRXTSHIFT) { 14010396Ssam tp = tcp_drop(tp, ETIMEDOUT); 14110396Ssam break; 1425691Swnj } 14317362Skarels /* 14417362Skarels * If losing, let the lower level know 14517362Skarels * and try for a better route. 14617362Skarels */ 14717362Skarels if (tp->t_rxtshift > TCP_MAXRXTSHIFT / 2) 14817362Skarels in_rtchange(tp->t_inpcb); 1495165Swnj TCPT_RANGESET(tp->t_timer[TCPT_REXMT], 1505952Swnj (int)tp->t_srtt, TCPTV_MIN, TCPTV_MAX); 1516474Sroot if (tcpexprexmtbackoff) { 1526474Sroot TCPT_RANGESET(tp->t_timer[TCPT_REXMT], 1536474Sroot tp->t_timer[TCPT_REXMT] << tp->t_rxtshift, 1546474Sroot TCPTV_MIN, TCPTV_MAX); 1556474Sroot } else { 1566474Sroot TCPT_RANGESET(tp->t_timer[TCPT_REXMT], 1576474Sroot tp->t_timer[TCPT_REXMT] * 1586474Sroot tcp_backoff[tp->t_rxtshift - 1], 1596474Sroot TCPTV_MIN, TCPTV_MAX); 1606474Sroot } 1615165Swnj tp->snd_nxt = tp->snd_una; 16217314Skarels /* 16321120Skarels * If timing a segment in this window, 16421120Skarels * and we have already gotten some timing estimate, 16521120Skarels * stop the timer. 16617314Skarels */ 16721120Skarels if (tp->t_rtt && tp->t_srtt) 16817314Skarels tp->t_rtt = 0; 1695165Swnj (void) tcp_output(tp); 17010396Ssam break; 1715069Swnj 1725165Swnj /* 1735165Swnj * Persistance timer into zero window. 1745165Swnj * Force a byte to be output, if possible. 1755165Swnj */ 1765075Swnj case TCPT_PERSIST: 1777156Swnj tcp_setpersist(tp); 1785165Swnj tp->t_force = 1; 1795165Swnj (void) tcp_output(tp); 1805165Swnj tp->t_force = 0; 18110396Ssam break; 1825069Swnj 1835165Swnj /* 1845165Swnj * Keep-alive timer went off; send something 1855165Swnj * or drop connection if idle for too long. 1865165Swnj */ 1875075Swnj case TCPT_KEEP: 1886304Sroot if (tp->t_state < TCPS_ESTABLISHED) 1896304Sroot goto dropit; 1906267Sroot if (tp->t_inpcb->inp_socket->so_options & SO_KEEPALIVE) { 1916304Sroot if (tp->t_idle >= TCPTV_MAXIDLE) 1926304Sroot goto dropit; 1936267Sroot /* 1946267Sroot * Saying tp->rcv_nxt-1 lies about what 1956267Sroot * we have received, and by the protocol spec 1966267Sroot * requires the correspondent TCP to respond. 1976267Sroot * Saying tp->snd_una-1 causes the transmitted 1986267Sroot * byte to lie outside the receive window; this 1996267Sroot * is important because we don't necessarily 2006267Sroot * have a byte in the window to send (consider 2016267Sroot * a one-way stream!) 2026267Sroot */ 2035392Swnj tcp_respond(tp, 2046267Sroot tp->t_template, tp->rcv_nxt-1, tp->snd_una-1, 0); 2056267Sroot } else 2066212Swnj tp->t_idle = 0; 2075165Swnj tp->t_timer[TCPT_KEEP] = TCPTV_KEEP; 20810396Ssam break; 2096304Sroot dropit: 21010396Ssam tp = tcp_drop(tp, ETIMEDOUT); 21110396Ssam break; 2125069Swnj } 21310396Ssam return (tp); 2145069Swnj } 215