Full-TCP Reno Bug Fixes

July 23, 2002
Symptom: "recv'd pkt in CLOSED state flags: 0x18 (<PSH,ACK>)" error message
Problem: When the passive FIN and ACK are dropped (passive FIN and ACK are defined in Stevens' TCP/IP Illustrated vol. I pg. 233), the active closer retransmits its FIN. The passive closer is in the LAST_ACK state and should not ACK this FIN, but it does (plus, the ACK has the FIN bit set). The passive closer will retransmit its FIN, which will include the ACK of the active closer's FIN. Receiving two FINs will only confuse the active closer.
Dependency: Getting exactly the same results here depends on first applying the
Reno fix for when the last ACK is dropped.
Example: ns tcp-full-bugs.tcl reno-drop-passive-fin (Results)
Fix: Ignore FINs received when in the LAST_ACK state

FullTcpAgent::recv()

*** 1784,1789 ****
--- 1828,1842 ----
  		goto step6;
  
  	case TCPS_LAST_ACK:
+ 		/* 
+ 		 * The only way we're in LAST_ACK is if we've already
+ 		 * received a FIN, so ignore all retranmitted FINS.
+ 		 * -M. Weigle 7/23/02
+ 		 */
+ 		if (tiflags & TH_FIN) {
+ 			goto drop;
+ 		}
+ 		break;
  	case TCPS_CLOSING:
  		break;
  	} /* end switch(state_) */

November 24, 2001
Symptom: "unexpected timeout" error message
Problem: When Felix's fix (see August 6, 2000 below) is applied, a sender may send an ACK and not receive an answer
Fix: Allow timeouts in CLOSED state without printing an error message

FullTcpAgent::timeout()

*** 2486,2492 ****
  FullTcpAgent::timeout(int tno)
  {
  
! 	if (state_ == TCPS_CLOSED || state_ == TCPS_LISTEN) {
  	 	// shouldn't be getting timeouts here
  		fprintf(stderr, "%f: FullTcpAgent(%s): unexpected timeout %d in state %s\n",
  			now(), name(), tno, statestr(state_));
--- 2549,2559 ----
  FullTcpAgent::timeout(int tno)
  {
  
! 	/*
! 	 * Due to F. Hernandez-Campos' fix in recv(), we may send an ACK
! 	 * while in the CLOSED state.  -M. Weigle 7/24/01
! 	 */
! 	if (state_ == TCPS_LISTEN) {
  	 	// shouldn't be getting timeouts here
  		fprintf(stderr, "%f: FullTcpAgent(%s): unexpected timeout %d in state %s\n",
  			now(), name(), tno, statestr(state_));

November 24, 2001
Symptom: "got packet lacking ACK" error message
Problem: When a SYN/ACK is dropped, the connection initiator will retransmit its SYN (which doesn't contain an ACK).
Example: ns tcp-full-bugs.tcl reno-drop-synack (Results)
Fix: If the state is SYN_RECEIVED, we may receive a packet without an ACK, so don't print the error message. Note that the only difference after the fix is that the error message isn't printed. There is no difference in the trace.
Note: This fix was modified Aug 31, 2006 to reflect correct implementation in the soon-to-be-released ns-2.30. The goto drop; statement should be outside of the if clause.

FullTcpAgent::recv()

*** 1921,1930 ****
  	}
  
  	if ((tiflags & TH_ACK) == 0) {
! 		fprintf(stderr, "%f: FullTcpAgent::recv(%s) got packet lacking ACK (state:%d): ",
! 			now(), name(), state_);
! 		prpkt(pkt);
! 		goto drop;
  	}
  
  	/*
--- 1974,1993 ----
  	}
  
  	if ((tiflags & TH_ACK) == 0) {
! 		/*
! 		 * Added check for state != SYN_RECEIVED.  We will receive a 
! 		 * duplicate SYN in SYN_RECEIVED when our SYN/ACK was dropped.
! 		 * We should just ignore the duplicate SYN (our timeout for 
! 		 * resending the SYN/ACK is about the same as the client's 
! 		 * timeout for resending the SYN), but give no error message. 
! 		 * -M. Weigle 07/24/01
! 		 */
! 		if (state_ != TCPS_SYN_RECEIVED) {
! 			fprintf(stderr, "%f: FullTcpAgent::recv(%s) got packet lacking ACK (state:%d): ",
! 				now(), name(), state_);
! 			prpkt(pkt);
! 		}
!		goto drop;
  	}
  
  	/*

August 6, 2000
Symptom: "recv'd pkt in CLOSED state flags: 0x19 (<PSH,ACK,FIN>)" error message
Problem: During connection teardown, the last ACK may be dropped. When this occurs, the passive closer will retransmit its FIN (3rd packet in connection teardown) forever, but the active closer thinks the connection has been successfully closed. So, the passive closer will retransmit its FIN forever.
Example: ns tcp-full-bugs.tcl reno-drop-lastack (
Results)
Fix: Acknowledge the FIN.
(Fix contributed by Felix Hernandez -- email sent to the ns-users list)

FullTcpAgent::recv()

*** 1461,1466 ****
--- 1494,1510 ----
  //prpkt(pkt);
  //}
  
+ 	/* 
+ 	 * Acknowledge FIN from passive closer even in TCPS_CLOSED state
+ 	 * (since we lack TIME_WAIT state and RST packets,
+ 	 * the loss of the FIN packet from the passive closer will make that
+ 	 * endpoint retransmit the FIN forever)
+ 	 * -F. Hernandez-Campos 8/6/00
+ 	 */
+ 	if ( (state_ == TCPS_CLOSED) && (tiflags & TH_FIN) ) {
+ 		goto dropafterack;
+ 	}
+ 
  	/*
  	 * Don't expect to see anything while closed
  	 */

Fixed as of ns-2.27

November 7, 2001
Problem: When using sendmsg(size,MSG_EOF), the sender sends a size byte packet followed by a size byte FIN with the same sequence number as the previous data packet. The FIN should have 0 bytes of data.
Example: ns tcp-full-bugs.tcl reno-msg_eof-fin (
Results)
Fix: Update seqno before sending the FIN.
(See email sent to ns-users mailing list)
Michele C. Weigle