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

tcp-qs.cc

Go to the documentation of this file.
00001 /*
00002  * Copyright (c) 2001 University of Southern California.
00003  * All rights reserved.                                            
00004  *                                                                
00005  * Redistribution and use in source and binary forms are permitted
00006  * provided that the above copyright notice and this paragraph are
00007  * duplicated in all such forms and that any documentation, advertising
00008  * materials, and other materials related to such distribution and use
00009  * acknowledge that the software was developed by the University of
00010  * Southern California, Information Sciences Institute.  The name of the
00011  * University may not be used to endorse or promote products derived from
00012  * this software without specific prior written permission.
00013  * 
00014  * THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
00015  * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
00016  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
00017  *
00018  **
00019  * Quick Start for TCP and IP.
00020  * A scheme for transport protocols to dynamically determine initial 
00021  * congestion window size.
00022  *
00023  * http://www.ietf.org/internet-drafts/draft-amit-quick-start-02.ps
00024  *
00025  * This implements the TCP Quick Start Source and Sink Agents.
00026  * TCP Quick Start Source Agent is based on the Rate Based 
00027  * implementation of TCP. "Agent/TCP/Newreno/QS", "Agent/TCPSink/QS"
00028  *
00029  * tcp-qs.cc
00030  *
00031  * Srikanth Sundarrajan, 2002
00032  * sundarra@usc.edu
00033  */
00034 
00035 #include <stdio.h>
00036 #include <stdlib.h>
00037 #include <sys/types.h>
00038 
00039 #include "ip.h"
00040 #include "tcp.h"
00041 #include "flags.h"
00042 #include "hdr_qs.h"
00043 #include "random.h"
00044 #include "tcp-sink.h"
00045 
00046 #ifndef MIN
00047 #define MIN(x, y) ((x)<(y) ? (x) : (y))
00048 #endif /* ! MIN */
00049 
00050 #if 0
00051 #define RBP_DEBUG_PRINTF(x) printf x
00052 #else /* ! 0 */
00053 #define RBP_DEBUG_PRINTF(x)
00054 #endif /* 0 */
00055 
00056 
00057 #define RBP_MIN_SEGMENTS 2
00058 
00059 /***********************************************************************
00060  *
00061  * The New reno-based version
00062  *
00063  */
00064 
00065 class QSNewRenoTcpAgent;
00066 
00067 class QSNewRenoPaceTimer : public TimerHandler {
00068 public:
00069         QSNewRenoPaceTimer(QSNewRenoTcpAgent *a) : TimerHandler() { a_ = a; }
00070 protected:
00071         virtual void expire(Event *e);
00072         QSNewRenoTcpAgent *a_;
00073 };
00074 // Hmmm... ``a is a'' in the construction of the QSNewRenoPaceTimer edifice :->
00075 
00076 class QSNewRenoTcpAgent : public virtual NewRenoTcpAgent {
00077         friend class QSNewRenoPaceTimer;
00078  public:
00079         QSNewRenoTcpAgent();
00080         virtual void recv(Packet *pkt, Handler *);
00081         virtual void timeout(int tno);
00082         virtual void send_much(int force, int reason, int maxburst);
00083         virtual void output(int force, int reason);
00084 
00085         double rbp_scale_;   // conversion from actual -> rbp send rates
00086         // enum rbp_rate_algorithms { RBP_NO_ALGORITHM, RBP_VEGAS_RATE_ALGORITHM, RBP_CWND_ALGORITHM };
00087         // int rbp_rate_algorithm_;
00088 protected:
00089         void paced_send_one();
00090         int able_to_rbp_send_one();
00091 
00092         // stats on what we did
00093         int rbp_segs_actually_paced_;
00094 
00095         int ttl_diff_;
00096         int qs_approved_;
00097         int rate_request_;
00098 
00099         int session_id_;
00100 
00101         static int next_flow_;
00102 
00103         enum rbp_modes { RBP_GOING, RBP_POSSIBLE, RBP_OFF };
00104         enum rbp_modes rbp_mode_;
00105         double rbp_inter_pace_delay_;
00106         QSNewRenoPaceTimer pace_timer_;
00107 };
00108 
00109 int QSNewRenoTcpAgent::next_flow_ = 0;
00110 
00111 static class QSNewRenoTcpClass : public TclClass {
00112 public:
00113         QSNewRenoTcpClass() : TclClass("Agent/TCP/Newreno/QS") {}
00114         TclObject* create(int, const char*const*) {
00115                 return (new QSNewRenoTcpAgent());
00116         }
00117 } class_newreno_qs;
00118 
00119 
00120 void QSNewRenoPaceTimer::expire(Event *) { a_->paced_send_one(); }
00121 
00122 QSNewRenoTcpAgent::QSNewRenoTcpAgent() : TcpAgent(),
00123         ttl_diff_(0), qs_approved_(0), rbp_mode_(RBP_OFF), pace_timer_(this)
00124 {
00125         bind("rbp_scale_", &rbp_scale_);
00126         // algorithm is not used in New Reno
00127         // bind("rbp_rate_algorithm_", &rbp_rate_algorithm_);
00128         bind("rbp_segs_actually_paced_", &rbp_segs_actually_paced_);
00129         bind("rbp_inter_pace_delay_", &rbp_inter_pace_delay_);
00130         bind("rate_request_", &rate_request_);
00131 
00132         session_id_ = next_flow_ % 32;
00133         next_flow_ = session_id_ + 1;
00134 }
00135 
00136 void
00137 QSNewRenoTcpAgent::recv(Packet *pkt, Handler *hand)
00138 {
00139         double now = Scheduler::instance().clock();
00140         int app_rate;
00141 
00142         hdr_tcp *tcph = hdr_tcp::access(pkt);
00143         if (rbp_mode_ != RBP_OFF) {
00144                 // reciept of anything disables rbp
00145                 rbp_mode_ = RBP_OFF;
00146 
00147                 // reset cwnd such that we're now ack clocked.
00148                 if (tcph->seqno() > last_ack_) {
00149                         cwnd_ = maxseq_ - last_ack_; //this is what we need for QS.
00150                         RBP_DEBUG_PRINTF(("\ncwnd-after-first-ack=%g\n", (double)cwnd_));
00151                 };
00152 
00153         };
00154         if (acked_ == 0) {
00155                 hdr_qs *qsh = hdr_qs::access(pkt);
00156 
00157                 if (qsh->flag() == QS_RESPONSE && qsh->ttl() == ttl_diff_ && qsh->rate() > 0) {
00158                         printf("Quick Start approved\t");
00159                         app_rate = (int) (qsh->rate() * (now - tcph->ts_echo()));
00160                         if (app_rate > initial_window()) {
00161                                 rbp_mode_ = RBP_POSSIBLE;
00162                                 wnd_init_option_ = 1;
00163                                 wnd_init_ = app_rate;
00164                                 printf("%d: rate= %d, rtt = %f\n", addr(), app_rate, (now - tcph->ts_echo()));
00165                                 qs_approved_ = 1;
00166                         }
00167                         else {
00168                                 printf("%d: quick start approved, but rate too low %d, fall-back to slow start\n", addr(), app_rate);
00169                                 rbp_mode_ = RBP_OFF;
00170                                 qsh->flag() = QS_DISABLE;
00171                                 qs_approved_ = 0;
00172                         }
00173                 } else { // Quick Start rejected
00174                         printf("Quick Start rejected\n");
00175                         rbp_mode_ = RBP_OFF;
00176                         qsh->flag() = QS_DISABLE;
00177                         qs_approved_ = 0;
00178                 }
00179         } else if (acked_ == 1 && qs_approved_ == 1) {
00180                 //don't have to do anything here, RBP is doing exactly what we need
00181         }
00182         NewRenoTcpAgent::recv(pkt, hand);
00183 }
00184 
00185 void
00186 QSNewRenoTcpAgent::timeout(int tno)
00187 {
00188         if (tno == TCP_TIMER_RTX) {
00189                 if (highest_ack_ == maxseq_) {
00190                         // Idle for a while => RBP next time.
00191                         //rbp_mode_ = RBP_POSSIBLE;
00192                         rbp_mode_ = RBP_OFF; //this is not an RBP implementation
00193                         return;
00194                 }
00195                 else {
00196                         rbp_mode_ = RBP_OFF; //turn off RBP
00197                         cwnd_ = initial_window();
00198                 }
00199         }
00200         NewRenoTcpAgent::timeout(tno);
00201 }
00202 
00203 void
00204 QSNewRenoTcpAgent::send_much(int force, int reason, int maxburst)
00205 {
00206         if (rbp_mode_ == RBP_POSSIBLE && able_to_rbp_send_one()) {
00207                 // start paced mode
00208                 rbp_mode_ = RBP_GOING; 
00209                 rbp_segs_actually_paced_ = 0;
00210 
00211                 // Pace out scaled cwnd.
00212                 double rbwin_reno;
00213                 rbwin_reno = cwnd_ * rbp_scale_;
00214 
00215                 rbwin_reno = int(rbwin_reno + 0.5);   // round
00216                 // Always pace at least RBP_MIN_SEGMENTS
00217                 if (rbwin_reno <= RBP_MIN_SEGMENTS) {
00218                         rbwin_reno = RBP_MIN_SEGMENTS;
00219                 };
00220 
00221                 // Conservatively set the congestion window to min of
00222                 // congestion window and the smoothed rbwin_reno
00223                 RBP_DEBUG_PRINTF(("cwnd before check = %g\n", double(cwnd_)));
00224                 cwnd_ = MIN(cwnd_,(TracedDouble) rbwin_reno);
00225                 RBP_DEBUG_PRINTF(("cwnd after check = %g\n", double(cwnd_)));
00226                 RBP_DEBUG_PRINTF(("recv win = %g\n", wnd_));
00227                 // RBP timer calculations must be based on the actual
00228                 // window which is the min of the receiver's
00229                 // advertised window and the congestion window.
00230                 // TcpAgent::window() does this job.
00231                 // What this means is we expect to send window() pkts
00232                 // in v_srtt_ time.
00233                 static double srtt_scale = 0.0;
00234                 if (srtt_scale == 0.0) {  // yuck yuck yuck!
00235                         srtt_scale = 1.0; // why are we doing fixed point?
00236                         int i;
00237                         for (i = T_SRTT_BITS; i > 0; i--) {
00238                                 srtt_scale /= 2.0;
00239                         };
00240                 }
00241                 rbp_inter_pace_delay_ = (t_srtt_ * srtt_scale * tcp_tick_) / (window() * 1.0);
00242                 RBP_DEBUG_PRINTF(("window is %d\n", window()));
00243                 RBP_DEBUG_PRINTF(("ipt = %g\n", rbp_inter_pace_delay_));
00244                 paced_send_one();
00245         } else {
00246                 NewRenoTcpAgent::send_much(force,reason, maxburst);
00247         };
00248 }
00249 
00250 void
00251 QSNewRenoTcpAgent::paced_send_one()
00252 {
00253         if (rbp_mode_ == RBP_GOING && able_to_rbp_send_one()) {
00254                 RBP_DEBUG_PRINTF(("Sending one rbp packet\n"));
00255                 // send one packet
00256                 output(t_seqno_++, TCP_REASON_RBP);
00257                 rbp_segs_actually_paced_++;
00258                 // schedule next pkt
00259                 pace_timer_.resched(rbp_inter_pace_delay_);
00260         };
00261 }
00262 
00263 int
00264 QSNewRenoTcpAgent::able_to_rbp_send_one()
00265 {
00266         return t_seqno_ < curseq_ && t_seqno_ <= highest_ack_ + window();
00267 }
00268 
00269 void QSNewRenoTcpAgent::output(int seqno, int reason)
00270 {
00271         int force_set_rtx_timer = 0;
00272         Packet* p = allocpkt();
00273         hdr_tcp *tcph = hdr_tcp::access(p);
00274         hdr_ip *iph = hdr_ip::access(p);
00275         hdr_qs *qsh = hdr_qs::access(p);
00276         hdr_flags* hf = hdr_flags::access(p);
00277         int databytes = hdr_cmn::access(p)->size();
00278         tcph->seqno() = seqno;
00279         tcph->ts() = Scheduler::instance().clock();
00280         tcph->ts_echo() = ts_peer_;
00281         tcph->reason() = reason;
00282         tcph->last_rtt() = int(int(t_rtt_)*tcp_tick_*1000);
00283         //iph->flowid() = session_id_;
00284 
00285         if (seqno == 0) {
00286                 qsh->flag() = QS_REQUEST;
00287                 Random::seed_heuristically();
00288                 qsh->ttl() = Random::integer(256);
00289                 ttl_diff_ = (iph->ttl() - qsh->ttl()) % 256;
00290                 qsh->rate() = rate_request_;
00291         }
00292         else {
00293                 qsh->flag() = QS_DISABLE;
00294         }
00295 
00296         if (ecn_) {
00297                 hf->ect() = 1;  // ECN-capable transport
00298         }
00299         if (cong_action_) {
00300                 hf->cong_action() = TRUE;  // Congestion action.
00301                 cong_action_ = FALSE;
00302                 }
00303         /* Check if this is the initial SYN packet. */
00304         if (seqno == 0) {
00305                 if (syn_) {
00306                         databytes = 0;
00307                         curseq_ += 1;
00308                         hdr_cmn::access(p)->size() = tcpip_base_hdr_size_;
00309                         //printf("inside initial syn packet\n");
00310                 }
00311                 if (ecn_) {
00312                         hf->ecnecho() = 1;
00313 //                      hf->cong_action() = 1;
00314                         hf->ect() = 0;
00315                 }
00316         }
00317         else if (useHeaders_ == true) {
00318                 hdr_cmn::access(p)->size() += headersize();
00319         }
00320                 hdr_cmn::access(p)->size();
00321 
00322         /* if no outstanding data, be sure to set rtx timer again */
00323         if (highest_ack_ == maxseq_)
00324                 force_set_rtx_timer = 1;
00325         /* call helper function to fill in additional fields */
00326         output_helper(p);
00327 
00328                 ++ndatapack_;
00329                 ndatabytes_ += databytes;
00330         send(p, 0);
00331         //printf("wnd_ %f, cwnd_ %f, ssthresh_ %f\n", wnd_+0, cwnd_+0, ssthresh_+0);
00332         if (seqno == curseq_ && seqno > maxseq_)
00333                 idle();  // Tell application I have sent everything so far
00334         if (seqno > maxseq_) {
00335                 maxseq_ = seqno;
00336                 if (!rtt_active_) {
00337                         rtt_active_ = 1;
00338                         if (seqno > rtt_seq_) {
00339                                 rtt_seq_ = seqno;
00340                                 rtt_ts_ = Scheduler::instance().clock();
00341                         }
00342                                         
00343                 }
00344         } else {
00345                         ++nrexmitpack_;
00346                 nrexmitbytes_ += databytes;
00347         }
00348         if (!(rtx_timer_.status() == TIMER_PENDING) || force_set_rtx_timer)
00349                 /* No timer pending.  Schedule one. */
00350                 set_rtx_timer();
00351 }
00352 
00353 class QSTcpSink;
00354 
00355 class QSTcpSink : public TcpSink {
00356 public:
00357         QSTcpSink(Acker *);
00358         virtual void ack(Packet * oPacket);
00359         void recv(Packet* pkt, Handler*);
00360 };
00361 
00362 static class QSTcpSinkClass : public TclClass {
00363 public:
00364         QSTcpSinkClass() : TclClass("Agent/TCPSink/QS") {}
00365         TclObject* create(int, const char*const*) {
00366                 return (new QSTcpSink(new Acker));
00367         }
00368 } class_sink_qs;
00369 
00370 QSTcpSink::QSTcpSink(Acker * acker) : TcpSink(acker) {
00371 }
00372 
00373 void QSTcpSink::recv(Packet* pkt, Handler*)
00374 {
00375         int numToDeliver;
00376         int numBytes = hdr_cmn::access(pkt)->size();
00377         // number of bytes in the packet just received
00378         hdr_tcp *th = hdr_tcp::access(pkt);
00379         /* W.N. Check if packet is from previous incarnation */
00380         if (th->ts() < lastreset_) {
00381                 // Remove packet and do nothing
00382                 Packet::free(pkt);
00383                 return;
00384         }
00385         acker_->update_ts(th->seqno(),th->ts());
00386         // update the timestamp to echo
00387         
00388         numToDeliver = acker_->update(th->seqno(), numBytes);
00389         // update the recv window; figure out how many in-order-bytes
00390         // (if any) can be removed from the window and handed to the
00391         // application
00392         if (numToDeliver)
00393                 recvBytes(numToDeliver);
00394         // send any packets to the application
00395                   ack(pkt);
00396         // ACK the packet
00397         Packet::free(pkt);
00398         // remove it from the system
00399 }
00400 
00401 void QSTcpSink::ack(Packet* opkt)
00402 {
00403         Packet* npkt = allocpkt();
00404         // opkt is the "old" packet that was received
00405         // npkt is the "new" packet being constructed (for the ACK)
00406         double now = Scheduler::instance().clock();
00407         hdr_flags *sf;
00408 
00409         hdr_tcp *otcp = hdr_tcp::access(opkt);
00410         hdr_tcp *ntcp = hdr_tcp::access(npkt);
00411 
00412         hdr_ip *oiph = hdr_ip::access(opkt);
00413 
00414         hdr_qs *oqsh = hdr_qs::access(opkt);
00415         hdr_qs *nqsh = hdr_qs::access(npkt);
00416 
00417         if (otcp->seqno() == 0 && oqsh->flag() == QS_REQUEST) {
00418                 nqsh->flag() = QS_RESPONSE;
00419                 nqsh->ttl() = (oiph->ttl() - oqsh->ttl()) % 256;
00420                 nqsh->rate() = (oqsh->rate() < MWS) ? oqsh->rate() : MWS;
00421         }
00422         else {
00423                 nqsh->flag() = QS_DISABLE;
00424         }
00425         
00426         // get the tcp headers
00427         ntcp->seqno() = acker_->Seqno();
00428         // get the cumulative sequence number to put in the ACK; this
00429         // is just the left edge of the receive window - 1
00430         ntcp->ts() = now;
00431         // timestamp the packet
00432 
00433         if (ts_echo_bugfix_)  /* TCP/IP Illustrated, Vol. 2, pg. 870 */
00434                 ntcp->ts_echo() = acker_->ts_to_echo();
00435         else
00436                 ntcp->ts_echo() = otcp->ts();
00437         // echo the original's time stamp
00438 
00439         // hdr_ip* oip = hdr_ip::access(opkt);
00440         // hdr_ip* nip = hdr_ip::access(npkt);
00441         // get the ip headers
00442         //nip->flowid() = oip->flowid();
00443         // copy the flow id
00444         
00445         hdr_flags* of = hdr_flags::access(opkt);
00446         hdr_flags* nf = hdr_flags::access(npkt);
00447         if (save_ != NULL) 
00448                 sf = hdr_flags::access(save_);
00449                 // Look at delayed packet being acked. 
00450         if ( (save_ != NULL && sf->cong_action()) || of->cong_action() ) 
00451                 // Sender has responsed to congestion. 
00452                 acker_->update_ecn_unacked(0);
00453         if ( (save_ != NULL && sf->ect() && sf->ce())  || 
00454                         (of->ect() && of->ce()) )
00455                 // New report of congestion.  
00456                 acker_->update_ecn_unacked(1);
00457         if ( (save_ != NULL && sf->ect()) || of->ect() )
00458                 // Set EcnEcho bit.  
00459                 nf->ecnecho() = acker_->ecn_unacked();
00460         if (!of->ect() && of->ecnecho() ||
00461                 (save_ != NULL && !sf->ect() && sf->ecnecho()) ) 
00462                  // This is the negotiation for ECN-capability.
00463                  // We are not checking for of->cong_action() also. 
00464                  // In this respect, this does not conform to the 
00465                  // specifications in the internet draft 
00466                 nf->ecnecho() = 1;
00467         acker_->append_ack(hdr_cmn::access(npkt),
00468                            ntcp, otcp->seqno());
00469         add_to_ack(npkt);
00470         // the above function is used in TcpAsymSink
00471         
00472         send(npkt, 0);
00473         // send it
00474 }
00475 

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