tcp-newreno.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 #ifndef lint
00021 static const char rcsid[] =
00022     "@(#) $Header: /nfs/jade/vint/CVSROOT/ns-2/tcp/tcp-newreno.cc,v 1.56 2004/10/26 22:59:42 sfloyd Exp $ (LBL)";
00023 #endif
00024 
00025 //
00026 // newreno-tcp: a revised reno TCP source, without sack
00027 //
00028 
00029 #include <stdio.h>
00030 #include <stdlib.h>
00031 #include <sys/types.h>
00032 
00033 #include "packet.h"
00034 #include "ip.h"
00035 #include "tcp.h"
00036 #include "flags.h"
00037 
00038 
00039 static class NewRenoTcpClass : public TclClass {
00040 public:
00041     NewRenoTcpClass() : TclClass("Agent/TCP/Newreno") {}
00042     TclObject* create(int, const char*const*) {
00043         return (new NewRenoTcpAgent());
00044     }
00045 } class_newreno;
00046 
00047 NewRenoTcpAgent::NewRenoTcpAgent() : newreno_changes_(0), 
00048   newreno_changes1_(0), acked_(0), firstpartial_(0), 
00049   partial_window_deflation_(0), exit_recovery_fix_(0)
00050 {
00051     bind("newreno_changes_", &newreno_changes_);
00052     bind("newreno_changes1_", &newreno_changes1_);
00053     bind("exit_recovery_fix_", &exit_recovery_fix_);
00054     bind("partial_window_deflation_", &partial_window_deflation_);
00055 }
00056 
00057 /* 
00058  * Process a packet that acks previously unacknowleges data, but
00059  * does not take us out of Fast Retransmit.
00060  */
00061 void NewRenoTcpAgent::partialnewack(Packet* pkt)
00062 {
00063     hdr_tcp *tcph = hdr_tcp::access(pkt);
00064     if (partial_window_deflation_) {
00065         // Do partial window deflation before resetting last_ack_
00066         unsigned int deflate = 0; // Should initialize it?? - haoboy
00067         if (tcph->seqno() > last_ack_) // assertion
00068             deflate = tcph->seqno() - last_ack_;
00069         else 
00070             printf("False call to partialnewack:  deflate %u \
00071 last_ack_ %d\n", deflate, last_ack_);
00072         if (dupwnd_ > deflate)
00073             dupwnd_ -= (deflate - 1);
00074         else {
00075             cwnd_ -= (deflate - dupwnd_);
00076             // Leave dupwnd_ > 0 to flag "fast recovery" phase
00077             dupwnd_ = 1; 
00078         }
00079         if (cwnd_ < 1) {cwnd_ = 1;}
00080     }
00081     last_ack_ = tcph->seqno();
00082     highest_ack_ = last_ack_;
00083     if (t_seqno_ < last_ack_ + 1)
00084         t_seqno_ = last_ack_ + 1;
00085     if (rtt_active_ && tcph->seqno() >= rtt_seq_) {
00086         rtt_active_ = 0;
00087         t_backoff_ = 1;
00088     }
00089 }
00090 
00091 void NewRenoTcpAgent::partialnewack_helper(Packet* pkt)
00092 {
00093     if (!newreno_changes1_ || firstpartial_ == 0) {
00094         firstpartial_ = 1;
00095         /* For newreno_changes1_, 
00096          * only reset the retransmit timer for the first
00097          * partial ACK, so that, in the worst case, we
00098          * don't have to wait for one packet retransmitted
00099          * per RTT.
00100          */
00101         newtimer(pkt);
00102     }
00103     partialnewack(pkt);
00104     output(last_ack_ + 1, 0);
00105 }
00106 
00107 int
00108 NewRenoTcpAgent::allow_fast_retransmit(int /* last_cwnd_action_*/)
00109 {
00110     return 0;
00111 }
00112 
00113 void
00114 NewRenoTcpAgent::dupack_action()
00115 {
00116         int recovered = (highest_ack_ > recover_);
00117     int recovered1 = (highest_ack_ == recover_);
00118         int allowFastRetransmit = allow_fast_retransmit(last_cwnd_action_);
00119         if (recovered || (!bug_fix_ && !ecn_) || allowFastRetransmit) {
00120                 goto reno_action;
00121         }
00122     if (bug_fix_ && less_careful_ && recovered1) {
00123         /*
00124          * For the Less Careful variant, allow a Fast Retransmit
00125          *  if highest_ack_ == recover.
00126          * RFC 2582 recommends the Careful variant, not the 
00127          *  Less Careful one.
00128          */
00129                 goto reno_action;
00130     }
00131 
00132         if (ecn_ && last_cwnd_action_ == CWND_ACTION_ECN) {
00133                 last_cwnd_action_ = CWND_ACTION_DUPACK;
00134                 /*
00135                  * What if there is a DUPACK action followed closely by ECN
00136                  * followed closely by a DUPACK action?
00137                  * The optimal thing to do would be to remember all
00138                  * congestion actions from the most recent window
00139                  * of data.  Otherwise "bugfix" might not prevent
00140                  * all unnecessary Fast Retransmits.
00141                  */
00142                 reset_rtx_timer(1,0);
00143                 output(last_ack_ + 1, TCP_REASON_DUPACK);
00144         dupwnd_ = numdupacks_;
00145                 return;
00146         }
00147 
00148         if (bug_fix_) {
00149         if (bugfix_ts_ && tss[highest_ack_ % tss_size_] == ts_echo_)
00150             goto reno_action;
00151         else if (bugfix_ack_ && cwnd_ > 1 && highest_ack_ - prev_highest_ack_ <= numdupacks_)
00152             goto reno_action;
00153         else
00154                 /*
00155                  * The line below, for "bug_fix_" true, avoids
00156                  * problems with multiple fast retransmits in one
00157                  * window of data.
00158                  */
00159                     return;
00160         }
00161 
00162 reno_action:
00163         recover_ = maxseq_;
00164         reset_rtx_timer(1,0);
00165         if (!lossQuickStart()) {
00166                 trace_event("NEWRENO_FAST_RETX");
00167             last_cwnd_action_ = CWND_ACTION_DUPACK;
00168             slowdown(CLOSE_SSTHRESH_HALF|CLOSE_CWND_HALF);
00169             output(last_ack_ + 1, TCP_REASON_DUPACK);       // from top
00170         dupwnd_ = numdupacks_;
00171     }
00172         return;
00173 }
00174 
00175 
00176 void NewRenoTcpAgent::recv(Packet *pkt, Handler*)
00177 {
00178     hdr_tcp *tcph = hdr_tcp::access(pkt);
00179     int valid_ack = 0;
00180 
00181     /* Use first packet to calculate the RTT  --contributed by Allman */
00182 
00183         if (qs_approved_ == 1 && tcph->seqno() > last_ack_)
00184         endQuickStart();
00185         if (qs_requested_ == 1)
00186                 processQuickStart(pkt);
00187     if (++acked_ == 1) 
00188         basertt_ = Scheduler::instance().clock() - firstsent_;
00189 
00190     /* Estimate ssthresh based on the calculated RTT and the estimated
00191        bandwidth (using ACKs 2 and 3).  */
00192 
00193     else if (acked_ == 2)
00194         ack2_ = Scheduler::instance().clock();
00195     else if (acked_ == 3) {
00196         ack3_ = Scheduler::instance().clock();
00197         new_ssthresh_ = int((basertt_ * (size_ / (ack3_ - ack2_))) / size_);
00198         if (newreno_changes_ > 0 && new_ssthresh_ < ssthresh_)
00199             ssthresh_ = new_ssthresh_;
00200     }
00201 
00202 #ifdef notdef
00203     if (pkt->type_ != PT_ACK) {
00204         fprintf(stderr,
00205             "ns: confiuration error: tcp received non-ack\n");
00206         exit(1);
00207     }
00208 #endif
00209         /* W.N.: check if this is from a previous incarnation */
00210         if (tcph->ts() < lastreset_) {
00211                 // Remove packet and do nothing
00212                 Packet::free(pkt);
00213                 return;
00214         }
00215     ++nackpack_;
00216     ts_peer_ = tcph->ts();
00217 
00218     if (hdr_flags::access(pkt)->ecnecho() && ecn_)
00219         ecn(tcph->seqno());
00220     recv_helper(pkt);
00221     recv_frto_helper(pkt);
00222     if (tcph->seqno() > last_ack_) {
00223         if (tcph->seqno() >= recover_ 
00224             || (last_cwnd_action_ != CWND_ACTION_DUPACK)) {
00225             if (dupwnd_ > 0) {
00226                  dupwnd_ = 0;
00227                  if (last_cwnd_action_ == CWND_ACTION_DUPACK)
00228                 last_cwnd_action_ = CWND_ACTION_EXITED;
00229                  if (exit_recovery_fix_) {
00230                 int outstanding = maxseq_ - tcph->seqno() + 1;
00231                 if (ssthresh_ < outstanding)
00232                                         cwnd_ = ssthresh_;
00233                                 else
00234                                         cwnd_ = outstanding;
00235                 }
00236             }
00237             firstpartial_ = 0;
00238             recv_newack_helper(pkt);
00239             if (last_ack_ == 0 && delay_growth_) {
00240                 cwnd_ = initial_window();
00241             }
00242         } else {
00243             /* received new ack for a packet sent during Fast
00244              *  Recovery, but sender stays in Fast Recovery */
00245             if (partial_window_deflation_ == 0)
00246                 dupwnd_ = 0;
00247             partialnewack_helper(pkt);
00248         }
00249     } else if (tcph->seqno() == last_ack_) {
00250         if (hdr_flags::access(pkt)->eln_ && eln_) {
00251             tcp_eln(pkt);
00252             return;
00253         }
00254         if (++dupacks_ == numdupacks_) {
00255             dupack_action();
00256                         if (!exitFastRetrans_)
00257                                 dupwnd_ = numdupacks_;
00258         } else if (dupacks_ > numdupacks_ && (!exitFastRetrans_
00259               || last_cwnd_action_ == CWND_ACTION_DUPACK)) {
00260             trace_event("NEWRENO_FAST_RECOVERY");
00261             ++dupwnd_;  // fast recovery
00262 
00263             /* For every two duplicate ACKs we receive (in the
00264              * "fast retransmit phase"), send one entirely new
00265              * data packet "to keep the flywheel going".  --Allman
00266              */
00267             if (newreno_changes_ > 0 && (dupacks_ % 2) == 1)
00268                 output (t_seqno_++,0);
00269         } else if (dupacks_ < numdupacks_ && singledup_ ) {
00270                         send_one();
00271                 }
00272     }
00273         if (tcph->seqno() >= last_ack_)
00274                 // Check if ACK is valid.  Suggestion by Mark Allman.
00275                 valid_ack = 1;
00276     Packet::free(pkt);
00277 #ifdef notyet
00278     if (trace_)
00279         plot();
00280 #endif
00281 
00282     /*
00283      * Try to send more data
00284      */
00285 
00286         if (valid_ack || aggressive_maxburst_)
00287         if (dupacks_ == 0) 
00288             /*
00289              * Maxburst is really only needed for the first
00290              *  window of data on exiting Fast Recovery.
00291              */
00292             send_much(0, 0, maxburst_);
00293         else if (dupacks_ > numdupacks_ - 1 && newreno_changes_ == 0)
00294             send_much(0, 0, 2);
00295 }
00296 

Generated on Tue Mar 6 16:47:52 2007 for ns2 Network Simulator 2.29 by  doxygen 1.4.6