summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/net/tcp.c41
1 files changed, 32 insertions, 9 deletions
diff --git a/src/net/tcp.c b/src/net/tcp.c
index 88411a4d7..67deac900 100644
--- a/src/net/tcp.c
+++ b/src/net/tcp.c
@@ -84,6 +84,8 @@ struct tcp_connection {
struct list_head queue;
/** Retransmission timer */
struct retry_timer timer;
+ /** Shutdown (TIME_WAIT) timer */
+ struct retry_timer wait;
};
/**
@@ -94,6 +96,7 @@ static LIST_HEAD ( tcp_conns );
/* Forward declarations */
static struct interface_descriptor tcp_xfer_desc;
static void tcp_expired ( struct retry_timer *timer, int over );
+static void tcp_wait_expired ( struct retry_timer *timer, int over );
static int tcp_rx_ack ( struct tcp_connection *tcp, uint32_t ack,
uint32_t win );
@@ -229,6 +232,7 @@ static int tcp_open ( struct interface *xfer, struct sockaddr *peer,
ref_init ( &tcp->refcnt, NULL );
intf_init ( &tcp->xfer, &tcp_xfer_desc, &tcp->refcnt );
timer_init ( &tcp->timer, tcp_expired );
+ timer_init ( &tcp->wait, tcp_wait_expired );
tcp->prev_tcp_state = TCP_CLOSED;
tcp->tcp_state = TCP_STATE_SENT ( TCP_SYN );
tcp_dump_state ( tcp );
@@ -514,13 +518,12 @@ static int tcp_xmit ( struct tcp_connection *tcp, int force_send ) {
/**
* Retransmission timer expired
*
- * @v timer Retry timer
- * @v over Failure indicator
+ * @v timer Retransmission timer
+ * @v over Failure indicator
*/
static void tcp_expired ( struct retry_timer *timer, int over ) {
struct tcp_connection *tcp =
container_of ( timer, struct tcp_connection, timer );
- int graceful_close = TCP_CLOSED_GRACEFULLY ( tcp->tcp_state );
DBGC ( tcp, "TCP %p timer %s in %s for %08x..%08x %08x\n", tcp,
( over ? "expired" : "fired" ), tcp_state ( tcp->tcp_state ),
@@ -530,14 +533,12 @@ static void tcp_expired ( struct retry_timer *timer, int over ) {
( tcp->tcp_state == TCP_SYN_RCVD ) ||
( tcp->tcp_state == TCP_ESTABLISHED ) ||
( tcp->tcp_state == TCP_FIN_WAIT_1 ) ||
- ( tcp->tcp_state == TCP_TIME_WAIT ) ||
( tcp->tcp_state == TCP_CLOSE_WAIT ) ||
( tcp->tcp_state == TCP_CLOSING_OR_LAST_ACK ) );
- if ( over || graceful_close ) {
- /* If we have finally timed out and given up, or if
- * this is the result of a graceful close, terminate
- * the connection
+ if ( over ) {
+ /* If we have finally timed out and given up,
+ * terminate the connection
*/
tcp->tcp_state = TCP_CLOSED;
tcp_dump_state ( tcp );
@@ -549,6 +550,27 @@ static void tcp_expired ( struct retry_timer *timer, int over ) {
}
/**
+ * Shutdown timer expired
+ *
+ * @v timer Shutdown timer
+ * @v over Failure indicator
+ */
+static void tcp_wait_expired ( struct retry_timer *timer, int over __unused ) {
+ struct tcp_connection *tcp =
+ container_of ( timer, struct tcp_connection, wait );
+
+ assert ( tcp->tcp_state == TCP_TIME_WAIT );
+
+ DBGC ( tcp, "TCP %p wait complete in %s for %08x..%08x %08x\n", tcp,
+ tcp_state ( tcp->tcp_state ), tcp->snd_seq,
+ ( tcp->snd_seq + tcp->snd_sent ), tcp->rcv_ack );
+
+ tcp->tcp_state = TCP_CLOSED;
+ tcp_dump_state ( tcp );
+ tcp_close ( tcp, 0 );
+}
+
+/**
* Send RST response to incoming packet
*
* @v in_tcphdr TCP header of incoming packet
@@ -1020,7 +1042,8 @@ static int tcp_rx ( struct io_buffer *iobuf,
* timer to expire and cause the connection to be freed.
*/
if ( TCP_CLOSED_GRACEFULLY ( tcp->tcp_state ) ) {
- start_timer_fixed ( &tcp->timer, ( 2 * TCP_MSL ) );
+ stop_timer ( &tcp->wait );
+ start_timer_fixed ( &tcp->wait, ( 2 * TCP_MSL ) );
}
return 0;