/* * This code is based on Arizona's Vegas TCP/IP. Written by * O. Ait-Hellal by changing Jacobson-Karels FTP source. * Originally written by S. Keshav. Revised by S. McCanne@LBL */ /* The time resolution is supposed to be 10 ns */ #include #include #include "../kernel/real.h" #include "tcp.h" /* parameters for Vegas control prtocol */ #define beta_thresh 3 /* max extrat buffers in the network (Vegas 2-4) */ #define alpha_thresh 1 /* min extrat buffers in the network s */ #define gamma_thresh 1 /* extra buffers at slow-start */ static void vegas_output (); static void vegas_slowtimo (); static void vegas_timers (); static void vegas_ack (); struct frame { PKT_PTR pkt; /* in BSD, packets are queued for retransmissions (prev, next). Here we create a packet every time we need to retransmit it */ short nn; int timeout; long last_sent; /* seq of last pkt. sent */ long last_ack; /* last ack recvd */ int cur_win; /* current window size */ int ssthresh; /* slow start threshold */ int count; /* count of # of increments made * to cur_window so far */ int max_win; /* max window size allowed */ short mdev; /* of rtt estimate from mean */ short rtt; /* actually, the estimate */ short srtt; long t_rtseq; char back_off; /* determines multiplier for timeout */ int line_busy; /* respect of the interpacket delay */ char n_retx[MAX_WINDOW]; /* number of retxs of packet i * in cur win */ int t_timer[MAX_TIMERS]; char t_dupacks; /* number of dupliacte Acks seen */ char t_rexmtthresh; /* number of duplicate Acks for retxsion */ long max_sent; char dropped[MAX_WINDOW_SIZE]; /* parameters for Vegas */ char transmits[MAX_WINDOW]; /* largest number an acked * segment was sent */ timev time_sent[MAX_WINDOW]; float base_RTT; /* the smaller round-trip-time */ /* Vegas timer stuff */ float v_rtt; float v_sa; float v_sd; float v_timeout; int v_newcwnd; /* Stores new uninflated cwnd, used to reset cwnd later.*/ /* Exp increase every other during slow-start. */ char v_exp_inc_do; char v_exp_inc_flag; char v_exp_inc_isless; /* Congestion detection (or more correctly avoidance ) variables. * The mechanism checks once per RTT. Current RTT began with sequence * v_cong_detect_begseq */ char num_ack_after_retx; /* sequence number of ack after * retx is it the first or second */ char v_cong_detect_do; /* whether to do it */ long v_cong_detect_begseq; /* When RTT began */ char v_cong_detect_predict_do; char Transmits; /* Whether to predict the available bw during slow-start */ float v_cong_detect_sumRTT; /* Used to get average (avera = sumRTT/cntRTT) */ int v_cong_detect_cntRTT; /* number of rtt mesured during a RTT interval */ /* The congestion window should stay within band. Going above * it means that not enough buffers are being used at the * bottleneck, so cwnd should increase. Going below the band means * that too many buffers are being used, so cwnd should decrease. */ char v_cong_detect_top_isless; /* => cwnd < top of band */ char v_cong_detect_bot_isless; /* => cwnd < bot of band */ int v_incr; /* How much to increase cwnd when an ACK is received */ timev v_time_cwin_chg; timev v_cong_detect_begtime; timev v_cong_detect_last_sendtime; /* Time when congestion window was last decreased. Used to prevent *decreasing cwin more than once for losses which occurred at the same cwin level */ int s_n_retx; }; vegas() { /* Dummy variables for nest that we don't use. */ ident sender, destn; int key; int i; timev now; struct frame f; bzero ((char *) &f, sizeof f); f.nn = get_node_id (); abs_advance (node_start_time[f.nn]); printf ("Vegas ftp source: %d ", f.nn); printf ("sending to sink %d\n", assigned_sink[f.nn]); source_node_type[f.nn] = FTP_SOURCE; for (i = 0; i< MAX_TIMERS; i++) f.t_timer[i] = 0; f.last_sent = -1; f.max_sent = -1; f.last_ack = -1; f.t_rexmtthresh = 3; f.t_dupacks = 0; f.num_ack_after_retx= 0; f.max_win = ftp_window; f.ssthresh = 16; f.cur_win = 1; f.count = 0; f.mdev = 4; f.rtt = 1; f.t_rtseq = -1; f.srtt = 1; f.back_off = 0; f.timeout = 2; f.s_n_retx = 0; f.line_busy = 0; f.v_cong_detect_predict_do = 0; f.v_exp_inc_do = 1; /* 1 if you want to use vegas, otherwise Reno is running */ f.v_exp_inc_flag = 1; f.v_exp_inc_isless = 0; f.v_cong_detect_do = 1; /* 1 if you want to use vegas, otherwise Reno is running */ f.v_cong_detect_begseq = -1; (f.v_cong_detect_begtime).tv_sec = 0; (f.v_cong_detect_begtime).tv_usec = 0; f.base_RTT = 100; f.v_cong_detect_sumRTT = 0; f.v_cong_detect_cntRTT = 0; f.v_cong_detect_top_isless = 0; f.v_cong_detect_bot_isless = 0; f.v_incr = 0; f.v_timeout = 1000; f.v_sa = 300 * 8; f.v_rtt = f.v_sd = 0; f.Transmits = 0; f.t_rtseq = -1; f.back_off = 0; f.timeout = 2; f.t_timer[TCPT_REXMT] = f.timeout; /* send the first packet */ (void) vegas_output (&f); /* schedule the next software timer 500 ms */ { PKT_PTR tick_pkt; float delay; timev now; now = runtime (); hold(); tick_pkt = (PKT_PTR) malloc ((unsigned) sizeof (PKT)); release(1); tick_pkt->source = tick_pkt->dest = f.nn; tick_pkt->size = ftp_size; tick_pkt->type = TICK; delay = 0.5; set_timer (delay, tick_pkt); } while (1) { sender = recvm (&destn, &key, &f.pkt); switch (f.pkt->type) { case ACK: vegas_ack (&f); break; case INT: hold (); f.line_busy = 0; free (f.pkt); release (1); vegas_output (&f); break; case TIMEOUT: hold (); free (f.pkt); release (1); break; case TICK: hold (); free (f.pkt); release (1); vegas_slowtimo (&f); break; default: hold (); free (f.pkt); release (1); pr_error ("Vegas_source recvd. a vague pkt "); break; } } } /*---------------------------------------------------------------------------*/ static void vegas_ack (fp) struct frame *fp; { PKT_PTR pkt; int i; long last, cum_ack; timev now; float rtt; now = runtime(); pkt = fp->pkt; last = fp->last_ack; cum_ack = pkt->seq_no; rtt = make_float(now) - make_float(fp->time_sent[cum_ack %MWS]); /* if (fp->base_RTT > rtt) fp->base_RTT = rtt;*/ hold (); free (pkt); release (1); make_plot ("ack", cum_ack); if (cum_ack == fp->last_ack) /* duplicate Ack seen */ { if (++fp->t_dupacks > fp->t_rexmtthresh && fp->cur_win > fp->ssthresh) { if (fp->cur_win < fp->max_win) fp->cur_win++; (void) vegas_output(fp); goto drop; } if (fp->t_dupacks == fp->t_rexmtthresh || (expire(fp, cum_ack))) { /* * Vegas retransmits when it receives a first or a second dup ack * if v_timeout has expired */ long oldsent = fp->last_sent; int old_cwnd = fp->cur_win; int win = min (fp->cur_win, fp->max_win); /* * we will check for timed out segments when * we receive the next two non-duplicate ACKS * (or less depending on how much is in transit) */ fp->num_ack_after_retx = min(2, fp->last_sent-fp->last_ack); fp->last_sent = fp->last_ack; fp->cur_win = 1; if (fp->Transmits > 1) fp->v_timeout * 2; else fp->v_timeout += fp->v_timeout/8; /* * If cwnd hasn't changed since the packet was sent, * then we need to decrease it */ if (make_float(fp->time_sent[(fp->last_ack+1) % MWS]) >= make_float(fp->v_time_cwin_chg)) { if (win <= 3) win = 2; else if (fp->Transmits > 1) /* it is retxmd more than once */ { fp->count = fp->count/2; win /= 2; /* exponential decrease */ } else{ fp->count = fp->count/4; win = 3*win/4; } fp->v_newcwnd = win; fp->t_timer[TCPT_REXMT] = 0; fp->rtt = 0; (void) vegas_output(fp); fp->v_time_cwin_chg = runtime (); fp->cur_win = fp->v_newcwnd + fp->t_dupacks; } else /* no need to decrease cwnd */ { (void) vegas_output(fp); fp->cur_win = old_cwnd; } if (fp->Transmits == 1) fp->t_dupacks = fp->t_rexmtthresh; if ( fp->last_sent < oldsent) fp->last_sent = oldsent ; } goto drop; } if (cum_ack < fp->last_ack) { fp->t_dupacks = 0; goto drop; } /* ack not seen before */ if (fp->t_dupacks >= fp->t_rexmtthresh && fp->cur_win > fp->ssthresh) fp->cur_win = fp->ssthresh; fp->t_dupacks = 0; /* * If transmit timer is running and timed sequence * number was acked, update smoothed round trip time. * Since we now have an rtt measurement, cancel the * timer backoff (cfp->, Phil Karn's retransmit alg.). * Recompute the initial retransmit timer. */ if (fp->rtt && cum_ack >= fp->t_rtseq) { if (fp->v_timeout == 1000) { fp->v_rtt = rtt; fp->v_sa = fp->v_rtt*8; fp->v_sd = fp->v_rtt; fp->v_timeout = ((fp->v_sa / 4) + fp->v_sd) / 2; make_flt_plot("v_timeout", fp->v_timeout); } if (fp->srtt != 0) { register short delta; /* * to the smoothing algorithm in rfc793 * with an alpha of .875 * (srtt = rtt/8 + srtt*7/8 in integers ???). * Adjust t_rtt to origin 0. */ fp->rtt--; delta = fp->rtt - (fp->srtt >> 3); if ((fp->srtt += delta) <= 0) fp->srtt = 1; /* * We accumulate a smoothed rtt variance * (actually, a smoothed mean difference), * then set the retransmit timer to smoothed * rtt + 2 times the smoothed variance. * rttvar is stored integer * The following is equivalent * to rfc793 smoothing with an alpha of .75 * (rttvar = rttvar*3/4 + |delta| / 4). * This replaces rfc793's wired-in beta. */ if (delta < 0) delta = -delta; delta -= (fp->mdev >> 2); if ((fp->mdev += delta) <= 0) fp->mdev = 1; } else { /* * No rtt measurement yet - use the * unsmoothed rtt. Set the variance * to half the rtt (so our first * retransmit happens at 2*rtt) */ fp->srtt = fp->rtt << 3; fp->mdev = fp->rtt << 1; } fp->rtt = 0; fp->back_off = 0; TCPT_RANGESET(fp->timeout, ((fp->srtt >> 2) + fp->mdev) >> 1, TCPTV_MIN, TCPTV_REXMTMAX); } if (cum_ack == fp->max_sent) fp->t_timer[TCPT_REXMT] = 0; else fp->t_timer[TCPT_REXMT] = fp->timeout; make_plot("timeout", fp->timeout); for (i = 0; i <= cum_ack - last; ++i) { /* reset the bit anyhow */ fp->n_retx[(last + i) % MWS] = 0; } fp->last_ack = cum_ack; /* * when new data is acked, Reno increases * its cwnd on every Ack if doing slow-start * or on every RTT i doing congestion avoidance. */ if (fp->last_ack >= fp->v_cong_detect_begseq) { float rtt1, sendEpoch, actual_rate, pred_rate, diff_rate, thresh_rate; int win, rttlen; if(fp->v_cong_detect_cntRTT > 0) rtt1 = (fp->v_cong_detect_sumRTT / (float) fp->v_cong_detect_cntRTT); else { rtt1 = rtt; } make_flt_plot("rtt",(float)rtt1); fp->v_cong_detect_sumRTT = 0; fp->v_cong_detect_cntRTT = 0; rttlen = fp->last_sent - fp->v_cong_detect_begseq; /* * if there was only one packet in transit, then update * base_RTT */ if (rttlen <= 1 && rtt1 > 0) { fp->base_RTT = rtt1; } if (rtt1 > 0) { /* * the rates are in pakets/sec, a time is given in 10 nsec */ thresh_rate = 1.0/fp->base_RTT; actual_rate = (float)fp->cur_win/ rtt1; pred_rate = (float)fp->cur_win/fp->base_RTT; diff_rate = pred_rate - actual_rate; make_flt_plot("rate", actual_rate); make_flt_plot("diff", diff_rate); /* If we are currently doing slow-start */ if (fp->cur_win < fp->ssthresh) { /* chek if we are sending faster than available bandwith */ if (diff_rate > gamma_thresh*thresh_rate) fp->v_exp_inc_isless = 1; else fp->v_exp_inc_isless = 0; /* * sendEpoch is the interval of time during which we sent * data. during slow-start, we usually don't send * during the whole RTT, only during part of beginning. */ sendEpoch = make_float(fp->v_cong_detect_last_sendtime) - make_float(fp->time_sent[fp->v_cong_detect_begseq % MWS]); win = fp->cur_win - 1; /* * If we trying to predict the available bandwith * during slow-start */ if (fp->v_cong_detect_predict_do && sendEpoch > 0) { if (fp->v_exp_inc_do) { if (fp->v_exp_inc_flag) { if (fp->cur_win >= 8) { fp->ssthresh = (win*rtt1)/(2*sendEpoch); fp->v_cong_detect_predict_do = 0; } } else { if (fp->cur_win >= 4){ fp->ssthresh = (win*rtt1)/(sendEpoch); fp->v_cong_detect_predict_do = 0; } } } else if (fp->cur_win >= 8){ fp->ssthresh = (win*rtt1)/(2*sendEpoch); fp->v_cong_detect_predict_do = 0; } if (fp->ssthresh <= fp->cur_win) fp->ssthresh = 2; } fp->v_exp_inc_flag = !fp->v_exp_inc_flag; } /* * chek if we are sending too slowly (using less than * alpha_thresh buffers 2) */ if (diff_rate > alpha_thresh*thresh_rate) fp->v_cong_detect_top_isless = 1; else fp->v_cong_detect_top_isless = 0; /* * Chek if we are sending too fast (using more than * beta_thresh-thresh worth of buffers) */ if (diff_rate > beta_thresh*thresh_rate) fp->v_cong_detect_bot_isless = 1; else fp->v_cong_detect_bot_isless = 0; } /* end of rtt1 > 0 */ fp->v_cong_detect_begseq = fp->last_sent + 1 ; /* * In vegas, the congestion avoidance mechanism decides by how * much data to inrease or dcrease the congestion window. During * slow-start, the increase only happen during every other RTT, * and then the increase is one segment, as long as we have not * overran the available bandwith (v_exp_inc_isless==0). * If we are not doing slow-start now, then if we need to send * faster (top_is_less == 0) we increase it by (1/cwnd), if * we need to send slower, then we decrease cwnd by one * immediately and we don't change the cwnd during the next RTT */ if (fp->cur_win < fp->ssthresh){ /* slow-start */ if (fp->v_exp_inc_do){ /* congestion avoidance during slow-start */ if (!fp->v_exp_inc_flag) /* not increasing during this RTT */ fp->v_incr = 0; else if (fp->v_exp_inc_isless) { /* Going faster than avail bw, so slow down */ fp->ssthresh = 2; fp->count = 7*fp->count/8; fp->v_incr = 0; fp->cur_win = (7*fp->cur_win/8); if (fp->cur_win < 2) fp->cur_win = 2; } else /* increase by one with each ACK */ fp->v_incr = ftp_size; } else /* not doing congestion avoidance during slow-start */ fp->v_incr = ftp_size; }/* end of doing slow-start (cur_winv_cong_detect_do) { /* doing congestion/detection avoidance */ if (fp->v_cong_detect_bot_isless) { /* slow down */ if (fp->cur_win < 2) fp->cur_win = 2; fp->cur_win -= 1; fp->v_incr = 0; } else if (fp->v_cong_detect_top_isless) /* the current rate is fine */ fp->v_incr = 0; else /* we can go faster */ fp->v_incr = max(ftp_size / fp->cur_win, 1); } else /* Not doing congestion detection/avoidance */ fp->v_incr = max(ftp_size / fp->cur_win, 1); } } /* end of code done once per RTT (cum_ack > beg_seq) */ /* * Since we set how much to increase only once per RTT, * we need to check during slow-start if we have * surpassed sstthresh before the RTT period is over. */ if (fp->v_incr == ftp_size && fp->cur_win >= fp->ssthresh) fp->v_incr = 0; /* increase congestion win */ fp->count += fp->v_incr; if ((fp->count >= ftp_size) && ((fp->cur_win - (fp->last_sent - fp->last_ack + 1)) <= 2)) { fp->cur_win = min (fp->cur_win + 1, fp->max_win); fp->count -= ftp_size; if (fp->cur_win == fp->max_win) fp->count = 0; } fp->cur_win = min (fp->cur_win, fp->max_win); /* * Do vegas RTT stuffp-> Vegas cheks for timeout when acks are received * (usually duplicate acks). */ any_retransmission (fp, last); /* * transmits to the largest number of times * an acked segment was sent. */ if ( (make_float(fp->time_sent[last%MWS]) !=0) && (fp->Transmits == 1)) { int n; float rtt2; /* the rtt here is given, we have not to compute it again */ rtt2 = rtt; fp->v_cong_detect_sumRTT += rtt2; fp->v_cong_detect_cntRTT++; if (rtt2 > 0) { fp->v_rtt = rtt2; /* * spike_timeInc is not used * vtcp_input.c line 1305 * the time between two packets */ if (fp->v_rtt < fp->base_RTT) { fp->base_RTT = fp->v_rtt; } n = fp->v_rtt - (fp->v_sa/8); fp->v_sa += n; if (n <0) n = -n; n -= (fp->v_sd /4); fp->v_sd += n; fp->v_timeout = (((fp->v_sa / 4) + fp->v_sd) / 2); /* * since we use more occurate time, we need * to make it a little bigger */ fp->v_timeout += (fp->v_timeout / 16); make_flt_plot("v_timeout", fp->v_timeout); }/* end of if rtt2 > 0 */ }/* end of vegas stuff */ /* * Vegas cheks for timed out segments if we receive * a first or second no duplicate ack after retransmission */ fp->last_ack = cum_ack; if (fp->last_sent < fp->last_ack) fp->last_sent = fp->last_ack; if (fp->num_ack_after_retx > 0) { fp->num_ack_after_retx--; if (expire (fp, fp->last_ack)) { long old_sent = fp->last_sent; int old_cwn = fp->cur_win; fp->t_dupacks = fp->t_rexmtthresh; fp->last_sent = fp->last_ack; fp->cur_win = 1; (void) vegas_output (fp); if (fp->last_sent < old_sent) fp->last_sent = old_sent; fp->cur_win = old_cwn; goto drop; }else fp->num_ack_after_retx = 0; } (void) vegas_output (fp); drop: ; } static void vegas_output (fp) struct frame *fp; { PKT_PTR pkt; int win, nqueued; win = min (fp->cur_win, fp->max_win); /* Send as mutch data as we can */ if (fp->line_busy) return; if (fp->last_sent + 1 <= fp->last_ack + win) { int startseq = (++fp->last_sent); /* ok to send now */ hold (); pkt = (PKT_PTR) calloc (1,(unsigned) sizeof (PKT)); release (1); fp->pkt = pkt; pkt->size = ftp_size; pkt->gen_time = runtime (); pkt->type = FTP; pkt->gs = 0; pkt->seq_no = max (fp->last_sent, fp->last_ack+1); pkt->dest = assigned_sink[fp->nn]; if (pkt->dest < ABSOLUTE_MAX_NODES) pkt->dest += ABSOLUTE_MAX_NODES * realnum; pkt->source = realnum * ABSOLUTE_MAX_NODES + fp->nn; fp->dropped[pkt->seq_no % MAX_WINDOW_SIZE] = 0; fp->line_busy = 1; if (fp->last_sent > fp->max_sent) { fp->max_sent = fp->last_sent; if (fp->rtt == 0) { fp->rtt = 1; fp->t_rtseq = startseq; } }else { ++fp->n_retx[fp->last_sent % MWS]; ++fp->s_n_retx; } if (fp->t_timer[TCPT_REXMT] == 0 && fp->last_sent != fp->last_ack) { fp->t_timer[TCPT_REXMT] = fp->timeout; fp->back_off = 0; } ++fp->transmits[fp->last_sent % MWS]; fp->time_sent[fp->last_sent % MWS] = runtime(); fp->v_cong_detect_last_sendtime = runtime(); vegas_send (fp); } } /* * This routine is called every 500 ms, updates the timers * (retransmission timer), and the round trip times. * The fast timeout for processing delayed Acks is not used */ static void vegas_slowtimo (fp) struct frame *fp; { if (fp->t_timer[TCPT_REXMT] && --fp->t_timer[TCPT_REXMT] == 0) { /* * when a timer expire, this routine calls tcp_usrreq * which determines which timer has been timed out, * and calls the appropriate routine. In our case * the only even which may happen is the retransmission * timer which has expired. So tcp_timers is directly called */ (void) vegas_timers (fp); } if (fp->rtt) fp->rtt++; /* schedule the next software timer 500 ms */ { PKT_PTR tick_pkt; float delay; timev now; now = runtime (); hold(); tick_pkt = (PKT_PTR) malloc ((unsigned) sizeof (PKT)); release(1); tick_pkt->source = tick_pkt->dest = fp->nn; tick_pkt->size = ftp_size; tick_pkt->type = TICK; delay = 0.5; set_timer (delay, tick_pkt); } } /* TCP timeout processing */ static void vegas_timers (fp) struct frame *fp; { register int rexmt; if (++fp->back_off > TCP_MAXRXTSHIFT){ printf ("connection timed out \n"); printf("Acknowledged %ld packets\n", fp->last_ack); exit(); } rexmt = ((fp->srtt >> 2) + fp->mdev) >> 1; rexmt *= tcp_backoff[fp->back_off]; TCPT_RANGESET(fp->timeout, rexmt, TCPTV_MIN, TCPTV_REXMTMAX); fp->t_timer[TCPT_REXMT] = fp->timeout; make_plot("timeout", fp->timeout); /* * If losing, let the lower level know and try for * a better route. Also, if we backed off this far, * our srtt estimate is probably bogus. Clobber it * so we'll take the next rtt measurement as our srtt; * move the current srtt into rttvar to keep the current * retransmit times until then. */ if (fp->back_off > MAX_RT_SHIFT / 4) { fp->mdev += fp->srtt; fp->srtt = 0; } fp->last_sent = fp->last_ack; /* * If timing a segment in this window, stop the timer. */ fp->rtt = 0; /* * Close the congestion window down to one segment * (we'll open it by one segment for each ack we get). * Since we probably have a window's worth of unacked * data accumulated, this "slow start" keeps us from * dumping all that data as back-to-back packets (which * might overwhelm an intermediate gateway). * * There are two phases to the opening: Initially we * open by one mss on each ack. This makes the window * size increase exponentially with time. If the * window is larger than the path can handle, this * exponential growth results in dropped packet(s) * almost immediately. To get more time between * drops but still "push" the network to take advantage * of improving conditions, we switch from exponential * to linear window opening at some threshhold size. * For a threshhold, we use half the current window * size, truncated to a multiple of the mss. * * (the minimum cwnd that will give us exponential * growth is 2 mss. We don't allow the threshhold * to go below this.) */ { int win = fp->cur_win / 2; if (win < 2) win = 2; fp->cur_win = 1; fp->ssthresh = win; fp->t_dupacks = fp->t_rexmtthresh; fp->count = 0; } (void) vegas_output(fp); } static vegas_send (fp) struct frame *fp; { PKT_PTR pkt; ident dest; timev now; ident node_number; int win; node_number = get_node_id (); now = runtime (); pkt = fp->pkt; make_plot ("win", fp->cur_win); dest = pkt->dest; if ((dest / ABSOLUTE_MAX_NODES) != realnum) /* not local */ dest = cc_router; else dest = dest % ABSOLUTE_MAX_NODES; pkt->timeout = (long) (200 * MILLION); if (route[node_number][dest] == 0) pr_error ("Routing error in Vegas_source"); if (sendm (route[node_number][dest], 0, (char *) pkt) isnt 1) pr_error ("Vegas_safe_send: message undeliverable ", pkt); } int expire (fp, seq) struct frame *fp; int seq; { int len = 0; float time; timev now; now = runtime(); time = make_float(now)- make_float(fp->time_sent[(seq + 1) % MWS]); if (time >= fp->v_timeout && seq + 1 <= fp->max_sent){ len++; fp->Transmits = fp->transmits[(seq + 1) % MWS]; } return len; } any_retransmission (fp, sequence) struct frame *fp; int sequence; { int i; fp->Transmits = 0; for (i=1; i <= (fp->last_ack - sequence); i++) { fp->Transmits = max(fp->Transmits, fp->transmits[(sequence+ i)%MWS]); fp->transmits[(sequence+ i)%MWS] = 0; } }