--- ../ns-2.1b8-ref/tcp.cc	Wed May 30 02:11:04 2001
+++ tcp.cc	Sat Jun 22 11:34:37 2002
@@ -37,6 +37,10 @@
     "@(#) $Header: /nfs/jade/vint/CVSROOT/ns-2/tcp.cc,v 1.123 2001/05/29 23:11:04 haldar Exp $ (LBL)";
 #endif
 
+//AG: eifel stuff
+//#define DEBUG(x) printf x;
+#define DEBUG(x) 
+
 #include <stdlib.h>
 #include <math.h>
 #include "ip.h"
@@ -96,6 +100,10 @@
         bind("nrexmitpack_", &nrexmitpack_);
         bind("nrexmitbytes_", &nrexmitbytes_);
 	bind("singledup_", &singledup_);
+	bind("eifel_", &eifel_); //AG
+	bind("eifel_cc_", &eifel_cc_); //AG
+	bind("eifel_rto_", &eifel_rto_); //AG
+
 #endif /* TCP_DELAY_BIND_ALL */
 
 }
@@ -173,6 +181,9 @@
         delay_bind_init_one("nrexmitpack_");
         delay_bind_init_one("nrexmitbytes_");
 	delay_bind_init_one("singledup_");
+	delay_bind_init_one("eifel_"); //AG
+	delay_bind_init_one("eifel_cc_"); //AG
+	delay_bind_init_one("eifel_rto_"); //AG
 #endif /* TCP_DELAY_BIND_ALL */
 
 	Agent::delay_bind_init_all();
@@ -228,6 +239,9 @@
         if (delay_bind(varName, localName, "control_increase_", &control_increase_ , tracer)) return TCL_OK;
 	if (delay_bind_bool(varName, localName, "oldCode_", &oldCode_, tracer)) return TCL_OK;
 	if (delay_bind_bool(varName, localName, "timerfix_", &timerfix_, tracer)) return TCL_OK;
+	if (delay_bind_bool(varName, localName, "eifel_", &eifel_, tracer)) return TCL_OK; //AG
+	if (delay_bind(varName, localName, "eifel_cc_", &eifel_cc_, tracer)) return TCL_OK; //AG
+	if (delay_bind(varName, localName, "eifel_rto_", &eifel_rto_, tracer)) return TCL_OK; //AG
 
 
 #ifdef TCP_DELAY_BIND_ALL
@@ -396,6 +410,8 @@
 	boot_time_ = Random::uniform(tcp_tick_);
 	first_decrease_ = 1;
 
+	cancel_timers(); //AG
+
 	/* Now these variables will be reset 
 	   - Debojyoti Dutta 12th Oct'2000 */
  
@@ -528,7 +544,7 @@
 	if (ecn_) {
 		hf->ect() = 1;	// ECN-capable transport
 	}
-	if (cong_action_) {
+	if (ecn_ && cong_action_) { //AG
 		hf->cong_action() = TRUE;  // Congestion action.
 		cong_action_ = FALSE;
         }
@@ -937,6 +953,11 @@
 {
 	double now = Scheduler::instance().clock();
 	hdr_tcp *tcph = hdr_tcp::access(pkt);
+        /* AG: FIXME to coexist with FullTcp which sets 
+	   t_seqno_ =highest_ack_, while TCP sets t_seqno_ = highest_ack_+ 1;	*/
+
+	eifel_ack(pkt);
+
 	/* 
 	 * Wouldn't it be better to set the timer *after*
 	 * updating the RTT, instead of *before*? 
@@ -957,8 +978,12 @@
 	 */	
 	hdr_flags *fh = hdr_flags::access(pkt);
 	if (!fh->no_ts_) {
-		if (ts_option_)
+		if (ts_option_) {
 			rtt_update(now - tcph->ts_echo());
+			/*AG: reset only if not controlled by eifel routines */
+			if (eifel_rto_ != 2)
+                           t_backoff_ = 1; //AG: wasn't reset!
+		}
 
 		if (rtt_active_ && tcph->seqno() >= rtt_seq_) {
 			if (!ect_ || !ecn_backoff_ || 
@@ -967,7 +992,10 @@
 				 * Don't end backoff if still in ECN-Echo with
 			 	 * a congestion window of 1 packet. 
 				 */
-				t_backoff_ = 1;
+
+				/*AG: reset only if not controlled by eifel routines */
+				if (eifel_rto_ != 2)
+					t_backoff_ = 1;
 				ecn_backoff_ = 0;
 			}
 			rtt_active_ = 0;
@@ -1031,7 +1059,8 @@
 		
 		if (!control_increase_ || 
 		   (control_increase_ && (network_limited() == 1))) 
-	      		opencwnd();
+//AG FIXME need this?			if (!eifel_)
+				opencwnd();
 	}
 	if (ect_) {
 		if (!hdr_flags::access(pkt)->ecnecho())
@@ -1238,6 +1267,7 @@
 			 // What if CWND_ACTION_ECN and cwnd < 1?
 			 // return;
 		} else {
+			eifel_timeout(); //AG
 			recover_ = maxseq_;
 			if (highest_ack_ == -1 && wnd_init_option_ == 2)
 				/* 
@@ -1307,7 +1337,9 @@
  * invokes the Tcl finish procedure that was registered with TCP.
  */
 void TcpAgent::finish()
-{
+{		
+	reset(); //AG
+	reset_eifel(); //AG
 	Tcl::instance().evalf("%s done", this->name());
 }
 
@@ -1551,4 +1583,132 @@
 			dport()                       // dst port id
 			);
 	et_->trace();
+}
+
+void TcpAgent::eifel_timeout() {
+//now, even when eifel is off we detect bad RTOs
+//	if (!eifel_) return; 
+ 
+	// Get the oldest unacknowledeged segment
+	//AG fixme iss is not used in single TCP agents
+	int oldest_seg = (highest_ack_ < 0) ? 0 : int(highest_ack_);
+ 
+	if (eifel_rexmit_no_ == 0){
+		DEBUG(("EifelFullTcpAgent::timeout_action - first retransmission  %d\n",oldest_seg));
+		eifel_rexmit_seq_ = oldest_seg;
+		eifel_rexmit_no_ = 1;
+ 
+		eifel_ts_first_rexmit_ = Scheduler::instance().clock(); // that's how FullTcp implements TS
+		eifel_last_cwnd_ = cwnd_;
+		eifel_last_ssthresh_ = ssthresh_;
+	}else{ // eifel_rexmit_no != 0
+		if (oldest_seg == eifel_rexmit_seq_){
+			DEBUG(("EifelFullTcpAgent::timeout_action - Retransmission No: %d\n",eifel_rexmit_no_+1))
+				eifel_rexmit_no_++;
+		}
+	}   
+}
+
+/*
+ * For every ACK check if 1) timeout occured 2) it was good or bad timeout
+ * If Eifel is enabled, respond to timeouts
+ */
+void TcpAgent::eifel_ack(Packet *p) {	
+
+  if (!ts_option_) return; /* cannot detect without timestamps */
+  
+  if ( eifel_rexmit_no_ > 0) {  /* check if timeout occured */
+    hdr_tcp *tcph = hdr_tcp::access(p);
+    double ack_ts = tcph->ts_echo();
+
+    DEBUG(("EifelFullTcpAgent::ack_action - Rexmit Mode: t_seqn_=%d,maxseq_=%d\n",(int)t_seqno_,(int)maxseq_))
+    DEBUG(("eifel_ts_first = %f, ts=%f, ts_echo=%f\n",eifel_ts_first_rexmit_,tcph->ts(),tcph->ts_echo()))
+
+    if (ack_ts < eifel_ts_first_rexmit_) { // FIXME: Time is endless,isn't it
+
+      DEBUG(("EifelFullTcpAgent::ack_action - Spurious Timeout: %d\n",recover_))      // so we continue with the next unsent packet
+ 
+      DEBUG(("bad timeout\n"));
+
+      /* if eifel is disabled, only detect bad timeouts, but no response */
+      if (!eifel_) goto no_response;
+
+      t_seqno_ = maxseq_; /* continue transmission from high_seq */
+
+      /* 
+       * Now revert congestion control
+       * Note: we don't care about the number of timeouts
+       */
+      switch (eifel_cc_) {
+      case 1 : /*** full undo */
+        cwnd_ = eifel_last_cwnd_;
+        ssthresh_ = eifel_last_ssthresh_;
+        break;
+      case 2 :
+	/******* cwnd/=2, ssthresh=cwnd */
+        cwnd_ = eifel_last_cwnd_/2;
+        if (int(cwnd_)<2) cwnd_ = 1;
+        ssthresh_ = cwnd_; //eifel_last_ssthresh_/2;
+        if (int(ssthresh_) <2) ssthresh_ = 2;
+        break;
+      case 3:
+	/******* sstresh=cwnd, cwnd=1 */
+        ssthresh_ = int(eifel_last_cwnd_);
+        if (int(ssthresh_) <2) ssthresh_ = 2;
+        cwnd_ = 1;
+        break;
+      case 4:
+	/******* sstresh=cwnd, cwnd/=2 */
+        ssthresh_ = int(eifel_last_cwnd_);
+        if (int(ssthresh_) <2) ssthresh_ = 2;
+        cwnd_ = eifel_last_cwnd_/2;
+        if (int(cwnd_)<2) cwnd_ = 1;
+        break;
+      case 5:
+	/******* sstresh=cwnd, cwnd=cwnd */
+        ssthresh_ = int(eifel_last_cwnd_);
+        if (int(ssthresh_) <2) ssthresh_ = 2;
+        cwnd_ = eifel_last_cwnd_;
+        if (int(cwnd_)<2) cwnd_ = 1;
+        break;
+      default:
+        ;
+        /* nothing: We go on with standard TCP behavior
+           cwnd_ = 1;
+           ssthresh_ = eifel_last_ssthresh_/2;
+	*/
+      } /*switch*/
+
+    DEBUG(("Eifel::CC: new cwnd=%d, new ssthresh=%d\n",(int)cwnd_,(int)ssthresh_))      
+
+    DEBUG(("Eifel::CC: old RTO %.2f\n", rtt_timeout()))      
+
+    /* eifel_rto=2 is dealed with else where */
+    if (eifel_rto_==1) {
+      /* reseed RTO with new RTT sample (ra=rtt, va=rtt/2, t=rtt+4*va) */	
+      double now = Scheduler::instance().clock();
+      t_srtt_=0; //this forces rtt_update to reset srtt and svar
+      rtt_update(now - tcph->ts_echo());
+      set_rtx_timer();
+      DEBUG(("Eifel::CC: new RTO %.2f\n", rtt_timeout()))      
+    } 
+
+  } /* spurious timeout */
+  else {
+	DEBUG(("unavoidable timeout\n"));
+	if (eifel_rto_==2) t_backoff_=1;
+  }
+
+no_response:
+   //fflush(stdout);
+   reset_eifel(); /* this timeout was resolved, prepare for next one */
+
+ } /* timeout */   
+}
+
+/* init eifel-related variables */
+void TcpAgent::reset_eifel() {
+    eifel_ts_first_rexmit_ = 0;
+    eifel_rexmit_seq_ = 0;
+    eifel_rexmit_no_ = 0;
 }
--- ../ns-2.1b8-ref/tcp.h	Mon May 21 22:27:32 2001
+++ tcp.h	Sat Jun 22 11:20:43 2002
@@ -263,6 +263,20 @@
 				 * old ECN implementation, which never
 				 * reduced the congestion window below
 				 * one packet. */ 
+
+  /* AG: Eifel related stuff */	  
+  int eifel_;             /* use the Eifel algorithm or not */
+  int eifel_cc_;          /* 1-restore CC fully, 0-not all, etc... see tcp.cc*/
+  int eifel_rto_;         /* 0-standard rto, 1-reseed after timeout, 2-backoff */
+  double eifel_ts_first_rexmit_;   /* holds timestamp of first retransmission */
+  double eifel_last_cwnd_;         /* to save the current cwnd */
+  int eifel_last_ssthresh_;        /* same for ssthresh */
+  int eifel_rexmit_seq_;           /* sequence number of retransmitted segment */
+  int eifel_rexmit_no_;            /* number of retransmissions */
+  void eifel_timeout();
+  void eifel_ack(Packet*);
+  void reset_eifel();
+
 	FILE *plotfile_;
 	/*
 	 * Dynamic state.
--- ../ns-2.1b8-ref/tcp-reno.cc	Wed May 30 02:11:04 2001
+++ tcp-reno.cc	Sat Jun 22 11:38:47 2002
@@ -1,4 +1,5 @@
-/* -*-	Mode:C++; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */
+/* -*-	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.
@@ -71,6 +72,7 @@
 
 void RenoTcpAgent::recv(Packet *pkt, Handler*)
 {
+	static int k=0;
 	hdr_tcp *tcph = hdr_tcp::access(pkt);
 #ifdef notdef
 	if (pkt->type_ != PT_ACK) {
@@ -98,11 +100,29 @@
 		}
 		if (++dupacks_ == numdupacks_) {
 			dupack_action();
-			dupwnd_ = numdupacks_;
+			//FIXME AG: ECN
+			//AG this fixes new segments on dupacks.
+			if (!bug_fix_ || highest_ack_ > recover_ ||allow_fast_retransmit(last_cwnd_action_))  //AG
+				dupwnd_ = numdupacks_;
+/* AG: this is purely experimental, send new segments on DUPACKs
+			else if (k++ %2) {
+				send_one();
+				cwnd_++; 
+			}
+*/
 		} else if (dupacks_ > numdupacks_) {
-			++dupwnd_;	// fast recovery
+			//AG this fixes new segments on dupacks.
+			if (!bug_fix_ || highest_ack_ > recover_ ||allow_fast_retransmit(last_cwnd_action_))  //AG
+				++dupwnd_;	// fast recovery
+/* AG: this is purely experimental, send new segments on DUPACKs
+			else if (k++ %2) {
+				send_one();
+				cwnd_++; 
+			}
+*/
 		} else if (dupacks_ < numdupacks_ && singledup_ ) {
 			send_one();
+//			cwnd_++; //AG
 		}
 	}
 	Packet::free(pkt);
--- ../ns-2.1b8-ref/tcp-fack.cc	Fri Sep  1 06:04:07 2000
+++ tcp-fack.cc	Fri May 31 16:42:29 2002
@@ -254,6 +254,7 @@
 			 */
 			return;
 		};
+		eifel_timeout(); //AG 
 		// Do not clear fastrecov_ or alter recover_ variable
 		timeout_ = FALSE;
 		if (highest_ack_ > last_ack_)
--- ../ns-2.1b8-ref/tcp-full.cc	Wed May 30 19:58:36 2001
+++ tcp-full.cc	Sat Jun 22 11:44:12 2002
@@ -90,6 +90,9 @@
 #include "random.h"
 #include "template.h"
 
+//AG uncomment to enable logging
+//#include "lib-atp.h"
+
 #define	TRUE 	1
 #define	FALSE 	0
 
@@ -238,8 +241,9 @@
  */
 
 void
-FullTcpAgent::pack_action(Packet*)
+FullTcpAgent::pack_action(Packet* p)
 {
+	eifel_ack(p); //AG
 	if (reno_fastrecov_ && fastrecov_ && cwnd_ > ssthresh_) {
 		cwnd_ = ssthresh_; // retract window if inflated
 	}
@@ -443,7 +447,9 @@
 	else {
 		fh->ecnecho() = recent_ce_;
 	}
-	fh->cong_action() =  cong_action_;
+
+	if (ecn_) //AG
+		fh->cong_action() =  cong_action_;
 
 
 	/* actual size is data length plus header length */
@@ -463,7 +469,13 @@
                 nrexmitbytes_ += datalen;
         }
 
-	last_ack_sent_ = ackno;
+	last_ack_sent_ = ackno; 
+
+        //AG also add t_rtt_ ?
+	//uncomment to set TTL=RTO
+	//atp_set_ttl(p, now()+ rtt_timeout());
+	//+ (int(t_srtt_) >> T_SRTT_BITS)*tcp_tick_);
+
 	send(p, 0);
 }
 
@@ -818,6 +830,7 @@
 			recent_age_ = now();
 			recent_ = tcph->ts();
                         rtt_update(now() - tcph->ts_echo());
+			 t_backoff_ = 1; //AG: wasn't reset!
 		} else if (rtt_active_ && ackno > rtt_seq_) {
 			// got an RTT sample, record it
                         t_backoff_ = 1;
@@ -966,6 +979,11 @@
 	int datalen = th->size() - tcph->hlen(); // # payload bytes
 	int ackno = tcph->ackno();		 // ack # from packet
 	int tiflags = tcph->flags() ; 		 // tcp flags from packet
+
+	//AG fix by Felix to retransmit lost FINs
+	if (state_ == TCPS_CLOSED && tiflags & TH_FIN)
+		goto dropafterack;
+
 	if (state_ == TCPS_CLOSED)
 		goto drop;
 
@@ -1357,8 +1375,8 @@
 	if ((tiflags & TH_ACK) == 0) {
 		fprintf(stderr, "%f: FullTcpAgent::recv(%s) got packet lacking ACK (seq %d)\n",
 			now(), name(), tcph->seqno());
-		goto drop;
-	}
+		// goto drop; AG: this kills SYNS!!
+       	}
 
 	/*
 	 * Ack processing.
@@ -1427,6 +1445,7 @@
 		//
 		// do fast retransmit/recovery if at/past thresh
 		if (ackno <= highest_ack_) {
+
 			// an ACK which doesn't advance highest_ack_
 			if (datalen == 0 && (!dupseg_fix_ || !dupseg)) {
 				--pipe_; // ACK indicates pkt cached @ receiver
@@ -1454,30 +1473,36 @@
 				    ackno != highest_ack_) {
 					// not timed, or re-ordered ACK
 					dupacks_ = 0;
-				} else if (++dupacks_ == tcprexmtthresh_) {
-
-					fastrecov_ = TRUE;
-
-					/* re-sync the pipe_ estimate */
-					pipe_ = maxseq_ - highest_ack_;
-					pipe_ /= maxseg_;
-					pipe_ -= (dupacks_ + 1);
-
-pipe_ = int(cwnd_) - dupacks_ - 1;
-
-					dupack_action(); // maybe fast rexmt
+				} else {
+					
+				//AG fix to prevent fast recovery after RTO
+			             if (cwnd_==1)
 					goto drop;
 
-				} else if (dupacks_ > tcprexmtthresh_) {
-					if (reno_fastrecov_) {
-						// we just measure cwnd in
-						// packets, so don't scale by
-						// maxseg_ as real
-						// tcp does
-						cwnd_++;
+					if (++dupacks_ == tcprexmtthresh_) {									
+						fastrecov_ = TRUE;
+
+						/* re-sync the pipe_ estimate */
+						pipe_ = maxseq_ - highest_ack_;
+						pipe_ /= maxseg_;
+						pipe_ -= (dupacks_ + 1);
+						
+						pipe_ = int(cwnd_) - dupacks_ - 1;
+
+						dupack_action(); // maybe fast rexmt
+						goto drop;
+
+					} else if (dupacks_ > tcprexmtthresh_) {
+						if (reno_fastrecov_) {
+							// we just measure cwnd in
+							// packets, so don't scale by
+							// maxseg_ as real
+							// tcp does
+							cwnd_++;
+						}
+						send_much(0, REASON_NORMAL, maxburst_);
+						goto drop;
 					}
-					send_much(0, REASON_NORMAL, maxburst_);
-					goto drop;
 				}
 			} else {
 				// non zero-length [dataful] segment
@@ -1618,6 +1643,7 @@
 cancel_timers();	// DOES THIS BELONG HERE?, probably (see tcp_cose
 #endif
 				finish();
+				reset(); //AG
 				goto drop;
 			} else {
 				// should be a FIN we've seen
@@ -1832,6 +1858,7 @@
 void
 FullTcpAgent::timeout_action()
 {
+	eifel_timeout(); //AG
 	recover_ = maxseq_;
 	if (last_cwnd_action_ == CWND_ACTION_ECN) {
 		slowdown(CLOSE_CWND_ONE);
--- ./tcl/lib/ns-default.tcl	Wed May 30 19:58:39 2001
+++ ../ns-2.1b8/tcl/lib/ns-default.tcl	Thu Apr 25 16:00:16 2002
@@ -664,6 +664,11 @@
 
 Agent/TCP set CoarseTimer_      0
 
+#AG
+Agent/TCP set eifel_ 0
+Agent/TCP set eifel_cc_ 1
+Agent/TCP set eifel_rto_ 0
+
 Agent/TCPSink set sport_        0
 Agent/TCPSink set dport_        0         
 

