BUG/MINOR: quic: Avoid sending truncated datagrams

There is a remaining loop in this ugly qc_snd_buf() function which could
lead haproxy to send truncated UDP datagrams. For now on, we send
a complete UDP datagram or nothing!

Must be backported to 2.6.
This commit is contained in:
Frdric Lcaille 2022-08-03 20:52:20 +02:00 committed by Willy Tarreau
parent 30e260e2e6
commit 48bb875908

View File

@ -362,64 +362,46 @@ void quic_sock_fd_iocb(int fd)
MT_LIST_APPEND(&l->rx.rxbuf_list, &rxbuf->mt_list);
}
/* TODO standardize this function for a generic UDP sendto wrapper. This can be
/* Send a datagram stored into <buf> buffer with <sz> as size.
* The caller must ensure there is at least <sz> bytes in this buffer.
* Return the size of this datagram if succeeded, 0 if truncated and -1 in case of
* any error.
* 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.
*/
size_t qc_snd_buf(struct quic_conn *qc, const struct buffer *buf, size_t count,
size_t qc_snd_buf(struct quic_conn *qc, const struct buffer *buf, size_t sz,
int flags)
{
ssize_t ret;
size_t try, done;
int send_flag;
done = 0;
/* send the largest possible block. For this we perform only one call
* to send() unless the buffer wraps and we exactly fill the first hunk,
* in which case we accept to do it once again.
*/
while (count) {
try = b_contig_data(buf, done);
if (try > count)
try = count;
send_flag = MSG_DONTWAIT | MSG_NOSIGNAL;
if (try < count || flags & CO_SFL_MSG_MORE)
send_flag |= MSG_MORE;
ret = sendto(qc->li->rx.fd, b_peek(buf, done), try, send_flag,
do {
ret = sendto(qc->li->rx.fd, b_peek(buf, b_head_ofs(buf)), sz,
MSG_DONTWAIT | MSG_NOSIGNAL,
(struct sockaddr *)&qc->peer_addr, get_addr_len(&qc->peer_addr));
if (ret > 0) {
/* TODO remove partial sending support for UDP */
count -= ret;
done += ret;
} while (ret < 0 && errno == EINTR);
if (ret < try)
break;
if (ret > 0) {
if (ret != sz)
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 that it's not enough per second.
*/
_HA_ATOMIC_ADD(&global.out_bytes, ret);
update_freq_ctr(&global.out_32bps, (ret + 16) / 32);
}
else if (errno == EINTR) {
/* try again */
continue;
}
else if (ret == 0 || errno == EAGAIN || errno == EWOULDBLOCK || errno == ENOTCONN || errno == EINPROGRESS || errno == EBADF) {
else if (ret == 0 || errno == EAGAIN || errno == EWOULDBLOCK ||
errno == ENOTCONN || errno == EINPROGRESS || errno == EBADF) {
/* TODO must be handle properly. It is justified for UDP ? */
qc->sendto_err++;
break;
}
else if (errno) {
/* TODO unlisted errno : handle it explicitely. */
ABORT_NOW();
}
}
if (done > 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 that it's not enough per second.
*/
_HA_ATOMIC_ADD(&global.out_bytes, done);
update_freq_ctr(&global.out_32bps, (done + 16) / 32);
}
return done;
return ret;
}