From: Yeonggi Kim <[email protected]>
When a QUIC listener is bound on a wildcard address, the source address
of emitted datagrams must be the local address targeted by the client,
else the kernel selects it from the route to the client. Both may differ
when several addresses are assigned to the host, for example VIPs
configured on a loopback interface behind a load balancer in direct
routing mode. In this case, QUIC clients relying on a connected UDP
socket silently drop the datagram.
Version Negotiation packets are emitted without connection, via
send_version_negotiation() which used a bare sendto() on the listener
socket without source address. In the above conditions, a client which
offers an unsupported QUIC version never receives the list of supported
versions, so it cannot switch to one of them.
Fix this by adding a source address parameter to
send_version_negotiation(), similarly to the previous patches for Retry
and stateless reset. Its caller sets it to the datagram destination
address, only when the listener is bound on a wildcard address. Emission
now relies on quic_sock_sendto(). Apart from the source address, the
emission is unchanged, except that the MSG_DONTWAIT|MSG_NOSIGNAL flags
are now set and EINTR is retried, as in qc_snd_buf().
This should fix github issue #3503.
This should be backported up to 2.8. Note that it relies on patch
"MINOR: quic: define a function to emit a datagram without connection".
Prior to 2.9, send_version_negotiation() is a static function defined in
quic_conn.c.
---
include/haproxy/quic_tx.h | 1 +
src/quic_rx.c | 5 ++++-
src/quic_tx.c | 8 +++++---
3 files changed, 10 insertions(+), 4 deletions(-)
diff --git a/include/haproxy/quic_tx.h b/include/haproxy/quic_tx.h
index 4cbbe5b7a..196b42098 100644
--- a/include/haproxy/quic_tx.h
+++ b/include/haproxy/quic_tx.h
@@ -54,6 +54,7 @@ int send_stateless_reset(struct listener *l, struct
sockaddr_storage *dstaddr,
struct sockaddr_storage *srcaddr,
struct quic_rx_packet *rxpkt);
int send_version_negotiation(int fd, struct sockaddr_storage *addr,
+ struct sockaddr_storage *src,
struct quic_rx_packet *pkt);
/* The TX packets sent in the same datagram are linked to each others in
diff --git a/src/quic_rx.c b/src/quic_rx.c
index 08c845d91..399ec2f68 100644
--- a/src/quic_rx.c
+++ b/src/quic_rx.c
@@ -2050,8 +2050,11 @@ static int quic_rx_pkt_parse(struct quic_conn *qc,
struct quic_rx_packet *pkt,
* sent only by servers.
*/
if (l && !pkt->version) {
+ struct sockaddr_storage laddr;
+
/* unsupported version, send Negotiation packet */
- if (send_version_negotiation(l->rx.fd, (struct
sockaddr_storage *)&dgram->saddr, pkt)) {
+ if (send_version_negotiation(l->rx.fd, (struct
sockaddr_storage *)&dgram->saddr,
+
quic_dgram_reply_src(dgram, l, &laddr), pkt)) {
TRACE_ERROR("VN packet not sent",
QUIC_EV_CONN_LPKT);
goto drop_silent;
}
diff --git a/src/quic_tx.c b/src/quic_tx.c
index cb31df822..b57b39690 100644
--- a/src/quic_tx.c
+++ b/src/quic_tx.c
@@ -1137,7 +1137,9 @@ int qc_dgrams_retransmit(struct quic_conn *qc)
/*
* Send a Version Negotiation packet on response to <pkt> on socket <fd> to
- * address <addr>.
+ * address <addr>. <src> is the local address to use as datagram source, or
+ * NULL to let the kernel select it. It must be NULL when <fd> is bound on a
+ * specific address (see quic_lstnr_may_set_src()).
* Implementation of RFC9000 6. Version Negotiation
*
* TODO implement a rate-limiting sending of Version Negotiation packets
@@ -1145,12 +1147,12 @@ int qc_dgrams_retransmit(struct quic_conn *qc)
* Returns 0 on success else non-zero
*/
int send_version_negotiation(int fd, struct sockaddr_storage *addr,
+ struct sockaddr_storage *src,
struct quic_rx_packet *pkt)
{
char buf[256];
int ret = 0, i = 0, j;
uint32_t version;
- const socklen_t addrlen = get_addr_len(addr);
TRACE_ENTER(QUIC_EV_CONN_TXPKT);
/*
@@ -1187,7 +1189,7 @@ int send_version_negotiation(int fd, struct
sockaddr_storage *addr,
i += sizeof(version);
}
- if (sendto(fd, buf, i, 0, (struct sockaddr *)addr, addrlen) < 0)
+ if (quic_sock_sendto(fd, buf, i, addr, src) < 0)
goto out;
ret = 1;
--
2.50.1 (Apple Git-155)