#include <haproxy/quic_conn.h>
#define _GNU_SOURCE
-#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
/* Send datagrams stored in <buf>.
*
- * This function returns 1 for success. Even if sendto() syscall failed,
- * buffer is drained and packets are considered as emitted and this function returns 1
- * There is a unique exception when sendto() fails with ECONNREFUSED as errno,
- * this function returns 0.
- * QUIC loss detection mechanism is used as a back door way to retry sending.
+ * This function returns 1 for success. On error, there is several behavior
+ * depending on underlying sendto() error :
+ * - for a fatal error, 0 is returned and connection is killed.
+ * - a transient error is assimilated to a success case with 1 returned.
+ * Remaining data are purged from the buffer and will eventually be detected
+ * as lost which gives the opportunity to retry sending.
*/
int qc_send_ppkts(struct buffer *buf, struct ssl_sock_ctx *ctx)
{
* quic-conn fd management.
*/
if (!skip_sendto) {
- 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);
- b_del(buf, buf->data);
- goto leave;
- }
-
+ int ret = qc_snd_buf(qc, &tmpbuf, tmpbuf.data, 0);
+ if (ret < 0) {
+ qc_kill_conn(qc);
+ b_del(buf, buf->data);
+ goto leave;
+ }
+ else if (!ret) {
skip_sendto = 1;
TRACE_ERROR("sendto error, simulate sending for the rest of data", QUIC_EV_CONN_SPPKTS, qc);
}
/* 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. 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.
+ * Returns the total bytes sent over the socket. 0 is returned if a transient
+ * error is encountered which allows send to be retry later. A negative value
+ * is used for a fatal error which guarantee that all future send operation for
+ * this connection will fail.
*
* 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 *syscall_errno)
+ int flags)
{
ssize_t ret;
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) {
if (errno == EAGAIN || errno == EWOULDBLOCK)
HA_ATOMIC_INC(&prx_counters->socket_full);
else
HA_ATOMIC_INC(&prx_counters->sendto_err);
+
+ /* transient error */
+ TRACE_PRINTF(TRACE_LEVEL_USER, QUIC_EV_CONN_SPPKTS, qc, 0, 0, 0,
+ "UDP send failure errno=%d (%s)", errno, strerror(errno));
+ return 0;
}
- else if (errno) {
- /* TODO unlisted errno : handle it explicitly.
- * ECONNRESET may be encounter on quic-conn socket.
- */
+ else {
+ /* unrecoverable error */
HA_ATOMIC_INC(&prx_counters->sendto_err_unknown);
+ TRACE_PRINTF(TRACE_LEVEL_USER, QUIC_EV_CONN_SPPKTS, qc, 0, 0, 0,
+ "UDP send failure errno=%d (%s)", errno, strerror(errno));
+ return -1;
}
-
- /* 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;
}
if (ret != sz)
- return 1;
+ return 0;
/* we count the total bytes sent, and the send rate for 32-byte blocks.
* The reason for the latter is that freq_ctr are limited to 4GB and
_HA_ATOMIC_ADD(&th_ctx->out_bytes, ret);
update_freq_ctr(&th_ctx->out_32bps, (ret + 16) / 32);
- return 0;
+ return ret;
}
/* Receive datagram on <qc> FD-owned socket.