00001 /* -*- Mode:C++; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */ 00002 /* 00003 * Copyright (c) 1997 Regents of the University of California. 00004 * All rights reserved. 00005 * 00006 * Redistribution and use in source and binary forms, with or without 00007 * modification, are permitted provided that the following conditions 00008 * are met: 00009 * 1. Redistributions of source code must retain the above copyright 00010 * notice, this list of conditions and the following disclaimer. 00011 * 2. Redistributions in binary form must reproduce the above copyright 00012 * notice, this list of conditions and the following disclaimer in the 00013 * documentation and/or other materials provided with the distribution. 00014 * 3. All advertising materials mentioning features or use of this software 00015 * must display the following acknowledgement: 00016 * This product includes software developed by the Daedalus Research 00017 * Group at the University of California Berkeley. 00018 * 4. Neither the name of the University nor of the Laboratory 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 THE REGENTS 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 THE REGENTS 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 * Contributed by the Daedalus Research Group, U.C.Berkeley 00035 * http://daedalus.cs.berkeley.edu 00036 * 00037 * @(#) $Header: 00038 */ 00039 00040 /* 00041 * tcp-asym includes modifications to several flavors of TCP to enhance 00042 * performance over asymmetric networks, where the ack channel is 00043 * constrained. Types of asymmetry we have studied and used these mods 00044 * include bandwidth asymmetry and latency asymmetry (where variable 00045 * latencies cause problems to TCP, e.g., in packet radio networks. 00046 * The receiver-side code in this file is derived from the regular 00047 * TCP sink code. The main additional functionality is that the sink responds 00048 * to ECN by performing ack congestion control, i.e. it multiplicatively backs 00049 * off the frequency with which it sends acks (up to a limit). For each 00050 * subsequent round-trip period during which it does not receive an ECN, 00051 * it gradually increases the frequency of acks (up to a maximum of 1 00052 * per data packet). 00053 * 00054 * For questions/comments, please contact: 00055 * Venkata N. Padmanabhan (padmanab@cs.berkeley.edu) 00056 * http://www.cs.berkeley.edu/~padmanab 00057 */ 00058 00059 #ifndef lint 00060 static const char rcsid[] = 00061 "@(#) $Header: /nfs/jade/vint/CVSROOT/ns-2/tcp/tcp-asym-sink.cc,v 1.16 1999/09/09 03:22:49 salehi Exp $ (UCB)"; 00062 #endif 00063 00064 #include "template.h" 00065 #include "flags.h" 00066 #include "tcp-sink.h" 00067 #include "tcp-asym.h" 00068 00069 00070 class TcpAsymSink : public DelAckSink { 00071 public: 00072 TcpAsymSink(Acker*); 00073 virtual void recv(Packet* pkt, Handler* h); 00074 virtual void timeout(int tno); 00075 protected: 00076 virtual void add_to_ack(Packet* pkt); 00077 int delackcount_; /* the number of consecutive packets that have 00078 not been acked yet */ 00079 int maxdelack_; /* the maximum extent to which acks can be 00080 delayed */ 00081 int delackfactor_; /* the dynamically varying limit on the extent 00082 to which acks can be delayed */ 00083 int delacklim_; /* limit on the extent of del ack based on the 00084 sender's window */ 00085 double ts_ecn_; /* the time when an ECN was received last */ 00086 double ts_decrease_; /* the time when delackfactor_ was decreased last */ 00087 double highest_ts_echo_;/* the highest timestamp echoed by the peer */ 00088 }; 00089 00090 static class TcpAsymSinkClass : public TclClass { 00091 public: 00092 TcpAsymSinkClass() : TclClass("Agent/TCPSink/Asym") {} 00093 TclObject* create(int, const char*const*) { 00094 return (new TcpAsymSink(new Acker)); 00095 } 00096 } class_tcpasymsink; 00097 00098 TcpAsymSink::TcpAsymSink(Acker* acker) : DelAckSink(acker), delackcount_(0), 00099 delackfactor_(1), delacklim_(0), ts_ecn_(0), ts_decrease_(0) 00100 { 00101 bind("maxdelack_", &maxdelack_); 00102 } 00103 00104 /* Add fields to the ack. Not needed? */ 00105 void TcpAsymSink::add_to_ack(Packet* pkt) 00106 { 00107 hdr_tcpasym *tha = hdr_tcpasym::access(pkt); 00108 tha->ackcount() = delackcount_; 00109 } 00110 00111 void TcpAsymSink::recv(Packet* pkt, Handler*) 00112 { 00113 int olddelackfactor = delackfactor_; 00114 int olddelacklim = delacklim_; 00115 int max_sender_can_send = 0; 00116 hdr_flags *fh = hdr_flags::access(pkt); 00117 hdr_tcp *th = hdr_tcp::access(pkt); 00118 hdr_tcpasym *tha = hdr_tcpasym::access(pkt); 00119 double now = Scheduler::instance().clock(); 00120 int numBytes = hdr_cmn::access(pkt)->size(); 00121 00122 00123 acker_->update_ts(th->seqno(),th->ts()); 00124 acker_->update(th->seqno(), numBytes); 00125 00126 #if 0 // johnh 00127 int numToDeliver; 00128 /* XXX if the #if 0 is removed, delete the call to acker_->update() above */ 00129 numToDeliver = acker_->update(th->seqno(), numBytes); 00130 if (numToDeliver) 00131 recvBytes(numToDeliver); 00132 #endif /* 0 */ 00133 00134 /* determine the highest timestamp the sender has echoed */ 00135 highest_ts_echo_ = max(highest_ts_echo_, th->ts_echo()); 00136 /* 00137 * if we receive an ECN and haven't received one in the past 00138 * round-trip, double delackfactor_ (and consequently halve 00139 * the frequency of acks) subject to a maximum 00140 */ 00141 if (fh->ecnecho() && highest_ts_echo_ >= ts_ecn_) { 00142 delackfactor_ = min(2*delackfactor_, maxdelack_); 00143 ts_ecn_ = now; 00144 } 00145 /* 00146 * else if we haven't received an ECN in the past round trip and 00147 * haven't (linearly) decreased delackfactor_ in the past round 00148 * trip, we decrease delackfactor_ by 1 (and consequently increase 00149 * the frequency of acks) subject to a minimum 00150 */ 00151 else if (highest_ts_echo_ >= ts_ecn_ && highest_ts_echo_ >= ts_decrease_) { 00152 delackfactor_ = max(delackfactor_ - 1, 1); 00153 ts_decrease_ = now; 00154 } 00155 00156 /* 00157 * if this is the next packet in sequence, we can consider delaying the ack. 00158 * Set delacklim_ based on how much data the sender can send if we don't 00159 * send back any more acks. The idea is to avoid stalling the sender because 00160 * of a lack of acks. 00161 */ 00162 if (th->seqno() == acker_->Seqno()) { 00163 max_sender_can_send = (int) min(tha->win()+acker_->Seqno()-tha->highest_ack(), tha->max_left_to_send()); 00164 /* XXXX we use a safety factor 2 */ 00165 delacklim_ = min(maxdelack_, max_sender_can_send/2); 00166 } 00167 else 00168 delacklim_ = 0; 00169 00170 if (delackfactor_ < delacklim_) 00171 delacklim_ = delackfactor_; 00172 00173 /* 00174 * Log values of variables of interest. Since this is the only place 00175 * where this is done, we decided against using a more general method 00176 * as used for logging TCP sender state variables. 00177 */ 00178 if (channel_ && (olddelackfactor != delackfactor_ || olddelacklim != delacklim_)) { 00179 char wrk[500]; 00180 int n; 00181 00182 /* we print src and dst in reverse order to conform to sender side */ 00183 sprintf(wrk, "time: %-6.3f saddr: %-2d sport: %-2d daddr:" 00184 " %-2d dport: %-2d dafactor: %2d dalim: %2d max_scs:" 00185 " %4d win: %4d\n", now, addr(), port(), 00186 daddr(), dport(), delackfactor_, 00187 delacklim_,max_sender_can_send, tha->win()); 00188 n = strlen(wrk); 00189 wrk[n] = '\n'; 00190 wrk[n+1] = 0; 00191 (void)Tcl_Write(channel_, wrk, n+1); 00192 wrk[n] = 0; 00193 } 00194 00195 delackcount_++; 00196 /* check if we have waited long enough that we should send an ack */ 00197 if (delackcount_ < delacklim_) { /* it is not yet time to send an ack */ 00198 /* if the delayed ack timer is not set, set it now */ 00199 if (!(delay_timer_.status() == TIMER_PENDING)) { 00200 save_ = pkt; 00201 delay_timer_.resched(interval_); 00202 } 00203 else { 00204 hdr_tcp *sth = hdr_tcp::access(save_); 00205 /* save the pkt with the more recent timestamp */ 00206 if (th->ts() > sth->ts()) { 00207 Packet::free(save_); 00208 save_ = pkt; 00209 } 00210 } 00211 return; 00212 } 00213 else { /* send back an ack now */ 00214 if (delay_timer_.status() == TIMER_PENDING) { 00215 delay_timer_.cancel(); 00216 Packet::free(save_); 00217 save_ = 0; 00218 } 00219 hdr_flags* hf = hdr_flags::access(pkt); 00220 hf->ect() = 1; 00221 ack(pkt); 00222 delackcount_ = 0; 00223 Packet::free(pkt); 00224 } 00225 } 00226 00227 00228 void TcpAsymSink::timeout(int /*tno*/) 00229 { 00230 /* 00231 * The timer expired so we ACK the last packet seen. 00232 */ 00233 Packet* pkt = save_; 00234 delackcount_ = 0; 00235 ack(pkt); 00236 save_ = 0; 00237 Packet::free(pkt); 00238 } 00239
1.3.3