/* -*- Mode:C++; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */ /* * Copyright (c) 1990, 1997 Regents of the University of California. * All rights reserved. * * Redistribution and use in source and binary forms are permitted * provided that the above copyright notice and this paragraph are * duplicated in all such forms and that any documentation, * advertising materials, and other materials related to such * distribution and use acknowledge that the software was developed * by the University of California, Lawrence Berkeley Laboratory, * Berkeley, CA. The name of the University may not be used to * endorse or promote products derived from this software without * specific prior written permission. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. */ #ifndef lint static const char rcsid[] = "@(#) $Header: /home1/casetti/ns/ns-2.1b6/RCS/tcp-westwood.cc,v 1.1 2000/07/17 15:12:29 casetti Exp casetti $ (LBL)"; #endif // // tcp-westwood: a revised reno TCP source, with faster recovery // #include #include #include #include "packet.h" #include "ip.h" #include "tcp.h" #include "flags.h" #include "address.h" #ifndef CLOSE_FASTER #define CLOSE_FASTER 0x000000400 #endif static class FasterRecTcpClass : public TclClass { public: FasterRecTcpClass() : TclClass("Agent/TCP/Westwood") {} TclObject* create(int, const char*const*) { return (new FasterRecTcpAgent()); } } class_fasterrec; FasterRecTcpAgent::FasterRecTcpAgent() : lastackrx_(0.0), fr_alpha_(0.9), filter_type_(1), tau_(1.0) { bind("fr_alpha_", &fr_alpha_); bind("filter_type_", &filter_type_); bind("tau_", &tau_); // min_rtt_estimate = 100000000; // printf("Faster Recovery binding done!\n"); } void FasterRecTcpAgent::dupack_action() { /* New adaptive BWE/a strategy */ if (ssthresh_ > cwnd_) { /* 3dupacks were received while I was in slowstart */ fr_a_+=0.25; if (fr_a_ > 4) fr_a_=4; } else { /* 3dupacks were received while I was in congestion avoidance */ fr_a_ = 1; } // // ssthresh_ = (int)((current_bwe_/size_/8) * min_rtt_estimate)/fr_a_; // // WARNING!!!'a' is no longer being used ssthresh_ = (int)((current_bwe_/size_/8) * min_rtt_estimate); /* our algorithm dictates that CWIN=ssthresh after a 3DUPACK, but */ /* we should not forcefully increase CWIN if it is smaller than */ /* ssthresh */ if (cwnd_ > ssthresh_) { cwnd_ = ssthresh_; } recover_ = maxseq_; // used by ECN last_cwnd_action_ = CWND_ACTION_DUPACK; // used by ECN reset_rtx_timer(1,0); // reset RTO as in Reno output(last_ack_ + 1, TCP_REASON_DUPACK); // resend missing packet return; } void FasterRecTcpAgent::timeout(int tno) { /* retransmit timer */ if (tno == TCP_TIMER_RTX) { if (highest_ack_ == maxseq_ && !slow_start_restart_) { /* * TCP option: * If no outstanding data, then don't do anything. */ return; }; recover_ = maxseq_; if (highest_ack_ == -1 && wnd_init_option_ == 2) /* * First packet dropped, so don't use larger * initial windows. */ wnd_init_option_ = 1; if (highest_ack_ == maxseq_ && restart_bugfix_) /* * if there is no outstanding data, don't cut * down ssthresh_. */ slowdown(CLOSE_CWND_ONE); else if (highest_ack_ < recover_ && last_cwnd_action_ == CWND_ACTION_ECN) { /* * if we are in recovery from a recent ECN, * don't cut down ssthresh_. */ slowdown(CLOSE_CWND_ONE); } else { ++nrexmit_; slowdown(CLOSE_FASTER); } /* if there is no outstanding data, don't back off rtx timer */ if (highest_ack_ == maxseq_ && restart_bugfix_) { reset_rtx_timer(0,0); } else { reset_rtx_timer(0,1); } last_cwnd_action_ = CWND_ACTION_TIMEOUT; send_much(0, TCP_REASON_TIMEOUT, maxburst_); } else { timeout_nonrtx(tno); } } void FasterRecTcpAgent::recv(Packet *pkt, Handler*) { hdr_tcp *tcph = hdr_tcp::access(pkt); ts_peer_ = tcph->ts(); double fr_now = Scheduler::instance().clock(); // last_ack_ indicates the ack no. of the ack received _before_ // the current one // START BWE COMPUTATION // Idea: cumulative ACKs acking more than 2 packets count for 1 packet // since DUPACKs have already been accounted for int cumul_ack = tcph->seqno_ - last_ack_; myseqno_ = tcph->seqno_; if (cumul_ack > 1) { /* check if current ACK ACKs fewer or same number of segments than */ /* expected: if so, the missing ones were already accounted for by */ /* DUPACKs, and current ACK only counts as 1 */ if (unaccounted_ >= cumul_ack) { unaccounted_-=cumul_ack; cumul_ack=1; } else /* check if current ACK ACKs more segments than expected: if so, */ /* part of them were already accounted for by DUPACKs; the rest */ /* are cumulatively ACKed by present ACK. Make present ACK count */ /* as the unacknowledged ACKs in excess*/ if (unaccounted_ < cumul_ack) { cumul_ack-=unaccounted_; unaccounted_=0; } } /* if cumul_ack=0, the current ACK is clearly a DUPACK and should */ /* count 1 */ if (cumul_ack == 0) { unaccounted_++; cumul_ack=1; } /* safety check; if the previous steps are followed exactly, */ /* cumul_ack should not be >2 unless some strage events occur */ /* (e.g., an ACK is dropped on the way back and the following one */ /* appears to ACK more than its due) */ if (cumul_ack > 2) { cumul_ack=2; } double rtt_estimate = t_rtt_ * tcp_tick_; if ((rtt_estimate < min_rtt_estimate)&&(rtt_estimate > 0)) { min_rtt_estimate = rtt_estimate; } nackpack_+= cumul_ack; int acked_size = size_ * 8 * cumul_ack; double ack_interv = fr_now - lastackrx_; double sample_bwe = acked_size/ack_interv; switch (filter_type_) { case 0: // original filter current_bwe_ = current_bwe_ * fr_alpha_ + sample_bwe * (1 - fr_alpha_); break; case 1: // filter type 1 current_bwe_ = current_bwe_ * .9047 + (sample_bwe+last_bwe_sample_) * .0476; break; case 2: // filter type 2: 'lower' pass current_bwe_ = current_bwe_ * .93548 + (sample_bwe+last_bwe_sample_) * .03225; break; case 3: // filter type 3: time constant tau_ // compute how many intervals of length tau_ went by since we // received the last ACK. For each tau_ interval without ACK, feed // a zero-bandwidth sample to the filter. int idle_intervals = (int)(ack_interv / tau_*2); // printf("idle_intervals = %d (%f,%f)=%f\n", idle_intervals, ack_interv, tau_, // ack_interv / tau_); // printf("idle_intervals = %d, ratio= %f\n",idle_intervals, ack_interv / tau_); double tmp_current_bwe_; if (idle_intervals > 0) { int i; // printf("idle_intervals = %d\n", idle_intervals); for (i=0;isport()); printf("sc%s: ack. no. %d at time %f, bwe=%f, cwnd = %d, ssthresh_ = %d\n", src_portaddr, tcph->seqno_, fr_now, current_bwe_/1000000, (int)cwnd_, (int)ssthresh_); printf("sc%s: now = %f, acked_size = %d, rxdiff = %f, last_ack_ = %d\n", src_portaddr, fr_now, acked_size, (fr_now - lastackrx_), last_ack_); printf("sc%s: unaccounted_ = %d, fr_a_= %f, min_rtt_estimate = %f\n", src_portaddr, unaccounted_, fr_a_, min_rtt_estimate); #endif #ifdef MYDEBUG_RTT double f = t_rtt_ * tcp_tick_; printf("source %s: %f cwnd=%d bwe=%f rtt=%f\n", src_portaddr, fr_now, (int)cwnd_, current_bwe_/1000000, f); #endif #ifdef MYREPORT hdr_ip *iph = hdr_ip::access(pkt); char *src_portaddr = Address::instance().print_portaddr(iph->src()); printf("%s %f %d %f %d\n", src_portaddr, fr_now, (int)cwnd_, current_bwe_/1000000, (int)ssthresh_); #endif last_bwe_sample_ = sample_bwe; lastackrx_ = fr_now; /* grow cwnd and check if the connection is done */ if (tcph->seqno() > last_ack_) { recv_newack_helper(pkt); if (last_ack_ == 0 && delay_growth_) { cwnd_ = initial_window(); } } else if (tcph->seqno() == last_ack_) { if (hdr_flags::access(pkt)->eln_ && eln_) { tcp_eln(pkt); return; } if (++dupacks_ == NUMDUPACKS) { dupack_action(); } } Packet::free(pkt); /* * Try to send more data. */ send_much(0, 0, maxburst_); }