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 * 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 char *inany_ntop(const union inany_addr *src, char *dst, socklen_t size)
{ {
const struct in_addr *v4 = inany_v4(src); 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) if (s < 0)
goto cancel; goto cancel;
if (c->mode == MODE_PASTA && if (tcp_splice_conn_from_sock(c, ref.tcp_listen, flow, s, &sa))
tcp_splice_conn_from_sock(c, ref.tcp_listen, flow, s, &sa))
return; return;
tcp_tap_conn_from_sock(c, ref.tcp_listen, flow, s, &sa, now); tcp_tap_conn_from_sock(c, ref.tcp_listen, flow, s, &sa, now);

View file

@ -431,13 +431,43 @@ bool tcp_splice_conn_from_sock(const struct ctx *c,
sa_family_t af; sa_family_t af;
uint8_t pif1; uint8_t pif1;
ASSERT(c->mode == MODE_PASTA); if (c->mode != MODE_PASTA)
return false;
inany_from_sockaddr(&src, &srcport, sa); 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)) if (!inany_is_loopback(&src))
return false; return false;
af = inany_v4(&src) ? AF_INET : AF_INET6; 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 = FLOW_START(flow, FLOW_TCP_SPLICE, tcp_splice, 0);
@ -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))) if (setsockopt(s0, SOL_TCP, TCP_QUICKACK, &((int){ 1 }), sizeof(int)))
flow_trace(conn, "failed to set TCP_QUICKACK on %i", s0); 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)) if (tcp_splice_connect(c, conn, af, pif1, dstport))
conn_flag(c, conn, CLOSING); conn_flag(c, conn, CLOSING);