]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MINOR: quic: Kill the connections on ICMP (port unreachable) packet receipt
authorFrédéric Lécaille <flecaille@haproxy.com>
Fri, 10 Feb 2023 13:13:43 +0000 (14:13 +0100)
committerAmaury Denoyelle <adenoyelle@haproxy.com>
Fri, 17 Feb 2023 16:36:30 +0000 (17:36 +0100)
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.

include/haproxy/quic_sock.h
src/quic_conn.c
src/quic_sock.c

index 55ea170a18c06b321fe1ce0895a796bda23678e9..073850103144812e54ba4ee3ee9495cfbd9b6ba6 100644 (file)
@@ -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 <qc> socket as uninitialized. */
index 4a3730958fba3f20975b4a577949249104cb02a9..5b9d5a327561a6f21142435e7de12f854144fe01 100644 (file)
@@ -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 <buf> buffer a stateless reset token depending on the
index fd43859f3da45742dbbdec6ba8e624f73f7b956c..b5ef5a0400e8bb3f15d232e72ce4770309e2122b 100644 (file)
@@ -501,13 +501,17 @@ static void quic_conn_sock_fd_iocb(int fd)
 /* 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 @@ 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;
        }