qemu/slirp/tcp_timer.c
<<
>>
Prefs
   1/*
   2 * Copyright (c) 1982, 1986, 1988, 1990, 1993
   3 *      The Regents of the University of California.  All rights reserved.
   4 *
   5 * Redistribution and use in source and binary forms, with or without
   6 * modification, are permitted provided that the following conditions
   7 * are met:
   8 * 1. Redistributions of source code must retain the above copyright
   9 *    notice, this list of conditions and the following disclaimer.
  10 * 2. Redistributions in binary form must reproduce the above copyright
  11 *    notice, this list of conditions and the following disclaimer in the
  12 *    documentation and/or other materials provided with the distribution.
  13 * 3. Neither the name of the University nor the names of its contributors
  14 *    may be used to endorse or promote products derived from this software
  15 *    without specific prior written permission.
  16 *
  17 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
  18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  20 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
  21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  27 * SUCH DAMAGE.
  28 *
  29 *      @(#)tcp_timer.c 8.1 (Berkeley) 6/10/93
  30 * tcp_timer.c,v 1.2 1994/08/02 07:49:10 davidg Exp
  31 */
  32
  33#include <slirp.h>
  34
  35static struct tcpcb *tcp_timers(register struct tcpcb *tp, int timer);
  36
  37/*
  38 * Fast timeout routine for processing delayed acks
  39 */
  40void
  41tcp_fasttimo(Slirp *slirp)
  42{
  43        register struct socket *so;
  44        register struct tcpcb *tp;
  45
  46        DEBUG_CALL("tcp_fasttimo");
  47
  48        so = slirp->tcb.so_next;
  49        if (so)
  50        for (; so != &slirp->tcb; so = so->so_next)
  51                if ((tp = (struct tcpcb *)so->so_tcpcb) &&
  52                    (tp->t_flags & TF_DELACK)) {
  53                        tp->t_flags &= ~TF_DELACK;
  54                        tp->t_flags |= TF_ACKNOW;
  55                        (void) tcp_output(tp);
  56                }
  57}
  58
  59/*
  60 * Tcp protocol timeout routine called every 500 ms.
  61 * Updates the timers in all active tcb's and
  62 * causes finite state machine actions if timers expire.
  63 */
  64void
  65tcp_slowtimo(Slirp *slirp)
  66{
  67        register struct socket *ip, *ipnxt;
  68        register struct tcpcb *tp;
  69        register int i;
  70
  71        DEBUG_CALL("tcp_slowtimo");
  72
  73        /*
  74         * Search through tcb's and update active timers.
  75         */
  76        ip = slirp->tcb.so_next;
  77        if (ip == NULL) {
  78            return;
  79        }
  80        for (; ip != &slirp->tcb; ip = ipnxt) {
  81                ipnxt = ip->so_next;
  82                tp = sototcpcb(ip);
  83                if (tp == NULL) {
  84                        continue;
  85                }
  86                for (i = 0; i < TCPT_NTIMERS; i++) {
  87                        if (tp->t_timer[i] && --tp->t_timer[i] == 0) {
  88                                tcp_timers(tp,i);
  89                                if (ipnxt->so_prev != ip)
  90                                        goto tpgone;
  91                        }
  92                }
  93                tp->t_idle++;
  94                if (tp->t_rtt)
  95                   tp->t_rtt++;
  96tpgone:
  97                ;
  98        }
  99        slirp->tcp_iss += TCP_ISSINCR/PR_SLOWHZ;        /* increment iss */
 100        slirp->tcp_now++;                               /* for timestamps */
 101}
 102
 103/*
 104 * Cancel all timers for TCP tp.
 105 */
 106void
 107tcp_canceltimers(struct tcpcb *tp)
 108{
 109        register int i;
 110
 111        for (i = 0; i < TCPT_NTIMERS; i++)
 112                tp->t_timer[i] = 0;
 113}
 114
 115const int tcp_backoff[TCP_MAXRXTSHIFT + 1] =
 116   { 1, 2, 4, 8, 16, 32, 64, 64, 64, 64, 64, 64, 64 };
 117
 118/*
 119 * TCP timer processing.
 120 */
 121static struct tcpcb *
 122tcp_timers(register struct tcpcb *tp, int timer)
 123{
 124        register int rexmt;
 125
 126        DEBUG_CALL("tcp_timers");
 127
 128        switch (timer) {
 129
 130        /*
 131         * 2 MSL timeout in shutdown went off.  If we're closed but
 132         * still waiting for peer to close and connection has been idle
 133         * too long, or if 2MSL time is up from TIME_WAIT, delete connection
 134         * control block.  Otherwise, check again in a bit.
 135         */
 136        case TCPT_2MSL:
 137                if (tp->t_state != TCPS_TIME_WAIT &&
 138                    tp->t_idle <= TCP_MAXIDLE)
 139                        tp->t_timer[TCPT_2MSL] = TCPTV_KEEPINTVL;
 140                else
 141                        tp = tcp_close(tp);
 142                break;
 143
 144        /*
 145         * Retransmission timer went off.  Message has not
 146         * been acked within retransmit interval.  Back off
 147         * to a longer retransmit interval and retransmit one segment.
 148         */
 149        case TCPT_REXMT:
 150
 151                /*
 152                 * XXXXX If a packet has timed out, then remove all the queued
 153                 * packets for that session.
 154                 */
 155
 156                if (++tp->t_rxtshift > TCP_MAXRXTSHIFT) {
 157                        /*
 158                         * This is a hack to suit our terminal server here at the uni of canberra
 159                         * since they have trouble with zeroes... It usually lets them through
 160                         * unharmed, but under some conditions, it'll eat the zeros.  If we
 161                         * keep retransmitting it, it'll keep eating the zeroes, so we keep
 162                         * retransmitting, and eventually the connection dies...
 163                         * (this only happens on incoming data)
 164                         *
 165                         * So, if we were gonna drop the connection from too many retransmits,
 166                         * don't... instead halve the t_maxseg, which might break up the NULLs and
 167                         * let them through
 168                         *
 169                         * *sigh*
 170                         */
 171
 172                        tp->t_maxseg >>= 1;
 173                        if (tp->t_maxseg < 32) {
 174                                /*
 175                                 * We tried our best, now the connection must die!
 176                                 */
 177                                tp->t_rxtshift = TCP_MAXRXTSHIFT;
 178                                tp = tcp_drop(tp, tp->t_softerror);
 179                                /* tp->t_softerror : ETIMEDOUT); */ /* XXX */
 180                                return (tp); /* XXX */
 181                        }
 182
 183                        /*
 184                         * Set rxtshift to 6, which is still at the maximum
 185                         * backoff time
 186                         */
 187                        tp->t_rxtshift = 6;
 188                }
 189                rexmt = TCP_REXMTVAL(tp) * tcp_backoff[tp->t_rxtshift];
 190                TCPT_RANGESET(tp->t_rxtcur, rexmt,
 191                    (short)tp->t_rttmin, TCPTV_REXMTMAX); /* XXX */
 192                tp->t_timer[TCPT_REXMT] = tp->t_rxtcur;
 193                /*
 194                 * If losing, let the lower level know and try for
 195                 * a better route.  Also, if we backed off this far,
 196                 * our srtt estimate is probably bogus.  Clobber it
 197                 * so we'll take the next rtt measurement as our srtt;
 198                 * move the current srtt into rttvar to keep the current
 199                 * retransmit times until then.
 200                 */
 201                if (tp->t_rxtshift > TCP_MAXRXTSHIFT / 4) {
 202                        tp->t_rttvar += (tp->t_srtt >> TCP_RTT_SHIFT);
 203                        tp->t_srtt = 0;
 204                }
 205                tp->snd_nxt = tp->snd_una;
 206                /*
 207                 * If timing a segment in this window, stop the timer.
 208                 */
 209                tp->t_rtt = 0;
 210                /*
 211                 * Close the congestion window down to one segment
 212                 * (we'll open it by one segment for each ack we get).
 213                 * Since we probably have a window's worth of unacked
 214                 * data accumulated, this "slow start" keeps us from
 215                 * dumping all that data as back-to-back packets (which
 216                 * might overwhelm an intermediate gateway).
 217                 *
 218                 * There are two phases to the opening: Initially we
 219                 * open by one mss on each ack.  This makes the window
 220                 * size increase exponentially with time.  If the
 221                 * window is larger than the path can handle, this
 222                 * exponential growth results in dropped packet(s)
 223                 * almost immediately.  To get more time between
 224                 * drops but still "push" the network to take advantage
 225                 * of improving conditions, we switch from exponential
 226                 * to linear window opening at some threshold size.
 227                 * For a threshold, we use half the current window
 228                 * size, truncated to a multiple of the mss.
 229                 *
 230                 * (the minimum cwnd that will give us exponential
 231                 * growth is 2 mss.  We don't allow the threshold
 232                 * to go below this.)
 233                 */
 234                {
 235                u_int win = min(tp->snd_wnd, tp->snd_cwnd) / 2 / tp->t_maxseg;
 236                if (win < 2)
 237                        win = 2;
 238                tp->snd_cwnd = tp->t_maxseg;
 239                tp->snd_ssthresh = win * tp->t_maxseg;
 240                tp->t_dupacks = 0;
 241                }
 242                (void) tcp_output(tp);
 243                break;
 244
 245        /*
 246         * Persistence timer into zero window.
 247         * Force a byte to be output, if possible.
 248         */
 249        case TCPT_PERSIST:
 250                tcp_setpersist(tp);
 251                tp->t_force = 1;
 252                (void) tcp_output(tp);
 253                tp->t_force = 0;
 254                break;
 255
 256        /*
 257         * Keep-alive timer went off; send something
 258         * or drop connection if idle for too long.
 259         */
 260        case TCPT_KEEP:
 261                if (tp->t_state < TCPS_ESTABLISHED)
 262                        goto dropit;
 263
 264                if ((SO_OPTIONS) && tp->t_state <= TCPS_CLOSE_WAIT) {
 265                        if (tp->t_idle >= TCPTV_KEEP_IDLE + TCP_MAXIDLE)
 266                                goto dropit;
 267                        /*
 268                         * Send a packet designed to force a response
 269                         * if the peer is up and reachable:
 270                         * either an ACK if the connection is still alive,
 271                         * or an RST if the peer has closed the connection
 272                         * due to timeout or reboot.
 273                         * Using sequence number tp->snd_una-1
 274                         * causes the transmitted zero-length segment
 275                         * to lie outside the receive window;
 276                         * by the protocol spec, this requires the
 277                         * correspondent TCP to respond.
 278                         */
 279                        tcp_respond(tp, &tp->t_template, (struct mbuf *)NULL,
 280                            tp->rcv_nxt, tp->snd_una - 1, 0);
 281                        tp->t_timer[TCPT_KEEP] = TCPTV_KEEPINTVL;
 282                } else
 283                        tp->t_timer[TCPT_KEEP] = TCPTV_KEEP_IDLE;
 284                break;
 285
 286        dropit:
 287                tp = tcp_drop(tp, 0);
 288                break;
 289        }
 290
 291        return (tp);
 292}
 293