Browse Source

[tcp] Send TCP keepalives on idle established connections

In some circumstances, intermediate devices may lose state in a way
that temporarily prevents the successful delivery of packets from a
TCP peer.  For example, a firewall may drop a NAT forwarding table
entry.

Since iPXE spends most of its time downloading files (and hence purely
receiving data, sending only TCP ACKs), this can easily happen in a
situation in which there is no reason for iPXE's TCP stack to generate
any retransmissions.  The temporary loss of connectivity can therefore
effectively become permanent.

Work around this problem by sending TCP keepalives after a period of
inactivity on an established connection.

TCP keepalives usually send a single garbage byte in sequence number
space that has already been ACKed by the peer.  Since we do not need
to elicit a response from the peer, we instead send pure ACKs (with no
garbage data) in order to keep the transmit code path simple.

Originally-implemented-by: Ladi Prosek <lprosek@redhat.com>
Debugged-by: Ladi Prosek <lprosek@redhat.com>
Signed-off-by: Michael Brown <mcb30@ipxe.org>
tags/v1.20.1
Michael Brown 8 years ago
parent
commit
188789eb3c
2 changed files with 46 additions and 0 deletions
  1. 8
    0
      src/include/ipxe/tcp.h
  2. 38
    0
      src/net/tcp.c

+ 8
- 0
src/include/ipxe/tcp.h View File

378
  */
378
  */
379
 #define TCP_MSL ( 2 * 60 * TICKS_PER_SEC )
379
 #define TCP_MSL ( 2 * 60 * TICKS_PER_SEC )
380
 
380
 
381
+/**
382
+ * TCP keepalive period
383
+ *
384
+ * We send keepalive ACKs after this period of inactivity has elapsed
385
+ * on an established connection.
386
+ */
387
+#define TCP_KEEPALIVE_DELAY ( 15 * TICKS_PER_SEC )
388
+
381
 /**
389
 /**
382
  * TCP maximum header length
390
  * TCP maximum header length
383
  *
391
  *

+ 38
- 0
src/net/tcp.c View File

113
 	struct process process;
113
 	struct process process;
114
 	/** Retransmission timer */
114
 	/** Retransmission timer */
115
 	struct retry_timer timer;
115
 	struct retry_timer timer;
116
+	/** Keepalive timer */
117
+	struct retry_timer keepalive;
116
 	/** Shutdown (TIME_WAIT) timer */
118
 	/** Shutdown (TIME_WAIT) timer */
117
 	struct retry_timer wait;
119
 	struct retry_timer wait;
118
 
120
 
177
 static struct process_descriptor tcp_process_desc;
179
 static struct process_descriptor tcp_process_desc;
178
 static struct interface_descriptor tcp_xfer_desc;
180
 static struct interface_descriptor tcp_xfer_desc;
179
 static void tcp_expired ( struct retry_timer *timer, int over );
181
 static void tcp_expired ( struct retry_timer *timer, int over );
182
+static void tcp_keepalive_expired ( struct retry_timer *timer, int over );
180
 static void tcp_wait_expired ( struct retry_timer *timer, int over );
183
 static void tcp_wait_expired ( struct retry_timer *timer, int over );
181
 static struct tcp_connection * tcp_demux ( unsigned int local_port );
184
 static struct tcp_connection * tcp_demux ( unsigned int local_port );
182
 static int tcp_rx_ack ( struct tcp_connection *tcp, uint32_t ack,
185
 static int tcp_rx_ack ( struct tcp_connection *tcp, uint32_t ack,
284
 	intf_init ( &tcp->xfer, &tcp_xfer_desc, &tcp->refcnt );
287
 	intf_init ( &tcp->xfer, &tcp_xfer_desc, &tcp->refcnt );
285
 	process_init_stopped ( &tcp->process, &tcp_process_desc, &tcp->refcnt );
288
 	process_init_stopped ( &tcp->process, &tcp_process_desc, &tcp->refcnt );
286
 	timer_init ( &tcp->timer, tcp_expired, &tcp->refcnt );
289
 	timer_init ( &tcp->timer, tcp_expired, &tcp->refcnt );
290
+	timer_init ( &tcp->keepalive, tcp_keepalive_expired, &tcp->refcnt );
287
 	timer_init ( &tcp->wait, tcp_wait_expired, &tcp->refcnt );
291
 	timer_init ( &tcp->wait, tcp_wait_expired, &tcp->refcnt );
288
 	tcp->prev_tcp_state = TCP_CLOSED;
292
 	tcp->prev_tcp_state = TCP_CLOSED;
289
 	tcp->tcp_state = TCP_STATE_SENT ( TCP_SYN );
293
 	tcp->tcp_state = TCP_STATE_SENT ( TCP_SYN );
380
 		/* Remove from list and drop reference */
384
 		/* Remove from list and drop reference */
381
 		process_del ( &tcp->process );
385
 		process_del ( &tcp->process );
382
 		stop_timer ( &tcp->timer );
386
 		stop_timer ( &tcp->timer );
387
+		stop_timer ( &tcp->keepalive );
383
 		stop_timer ( &tcp->wait );
388
 		stop_timer ( &tcp->wait );
384
 		list_del ( &tcp->list );
389
 		list_del ( &tcp->list );
385
 		ref_put ( &tcp->refcnt );
390
 		ref_put ( &tcp->refcnt );
394
 	if ( ! ( tcp->tcp_state & TCP_STATE_ACKED ( TCP_SYN ) ) )
399
 	if ( ! ( tcp->tcp_state & TCP_STATE_ACKED ( TCP_SYN ) ) )
395
 		tcp_rx_ack ( tcp, ( tcp->snd_seq + 1 ), 0 );
400
 		tcp_rx_ack ( tcp, ( tcp->snd_seq + 1 ), 0 );
396
 
401
 
402
+	/* Stop keepalive timer */
403
+	stop_timer ( &tcp->keepalive );
404
+
397
 	/* If we have no data remaining to send, start sending FIN */
405
 	/* If we have no data remaining to send, start sending FIN */
398
 	if ( list_empty ( &tcp->tx_queue ) &&
406
 	if ( list_empty ( &tcp->tx_queue ) &&
399
 	     ! ( tcp->tcp_state & TCP_STATE_SENT ( TCP_FIN ) ) ) {
407
 	     ! ( tcp->tcp_state & TCP_STATE_SENT ( TCP_FIN ) ) ) {
801
 	}
809
 	}
802
 }
810
 }
803
 
811
 
812
+/**
813
+ * Keepalive timer expired
814
+ *
815
+ * @v timer		Keepalive timer
816
+ * @v over		Failure indicator
817
+ */
818
+static void tcp_keepalive_expired ( struct retry_timer *timer,
819
+				    int over __unused ) {
820
+	struct tcp_connection *tcp =
821
+		container_of ( timer, struct tcp_connection, keepalive );
822
+
823
+	DBGC ( tcp, "TCP %p sending keepalive\n", tcp );
824
+
825
+	/* Reset keepalive timer */
826
+	start_timer_fixed ( &tcp->keepalive, TCP_KEEPALIVE_DELAY );
827
+
828
+	/* Send keepalive.  We do this only to preserve or restore
829
+	 * state in intermediate devices (e.g. firewall NAT tables);
830
+	 * we don't actually care about eliciting a response to verify
831
+	 * that the peer is still alive.  We therefore send just a
832
+	 * pure ACK, to keep our transmit path simple.
833
+	 */
834
+	tcp->flags |= TCP_ACK_PENDING;
835
+	tcp_xmit ( tcp );
836
+}
837
+
804
 /**
838
 /**
805
  * Shutdown timer expired
839
  * Shutdown timer expired
806
  *
840
  *
1105
 	/* Update window size */
1139
 	/* Update window size */
1106
 	tcp->snd_win = win;
1140
 	tcp->snd_win = win;
1107
 
1141
 
1142
+	/* Hold off (or start) the keepalive timer, if applicable */
1143
+	if ( ! ( tcp->tcp_state & TCP_STATE_SENT ( TCP_FIN ) ) )
1144
+		start_timer_fixed ( &tcp->keepalive, TCP_KEEPALIVE_DELAY );
1145
+
1108
 	/* Ignore ACKs that don't actually acknowledge any new data.
1146
 	/* Ignore ACKs that don't actually acknowledge any new data.
1109
 	 * (In particular, do not stop the retransmission timer; this
1147
 	 * (In particular, do not stop the retransmission timer; this
1110
 	 * avoids creating a sorceror's apprentice syndrome when a
1148
 	 * avoids creating a sorceror's apprentice syndrome when a

Loading…
Cancel
Save