tcp-qs.cc

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

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