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 55ea170..0738501 100644
--- a/include/haproxy/quic_sock.h
+++ b/include/haproxy/quic_sock.h
@@ -44,7 +44,7 @@
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 <qc> socket as uninitialized. */
diff --git a/src/quic_conn.c b/src/quic_conn.c
index 4a37309..5b9d5a3 100644
--- a/src/quic_conn.c
+++ b/src/quic_conn.c
@@ -3422,6 +3422,7 @@
*/
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 @@
* 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 @@
}
}
+ ret = 1;
+leave:
TRACE_LEAVE(QUIC_EV_CONN_SPPKTS, qc);
- return 1;
+ return ret;
}
/* Copy into <buf> buffer a stateless reset token depending on the
diff --git a/src/quic_sock.c b/src/quic_sock.c
index fd43859..b5ef5a0 100644
--- a/src/quic_sock.c
+++ b/src/quic_sock.c
@@ -501,13 +501,17 @@
/* Send a datagram stored into <buf> buffer with <sz> as size.
* The caller must ensure there is at least <sz> 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 <qc> 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 @@
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 @@
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;
}