Main Page | Namespace List | Class Hierarchy | Alphabetical List | Compound List | File List | Compound Members | File Members

tcp-sack1.cc

Go to the documentation of this file.
00001 /* -*-  Mode:C++; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */
00002 /*
00003  * Copyright (c) 1990, 1997 Regents of the University of California.
00004  * All rights reserved.
00005  *
00006  * Redistribution and use in source and binary forms are permitted
00007  * provided that the above copyright notice and this paragraph are
00008  * duplicated in all such forms and that any documentation,
00009  * advertising materials, and other materials related to such
00010  * distribution and use acknowledge that the software was developed
00011  * by the University of California, Lawrence Berkeley Laboratory,
00012  * Berkeley, CA.  The name of the University may not be used to
00013  * endorse or promote products derived from this software without
00014  * specific prior written permission.
00015  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
00016  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
00017  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
00018  */
00019 
00020 /* 8/02 Tom Kelly - Made scoreboard a general interface to allow
00021  *                  easy swapping of scoreboard algorithms.  
00022  */
00023 
00024 #include <stdio.h>
00025 #include <stdlib.h>
00026 #include <sys/types.h>
00027 
00028 #include "ip.h"
00029 #include "tcp.h"
00030 #include "flags.h"
00031 #include "scoreboard.h"
00032 #include "scoreboard-rq.h"
00033 #include "random.h"
00034 
00035 #define TRUE    1
00036 #define FALSE   0
00037 #define RECOVER_DUPACK  1
00038 #define RECOVER_TIMEOUT 2
00039 #define RECOVER_QUENCH  3
00040 
00041 class Sack1TcpAgent : public TcpAgent {
00042  public:
00043         Sack1TcpAgent();
00044         virtual ~Sack1TcpAgent();
00045         virtual void recv(Packet *pkt, Handler*);
00046         void reset();
00047         virtual void timeout(int tno);
00048         virtual void dupack_action();
00049         virtual void partial_ack_action();
00050         void plot();
00051         virtual void send_much(int force, int reason, int maxburst);
00052  protected:
00053         u_char timeout_;        /* boolean: sent pkt from timeout? */
00054         u_char fastrecov_;      /* boolean: doing fast recovery? */
00055         int pipe_;              /* estimate of pipe size (fast recovery) */ 
00056         int partial_ack_;       /* Set to "true" to ensure sending */
00057                                 /*  a packet on a partial ACK.     */
00058         int next_pkt_;          /* Next packet to transmit during Fast */
00059                                 /*  Retransmit as a result of a partial ack. */
00060         int firstpartial_;      /* First of a series of partial acks. */
00061         ScoreBoard* scb_;
00062         static const int SBSIZE=64; /* Initial scoreboard size */
00063 };
00064 
00065 static class Sack1TcpClass : public TclClass {
00066 public:
00067         Sack1TcpClass() : TclClass("Agent/TCP/Sack1") {}
00068         TclObject* create(int, const char*const*) {             
00069                 return (new Sack1TcpAgent());
00070         }
00071 } class_sack;
00072 
00073 Sack1TcpAgent::Sack1TcpAgent() : fastrecov_(FALSE), pipe_(-1), next_pkt_(0), firstpartial_(0)
00074 {
00075         bind_bool("partial_ack_", &partial_ack_);
00076         /* Use the Reassembly Queue based scoreboard as
00077          * ScoreBoard is O(cwnd) which is bad for HSTCP
00078          * scb_ = new ScoreBoard(new ScoreBoardNode[SBSIZE],SBSIZE);
00079          */
00080         scb_ = new ScoreBoardRQ();
00081 }
00082 
00083 Sack1TcpAgent::~Sack1TcpAgent(){
00084         delete scb_;
00085 }
00086 
00087 void Sack1TcpAgent::reset ()
00088 {
00089         scb_->ClearScoreBoard();
00090         TcpAgent::reset ();
00091 }
00092 
00093 
00094 void Sack1TcpAgent::recv(Packet *pkt, Handler*)
00095 {
00096         hdr_tcp *tcph = hdr_tcp::access(pkt);
00097 
00098         if (qs_approved_ == 1 && tcph->seqno() > last_ack_)
00099                 endQuickStart();
00100         if (qs_requested_ == 1)
00101                 processQuickStart(pkt);
00102 #ifdef notdef
00103         if (pkt->type_ != PT_ACK) {
00104                 Tcl::instance().evalf("%s error \"received non-ack\"",
00105                                       name());
00106                 Packet::free(pkt);
00107                 return;
00108         }
00109 #endif
00110         /* W.N.: check if this is from a previous incarnation */
00111         if (tcph->ts() < lastreset_) {
00112                 // Remove packet and do nothing
00113                 Packet::free(pkt);
00114                 return;
00115         }
00116         ++nackpack_;
00117         int ecnecho = hdr_flags::access(pkt)->ecnecho();
00118         if (ecnecho && ecn_)
00119                 ecn(tcph->seqno());
00120         /*
00121          * If DSACK is being used, check for DSACK blocks here.
00122          * Possibilities:  Check for unnecessary Fast Retransmits.
00123          */
00124         if (!fastrecov_) {
00125                 /* normal... not fast recovery */
00126                 if ((int)tcph->seqno() > last_ack_) {
00127                         /*
00128                          * regular ACK not in fast recovery... normal
00129                          */
00130                         firstpartial_ = 0;
00131                         recv_newack_helper(pkt);
00132                         timeout_ = FALSE;
00133                         scb_->ClearScoreBoard();
00134                         if (last_ack_ == 0 && delay_growth_) {
00135                                 cwnd_ = initial_window();
00136                         }
00137                 } else if ((int)tcph->seqno() < last_ack_) {
00138                         /*NOTHING*/
00139                 } else if (timeout_ == FALSE) {
00140                         if (tcph->seqno() != last_ack_) {
00141                                 fprintf(stderr, "pkt seq %d should be %d\n" ,
00142                                         tcph->seqno(), last_ack_);
00143                                 abort();
00144                         }
00145                         scb_->UpdateScoreBoard (highest_ack_, tcph);
00146                         /*
00147                          * Check for a duplicate ACK.
00148                          * Check that the SACK block actually
00149                          *  acknowledges new data.
00150                          */
00151                         if(scb_->CheckUpdate()) {
00152                                 if (++dupacks_ == numdupacks(cwnd_)) {
00153                                         /*
00154                                          * Assume we dropped just one packet.
00155                                          * Retransmit last ack + 1
00156                                          * and try to resume the sequence.
00157                                          */
00158                                         dupack_action();
00159                                 } else if (dupacks_ < numdupacks(cwnd_) && singledup_ ) {
00160                                         send_one();
00161                                 }
00162                         }
00163                 }
00164                 if (dupacks_ == 0)
00165                         send_much(FALSE, 0, maxburst_);
00166         } else {
00167                 /* we are in fast recovery */
00168                 --pipe_;
00169                 if ((int)tcph->seqno() >= recover_) {
00170                         /* ACK indicates fast recovery is over */
00171                         recover_ = 0;
00172                         fastrecov_ = FALSE;
00173                         newack(pkt);
00174                         /* if the connection is done, call finish() */
00175                         if ((highest_ack_ >= curseq_-1) && !closed_) {
00176                                 closed_ = 1;
00177                                 finish();
00178                         }
00179                         timeout_ = FALSE;
00180                         scb_->ClearScoreBoard();
00181 
00182                         /* New window: W/2 - K or W/2? */
00183                 } else if ((int)tcph->seqno() > highest_ack_) {
00184                         /* Not out of fast recovery yet.
00185                          * Update highest_ack_, but not last_ack_. */
00186                         highest_ack_ = (int)tcph->seqno();
00187                         scb_->UpdateScoreBoard (highest_ack_, tcph);
00188                         if (partial_ack_) {
00189                           /* partial_ack_ is needed to guarantee that */
00190                           /*  a new packet is sent in response to a   */
00191                           /*  partial ack.                            */
00192                                 partial_ack_action();
00193                                 ++pipe_;
00194                                 if (firstpartial_ == 0) {
00195                                         newtimer(pkt);
00196                                         t_backoff_ = 1;
00197                                         firstpartial_ = 1;
00198                                 }
00199                         } else {
00200                                 --pipe_;
00201                                 newtimer(pkt);
00202                                 t_backoff_ = 1;
00203                          /* If this partial ACK is from a retransmitted pkt,*/
00204                          /* then we decrement pipe_ again, so that we never */
00205                          /* do worse than slow-start.  If this partial ACK  */
00206                          /* was instead from the original packet, reordered,*/
00207                          /* then this might be too aggressive. */
00208                         }
00209                 } else if (timeout_ == FALSE) {
00210                         /* got another dup ack */
00211                         scb_->UpdateScoreBoard (highest_ack_, tcph);
00212                         if(scb_->CheckUpdate()) {
00213                                 if (dupacks_ > 0)
00214                                         dupacks_++;
00215                         }
00216                 }
00217                 send_much(FALSE, 0, maxburst_);
00218         }
00219 
00220         Packet::free(pkt);
00221 #ifdef notyet
00222         if (trace_)
00223                 plot();
00224 #endif
00225 }
00226 
00227 void
00228 Sack1TcpAgent::dupack_action()
00229 {
00230         int recovered = (highest_ack_ > recover_);
00231         if (recovered || (!bug_fix_ && !ecn_)) {
00232                 goto sack_action;
00233         }
00234  
00235         if (ecn_ && last_cwnd_action_ == CWND_ACTION_ECN) {
00236                 last_cwnd_action_ = CWND_ACTION_DUPACK;
00237                 /*
00238                  * What if there is a DUPACK action followed closely by ECN
00239                  * followed closely by a DUPACK action?
00240                  * The optimal thing to do would be to remember all
00241                  * congestion actions from the most recent window
00242                  * of data.  Otherwise "bugfix" might not prevent
00243                  * all unnecessary Fast Retransmits.
00244                  */
00245                 reset_rtx_timer(1,0);
00246                 /*
00247                  * There are three possibilities: 
00248                  * (1) pipe_ = int(cwnd_) - numdupacks_;
00249                  * (2) pipe_ = window() - numdupacks_;
00250                  * (3) pipe_ = maxseq_ - highest_ack_ - numdupacks_;
00251                  * equation (2) takes into account the receiver's
00252                  * advertised window, and equation (3) takes into
00253                  * account a data-limited sender.
00254                  */
00255                 pipe_ = maxseq_ - highest_ack_ - numdupacks(cwnd_);
00256                 //pipe_ = int(cwnd_) - numdupacks(cwnd_);
00257                 fastrecov_ = TRUE;
00258                 scb_->MarkRetran(highest_ack_+1);
00259                 output(last_ack_ + 1, TCP_REASON_DUPACK);
00260                 return;
00261         }
00262 
00263         if (bug_fix_) {
00264                 /*
00265                  * The line below, for "bug_fix_" true, avoids
00266                  * problems with multiple fast retransmits in one
00267                  * window of data.
00268                  */
00269                 return;
00270         }
00271 
00272 sack_action:
00273         // we are now going into fast_recovery and will trace that event
00274         trace_event("FAST_RECOVERY");
00275 
00276         recover_ = maxseq_;
00277         last_cwnd_action_ = CWND_ACTION_DUPACK;
00278         if (oldCode_) {
00279                 pipe_ = int(cwnd_) - numdupacks(cwnd_);
00280         } else { 
00281                 pipe_ = maxseq_ - highest_ack_ - numdupacks(cwnd_);
00282         }
00283         slowdown(CLOSE_SSTHRESH_HALF|CLOSE_CWND_HALF);
00284         reset_rtx_timer(1,0);
00285         fastrecov_ = TRUE;
00286         scb_->MarkRetran(highest_ack_+1);
00287         output(last_ack_ + 1, TCP_REASON_DUPACK);       // from top
00288         /*
00289          * If dynamically adjusting numdupacks_, record information
00290          *  at this point.
00291          */
00292         return;
00293 }
00294 
00295 /*
00296  * Process a packet that acks previously unacknowleges data, but
00297  * does not take us out of Fast Retransmit.
00298  *
00299  * The need for a mechanism to ensure that Sack TCP sends a packet in
00300  * response to a partial ACK has been discussed in
00301  * "Challenges to Reliable Data Transport over Heterogeneous
00302  * Wireless Networks", Hari Balakrishnan, 1998, and in
00303  * "Responding to Spurious Timeouts in TCP", Andrei Gurtov and 
00304  * Reiner Ludwig, 2003. 
00305  */
00306 void
00307 Sack1TcpAgent::partial_ack_action()
00308 {
00309         if (next_pkt_ < highest_ack_ + 1) {
00310                 next_pkt_ = highest_ack_ + 1;
00311         }
00312         // Output two packets in response to a partial ack,
00313         //   so as not to do worse than slow-start.
00314         int i;
00315         for (i = 1; i<=2; i++) {
00316                 int getNext = scb_->GetNextUnacked(next_pkt_);
00317                 if (getNext > next_pkt_) {
00318                         next_pkt_ = getNext;
00319                 }
00320                 if (t_seqno_ < next_pkt_) {
00321                         t_seqno_ = next_pkt_;
00322                 }
00323                 output(next_pkt_, TCP_REASON_PARTIALACK);       
00324                 scb_->MarkRetran(next_pkt_);
00325                 ++next_pkt_; 
00326         }
00327         return;
00328 }
00329 
00330 void Sack1TcpAgent::timeout(int tno)
00331 {
00332         if (tno == TCP_TIMER_RTX) {
00333                 /*
00334                  * IF DSACK and dynamic adjustment of numdupacks_,
00335                  *  check whether a smaller value of numdupacks_
00336                  *  would have prevented this retransmit timeout.
00337                  * If DSACK and detection of premature retransmit
00338                  *  timeouts, then save some info here.
00339                  */ 
00340                 dupacks_ = 0;
00341                 fastrecov_ = FALSE;
00342                 timeout_ = TRUE;
00343                 if (highest_ack_ > last_ack_)
00344                         last_ack_ = highest_ack_;
00345 #ifdef DEBUGSACK1A
00346                 printf ("timeout. highest_ack: %i seqno: %i fid: %i\n", 
00347                         (int)highest_ack_, (int)t_seqno_, fid_);
00348 #endif
00349                 recover_ = maxseq_;
00350                 scb_->ClearScoreBoard();
00351         }
00352         TcpAgent::timeout(tno);
00353 }
00354 
00355 void Sack1TcpAgent::send_much(int force, int reason, int maxburst)
00356 {
00357         register int found, npacket = 0;
00358         int win = window();
00359         int xmit_seqno;
00360 
00361         found = 1;
00362         if (!force && delsnd_timer_.status() == TIMER_PENDING)
00363                 return;
00364         /*
00365          * as long as the pipe is open and there is app data to send...
00366          */
00367         while (((!fastrecov_ && (t_seqno_ <= last_ack_ + win)) ||
00368                         (fastrecov_ && (pipe_ < int(cwnd_)))) 
00369                         && t_seqno_ < curseq_ && found) {
00370 
00371                 if (overhead_ == 0 || force) {
00372                         found = 0;
00373                         xmit_seqno = scb_->GetNextRetran ();
00374 
00375 #ifdef DEBUGSACK1A
00376                         printf("highest_ack: %d xmit_seqno: %d\n", 
00377                         (int)highest_ack_, xmit_seqno);
00378 #endif
00379                         if (xmit_seqno == -1) { 
00380                                 if ((!fastrecov_ && t_seqno_<=highest_ack_+win)||
00381                                         (fastrecov_ && t_seqno_<=highest_ack_+int(wnd_))) {
00382                                         found = 1;
00383                                         xmit_seqno = t_seqno_++;
00384 #ifdef DEBUGSACK1A
00385                                         printf("sending %d fastrecovery: %d win %d\n",
00386                                                 xmit_seqno, fastrecov_, win);
00387 #endif
00388                                 }
00389                         } else if (recover_>0 && xmit_seqno<=highest_ack_+int(wnd_)) {
00390                                 found = 1;
00391                                 scb_->MarkRetran (xmit_seqno);
00392                                 win = window();
00393                         }
00394                         if (found) {
00395                                 output(xmit_seqno, reason);
00396                                 if (t_seqno_ <= xmit_seqno)
00397                                         t_seqno_ = xmit_seqno + 1;
00398                                 npacket++;
00399                                 pipe_++;
00400                                 if (QOption_)
00401                                         process_qoption_after_send () ;
00402                                 if (qs_approved_ == 1) {
00403                                         double delay = (double) t_rtt_ * tcp_tick_ / cwnd_;
00404                                         delsnd_timer_.resched(delay);
00405                                         return;
00406                                 }
00407                         }
00408                 } else if (!(delsnd_timer_.status() == TIMER_PENDING)) {
00409                         /*
00410                          * Set a delayed send timeout.
00411                          * This is only for the simulator,to add some
00412                          *   randomization if speficied.
00413                          */
00414                         delsnd_timer_.resched(Random::uniform(overhead_));
00415                         return;
00416                 }
00417                 if (maxburst && npacket == maxburst)
00418                         break;
00419         } /* while */
00420 }
00421 
00422 void Sack1TcpAgent::plot()
00423 {
00424 #ifdef notyet
00425         double t = Scheduler::instance().clock();
00426         sprintf(trace_->buffer(), "t %g %d rtt %g\n", 
00427                 t, class_, t_rtt_ * tcp_tick_);
00428         trace_->dump();
00429         sprintf(trace_->buffer(), "t %g %d dev %g\n", 
00430                 t, class_, t_rttvar_ * tcp_tick_);
00431         trace_->dump();
00432         sprintf(trace_->buffer(), "t %g %d win %f\n", t, class_, cwnd_);
00433         trace_->dump();
00434         sprintf(trace_->buffer(), "t %g %d bck %d\n", t, class_, t_backoff_);
00435         trace_->dump();
00436 #endif
00437 }

Generated on Tue Apr 20 12:14:35 2004 for NS2.26SourcesOriginal by doxygen 1.3.3