When we receive new data on a TCP socket, we attempt to forward all of it to the tap interface immediately. However, if the tap buffers fill up, we might fail to transfer some of it. In that case we'll need to try again later. Currently that's handled by the EPOLLIN event being re-asserted in level mode at some point by complicated fiddling of the event masks. In preparation for a simpler way of triggering the retry, keep track of which connections are in this state with a new TAP_FULL flag. We also keep a count of the total number of connections currently in the TAP_FULL state. Signed-off-by: David Gibson <david(a)gibson.dropbear.id.au> --- tcp.c | 12 +++++++++++- tcp_buf.c | 3 +++ tcp_conn.h | 1 + 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/tcp.c b/tcp.c index 32e45e09..4b478432 100644 --- a/tcp.c +++ b/tcp.c @@ -349,7 +349,7 @@ static const char *tcp_state_str[] __attribute((__unused__)) = { static const char *tcp_flag_str[] __attribute((__unused__)) = { "STALLED", "LOCAL", "ACTIVE_CLOSE", "ACK_TO_TAP_DUE", - "ACK_FROM_TAP_DUE", + "ACK_FROM_TAP_DUE", "TAP_FULL", }; /* Listening sockets, used for automatic port forwarding in pasta mode only */ @@ -381,6 +381,11 @@ static struct iovec tcp_iov [UIO_MAXIOV]; int init_sock_pool4 [TCP_SOCK_POOL_SIZE]; int init_sock_pool6 [TCP_SOCK_POOL_SIZE]; +/* Number of connections with pending socket data that couldn't be sent because + * we ran out of buffer space on the tap side + */ +unsigned num_tap_full; + /** * conn_at_sidx() - Get TCP connection specific flow at given sidx * @sidx: Flow and side to retrieve @@ -597,6 +602,11 @@ void conn_flag_do(const struct ctx *c, struct tcp_tap_conn *conn, (flag == ~ACK_FROM_TAP_DUE && (conn->flags & ACK_TO_TAP_DUE)) || (flag == ~ACK_TO_TAP_DUE && (conn->flags & ACK_FROM_TAP_DUE))) tcp_timer_ctl(c, conn); + + if (flag == TAP_FULL) + num_tap_full++; + else if (flag == ~TAP_FULL) + num_tap_full--; } /** diff --git a/tcp_buf.c b/tcp_buf.c index 83f91a37..0ccb9e6b 100644 --- a/tcp_buf.c +++ b/tcp_buf.c @@ -250,6 +250,8 @@ static void tcp_revert_seq(const struct ctx *c, struct tcp_tap_conn **conns, uint32_t seq = ntohl(th->seq); uint32_t peek_offset; + conn_flag(c, conn, TAP_FULL); + if (SEQ_LE(conn->seq_to_tap, seq)) continue; @@ -526,6 +528,7 @@ int tcp_buf_data_from_sock(const struct ctx *c, struct tcp_tap_conn *conn) seq += dlen; } + conn_flag(c, conn, ~TAP_FULL); conn_flag(c, conn, ACK_FROM_TAP_DUE); return 0; diff --git a/tcp_conn.h b/tcp_conn.h index 6ae05115..9677678c 100644 --- a/tcp_conn.h +++ b/tcp_conn.h @@ -77,6 +77,7 @@ struct tcp_tap_conn { #define ACTIVE_CLOSE BIT(2) #define ACK_TO_TAP_DUE BIT(3) #define ACK_FROM_TAP_DUE BIT(4) +#define TAP_FULL BIT(5) #define SNDBUF_BITS 24 unsigned int sndbuf :SNDBUF_BITS; -- 2.46.0