Hi,
I tested a simple configuration where HAProxy connects to a backend
server
using TLS 1.3 with ktls and splice options enabled.
haproxy.cfg
```
global
expose-experimental-directives
nbthread 1
defaults
mode tcp
timeout connect 1s
timeout client 1s
timeout server 1s
option splice-request
option splice-response
frontend tcp_in
bind *:8080
default_backend tls_out
backend tls_out
server server 127.0.0.1:4043 ssl verify none ktls on force-tlsv13
```
After the initial handshake phase, the server sends two
new_session_ticket
handshake messages.
HAProxy tries to receive those messages with the splice syscall which
results
in the EINVAL error.
There is a comment in the HAProxy source (in the raw_sock_to_pipe
function)
regarding such situation:
```
* EINVAL, however, is a bit special.
* If we're trying to splice from a KTLS
* socket, the kernel may return EINVAL
* to signal that the current TLS record
* is not application data, and that we
* have to call recvmsg() to get it.
* This is not really an error, and doesn't
* mean we won't be able to splice later.
* Choosing EINVAL there is a bit unfortunate,
* because it can mean many things, but we
* should not get it for any other reason.
*/
```
Using strace I've seen only repeated splice calls returning EINVAL,
without
any recvmsg call to handle the failed splice call.
```
epoll_wait(4, [], 200, 0) = 0
clock_gettime(CLOCK_THREAD_CPUTIME_ID, {tv_sec=0, tv_nsec=429214144}) =
0
splice(9, NULL, 11, NULL, 1073741824, SPLICE_F_MOVE|SPLICE_F_NONBLOCK) =
-1 EINVAL (Invalid argument)
clock_gettime(CLOCK_THREAD_CPUTIME_ID, {tv_sec=0, tv_nsec=429392356}) =
0
epoll_wait(4, [], 200, 0) = 0
clock_gettime(CLOCK_THREAD_CPUTIME_ID, {tv_sec=0, tv_nsec=429572280}) =
0
splice(9, NULL, 11, NULL, 1073741824, SPLICE_F_MOVE|SPLICE_F_NONBLOCK) =
-1 EINVAL (Invalid argument)
clock_gettime(CLOCK_THREAD_CPUTIME_ID, {tv_sec=0, tv_nsec=429753718}) =
0
epoll_wait(4, [], 200, 0) = 0
clock_gettime(CLOCK_THREAD_CPUTIME_ID, {tv_sec=0, tv_nsec=429931308}) =
0
splice(9, NULL, 11, NULL, 1073741824, SPLICE_F_MOVE|SPLICE_F_NONBLOCK) =
-1 EINVAL (Invalid argument)
clock_gettime(CLOCK_THREAD_CPUTIME_ID, {tv_sec=0, tv_nsec=430114691}) =
0
epoll_wait(4, [], 200, 0) = 0
clock_gettime(CLOCK_THREAD_CPUTIME_ID, {tv_sec=0, tv_nsec=430520235}) =
0
splice(9, NULL, 11, NULL, 1073741824, SPLICE_F_MOVE|SPLICE_F_NONBLOCK) =
-1 EINVAL (Invalid argument)
clock_gettime(CLOCK_THREAD_CPUTIME_ID, {tv_sec=0, tv_nsec=430703514}) =
0
epoll_wait(4, [], 200, 0) = 0
clock_gettime(CLOCK_THREAD_CPUTIME_ID, {tv_sec=0, tv_nsec=430879870}) =
0
splice(9, NULL, 11, NULL, 1073741824, SPLICE_F_MOVE|SPLICE_F_NONBLOCK) =
-1 EINVAL (Invalid argument)
clock_gettime(CLOCK_THREAD_CPUTIME_ID, {tv_sec=0, tv_nsec=431062900}) =
0
epoll_wait(4, [], 200, 0) = 0
clock_gettime(CLOCK_THREAD_CPUTIME_ID, {tv_sec=0, tv_nsec=431239849}) =
0
splice(9, NULL, 11, NULL, 1073741824, SPLICE_F_MOVE|SPLICE_F_NONBLOCK) =
-1 EINVAL (Invalid argument)
clock_gettime(CLOCK_THREAD_CPUTIME_ID, {tv_sec=0, tv_nsec=431422559}) =
0
epoll_wait(4, [], 200, 0) = 0
```
I've tested versions 3.4.2 and git master, both with OpenSSL 3.5.6 and
AWS-LC 5.1.0.
I've added a simple hack in the raw_sock_to_pipe function:
```
diff --git a/src/raw_sock.c b/src/raw_sock.c
index 33fb8f5c3..1552e44bc 100644
--- a/src/raw_sock.c
+++ b/src/raw_sock.c
@@ -137,6 +137,21 @@ int raw_sock_to_pipe(struct connection *conn, void
*xprt_ctx, struct pipe *pipe,
*/
if (errno != EINVAL)
retval = -1;
+ else
+ {
+ char dbuff[1024];
+ char cbuff[64];
+ struct msghdr msg;
+ struct iovec iov;
+ memset(&msg, 0, sizeof(msg));
+ iov.iov_base = dbuff;
+ iov.iov_len = sizeof(dbuff);
+ msg.msg_control = cbuff;
+ msg.msg_controllen =
sizeof(cbuff);
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+ (void)recvmsg(conn->handle.fd,
&msg, 0);
+ }
goto leave;
}
else if (errno == EINTR) {
```
And it seems to work (HAProxy receives the data from the server and
forwards
it to the client).
Of course it's not a proper solution but unfortunately I'm not familiar
enough
with the HAProxy architecture to make a proper fix.
Best regards,
Karol