/* -*- 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/tcp-reno.cc,v 1.41 2004/10/26 22:59:42 sfloyd 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; 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 (frto_ == 2) { // First ack after RTO has arrived. // Open window to allow two new segments out with F-RTO. win = force_wnd(2); } 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), /* Added by Nadim (UofC) */ countfr(0),countto(0),countifr(0),countlossevent(0), countloss(0),counttotalloss(0),countpktacked(0),markpktacked(0), recovernp(0), traceflowno(-1),letracestart(0.0),fplossevent(NULL) { bind("traceflowno", &traceflowno); bind("countfr", &countfr); bind("countifr", &countifr); bind("countto", &countto); bind("countlossevent",&countlossevent); bind("counttotalloss", &counttotalloss); bind("countpktacked", &countpktacked); bind("letracestart", &letracestart); /* End of addition by Nadim (UofC) */ } /* Added by Nadim (UofC) */ int RenoTcpAgent::delay_bind_dispatch(const char *varName, const char *localName, TclObject *tracer) { if (delay_bind(varName, localName, "traceflowno", &traceflowno, tracer)) return TCL_OK; if (delay_bind(varName, localName, "countfr", &countfr, tracer)) return TCL_OK; if (delay_bind(varName, localName, "countifr", &countifr, tracer)) return TCL_OK; if (delay_bind(varName, localName, "countto", &countto, tracer)) return TCL_OK; if (delay_bind(varName, localName, "countlossevent", &countlossevent, tracer)) return TCL_OK; if (delay_bind(varName, localName, "counttotalloss", &counttotalloss, tracer)) return TCL_OK; if (delay_bind(varName, localName, "countpktacked", &countpktacked, tracer)) return TCL_OK; if (delay_bind(varName, localName, "letracestart", &letracestart, tracer)) return TCL_OK; return TcpAgent::delay_bind_dispatch(varName, localName, tracer); } /* End of addition by Nadim (UofC) */ void RenoTcpAgent::recv(Packet *pkt, Handler*) { hdr_tcp *tcph = hdr_tcp::access(pkt); // Single line added by Nadim char eventname[64]=""; int valid_ack = 0; if (qs_approved_ == 1 && tcph->seqno() > last_ack_) endQuickStart(); if (qs_requested_ == 1) processQuickStart(pkt); #ifdef notdef if (pkt->type_ != PT_ACK) { fprintf(stderr, "ns: confiuration error: tcp received non-ack\n"); exit(1); } #endif /* W.N.: check if this is from a previous incarnation */ if (tcph->ts() < lastreset_) { // Remove packet and do nothing Packet::free(pkt); return; } ++nackpack_; ts_peer_ = tcph->ts(); if (hdr_flags::access(pkt)->ecnecho() && ecn_) ecn(tcph->seqno()); recv_helper(pkt); recv_frto_helper(pkt); /* Added by Nadim (UofC): Counts Loss Event and Loss Rate */ { double ctime; Scheduler& s = Scheduler::instance(); ctime = &s ? s.clock() : 0; countpktacked = highest_ack_ - markpktacked; if(ctime < letracestart){ // resets all trace vars countfr = 0; countto = 0; countifr = 0; countlossevent = 0; countloss = 0; counttotalloss = 0; recovernp = 0; countpktacked = 0; markpktacked = highest_ack_; } } /* End of addition by nadim */ if (tcph->seqno() > last_ack_) { /* Count Loss Event and Loss Rate. Added by Nadim (UofC) */ if(tcph->seqno() >= recovernp && countloss > 0){ // save to disk if (countpktacked && traceflowno == fid_){ double ctime; Scheduler& s = Scheduler::instance(); ctime = &s ? s.clock() : 0; printf("Rtm %0.4f fid %1d NRer %0.4f Rer: %0.4f ",ctime,fid_,1.0*countlossevent/countpktacked,1.0*(countto+countfr)/countpktacked); printf("plr %0.4f ",1.0*counttotalloss/countpktacked); printf("NRe %5d Re %5d ",countlossevent,countto+countfr); printf("fr %4d ifr %4d to %3d",countfr,countifr,countto); printf(" pl %4d linw %1d packed %5u\n",counttotalloss,countloss,(unsigned)countpktacked); } countloss = 0; // reinitializing } /* End of addition by Nadim */ if (last_cwnd_action_ == CWND_ACTION_DUPACK) last_cwnd_action_ == CWND_ACTION_EXITED; dupwnd_ = 0; recv_newack_helper(pkt); if (last_ack_ == 0 && delay_growth_) { cwnd_ = initial_window(); } // Two lines Added by Nadim (UofC): Counting Loss Events countpktacked = highest_ack_ - markpktacked; strcpy(eventname,"REGULAR_ACK"); } else if (tcph->seqno() == last_ack_) { if (hdr_flags::access(pkt)->eln_ && eln_) { tcp_eln(pkt); return; } if (++dupacks_ == numdupacks_) { dupack_action(); if (!exitFastRetrans_) dupwnd_ = numdupacks_; } else if (dupacks_ > numdupacks_ && (!exitFastRetrans_ || last_cwnd_action_ == CWND_ACTION_DUPACK )) { ++dupwnd_; // fast recovery } else if (dupacks_ < numdupacks_ && singledup_ ) { send_one(); } // Single line added by Nadim strcpy(eventname,"DUP_ACK"); } if (tcph->seqno() >= last_ack_) // Check if ACK is valid. Suggestion by Mark Allman. valid_ack = 1; Packet::free(pkt); #ifdef notyet if (trace_) plot(); #endif /* * Try to send more data */ if (valid_ack || aggressive_maxburst_) if (dupacks_ == 0 || dupacks_ > numdupacks_ - 1) send_much(0, 0, maxburst_); // Single line added by Nadim trace_lossevent(eventname); } /* Added by Nadim (UofC), May 13, 2005*/ void RenoTcpAgent::trace_lossevent(char *eventname){ double ctime; Scheduler& s = Scheduler::instance(); ctime = &s ? s.clock() : 0; if(fid_ != traceflowno) return; printf("\n%s: LE %d PL %d backoff %d recover %d count %d time %0.4lf",eventname,countlossevent,counttotalloss,(unsigned)t_backoff_,recover_,count_,ctime); printf("\n\tcwnd %0.2f dupwnd %d ssthresh %d ack %u snd %u tseq %u",(double)cwnd_,dupwnd_,(unsigned)ssthresh_, (unsigned)highest_ack_,(unsigned)maxseq_,(unsigned) t_seqno_); } /* End of addition by Nadim */ 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); dupwnd_ = numdupacks_; 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 dupwnd_ = numdupacks_; /* Added by Nadim: Counts Loss Event and Loss Rate */ countfr++; countloss++; counttotalloss++; if(highest_ack_ > recovernp){ // enters in different window recovernp = maxseq_; // not for each loss countlossevent++; // lossevent is based on window } trace_lossevent("FAST_RECOVERY"); /* End of addition by Nadim */ return; } void RenoTcpAgent::timeout(int tno) { if (tno == TCP_TIMER_RTX) { // Count loss event and loss rate: Added by Nadim (UofC) countloss++; counttotalloss++; if(t_backoff_== 1) { // entering to timeout first time countto++; countlossevent++; if( highest_ack_ < recovernp ){ // was in same drop window countfr--; countlossevent--; // as already counted in fr countifr++; } } // End of addition by Nadim dupwnd_ = 0; dupacks_ = 0; if (bug_fix_) recover_ = maxseq_; TcpAgent::timeout(tno); // Single line added by Nadim trace_lossevent("TIMEOUT"); } else { timeout_nonrtx(tno); } }