tcp_splice: Improve logic deciding when to splice

This makes several tweaks to improve the logic which decides whether
we're able to use the splice method for a new connection.

 * Rather than only calling tcp_splice_conn_from_sock() in pasta mode, we
   check for pasta mode within it, better localising the checks.
 * Previously if we got a connection from a non-loopback address we'd
   always fall back to the "tap" path, even if the  connection was on a
   socket in the namespace.  If we did get a non-loopback address on a
   namespace socket, something has gone wrong and the "tap" path certainly
   won't be able to handle it.  Report the error and close, rather than
   passing it along to tap.

Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
Signed-off-by: Stefano Brivio <sbrivio@redhat.com>
This commit is contained in:
David Gibson 2024-02-28 22:25:15 +11:00 committed by Stefano Brivio
parent 4c2d923b12
commit ee677e0a42
3 changed files with 35 additions and 17 deletions

View file

@ -39,7 +39,6 @@ const union inany_addr inany_any4 = {
*
* Return: On success, a non-null pointer to @dst, NULL on failure
*/
/* cppcheck-suppress unusedFunction */
const char *inany_ntop(const union inany_addr *src, char *dst, socklen_t size)
{
const struct in_addr *v4 = inany_v4(src);

3
tcp.c
View file

@ -2737,8 +2737,7 @@ void tcp_listen_handler(struct ctx *c, union epoll_ref ref,
if (s < 0)
goto cancel;
if (c->mode == MODE_PASTA &&
tcp_splice_conn_from_sock(c, ref.tcp_listen, flow, s, &sa))
if (tcp_splice_conn_from_sock(c, ref.tcp_listen, flow, s, &sa))
return;
tcp_tap_conn_from_sock(c, ref.tcp_listen, flow, s, &sa, now);

View file

@ -431,14 +431,44 @@ bool tcp_splice_conn_from_sock(const struct ctx *c,
sa_family_t af;
uint8_t pif1;
ASSERT(c->mode == MODE_PASTA);
inany_from_sockaddr(&src, &srcport, sa);
if (!inany_is_loopback(&src))
if (c->mode != MODE_PASTA)
return false;
inany_from_sockaddr(&src, &srcport, sa);
af = inany_v4(&src) ? AF_INET : AF_INET6;
switch (ref.pif) {
case PIF_SPLICE:
if (!inany_is_loopback(&src)) {
char str[INANY_ADDRSTRLEN];
/* We can't use flow_err() etc. because we haven't set
* the flow type yet
*/
warn("Bad source address %s for splice, closing",
inany_ntop(&src, str, sizeof(str)));
/* We *don't* want to fall back to tap */
flow_alloc_cancel(flow);
return true;
}
pif1 = PIF_HOST;
dstport += c->tcp.fwd_out.delta[dstport];
break;
case PIF_HOST:
if (!inany_is_loopback(&src))
return false;
pif1 = PIF_SPLICE;
dstport += c->tcp.fwd_in.delta[dstport];
break;
default:
return false;
}
conn = FLOW_START(flow, FLOW_TCP_SPLICE, tcp_splice, 0);
conn->flags = af == AF_INET ? 0 : SPLICE_V6;
@ -450,16 +480,6 @@ bool tcp_splice_conn_from_sock(const struct ctx *c,
if (setsockopt(s0, SOL_TCP, TCP_QUICKACK, &((int){ 1 }), sizeof(int)))
flow_trace(conn, "failed to set TCP_QUICKACK on %i", s0);
if (ref.pif == PIF_SPLICE) {
pif1 = PIF_HOST;
dstport += c->tcp.fwd_out.delta[dstport];
} else {
ASSERT(ref.pif == PIF_HOST);
pif1 = PIF_SPLICE;
dstport += c->tcp.fwd_in.delta[dstport];
}
if (tcp_splice_connect(c, conn, af, pif1, dstport))
conn_flag(c, conn, CLOSING);