haproxy/include/haproxy/quic_tx-t.h
Amaury Denoyelle 2fffd85b97 BUG/MEDIUM: quic: prevent EMSGSIZE with GSO for larger bufsize
A UDP datagram cannot be greater than 65535 bytes, as UDP length header
field is encoded on 2 bytes. As such, sendmsg() will reject a bigger
input with error EMSGSIZE. By default, this does not cause any issue as
QUIC datagrams are limited to 1.252 bytes and sent individually.

However, with GSO support, value bigger than 1.252 bytes are specified
on sendmsg(). If using a bufsize equal to or greater than 65535, syscall
could reject the input buffer with EMSGSIZE. As this value is not
expected, the connection is immediately closed by haproxy and the
transfer is interrupted.

This bug can easily reproduced by requesting a large object on loopback
interface and using a bufsize of 65535 bytes. In fact, the limit is
slightly less than 65535, as extra room is also needed for IP + UDP
headers.

Fix this by reducing the count of datagrams encoded in a single GSO
invokation via qc_prep_pkts(). Previously, it was set to 64 as specified
by man 7 udp. However, with 1252 datagrams, this is still too many.
Reduce it to a value of 52. Input to sendmsg will thus be restricted to
at most 65.104 bytes if last datagram is full.

If there is still data available for encoding in qc_prep_pkts(), they
will be written in a separate batch of datagrams. qc_send_ppkts() will
then loop over the whole QUIC Tx buffer and call sendmsg() for each
series of at most 52 datagrams.

This does not need to be backported.
2024-11-26 11:49:30 +01:00

95 lines
3.2 KiB
C

#ifndef _HAPROXY_TX_T_H
#define _HAPROXY_TX_T_H
#define QUIC_MIN_CC_PKTSIZE 128
#define QUIC_DGRAM_HEADLEN (sizeof(uint16_t) + sizeof(void *))
#define QUIC_MAX_CC_BUFSIZE (2 * (QUIC_MIN_CC_PKTSIZE + QUIC_DGRAM_HEADLEN))
/* Sendmsg input buffer cannot be bigger than 65535 bytes. This comes from UDP
* header which uses a 2-bytes length field. QUIC datagrams are limited to 1252
* bytes for now so this does not cause any issue for serialized emission.
*
* However when using GSO large buffer can be transferred. By default, no more
* than 64 datagrams can be emitted via a single GSO call (man 7 udp). This is
* still too much with 1252 bytes datagram. Use a 52 datagrams max value, which
* ensures sendmsg input will be limited to 65104 bytes.
*/
#define QUIC_MAX_GSO_DGRAMS 52
#include <import/eb64tree.h>
#include <haproxy/list-t.h>
extern struct pool_head *pool_head_quic_tx_packet;
extern struct pool_head *pool_head_quic_cc_buf;
/* Flag a sent packet as being an ack-eliciting packet. */
#define QUIC_FL_TX_PACKET_ACK_ELICITING (1UL << 0)
/* Flag a sent packet as containing a PADDING frame. */
#define QUIC_FL_TX_PACKET_PADDING (1UL << 1)
/* Flag a sent packet as being in flight. */
#define QUIC_FL_TX_PACKET_IN_FLIGHT (QUIC_FL_TX_PACKET_ACK_ELICITING | QUIC_FL_TX_PACKET_PADDING)
/* Flag a sent packet as containing a CONNECTION_CLOSE frame */
#define QUIC_FL_TX_PACKET_CC (1UL << 2)
/* Flag a sent packet as containing an ACK frame */
#define QUIC_FL_TX_PACKET_ACK (1UL << 3)
/* Flag a sent packet as being coalesced to another one in the same datagram */
#define QUIC_FL_TX_PACKET_COALESCED (1UL << 4)
/* Flag a sent packet as being probing with old data */
#define QUIC_FL_TX_PACKET_PROBE_WITH_OLD_DATA (1UL << 5)
/* Structure to store enough information about TX QUIC packets. */
struct quic_tx_packet {
/* List entry point. */
struct list list;
/* Packet length */
size_t len;
/* This is not the packet length but the length of outstanding data
* for in flight TX packet.
*/
size_t in_flight_len;
struct eb64_node pn_node;
/* The list of frames of this packet. */
struct list frms;
/* The time this packet was sent (ms). */
unsigned int time_sent;
/* Packet number spakce. */
struct quic_pktns *pktns;
/* Flags. */
unsigned int flags;
/* Reference counter */
int refcnt;
/* Next packet in the same datagram */
struct quic_tx_packet *next;
/* Previous packet in the same datagram */
struct quic_tx_packet *prev;
/* Largest acknowledged packet number if this packet contains an ACK frame */
int64_t largest_acked_pn;
/* Delivery rate sampling information */
struct {
uint64_t delivered;
uint64_t tx_in_flight;
uint64_t lost;
int64_t end_seq;
uint32_t delivered_time;
uint32_t first_sent_time;
int is_app_limited;
} rs;
unsigned char type;
};
/* Return value for qc_build_pkt(). */
enum qc_build_pkt_err {
QC_BUILD_PKT_ERR_NONE = 0,
QC_BUILD_PKT_ERR_ALLOC, /* memory allocation failure */
QC_BUILD_PKT_ERR_ENCRYPT, /* error during encryption operation */
QC_BUILD_PKT_ERR_BUFROOM, /* no more room in input buf or congestion window */
};
enum quic_tx_err {
QUIC_TX_ERR_NONE,
QUIC_TX_ERR_FATAL,
QUIC_TX_ERR_PACING,
};
#endif /* _HAPROXY_TX_T_H */