|
|
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 |
|