tcp-rfc793edu.cc

Go to the documentation of this file.
00001 /*
00002  * Copyright (c) 1999  International Computer Science Institute
00003  * All rights reserved.
00004  *
00005  * Redistribution and use in source and binary forms, with or without
00006  * modification, are permitted provided that the following conditions
00007  * are met:
00008  * 1. Redistributions of source code must retain the above copyright
00009  *    notice, this list of conditions and the following disclaimer.
00010  * 2. Redistributions in binary form must reproduce the above copyright
00011  *    notice, this list of conditions and the following disclaimer in the
00012  *    documentation and/or other materials provided with the distribution.
00013  * 3. All advertising materials mentioning features or use of this software
00014  *    must display the following acknowledgement:
00015  *      This product includes software developed by ACIRI, the AT&T
00016  *      Center for Internet Research at ICSI (the International Computer
00017  *      Science Institute).
00018  * 4. Neither the name of ACIRI nor of ICSI may be used
00019  *    to endorse or promote products derived from this software without
00020  *    specific prior written permission.
00021  *
00022  * THIS SOFTWARE IS PROVIDED BY ICSI AND CONTRIBUTORS ``AS IS'' AND
00023  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00024  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
00025  * ARE DISCLAIMED.  IN NO EVENT SHALL ICSI OR CONTRIBUTORS BE LIABLE
00026  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
00027  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
00028  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
00029  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
00030  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
00031  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
00032  * SUCH DAMAGE.
00033  */
00034 
00035 #include <stdio.h>
00036 #include <stdlib.h>
00037 #include <sys/types.h>
00038 #include <math.h>
00039 
00040 
00041 #include "ip.h"
00042 #include "tcp-rfc793edu.h"
00043 #include "flags.h"
00044 
00045 // Original code contributed by Fernando Cela Diaz,
00046 // <fcela@ce.chalmers.se>.
00047 // For more information, see the following:
00048 // URL "http://www.ce.chalmers.se/~fcela/tcp-tour.html"
00049 
00050 static class  RFC793eduTcpClass : public TclClass {
00051 public:
00052     RFC793eduTcpClass() : TclClass("Agent/TCP/RFC793edu") {}
00053     TclObject* create(int, const char*const*) {
00054         return (new RFC793eduTcpAgent());
00055     }
00056 } class_rfc793edu;
00057 
00058 
00059 RFC793eduTcpAgent::RFC793eduTcpAgent() : TcpAgent() 
00060 {
00061 }
00062 
00063 void
00064 RFC793eduTcpAgent::delay_bind_init_all()
00065 {
00066     delay_bind_init_one("add793expbackoff_");
00067     delay_bind_init_one("add793jacobsonrtt_");
00068     delay_bind_init_one("add793fastrtx_");
00069     delay_bind_init_one("add793slowstart_");
00070     delay_bind_init_one("add793additiveinc_");
00071     delay_bind_init_one("add793karnrtt_");
00072     delay_bind_init_one("add793exponinc_"); 
00073     delay_bind_init_one("rto_");
00074     TcpAgent::delay_bind_init_all();
00075         reset();
00076 }
00077 
00078 int
00079 RFC793eduTcpAgent::delay_bind_dispatch(const char *varName, 
00080                        const char *localName,TclObject *tracer)
00081 {
00082         if (delay_bind_bool(varName, localName, "add793expbackoff_", 
00083                 &add793expbackoff_, tracer)) 
00084         return TCL_OK;
00085         if (delay_bind_bool(varName, localName, "add793jacobsonrtt_", 
00086                 &add793jacobsonrtt_, tracer)) 
00087         return TCL_OK;
00088         if (delay_bind_bool(varName, localName, "add793fastrtx_", 
00089                 &add793fastrtx_, tracer)) 
00090         return TCL_OK;
00091         if (delay_bind_bool(varName, localName, "add793slowstart_", 
00092                 &add793slowstart_, tracer)) 
00093         return TCL_OK;
00094         if (delay_bind_bool(varName, localName, "add793slowstart_", 
00095                 &add793slowstart_, tracer)) 
00096         return TCL_OK;
00097         if (delay_bind_bool(varName, localName, "add793additiveinc_", 
00098                 &add793additiveinc_, tracer)) 
00099         return TCL_OK;
00100         if (delay_bind_bool(varName, localName, "add793karnrtt_", 
00101                 &add793karnrtt_, tracer)) 
00102         return TCL_OK;
00103         if (delay_bind_bool(varName, localName, "add793exponinc_", 
00104                 &add793exponinc_, tracer)) 
00105         return TCL_OK;
00106         if (delay_bind(varName, localName, "rto_", 
00107                &rto_, tracer)) 
00108         return TCL_OK;
00109         return TcpAgent::delay_bind_dispatch(varName, localName, tracer);
00110 }
00111 
00112 void RFC793eduTcpAgent::reset()
00113 {
00114   //Reset here protected vars. 
00115 
00116   rto_ = rtxcur_init_ / tcp_tick_;
00117   TcpAgent::reset();
00118 }
00119 
00120 
00121 void RFC793eduTcpAgent::rtt_update(double tao)
00122 {
00123   double now = Scheduler::instance().clock();
00124   if (ts_option_)
00125     t_rtt_ = int(tao /tcp_tick_ + 0.5);
00126   else {
00127     double sendtime = now - tao;
00128     sendtime += boot_time_;
00129     double tickoff = fmod(sendtime, tcp_tick_);
00130     t_rtt_ = int((tao + tickoff) / tcp_tick_);
00131   }
00132   if (t_rtt_ < 1)
00133     t_rtt_ = 1;
00134   
00135       // Jacobson/Karels RTT estimation as implemented in tcp.cc
00136       //
00137       // Diference    = SampleRTT - EstimatedRTT
00138       // EstimatedRTT = EstimatedRTT + (delta * Difference)
00139       // Deviation    = Deviation + delta * (|Difference| - Deviation)
00140       // TimeOut      = EstimatedRTT + 4 * Deviation
00141 
00142       // srtt has 3 bits to the right of the binary point
00143       // rttvar has 2
00144       if (t_srtt_ != 0) {
00145     register short delta;
00146     delta = t_rtt_ - (t_srtt_ >> T_SRTT_BITS);  // d = (m - a0)
00147     if ((t_srtt_ += delta) <= 0)    // a1 = 7/8 a0 + 1/8 m
00148       t_srtt_ = 1;
00149     if (delta < 0)
00150       delta = -delta;
00151     delta -= (t_rttvar_ >> T_RTTVAR_BITS);
00152     if ((t_rttvar_ += delta) <= 0)  // var1 = 3/4 var0 + 1/4 |d|
00153       t_rttvar_ = 1;
00154       } else {
00155     t_srtt_ = t_rtt_ << T_SRTT_BITS;        // srtt = rtt
00156     t_rttvar_ = t_rtt_ << (T_RTTVAR_BITS-1);    // rttvar = rtt / 2
00157       }
00158 
00159    if (add793jacobsonrtt_) 
00160     {
00161       //
00162       // Current retransmit value is 
00163       //    (unscaled) smoothed round trip estimate
00164       //    plus 2^rttvar_exp_ times (unscaled) rttvar. 
00165       //
00166       rto_ = (((t_rttvar_ << (rttvar_exp_ + (T_SRTT_BITS - T_RTTVAR_BITS)))                   + t_srtt_)  >> T_SRTT_BITS );
00167     }
00168    else { 
00169      // RFC 793
00170      rto_ = (t_srtt_ >> ( T_SRTT_BITS - 1) );
00171    }
00172       t_rtxcur_ = rto_  * tcp_tick_;
00173   return;
00174 }
00175 
00176 
00177 void RFC793eduTcpAgent::rtt_backoff()
00178 {
00179   if (add793expbackoff_) {
00180     // Standard Tahoe Code
00181 
00182     if (t_backoff_ < 64)
00183         t_backoff_ <<= 1;
00184 
00185     if (t_backoff_ > 8) {
00186         /*
00187          * If backed off this far, clobber the srtt
00188          * value, storing it in the mean deviation
00189          * instead.
00190          */
00191         t_rttvar_ += (t_srtt_ >> T_SRTT_BITS);
00192         t_srtt_ = 0;
00193     }
00194   }
00195   // safe paranoia
00196   else 
00197     t_backoff_ = 1;
00198 }
00199 
00200 
00201 void RFC793eduTcpAgent::recv(Packet *pkt, Handler*)
00202 {
00203     hdr_tcp *tcph = hdr_tcp::access(pkt);
00204 #ifdef notdef
00205     if (pkt->type_ != PT_ACK) {
00206         Tcl::instance().evalf("%s error \"received non-ack\"",
00207                       name());
00208         Packet::free(pkt);
00209         return;
00210     }
00211 #endif
00212     ++nackpack_;
00213     ts_peer_ = tcph->ts();
00214     int ecnecho = hdr_flags::access(pkt)->ecnecho();
00215     if (ecnecho && ecn_)
00216         ecn(tcph->seqno());
00217     recv_helper(pkt);
00218     /* grow cwnd and check if the connection is done */ 
00219     if (tcph->seqno() > last_ack_) {
00220         recv_newack_helper(pkt);
00221         if (last_ack_ == 0 && delay_growth_) { 
00222             cwnd_ = initial_window();
00223         }
00224     } else if (tcph->seqno() == last_ack_) {
00225                 if (hdr_flags::access(pkt)->eln_ && eln_) {
00226                         tcp_eln(pkt);
00227                         return;
00228                 }
00229         // fast retransmission
00230         if ( (++dupacks_ == numdupacks_) && add793fastrtx_ ) {
00231             dupack_action();
00232         }
00233     }
00234     Packet::free(pkt);
00235     /*
00236      * Try to send more data.
00237      */
00238     send_much(0, 0, maxburst_);
00239 }
00240 
00241 
00242 
00243 void RFC793eduTcpAgent::opencwnd()
00244 {
00245         if ( !(add793slowstart_ || add793exponinc_ || add793additiveinc_)) {
00246       cwnd_ = wnd_;
00247         } else if (((cwnd_ < ssthresh_) && (!add793additiveinc_)) 
00248            || add793exponinc_)
00249       {
00250         /* slow-start (exponential) */
00251             if (add793exponinc_ && (cwnd_ < ssthresh_))
00252           cwnd_ = ssthresh_;
00253         else cwnd_ += 1;
00254     } else {
00255         /* linear */
00256         double f;
00257 
00258         if (add793additiveinc_ && (cwnd_ < ssthresh_))
00259           cwnd_ = ssthresh_;
00260         else {
00261           switch (wnd_option_) {
00262           case 0:
00263             if (++count_ >= cwnd_) {
00264               count_ = 0;
00265               ++cwnd_;
00266             }
00267             break;
00268             
00269           case 1:
00270             /* This is the standard algorithm. */
00271             cwnd_ += 1 / cwnd_;
00272             break;
00273             
00274           case 2:
00275             /* These are window increase algorithms
00276              * for experimental purposes only. */
00277             f = (t_srtt_ >> T_SRTT_BITS) * tcp_tick_;
00278             f *= f;
00279             f *= wnd_const_;
00280             f += fcnt_;
00281             if (f > cwnd_) {
00282               fcnt_ = 0;
00283               ++cwnd_;
00284             } else
00285               fcnt_ = f;
00286             break;
00287             
00288           case 3:
00289             f = awnd_;
00290             f *= f;
00291             f *= wnd_const_;
00292             f += fcnt_;
00293             if (f > cwnd_) {
00294               fcnt_ = 0;
00295               ++cwnd_;
00296             } else
00297               fcnt_ = f;
00298             break;
00299             
00300           case 4:
00301             f = awnd_;
00302             f *= wnd_const_;
00303             f += fcnt_;
00304             if (f > cwnd_) {
00305               fcnt_ = 0;
00306               ++cwnd_;
00307             } else
00308                                 fcnt_ = f;
00309             break;
00310           case 5:
00311             f = (t_srtt_ >> T_SRTT_BITS) * tcp_tick_;
00312             f *= wnd_const_;
00313             f += fcnt_;
00314             if (f > cwnd_) {
00315               fcnt_ = 0;
00316               ++cwnd_;
00317             } else
00318               fcnt_ = f;
00319             break;
00320             
00321           default:
00322 #ifdef notdef
00323             /*XXX*/
00324             error("illegal window option %d", wnd_option_);
00325 #endif
00326             abort();
00327           }
00328         }
00329     }
00330     // if maxcwnd_ is set (nonzero), make it the cwnd limit
00331     if (maxcwnd_ && (int(cwnd_) > maxcwnd_))
00332       cwnd_ = maxcwnd_;
00333     
00334     return;
00335 }
00336 
00337 
00338 void  RFC793eduTcpAgent::output(int seqno, int reason)
00339 {
00340     int force_set_rtx_timer = 0;
00341     Packet* p = allocpkt();
00342     hdr_tcp *tcph = hdr_tcp::access(p);
00343     hdr_flags* hf = hdr_flags::access(p);
00344     tcph->seqno() = seqno;
00345     tcph->ts() = Scheduler::instance().clock();
00346     tcph->ts_echo() = ts_peer_;
00347     tcph->reason() = reason;
00348     if (ecn_) {
00349         hf->ect() = 1;  // ECN-capable transport
00350     }
00351     if (cong_action_) {
00352         hf->cong_action() = TRUE;  // Congestion action.
00353         cong_action_ = FALSE;
00354         }
00355     /* Check if this is the initial SYN packet. */
00356     if (seqno == 0) {
00357         if (syn_) {
00358             hdr_cmn::access(p)->size() = tcpip_base_hdr_size_;
00359         }
00360         if (ecn_) {
00361             hf->ecnecho() = 1;
00362 //          hf->cong_action() = 1;
00363             hf->ect() = 0;
00364         }
00365     }
00366         int bytes = hdr_cmn::access(p)->size();
00367 
00368     /* if no outstanding data, be sure to set rtx timer again */
00369     if (highest_ack_ == maxseq_)
00370         force_set_rtx_timer = 1;
00371     /* call helper function to fill in additional fields */
00372     output_helper(p);
00373 
00374         ++ndatapack_;
00375         ndatabytes_ += bytes;
00376     send(p, 0);
00377     if (seqno == curseq_ && seqno > maxseq_)
00378         idle();  // Tell application I have sent everything so far
00379     if (seqno > maxseq_) {
00380         maxseq_ = seqno;
00381         if (!rtt_active_) {
00382             rtt_active_ = 1;            
00383             if (seqno > rtt_seq_) {
00384                 rtt_seq_ = seqno;           
00385                 rtt_ts_ = Scheduler::instance().clock();
00386             }
00387                     
00388         }
00389     } else {
00390       if (!add793karnrtt_) {
00391             rtt_active_ = 1;
00392         rtt_seq_ = seqno;           
00393         rtt_ts_ = Scheduler::instance().clock();
00394       } 
00395             ++nrexmitpack_;
00396             nrexmitbytes_ += bytes;
00397     }
00398     if (!(rtx_timer_.status() == TIMER_PENDING) || force_set_rtx_timer)
00399         /* No timer pending.  Schedule one. */
00400         set_rtx_timer();
00401 }
00402 
00403 void RFC793eduTcpAgent::recv_newack_helper(Packet *pkt) {
00404     //hdr_tcp *tcph = hdr_tcp::access(pkt);
00405     newack(pkt);
00406     if (!ect_ || !hdr_flags::access(pkt)->ecnecho() ||
00407         (old_ecn_ && ecn_burst_)) 
00408         /* If "old_ecn", this is not the first ACK carrying ECN-Echo
00409          * after a period of ACKs without ECN-Echo.
00410          * Therefore, open the congestion window. */
00411             opencwnd();
00412     if (ect_) {
00413         if (!hdr_flags::access(pkt)->ecnecho())
00414             ecn_backoff_ = 0;
00415         if (!ecn_burst_ && hdr_flags::access(pkt)->ecnecho())
00416             ecn_burst_ = TRUE;
00417         else if (ecn_burst_ && ! hdr_flags::access(pkt)->ecnecho())
00418             ecn_burst_ = FALSE;
00419     }
00420     if (!ect_ && hdr_flags::access(pkt)->ecnecho() &&
00421         !hdr_flags::access(pkt)->cong_action())
00422         ect_ = 1;
00423     /* if the connection is done, call finish() */
00424     if ((highest_ack_ >= curseq_-1) && !closed_) {
00425         closed_ = 1;
00426         finish();
00427     }
00428 }
00429 
00430 void RFC793eduTcpAgent::newack(Packet* pkt)
00431 {
00432     double now = Scheduler::instance().clock();
00433     hdr_tcp *tcph = hdr_tcp::access(pkt);
00434     /* 
00435      * Wouldn't it be better to set the timer *after*
00436      * updating the RTT, instead of *before*? 
00437      */
00438     newtimer(pkt);
00439     dupacks_ = 0;
00440     last_ack_ = tcph->seqno();
00441     highest_ack_ = last_ack_;
00442 
00443     if (t_seqno_ < last_ack_ + 1)
00444         t_seqno_ = last_ack_ + 1;
00445     /* 
00446      * Update RTT only if it's OK to do so from info in the flags header.
00447      * This is needed for protocols in which intermediate agents
00448      * in the network intersperse acks (e.g., ack-reconstructors) for
00449      * various reasons (without violating e2e semantics).
00450      */ 
00451     hdr_flags *fh = hdr_flags::access(pkt);
00452     if (!fh->no_ts_) {
00453         if (ts_option_)
00454             rtt_update(now - tcph->ts_echo());
00455 
00456         if (rtt_active_ && tcph->seqno() >= rtt_seq_) {
00457             if (!ect_ || !ecn_backoff_ || 
00458                 !hdr_flags::access(pkt)->ecnecho()) {
00459                 /* 
00460                  * Don't end backoff if still in ECN-Echo with
00461                  * a congestion window of 1 packet. 
00462                  */
00463                 t_backoff_ = 1;
00464                 ecn_backoff_ = 0;
00465             }
00466             rtt_active_ = 0;
00467             if (!ts_option_)
00468                 rtt_update(now - rtt_ts_);
00469         }
00470     }
00471     /* update average window */
00472     awnd_ *= 1.0 - wnd_th_;
00473     awnd_ += wnd_th_ * cwnd_;
00474 }
00475 
00476     

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