From: Frédéric Lécaille Date: Fri, 10 Feb 2023 13:13:43 +0000 (+0100) Subject: MINOR: quic: Kill the connections on ICMP (port unreachable) packet receipt X-Git-Tag: v2.8-dev5~179 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=a2c62c314;p=thirdparty%2Fhaproxy.git MINOR: quic: Kill the connections on ICMP (port unreachable) packet receipt The send*() syscall which are responsible of such ICMP packets reception fails with ECONNREFUSED as errno. man(7) udp ECONNREFUSED No receiver was associated with the destination address. This might be caused by a previous packet sent over the socket. We must kill asap the underlying connection. Must be backported to 2.7. --- diff --git a/include/haproxy/quic_sock.h b/include/haproxy/quic_sock.h index 55ea170a18..0738501031 100644 --- a/include/haproxy/quic_sock.h +++ b/include/haproxy/quic_sock.h @@ -44,7 +44,7 @@ struct connection *quic_sock_accept_conn(struct listener *l, int *status); struct task *quic_lstnr_dghdlr(struct task *t, void *ctx, unsigned int state); void quic_lstnr_sock_fd_iocb(int fd); int qc_snd_buf(struct quic_conn *qc, const struct buffer *buf, size_t count, - int flags); + int flags, int *syscall_errno); int qc_rcv_buf(struct quic_conn *qc); /* Set default value for socket as uninitialized. */ diff --git a/src/quic_conn.c b/src/quic_conn.c index 4a3730958f..5b9d5a3275 100644 --- a/src/quic_conn.c +++ b/src/quic_conn.c @@ -3422,6 +3422,7 @@ static int qc_prep_pkts(struct quic_conn *qc, struct buffer *buf, */ int qc_send_ppkts(struct buffer *buf, struct ssl_sock_ctx *ctx) { + int ret = 0; struct quic_conn *qc; char skip_sendto = 0; @@ -3457,7 +3458,17 @@ int qc_send_ppkts(struct buffer *buf, struct ssl_sock_ctx *ctx) * quic-conn fd management. */ if (!skip_sendto) { - if (qc_snd_buf(qc, &tmpbuf, tmpbuf.data, 0)) { + int syscall_errno; + + syscall_errno = 0; + if (qc_snd_buf(qc, &tmpbuf, tmpbuf.data, 0, &syscall_errno)) { + if (syscall_errno == ECONNREFUSED) { + /* Let's kill this connection asap. */ + TRACE_PROTO("UDP port unreachable", QUIC_EV_CONN_SPPKTS, qc); + qc_kill_conn(qc); + goto leave; + } + skip_sendto = 1; TRACE_ERROR("sendto error, simulate sending for the rest of data", QUIC_EV_CONN_SPPKTS, qc); } @@ -3506,9 +3517,11 @@ int qc_send_ppkts(struct buffer *buf, struct ssl_sock_ctx *ctx) } } + ret = 1; +leave: TRACE_LEAVE(QUIC_EV_CONN_SPPKTS, qc); - return 1; + return ret; } /* Copy into buffer a stateless reset token depending on the diff --git a/src/quic_sock.c b/src/quic_sock.c index fd43859f3d..b5ef5a0400 100644 --- a/src/quic_sock.c +++ b/src/quic_sock.c @@ -501,13 +501,17 @@ static void quic_conn_sock_fd_iocb(int fd) /* Send a datagram stored into buffer with as size. * The caller must ensure there is at least bytes in this buffer. * - * Returns 0 on success else non-zero. + * Returns 0 on success else non-zero. When failed, this function also + * sets <*syscall_errno> to the errno only when the send*() syscall failed. + * As the C library will never set errno to 0, the caller must set + * <*syscall_errno> to 0 before calling this function to be sure to get + * the correct errno in case a send*() syscall failure. * * TODO standardize this function for a generic UDP sendto wrapper. This can be * done by removing the arg and replace it with address/port. */ int qc_snd_buf(struct quic_conn *qc, const struct buffer *buf, size_t sz, - int flags) + int flags, int *syscall_errno) { ssize_t ret; @@ -615,6 +619,7 @@ int qc_snd_buf(struct quic_conn *qc, const struct buffer *buf, size_t sz, EXTRA_COUNTERS_GET(prx->extra_counters_fe, &quic_stats_module); + *syscall_errno = errno; /* TODO adjust errno for UDP context. */ if (errno == EAGAIN || errno == EWOULDBLOCK || errno == ENOTCONN || errno == EINPROGRESS || errno == EBADF) { @@ -630,6 +635,10 @@ int qc_snd_buf(struct quic_conn *qc, const struct buffer *buf, size_t sz, HA_ATOMIC_INC(&prx_counters->sendto_err_unknown); } + /* Note that one must not consider that this macro will not modify errno. */ + TRACE_PRINTF(TRACE_LEVEL_DEVELOPER, QUIC_EV_CONN_LPKT, qc, 0, 0, 0, + "syscall error (errno=%d)", *syscall_errno); + return 1; }