www.pudn.com > fast-tcp-ns2-v1_1b.rar > tcp-fast.cc, change:2005-06-21,size:28516b


/************************************************************************\ 
 * TCP-FAST  NS2 Module                                         	* 
 * University of Melbourne April 2005                         		* 
 *                                                              	* 
 * Coding: Tony Cui and Lachlan Andrew                          	* 
 *                                                                      * 
 * Revision History                                                     * 
 * Version 1.1.2                					* 
 *     Make FAST_CC_MIQ tunable						* 
 *     (using a mi_threshold_ (tunable TCL variable)  to replace it) 	* 
 *     Consider the calculation error when cwnd is an integer. Give	* 
 *     user 3 choices the calculate cwnd.(use integer without		* 
 *     considering calculation error (mode 0),  using integer 		* 
 *     with considering calculatioin error (mode 1) and using double	* 
 *     (mode 2).							* 
 *     Fix a bug in fast_cc: add acks_per_rtt++ in the function. 	* 
 *     Fix a bug in fast_cc: compare t_seqno and tcph->seqno() to       * 
 *     prevent t_seqno greater than tcph->seqno().			* 
 *     Allow user update cwnd every other rtt (default is every rtt)	* 
 * Version 1.1.1                                                        * 
 *     Set ECT bit correctly if ECN capable				* 
 * Version 1.1                                                          * 
 *     Add SACK function into Fast                                      * 
 *     Fix bug in output function which didn't consider SYN packets.    * 
 *     Fix bug in using INT_VARABLE_INVALID_VALUE instead of            * 
 *     DOUBLE_VARABLE_INVALID_VALUE                                     * 
 * Version 1.0.1 Released 13 November, 2004                             * 
 *     Fix bug in baseRTT_ estimation with pk loss                      * 
 * Version 1.0   Released 24 September, 2004                            * 
\************************************************************************/ 
 
#ifndef lint 
static const char rcsid[] = 
"@(#) $Header: /nfs/jade/vint/CVSROOT/ns-2/tcp/tcp-fast.cc,v 1.35 2004/12/14 05:05:21 xuanc Exp $ (NCSU/IBM)"; 
#endif 
 
#define FAST_ALLOW_CC 		0x1 
#define FAST_ALLOW_MI		0x2 
#define INT_VARABLE_INVALID_VALUE	999999999 
#define DOUBLE_VARABLE_INVALID_VALUE 	99999999999.0 
 
#include "ip.h" 
#include "tcp.h" 
#include "tcp-fast.h" 
#include "flags.h" 
#include "random.h" 
 
/******************************************************************************/ 
static class FastTcpClass : public TclClass { 
public: 
	FastTcpClass() : TclClass("Agent/TCP/Fast") {} 
	TclObject* create(int, const char*const*) { 
		return (new FastTcpAgent()); 
	} 
} class_fast; 
 
 
/******************************************************************************/ 
FastTcpAgent::~FastTcpAgent() 
{ 
#ifdef FASTTCPAGENT_DEBUG 
	fclose(fasttcpagent_recordfps[0]); 
	fclose(fasttcpagent_recordfps[1]); 
#endif 
 
	delete scb_; 
 
	if (sendtime_) 
		delete []sendtime_; 
	if (transmits_) 
		delete []transmits_; 
	if (cwnd_array_) 
		delete []cwnd_array_; 
} 
 
/******************************************************************************/ 
FastTcpAgent::FastTcpAgent() : TcpAgent(), 
	avgRTT_(0), baseRTT_(0), avg_cwnd_last_RTT_(1), 
	alpha_(0), beta_(0), fastrecov_(FALSE), 
	pipe_(-1), next_pkt_(0), firstpartial_(0), 
	last_natural_ack_number_(-1), on_first_rtt_(true) 
{ 
	sendtime_ = NULL; 
	transmits_ = NULL; 
	cwnd_array_ = NULL; 
	bind_bool("partial_ack_", &partial_ack_); 
	/* Use the Reassembly Queue based scoreboard as 
	 * ScoreBoard is O(cwnd) which is bad for HSTCP 
	 * scb_ = new ScoreBoard(new ScoreBoardNode[SBSIZE],SBSIZE); 
	 */ 
	scb_ = new ScoreBoardRQ(); 
 
#ifdef TCP_DELAY_BIND_ALL 
#else /* ! TCP_DELAY_BIND_ALL */ 
	/*bind tunable parameters*/ 
	bind("fast_update_cwnd_interval_", &cwnd_update_period_); 
	bind("avg_cwnd_last_RTT_", &avg_cwnd_last_RTT_); 
	bind("avgRTT_", &avgRTT_); 
	bind("baseRTT_", &baseRTT_); 
	bind("alpha_", &alpha_); 
	bind("beta_", &beta_); 
	bind("high_accuracy_cwnd_", &high_accuracy_cwnd_); 
	bind("mi_threshold_", &mi_threshold_); 
#endif /* TCP_DELAY_BIND_ALL */ 
 
#ifdef FASTTCPAGENT_DEBUG 
	static unsigned int s_agent_ID = 0; 
	char strTmp[30]; 
	sprintf(strTmp, "agent%d_%s.txt", s_agent_ID, "record0"); 
	fasttcpagent_recordfps[0] = fopen(strTmp, "w+"); 
	sprintf(strTmp, "agent%d_%s.txt", s_agent_ID, "record1"); 
	fasttcpagent_recordfps[1] = fopen(strTmp, "w+"); 
	s_agent_ID++; 
#endif 
} 
 
/******************************************************************************/ 
void 
FastTcpAgent::delay_bind_init_all() 
{ 
#ifdef TCP_DELAY_BIND_ALL 
        // Defaults for bound variables should be set in ns-default.tcl. 
	delay_bind_init_one("cwnd_update_period_"); 
	delay_bind_init_one("avg_cwnd_last_RTT_"); 
	delay_bind_init_one("avgRTT_"); 
	delay_bind_init_one("baseRTT_"); 
	delay_bind_init_one("alpha_"); 
	delay_bind_init_one("beta_"); 
	delay_bind_init_one("high_accuracy_cwnd_"); 
	delay_bind_init_one("mi_threshold_"); 
#endif /* TCP_DELAY_BIND_ALL */ 
	TcpAgent::delay_bind_init_all(); 
 
	reset(); 
} 
 
/******************************************************************************/ 
int 
FastTcpAgent::delay_bind_dispatch(const char *varName, const char *localName, TclObject *tracer) 
{ 
#ifdef TCP_DELAY_BIND_ALL 
	if (delay_bind(varName, localName, "cwnd_update_period_", &fast_update_cwnd_interval_, tracer)) 
		return TCL_OK; 
	if (delay_bind(varName, localName, "avg_cwnd_last_RTT_", &avg_cwnd_last_RTT_, tracer)) 
		return TCL_OK; 
	if (delay_bind(varName, localName, "avgRTT_", &avgRTT_, tracer)) 
		return TCL_OK; 
	if (delay_bind(varName, localName, "baseRTT_", &baseRTT_, tracer)) 
		return TCL_OK; 
	if (delay_bind(varName, localName, "alpha_", &alpha_, tracer)) 
		return TCL_OK; 
	if (delay_bind(varName, localName, "beta_", &beta_, tracer)) 
		return TCL_OK; 
	if (delay_bind(varName, localName, "high_accuracy_cwnd_", &high_accuracy_cwnd_, tracer)) 
		return TCL_OK; 
	if (delay_bind(varName, localName, "mi_threshold_", &mi_threshold_, tracer)) 
		return TCL_OK; 
#endif /* TCP_DELAY_BIND_ALL */ 
 
	return TcpAgent::delay_bind_dispatch(varName, localName, tracer); 
} 
 
/******************************************************************************/ 
/* Print out just the variable that is modified */ 
void 
FastTcpAgent::traceVar(TracedVar* v)  
{ 
	double curtime; 
	Scheduler& s = Scheduler::instance(); 
	char wrk[500]; 
	int n; 
 
	curtime = &s ? s.clock() : 0; 
	if (!strcmp(v->name(), "avgRTT_") || !strcmp(v->name(), "baseRTT_") || !strcmp(v->name(), "mi_threshold_")) 
		sprintf(wrk,"%-8.5f %-2d %-2d %-2d %-2d %s %-6.3f", 
			curtime, addr(), port(), daddr(), dport(), 
			v->name(), double(*((TracedDouble*) v)));  
	else if (!strcmp(v->name(), "avg_cwnd_last_RTT_") || !strcmp(v->name(), "alpha_") || !strcmp(v->name(), "high_accuracy_cwnd_" ) || !strcmp(v->name(), "beta_")) 
		sprintf(wrk,"%-8.5f %-2d %-2d %-2d %-2d %s %d", 
			curtime, addr(), port(), daddr(), dport(), 
			v->name(), int(*((TracedInt*) v)));  
	else 
	{ 
		TcpAgent::traceVar(v); 
		return; 
	} 
 
	n = strlen(wrk); 
	wrk[n] = '\n'; 
	wrk[n+1] = 0; 
	if (channel_) 
		(void)Tcl_Write(channel_, wrk, n+1); 
	wrk[n] = 0; 
 
	return; 
} 
 
/******************************************************************************/ 
void 
FastTcpAgent::reset () 
{ 
	fast_opts = 0; 
	fast_opts |= FAST_ALLOW_CC; 
	fast_opts |= FAST_ALLOW_MI; 
	cwnd_update_time = fasttime(); 
	cwnd_increments = 0; 
	firstrecv_ = -1.0; 
	slowstart_ = 2; 
	acks_per_rtt = 0; 
	acks_last_rtt = 0; 
	bc_ack = 0; 
	bc_spacing = 0; 
	t_backoff_=1; 
	baseRTT_ = DOUBLE_VARABLE_INVALID_VALUE; 
	newcwnd_ = DOUBLE_VARABLE_INVALID_VALUE; 
	currentTime = 0; 
	fast_calc_cwnd_end = 0; 
	scb_->ClearScoreBoard(); 
	cwnd_remainder = 0; 
 
	TcpAgent::reset (); 
} 
 
#define CWND_USE_INT	0 
#define CWND_USE_INT_CONSIDER_ERROR	1 
#define CWND_USE_DOUBLE	2 
/******************************************************************************/ 
/*this function should be used to calculate the new cwnd_ size in every update*/ 
double 
FastTcpAgent::fast_calc_cwnd(double cwnd,double old_cwnd) 
{ 
        double q = avgRTT_ - baseRTT_; 
        double cwnd_array_q = old_cwnd * q; 
        double target_cwnd = cwnd; 
 
        if ( avgRTT_ == 0 || baseRTT_ == DOUBLE_VARABLE_INVALID_VALUE ) 
        { 
                return cwnd; 
        } 
 
        if ( fast_opts & FAST_ALLOW_CC ) 
        { 
                if ( cwnd_array_q  alpha_ * avgRTT_ || 
		     cwnd_array_q >= beta_ * avgRTT_ ) 
                { 
                        target_cwnd = (cwnd + old_cwnd * (baseRTT_/avgRTT_) + alpha_) / 2; 
			if ( high_accuracy_cwnd_ == CWND_USE_INT_CONSIDER_ERROR || 
			     high_accuracy_cwnd_ == CWND_USE_INT )	//we use intergal to store cwnd. 
			{ 
				if (high_accuracy_cwnd_ == CWND_USE_INT_CONSIDER_ERROR)	//we consider the calculation error. 
				{ 
					cwnd_remainder += (target_cwnd - (int)target_cwnd); 
					if (cwnd_remainder > 1) 
					{ 
						target_cwnd++; 
						cwnd_remainder--; 
					} 
				} 
				target_cwnd = (int)target_cwnd; 
			} 
                } 
        } 
 
        return target_cwnd; 
} 
 
/******************************************************************************/ 
/* in this functioin, we can get the new average rtt which */ 
/* will be used to get the new cwnd_ size */ 
double 
FastTcpAgent::fast_est_update_avgrtt(double rtt) 
{ 
	double scale = avg_cwnd_last_RTT_/3; 
	double avgRTT=avgRTT_; 
	if ( !avgRTT) 
		avgRTT = rtt; 
	else if ( scale > 256 ) 
	{ 
		avgRTT = ((scale-1) * avgRTT + rtt)/scale; 
	} 
	else 
	{ 
		avgRTT = (avgRTT*255 + rtt)/256; 
	} 
	return (double)avgRTT; 
} 
 
/******************************************************************************/ 
/* We get average cwnd size which will be used as a history record. */ 
double 
FastTcpAgent::fast_est_update_avg_cwnd(int inst_cwnd) 
{ 
#if 0 
	unsigned int scale = avg_cwnd_last_RTT_/3; 
	double avg_cwnd=avg_cwnd_last_RTT_; 
 
	if ( !avg_cwnd) 
		avg_cwnd = inst_cwnd; 
	else if ( scale > 256 ) 
	{ 
		avg_cwnd = ((scale-1) * avg_cwnd + inst_cwnd)/scale; 
	} 
	else 
	{ 
		avg_cwnd = (avg_cwnd*255 + inst_cwnd)/256; 
	} 
	return avg_cwnd; 
#endif 
	return (double)inst_cwnd; 
} 
 
/******************************************************************************/ 
/* parameter estimation */ 
void 
FastTcpAgent::fast_est(Packet *pkt,double rtt) 
{			 
	hdr_tcp *tcph = hdr_tcp::access(pkt); 
 
	if(rtt  baseRTT_) 
		baseRTT_ = rtt; 
	avg_cwnd_last_RTT_ = (fast_est_update_avg_cwnd(t_seqno_ - tcph->seqno() - dupacks_)); 
	avgRTT_ = fast_est_update_avgrtt(rtt); 
} 
 
//#define UPDATE_CWND_EVERY_OTHER_RTT 
/******************************************************************************/ 
/* congestion control */ 
void 
FastTcpAgent::fast_cc(double rtt, double old_cwnd) 
{ 
	// We update acks_last_rtt every RTT. 
	if ( fast_calc_cwnd_end + rtt  currentTime ) { 
		fast_calc_cwnd_end = currentTime; 
		acks_last_rtt = acks_per_rtt; 
		acks_per_rtt = 0; 
		if ( on_first_rtt_ == true )	on_first_rtt_ = false; 
		else	on_first_rtt_ = true; 
	} 
	acks_per_rtt++; 
 
	// We use MI to increase cwnd_ when there is little queueing delay  
	if ( avgRTT_ - baseRTT_  mi_threshold_ && (fast_opts & FAST_ALLOW_MI) ) { 
		cwnd_++; 
		cwnd_update_time = currentTime; 
		return; 
	} 
 
	// If queueing delay is large, we use fast algorithm 
	if ( currentTime >= cwnd_update_time + fast_update_cwnd_interval_  
#ifdef	UPDATE_CWND_EVERY_OTHER_RTT 
	&& on_first_rtt_ == true 
#endif 
	) { 
		double updated_cwnd; 
		cwnd_increments = 0; 
		updated_cwnd = fast_calc_cwnd(cwnd_,old_cwnd); 
		if ( updated_cwnd >= cwnd_ && baseRTT_ >= 0.004 && baseRTT_ != DOUBLE_VARABLE_INVALID_VALUE ) { 
			fast_pace(&cwnd_, (int)(updated_cwnd-cwnd_)); 
		} 
		else 
			cwnd_ = updated_cwnd; 
		cwnd_update_time = currentTime; 
	} 
	else if ( baseRTT_ >= 0.004 && baseRTT_ != DOUBLE_VARABLE_INVALID_VALUE ) 
		fast_pace(&cwnd_, 0); 
} 
 
#define MIN(x, y) ((x)<(y) ? (x) : (y)) 
/******************************************************************************/ 
void 
FastTcpAgent::recv(Packet *pkt, Handler *) 
{ 
	currentTime = fasttime(); 
	hdr_tcp *tcph = hdr_tcp::access(pkt); 
	hdr_flags *flagh = hdr_flags::access(pkt); 
	int valid_ack = 0; 
 
	if (qs_approved_ == 1 && tcph->seqno() > last_ack_) 
		endQuickStart(); 
	if (qs_requested_ == 1) 
		processQuickStart(pkt); 
 
	/* W.N.: check if this is from a previous incarnation */ 
	if (tcph->ts()  lastreset_) { 
		// Remove packet and do nothing 
 		Packet::free(pkt); 
		return; 
	} 
	++nackpack_; 
	if(firstrecv_<0) { // init fast rtt vars 
		firstrecv_ = currentTime; 
		baseRTT_ = avgRTT_ = rtt_ = firstrecv_; 
	} 
 
	if (flagh->ecnecho()) 
		ecn(tcph->seqno()); 
 
	int ecnecho = hdr_flags::access(pkt)->ecnecho(); 
	if (ecnecho && ecn_) 
		ecn(tcph->seqno()); 
	// Check if ACK is valid.  Suggestion by Mark Allman. 
        if (tcph->seqno() >= last_ack_) 
		valid_ack = 1; 
 
#ifdef FASTTCPAGENT_DEBUG 
	if (cwnd_ = 0) 
		printf("%8.3f : cwnd is not positive! cwnd is %f .\n", (double)currentTime, (double)cwnd_); 
#endif	 
	/* 
	 * If DSACK is being used, check for DSACK blocks here. 
	 * Possibilities:  Check for unnecessary Fast Retransmits. 
	 */ 
	if (!fastrecov_) { 
		/* normal... not fast recovery */ 
		if ((int)tcph->seqno() > last_ack_) { 
			if (last_ack_ == 0 ) { 
				cwnd_ = initial_window(); 
			} 
 
			/* check if cwnd has been inflated */ 
			if(dupacks_ > numdupacks_ &&  cwnd_ > newcwnd_) { 
				//check t_seqno_ before changing cwnd. 
        			if (t_seqno_  tcph->seqno()) 
				        t_seqno_ = tcph->seqno(); 
				//cwnd becomes a negative number in some case. 
				 
				cwnd_ = t_seqno_ - tcph->seqno() + 1; 
				dupacks_ = 0; 
				for (int i=0;i<maxwnd_;i++) 
					cwnd_array_[i]=cwnd_; 
			} 
 
			firstpartial_ = 0; 
 
			// reset sendtime for acked pkts and incr transmits_ 
			double sendTime = sendtime_[tcph->seqno()%maxwnd_]; 
			double old_pif=cwnd_array_[tcph->seqno()%maxwnd_]; 
			int transmits = transmits_[tcph->seqno()% maxwnd_]; 
			for(int k= (last_natural_ack_number_+1); k=tcph->seqno(); ++k) { 
				sendtime_[k%maxwnd_] = -1.0; 
				transmits_[k%maxwnd_] = 0; 
				cwnd_array_[k%maxwnd_]=-1; 
			} 
			if (t_seqno_ > tcph->seqno()){ 
				if ((transmits == 1) && (currentTime - sendTime > 0)) 
					rtt_ = currentTime - sendTime; 
				else 
					rtt_ = avgRTT_; 
			}else rtt_ = avgRTT_; 
 
			fast_recv_newack_helper(pkt); 
			timeout_ = FALSE; 
			scb_->ClearScoreBoard(); 
			fast_est(pkt, rtt_); 
			fast_cc(rtt_, old_pif); 
			last_natural_ack_number_ = tcph->seqno(); 
		} else if ((int)tcph->seqno()  last_ack_) { 
			/*NOTHING*/ 
		//the follows are if (int)tcph->seqno() == last_ack_ 
		} else if (timeout_ == FALSE) { 
			if (tcph->seqno() != last_ack_) { 
				fprintf(stderr, "pkt seq %d should be %d\n", tcph->seqno(), last_ack_); 
				abort(); 
			} 
			scb_->UpdateScoreBoard (highest_ack_, tcph); 
			/* 
		 	 * Check for a duplicate ACK. 
			 * Check that the SACK block actually 
			 *  acknowledges new data. 
 			 */ 
			if(scb_->CheckUpdate()) { 
				if (++dupacks_ == numdupacks(cwnd_)) { 
 				/* 
 				 * Assume we dropped just one packet. 
 				 * Retransmit last ack + 1 
 				 * and try to resume the sequence. 
 				 */ 
 				   	dupack_action(); 
 				} else if (dupacks_  numdupacks(cwnd_) && singledup_ ) { 
					send_one(); 
 				} 
			} 
		} 
        	if (valid_ack || aggressive_maxburst_) 
			if (dupacks_ == 0) 
				send_much(FALSE, 0, maxburst_); 
	} else { 
		/* we are in fast recovery */ 
		cwnd_update_time = currentTime; 
		--pipe_; 
		if ((int)tcph->seqno() >= recover_) { 
			/* ACK indicates fast recovery is over */ 
			recover_ = 0; 
			fastrecov_ = FALSE; 
			newack(pkt); 
			/* if the connection is done, call finish() */ 
			if ((highest_ack_ >= curseq_-1) && !closed_) { 
				closed_ = 1; 
				finish(); 
			} 
			timeout_ = FALSE; 
			scb_->ClearScoreBoard(); 
 
			/* New window: W/2 - K or W/2? */ 
		} else if ((int)tcph->seqno() > highest_ack_) { 
			/* Not out of fast recovery yet. 
			 * Update highest_ack_, but not last_ack_. */ 
			highest_ack_ = (int)tcph->seqno(); 
			scb_->UpdateScoreBoard (highest_ack_, tcph); 
			if (partial_ack_) { 
			  /* partial_ack_ is needed to guarantee that */ 
			  /*  a new packet is sent in response to a   */ 
			  /*  partial ack.                            */ 
				partial_ack_action(); 
				++pipe_; 
				if (firstpartial_ == 0) { 
					newtimer(pkt); 
					t_backoff_ = 1; 
					firstpartial_ = 1; 
				} 
			} else { 
				--pipe_; 
				newtimer(pkt); 
				t_backoff_ = 1; 
 			 /* If this partial ACK is from a retransmitted pkt,*/ 
 			 /* then we decrement pipe_ again, so that we never */ 
 			 /* do worse than slow-start.  If this partial ACK  */ 
 			 /* was instead from the original packet, reordered,*/ 
 			 /* then this might be too aggressive. */ 
			} 
		} else if (timeout_ == FALSE) { 
			/* got another dup ack */ 
			scb_->UpdateScoreBoard (highest_ack_, tcph); 
 		        if(scb_->CheckUpdate()) { 
 				if (dupacks_ > 0) 
 			        	dupacks_++; 
 			} 
		} 
        	if (valid_ack || aggressive_maxburst_) 
			send_much(FALSE, 0, maxburst_); 
	} 
 
	Packet::free(pkt); 
} 
 
/******************************************************************************/ 
void 
FastTcpAgent::dupack_action() 
{ 
	int recovered = (highest_ack_ > recover_); 
	if (recovered || (!bug_fix_ && !ecn_)) { 
		goto sack_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); 
		/* 
		 * There are three possibilities:  
		 * (1) pipe_ = int(cwnd_) - numdupacks_; 
		 * (2) pipe_ = window() - numdupacks_; 
		 * (3) pipe_ = maxseq_ - highest_ack_ - numdupacks_; 
		 * equation (2) takes into account the receiver's 
		 * advertised window, and equation (3) takes into 
		 * account a data-limited sender. 
		 */ 
		if (singledup_ && LimTransmitFix_) { 
		  pipe_ = maxseq_ - highest_ack_ - 1; 
		} 
		else { 
		  // numdupacks(cwnd_) packets have left the pipe 
		  pipe_ = maxseq_ - highest_ack_ - numdupacks(cwnd_); 
		} 
		fastrecov_ = TRUE; 
		scb_->MarkRetran(highest_ack_+1); 
		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; 
	} 
 
sack_action: 
	// we are now going into fast_recovery and will trace that event 
	trace_event("FAST_RECOVERY"); 
 
	recover_ = maxseq_; 
	last_cwnd_action_ = CWND_ACTION_DUPACK; 
	if (oldCode_) { 
	 	pipe_ = int(cwnd_) - numdupacks(cwnd_); 
	} else if (singledup_ && LimTransmitFix_) { 
		  pipe_ = maxseq_ - highest_ack_ - 1; 
	} 
	else { 
		  // numdupacks(cwnd_) packets have left the pipe 
		  pipe_ = maxseq_ - highest_ack_ - numdupacks(cwnd_); 
	} 
	slowdown(CLOSE_SSTHRESH_HALF|CLOSE_CWND_HALF); 
	reset_rtx_timer(1,0); 
	fastrecov_ = TRUE; 
	scb_->MarkRetran(highest_ack_+1); 
	output(last_ack_ + 1, TCP_REASON_DUPACK);	// from top 
	/* 
	 * If dynamically adjusting numdupacks_, record information 
	 *  at this point. 
	 */ 
	return; 
} 
 
/* 
 * Process a packet that acks previously unacknowleges data, but 
 * does not take us out of Fast Retransmit. 
 * 
 * The need for a mechanism to ensure that Sack TCP sends a packet in 
 * response to a partial ACK has been discussed in 
 * "Challenges to Reliable Data Transport over Heterogeneous 
 * Wireless Networks", Hari Balakrishnan, 1998, and in 
 * "Responding to Spurious Timeouts in TCP", Andrei Gurtov and  
 * Reiner Ludwig, 2003.  
 */ 
void 
FastTcpAgent::partial_ack_action() 
{ 
	if (next_pkt_  highest_ack_ + 1) { 
		next_pkt_ = highest_ack_ + 1; 
	} 
	// Output two packets in response to a partial ack, 
	//   so as not to do worse than slow-start. 
	int i; 
	for (i = 1; i=2; i++) { 
		int getNext = scb_->GetNextUnacked(next_pkt_); 
 
		if (getNext > next_pkt_) { 
			next_pkt_ = getNext; 
		} 
		if (t_seqno_  next_pkt_) { 
			t_seqno_ = next_pkt_; 
		} 
		output(next_pkt_, TCP_REASON_PARTIALACK);	 
		scb_->MarkRetran(next_pkt_); 
		++next_pkt_;  
	} 
	return; 
} 
 
void FastTcpAgent::timeout(int tno) 
{ 
	if (tno == TCP_TIMER_RTX) { 
		/* 
		 * IF DSACK and dynamic adjustment of numdupacks_, 
		 *  check whether a smaller value of numdupacks_ 
		 *  would have prevented this retransmit timeout. 
		 * If DSACK and detection of premature retransmit 
		 *  timeouts, then save some info here. 
		 */  
		dupacks_ = 0; 
		fastrecov_ = FALSE; 
		timeout_ = TRUE; 
		if (highest_ack_ > last_ack_) 
			last_ack_ = highest_ack_; 
		recover_ = maxseq_; 
		scb_->ClearScoreBoard(); 
		if (highest_ack_ == maxseq_ && !slow_start_restart_) { 
			/* 
			 * TCP option: 
			 * If no outstanding data, then don't do anything. 
			 * 
			 * Note:  in the USC implementation, 
			 * slow_start_restart_ == 0. 
			 * I don't know what the U. Arizona implementation 
			 * defaults to. 
			 */ 
			return; 
		}; 
		last_cwnd_action_ = CWND_ACTION_TIMEOUT; 
		reset_rtx_timer(0, 0); 
		++nrexmit_; 
		slowdown(CLOSE_CWND_RESTART|CLOSE_SSTHRESH_HALF); 
		cwnd_ = double(slowstart_); 
		newcwnd_ = 0; 
		send_much(0, TCP_REASON_TIMEOUT, maxburst_); 
	} else { 
		/* delayed-sent timer, with random overhead to avoid 
		 * phase effect. */ 
		send_much(1, TCP_REASON_TIMEOUT, 3); 
	} 
} 
 
void FastTcpAgent::send_much(int force, int reason, int maxburst) 
{ 
	register int found, npacket = 0; 
	int win = window(); 
	int xmit_seqno; 
 
	found = 1; 
	if (!force && delsnd_timer_.status() == TIMER_PENDING) 
		return; 
	/* Save time when first packet was sent, for newreno  --Allman */ 
	if (t_seqno_ == 0) 
		firstsent_ = Scheduler::instance().clock(); 
	/* 
	 * as long as the pipe is open and there is app data to send... 
	 */ 
	while (((!fastrecov_ && (t_seqno_ = last_ack_ + win)) || 
			(fastrecov_ && (pipe_  int(cwnd_))))  
			&& t_seqno_  curseq_ && found) { 
 
		if (overhead_ == 0 || force) { 
			found = 0; 
			int oldest_unacked_pkt = scb_->GetNextUnacked(last_ack_); 
			if (oldest_unacked_pkt != -1 && 
			fasttime() - sendtime_[oldest_unacked_pkt%maxwnd_] > 2*avgRTT_){ 
				xmit_seqno = oldest_unacked_pkt; 
			} 
			else 
				xmit_seqno = scb_->GetNextRetran (); 
			if (xmit_seqno == -1) {  
				if ((!fastrecov_ && t_seqno_=highest_ack_+win)|| 
					(fastrecov_ && t_seqno_=highest_ack_+int(wnd_))) { 
					found = 1; 
					xmit_seqno = t_seqno_++; 
				} 
			} else if (recover_>0 && xmit_seqno=highest_ack_+int(wnd_)) { 
				found = 1; 
				scb_->MarkRetran (xmit_seqno); 
				win = window(); 
			} 
			if (found) { 
				output(xmit_seqno, reason); 
				if (t_seqno_ = xmit_seqno) 
					t_seqno_ = xmit_seqno + 1; 
				npacket++; 
				pipe_++; 
				if (QOption_) 
					process_qoption_after_send () ; 
	                        if (qs_approved_ == 1) { 
	                                double delay = (double) t_rtt_ * tcp_tick_ / cwnd_; 
	                                delsnd_timer_.resched(delay); 
	                                return; 
	                        } 
			} 
		} else if (!(delsnd_timer_.status() == TIMER_PENDING)) { 
			/* 
			 * Set a delayed send timeout. 
			 * This is only for the simulator,to add some 
			 *   randomization if speficied. 
			 */ 
			delsnd_timer_.resched(Random::uniform(overhead_)); 
			return; 
		} 
		if (maxburst && npacket == maxburst) 
			break; 
	} /* while */ 
} 
 
void 
FastTcpAgent::output(int seqno, int reason) 
{ 
	Packet* p = allocpkt(); 
	hdr_tcp *tcph = hdr_tcp::access(p); 
	double now = Scheduler::instance().clock(); 
	hdr_flags* hf = hdr_flags::access(p); 
	hdr_ip *iph = hdr_ip::access(p); 
	int databytes = hdr_cmn::access(p)->size(); 
	tcph->seqno() = seqno; 
	tcph->ts() = now; 
	tcph->reason() = reason; 
	/* if this is the 1st pkt, setup senttime[] and transmits[] 
	 * I alloc mem here, instrad of in the constructor, to cover 
	 * cases which windows get set by each different tcp flows */ 
	if (seqno==0) { 
		maxwnd_ = int(wnd_); 
		if (sendtime_) 
			delete []sendtime_; 
        	if (transmits_) 
			delete []transmits_; 
        	if (cwnd_array_) 
			delete []cwnd_array_; 
		sendtime_ = new double[maxwnd_]; 
		transmits_ = new int[maxwnd_]; 
		cwnd_array_= new double[maxwnd_]; 
		for(int i=0;i<maxwnd_;i++) { 
			sendtime_[i] = -1.; 
			transmits_[i] = 0; 
			cwnd_array_[i]=-1; 
		} 
	} 
 
	if (ecn_) { 
		hf->ect() = 1; // ECN capable transport. 
	} 
 
	/* Check if this is the initial SYN packet. */ 
	if (seqno == 0) { 
		if (syn_) { 
			databytes= 0; 
			curseq_ += 1; 
			hdr_cmn::access(p)->size() = tcpip_base_hdr_size_; 
		} 
		if (ecn_) { 
			hf->ecnecho() = 1; 
//			hf->cong_action() = 1; 
			hf->ect() = 0; 
		} 
	} 
	else if (useHeaders_ == true) { 
		hdr_cmn::access(p)->size() += headersize(); 
	} 
 
	// record a find grained send time and # of transmits  
	int index = seqno % maxwnd_; 
	sendtime_[index] = fasttime();  
	cwnd_array_[index]=avg_cwnd_last_RTT_;  
	++transmits_[index]; 
	/* support ndatabytes_ in output - Lloyd Wood 14 March 2000 */ 
	int bytes = hdr_cmn::access(p)->size();  
	ndatabytes_ += bytes;  
	ndatapack_++; // Added this - Debojyoti 12th Oct 2000 
	send(p, 0); 
 
	if (seqno == curseq_ && seqno > maxseq_) 
		idle();  // Tell application I have sent everything so far 
 
	if (seqno > maxseq_) { 
		maxseq_ = seqno; 
		if (!rtt_active_) { 
			rtt_active_ = 1; 
			if (seqno > rtt_seq_) { 
				rtt_seq_ = seqno; 
				rtt_ts_ = now; 
			} 
		} 
	} else { 
		++nrexmitpack_; 
       		nrexmitbytes_ += bytes; 
    	} 
 
	if (!(rtx_timer_.status() == TIMER_PENDING)) 
		/* No timer pending.  Schedule one. */ 
		set_rtx_timer(); 
} 
 
/******************************************************************************/ 
/* Space out increments to cwnd as acknowledegements arrive */ 
/* cwndp points to the actual cwnd value */ 
/* cwnd_incre (0 or 1) specifies the desired amount to increase cwnd by */ 
/* (eventually) */ 
void 
FastTcpAgent::fast_pace(TracedDouble *cwndp, int incre_4_cwnd) 
{ 
	if ( !avgRTT_ ) 
		return; 
 
	double acks_per_period = (double)acks_last_rtt * fast_update_cwnd_interval_ / avgRTT_; 
 
	if ( incre_4_cwnd >= 1 ) 
	{ 
		cwnd_increments += incre_4_cwnd; 
		/* bc_spacing: target number of ACKs between increments */ 
		bc_spacing = (short unsigned int)(acks_per_period/cwnd_increments); 
		/* bc_ack: number of ACKs since last increment */ 
		bc_ack = 1; 
	} 
	else {  
		bc_ack ++; 
	} 
 
	if (cwnd_increments) 
	{ 
		/* if cwnd small, increment immediately */ 
		if (*cwndp = 4) { 
		/* if increment would more than double cwnd, do it in stages */ 
			if (*cwndp  cwnd_increments) { 
				cwnd_increments -= (unsigned int)*cwndp; 
				*cwndp += *cwndp; 
			} 
			else { 
				*cwndp += cwnd_increments; 
				cwnd_increments=0; 
				bc_spacing=0; 
			} 
			bc_ack=0; 
		} 
		else if (bc_ack >= bc_spacing) 
		{ 
			(*cwndp)++; 
			cwnd_increments--; 
			bc_ack = 0; 
		} 
	} 
} 
 
/******************************************************************************/ 
/* 
 * return -1 if the oldest sent pkt has not been timeout (based on 
 * fine grained timer). 
 */ 
int 
FastTcpAgent::fast_expire(Packet* pkt) 
{ 
	hdr_tcp *tcph = hdr_tcp::access(pkt); 
	double elapse = fasttime() - sendtime_[(tcph->seqno()+1)%maxwnd_]; 
	if (elapse >= 2 * avgRTT_) { 
		return(tcph->seqno()+1); 
	} 
	return(-1); 
} 
 
/******************************************************************************/ 
void 
FastTcpAgent::fast_recv_newack_helper(Packet *pkt) { 
	newack(pkt); 
 
	if (ect_) { 
		if (!hdr_flags::access(pkt)->ecnecho()) 
			ecn_backoff_ = 0; 
		if (!ecn_burst_ && hdr_flags::access(pkt)->ecnecho()) 
			ecn_burst_ = TRUE; 
		else if (ecn_burst_ && ! hdr_flags::access(pkt)->ecnecho()) 
			ecn_burst_ = FALSE; 
	} 
	if (!ect_ && hdr_flags::access(pkt)->ecnecho() && 
		!hdr_flags::access(pkt)->cong_action()) 
		ect_ = 1; 
	/* if the connection is done, call finish() */ 
	if ((highest_ack_ >= curseq_-1) && !closed_) { 
		closed_ = 1; 
		finish(); 
	} 
	if (QOption_ && curseq_ == highest_ack_ +1) { 
		cancel_rtx_timer(); 
	} 
}