/* -*- 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: /nfs/jade/vint/CVSROOT/ns-2/tcp-reno.cc,v 1.35 2001/05/29 23:11:04 haldar Exp $ (LBL)"; #endif #include #include #include #include "ip.h" #include "tcp.h" #include "flags.h" static class RenoTcpClass : public TclClass { public: RenoTcpClass() : TclClass("Agent/TCP/Reno") {} TclObject* create(int, const char*const*) { return (new RenoTcpAgent()); } } class_reno; /*TCP_LP*/ /* added by Liang Guo, 12/19/2000, have to reset dupwnd_ as well */ void RenoTcpAgent::reset() { dupwnd_ = 0; return(TcpAgent::reset()); } /*end TCP_LP*/ int RenoTcpAgent::window() { // // reno: inflate the window by dupwnd_ // dupwnd_ will be non-zero during fast recovery, // at which time it contains the number of dup acks // int win = int(cwnd_) + dupwnd_; if (win > int(wnd_)) win = int(wnd_); return (win); } double RenoTcpAgent::windowd() { // // reno: inflate the window by dupwnd_ // dupwnd_ will be non-zero during fast recovery, // at which time it contains the number of dup acks // double win = cwnd_ + dupwnd_; if (win > wnd_) win = wnd_; return (win); } RenoTcpAgent::RenoTcpAgent() : TcpAgent(), dupwnd_(0) { } void RenoTcpAgent::recv(Packet *pkt, Handler*) { hdr_tcp *tcph = hdr_tcp::access(pkt); #ifdef notdef if (pkt->type_ != PT_ACK) { fprintf(stderr, "ns: confiuration error: tcp received non-ack\n"); exit(1); } #endif ++nackpack_; ts_peer_ = tcph->ts(); int ecnecho = hdr_flags::access(pkt)->ecnecho(); /*TCP_LP*/ hdr_flags* hf = hdr_flags::access(pkt); if ((wnd_option_ == 10) && (owd_opt_ == 1)) { /* if ( (first_loss_ind_ == 1) &&(int(t_owd_)*tcp_tick_ > t_owd_thr_) ) */ if ( (first_loss_ind_ == 1) &&(int(t_sowd_)*tcp_tick_ > t_owd_thr_) ) { hf->ecnecho() = 1; ecnecho = 1; }else{ hf->ecnecho() = 0; ecnecho = 0; } if (ecnecho){ ecn(tcph->seqno()); } else { /* delay printing */ if (t_owd_min_ < 5000.0) { //printf("\necn_: 0"); } } } else { //wnd_option != 10 || owd_opt_ != 1 if (hdr_flags::access(pkt)->ecnecho() && ecn_) { ecn(tcph->seqno()); } else { if (wnd_option_ == 10) { /* delay printing */ if (t_owd_min_ < 5000.0) { //printf("\necn_: 0"); } } } } /*delay printing*/ /*original of this printing is in tcp.cc*/ if ((wnd_option_ == 10) && (t_owd_min_ < 5000.0)) { /* printf( * "\ttime_: %6.3f rtt_ %6.3f srtt_ %6.3f ECN: %d owd: %6.3f min: %6.3f max: %6.3f sowd: %6.3f thr: %6.3f cwnd_: %-6.3f", * * now_dsp_, * int(t_rtt_)*tcp_tick_, * ((int(t_srtt_) >> T_SRTT_BITS)*tcp_tick_), * hdr_flags::access(pkt)->ecnecho(), * //snd: %6.3f rcv: %6.3f * // tcph->ts_echo(), * // tcph->ts(), * int(t_owd_)*tcp_tick_, * t_owd_min_, * t_owd_max_, * t_sowd_dbl_, * t_owd_thr_, * double(cwnd_) * // time_of_last_loss_ * //sdr: %-2d * // addr() * ); */ } /*TCP_LP end*/ recv_helper(pkt); if (tcph->seqno() > last_ack_) { dupwnd_ = 0; 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(); dupwnd_ = numdupacks_; } else if (dupacks_ > numdupacks_) { ++dupwnd_; // fast recovery } else if (dupacks_ < numdupacks_ && singledup_ ) { send_one(); } } Packet::free(pkt); #ifdef notyet if (trace_) plot(); #endif /* * Try to send more data */ if (dupacks_ == 0 || dupacks_ > numdupacks_ - 1) send_much(0, 0, maxburst_); } int RenoTcpAgent::allow_fast_retransmit(int last_cwnd_action_) { return (last_cwnd_action_ == CWND_ACTION_DUPACK); } /* * Dupack-action: what to do on a DUP ACK. After the initial check * of 'recover' below, this function implements the following truth * table: * * bugfix ecn last-cwnd == ecn action * * 0 0 0 reno_action * 0 0 1 reno_action [impossible] * 0 1 0 reno_action * 0 1 1 retransmit, return * 1 0 0 nothing * 1 0 1 nothing [impossible] * 1 1 0 nothing * 1 1 1 retransmit, return */ void RenoTcpAgent::dupack_action() { int recovered = (highest_ack_ > recover_); int allowFastRetransmit = allow_fast_retransmit(last_cwnd_action_); if (recovered || (!bug_fix_ && !ecn_) || allowFastRetransmit) { goto reno_action; } if (ecn_ && last_cwnd_action_ == CWND_ACTION_ECN) { last_cwnd_action_ = CWND_ACTION_DUPACK; /* * What if there is a DUPACK action followed closely by ECN * followed closely by a DUPACK action? * The optimal thing to do would be to remember all * congestion actions from the most recent window * of data. Otherwise "bugfix" might not prevent * all unnecessary Fast Retransmits. */ reset_rtx_timer(1,0); output(last_ack_ + 1, TCP_REASON_DUPACK); return; } if (bug_fix_) { /* * The line below, for "bug_fix_" true, avoids * problems with multiple fast retransmits in one * window of data. */ return; } reno_action: // we are now going to fast-retransmit and will trace that event trace_event("RENO_FAST_RETX"); recover_ = maxseq_; last_cwnd_action_ = CWND_ACTION_DUPACK; slowdown(CLOSE_SSTHRESH_HALF|CLOSE_CWND_HALF); reset_rtx_timer(1,0); output(last_ack_ + 1, TCP_REASON_DUPACK); // from top return; } void RenoTcpAgent::timeout(int tno) { if (tno == TCP_TIMER_RTX) { dupwnd_ = 0; dupacks_ = 0; if (bug_fix_) recover_ = maxseq_; TcpAgent::timeout(tno); } else { timeout_nonrtx(tno); } }