Blame SOURCES/ipxe-Send-TCP-keepalives-on-idle-established-connections.patch

40e044
From 506552bef7f9813d1cf6722ce25d2d46aa79b92f Mon Sep 17 00:00:00 2001
40e044
From: Ladi Prosek <lprosek@redhat.com>
40e044
Date: Wed, 13 Jul 2016 11:42:16 +0200
40e044
Subject: [PATCH 1/2] Send TCP keepalives on idle established connections
40e044
40e044
RH-Author: Ladi Prosek <lprosek@redhat.com>
40e044
Message-id: <1468410136-5607-1-git-send-email-lprosek@redhat.com>
40e044
Patchwork-id: 71165
40e044
O-Subject: [RHEL7.3 ipxe PATCH] [tcp] Send TCP keepalives on idle established connections
40e044
Bugzilla: 1322056
40e044
RH-Acked-by: Stefan Hajnoczi <stefanha@redhat.com>
40e044
RH-Acked-by: Gerd Hoffmann <kraxel@redhat.com>
40e044
RH-Acked-by: Laszlo Ersek <lersek@redhat.com>
40e044
40e044
From: Michael Brown <mcb30@ipxe.org>
40e044
40e044
In some circumstances, intermediate devices may lose state in a way
40e044
that temporarily prevents the successful delivery of packets from a
40e044
TCP peer.  For example, a firewall may drop a NAT forwarding table
40e044
entry.
40e044
40e044
Since iPXE spends most of its time downloading files (and hence purely
40e044
receiving data, sending only TCP ACKs), this can easily happen in a
40e044
situation in which there is no reason for iPXE's TCP stack to generate
40e044
any retransmissions.  The temporary loss of connectivity can therefore
40e044
effectively become permanent.
40e044
40e044
Work around this problem by sending TCP keepalives after a period of
40e044
inactivity on an established connection.
40e044
40e044
TCP keepalives usually send a single garbage byte in sequence number
40e044
space that has already been ACKed by the peer.  Since we do not need
40e044
to elicit a response from the peer, we instead send pure ACKs (with no
40e044
garbage data) in order to keep the transmit code path simple.
40e044
40e044
Originally-implemented-by: Ladi Prosek <lprosek@redhat.com>
40e044
Debugged-by: Ladi Prosek <lprosek@redhat.com>
40e044
Signed-off-by: Michael Brown <mcb30@ipxe.org>
40e044
(cherry picked from commit 188789eb3cb83496bd48847da59c74e3f06d413e)
40e044
Signed-off-by: Ladi Prosek <lprosek@redhat.com>
40e044
40e044
Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=1322056
40e044
Brew: https://brewweb.engineering.redhat.com/brew/taskinfo?taskID=11343040
40e044
Signed-off-by: Miroslav Rezanina <mrezanin@redhat.com>
40e044
---
40e044
 src/include/ipxe/tcp.h |  8 ++++++++
40e044
 src/net/tcp.c          | 38 ++++++++++++++++++++++++++++++++++++++
40e044
 2 files changed, 46 insertions(+)
40e044
40e044
diff --git a/src/include/ipxe/tcp.h b/src/include/ipxe/tcp.h
40e044
index 063ebaa..faf4154 100644
40e044
--- a/src/include/ipxe/tcp.h
40e044
+++ b/src/include/ipxe/tcp.h
40e044
@@ -381,6 +381,14 @@ struct tcp_options {
40e044
 #define TCP_MSL ( 2 * 60 * TICKS_PER_SEC )
40e044
 
40e044
 /**
40e044
+ * TCP keepalive period
40e044
+ *
40e044
+ * We send keepalive ACKs after this period of inactivity has elapsed
40e044
+ * on an established connection.
40e044
+ */
40e044
+#define TCP_KEEPALIVE_DELAY ( 15 * TICKS_PER_SEC )
40e044
+
40e044
+/**
40e044
  * TCP maximum header length
40e044
  *
40e044
  */
40e044
diff --git a/src/net/tcp.c b/src/net/tcp.c
40e044
index c69c83b..77a7d8e 100644
40e044
--- a/src/net/tcp.c
40e044
+++ b/src/net/tcp.c
40e044
@@ -113,6 +113,8 @@ struct tcp_connection {
40e044
 	struct process process;
40e044
 	/** Retransmission timer */
40e044
 	struct retry_timer timer;
40e044
+	/** Keepalive timer */
40e044
+	struct retry_timer keepalive;
40e044
 	/** Shutdown (TIME_WAIT) timer */
40e044
 	struct retry_timer wait;
40e044
 
40e044
@@ -177,6 +179,7 @@ static struct profiler tcp_xfer_profiler __profiler = { .name = "tcp.xfer" };
40e044
 static struct process_descriptor tcp_process_desc;
40e044
 static struct interface_descriptor tcp_xfer_desc;
40e044
 static void tcp_expired ( struct retry_timer *timer, int over );
40e044
+static void tcp_keepalive_expired ( struct retry_timer *timer, int over );
40e044
 static void tcp_wait_expired ( struct retry_timer *timer, int over );
40e044
 static struct tcp_connection * tcp_demux ( unsigned int local_port );
40e044
 static int tcp_rx_ack ( struct tcp_connection *tcp, uint32_t ack,
40e044
@@ -284,6 +287,7 @@ static int tcp_open ( struct interface *xfer, struct sockaddr *peer,
40e044
 	intf_init ( &tcp->xfer, &tcp_xfer_desc, &tcp->refcnt );
40e044
 	process_init_stopped ( &tcp->process, &tcp_process_desc, &tcp->refcnt );
40e044
 	timer_init ( &tcp->timer, tcp_expired, &tcp->refcnt );
40e044
+	timer_init ( &tcp->keepalive, tcp_keepalive_expired, &tcp->refcnt );
40e044
 	timer_init ( &tcp->wait, tcp_wait_expired, &tcp->refcnt );
40e044
 	tcp->prev_tcp_state = TCP_CLOSED;
40e044
 	tcp->tcp_state = TCP_STATE_SENT ( TCP_SYN );
40e044
@@ -380,6 +384,7 @@ static void tcp_close ( struct tcp_connection *tcp, int rc ) {
40e044
 		/* Remove from list and drop reference */
40e044
 		process_del ( &tcp->process );
40e044
 		stop_timer ( &tcp->timer );
40e044
+		stop_timer ( &tcp->keepalive );
40e044
 		stop_timer ( &tcp->wait );
40e044
 		list_del ( &tcp->list );
40e044
 		ref_put ( &tcp->refcnt );
40e044
@@ -394,6 +399,9 @@ static void tcp_close ( struct tcp_connection *tcp, int rc ) {
40e044
 	if ( ! ( tcp->tcp_state & TCP_STATE_ACKED ( TCP_SYN ) ) )
40e044
 		tcp_rx_ack ( tcp, ( tcp->snd_seq + 1 ), 0 );
40e044
 
40e044
+	/* Stop keepalive timer */
40e044
+	stop_timer ( &tcp->keepalive );
40e044
+
40e044
 	/* If we have no data remaining to send, start sending FIN */
40e044
 	if ( list_empty ( &tcp->tx_queue ) &&
40e044
 	     ! ( tcp->tcp_state & TCP_STATE_SENT ( TCP_FIN ) ) ) {
40e044
@@ -802,6 +810,32 @@ static void tcp_expired ( struct retry_timer *timer, int over ) {
40e044
 }
40e044
 
40e044
 /**
40e044
+ * Keepalive timer expired
40e044
+ *
40e044
+ * @v timer		Keepalive timer
40e044
+ * @v over		Failure indicator
40e044
+ */
40e044
+static void tcp_keepalive_expired ( struct retry_timer *timer,
40e044
+				    int over __unused ) {
40e044
+	struct tcp_connection *tcp =
40e044
+		container_of ( timer, struct tcp_connection, keepalive );
40e044
+
40e044
+	DBGC ( tcp, "TCP %p sending keepalive\n", tcp );
40e044
+
40e044
+	/* Reset keepalive timer */
40e044
+	start_timer_fixed ( &tcp->keepalive, TCP_KEEPALIVE_DELAY );
40e044
+
40e044
+	/* Send keepalive.  We do this only to preserve or restore
40e044
+	 * state in intermediate devices (e.g. firewall NAT tables);
40e044
+	 * we don't actually care about eliciting a response to verify
40e044
+	 * that the peer is still alive.  We therefore send just a
40e044
+	 * pure ACK, to keep our transmit path simple.
40e044
+	 */
40e044
+	tcp->flags |= TCP_ACK_PENDING;
40e044
+	tcp_xmit ( tcp );
40e044
+}
40e044
+
40e044
+/**
40e044
  * Shutdown timer expired
40e044
  *
40e044
  * @v timer		Shutdown timer
40e044
@@ -1063,6 +1097,10 @@ static int tcp_rx_ack ( struct tcp_connection *tcp, uint32_t ack,
40e044
 	/* Update window size */
40e044
 	tcp->snd_win = win;
40e044
 
40e044
+	/* Hold off (or start) the keepalive timer, if applicable */
40e044
+	if ( ! ( tcp->tcp_state & TCP_STATE_SENT ( TCP_FIN ) ) )
40e044
+		start_timer_fixed ( &tcp->keepalive, TCP_KEEPALIVE_DELAY );
40e044
+
40e044
 	/* Ignore ACKs that don't actually acknowledge any new data.
40e044
 	 * (In particular, do not stop the retransmission timer; this
40e044
 	 * avoids creating a sorceror's apprentice syndrome when a
40e044
-- 
40e044
1.8.3.1
40e044