mirror of
https://git.haproxy.org/git/haproxy.git/
synced 2025-08-07 15:47:01 +02:00
MEDIUM: quic: send version negotiation packet on unknown version
If the client announced a QUIC version not supported by haproxy, emit a Version Negotiation Packet, according to RFC9000 6. Version Negotiation. This is required to be able to use the framework for QUIC interop testing from https://github.com/marten-seemann/quic-interop-runner. The simulator checks that the server is available by sending packets to force the emission of a Version Negotiation Packet.
This commit is contained in:
parent
154bc7f864
commit
a22d860406
109
src/xprt_quic.c
109
src/xprt_quic.c
@ -52,6 +52,14 @@
|
|||||||
#include <haproxy/trace.h>
|
#include <haproxy/trace.h>
|
||||||
#include <haproxy/xprt_quic.h>
|
#include <haproxy/xprt_quic.h>
|
||||||
|
|
||||||
|
/* list of supported QUIC versions by this implementation */
|
||||||
|
static int quic_supported_version[] = {
|
||||||
|
0x00000001,
|
||||||
|
|
||||||
|
/* placeholder, do not add entry after this */
|
||||||
|
0x0
|
||||||
|
};
|
||||||
|
|
||||||
/* This is the values of some QUIC transport parameters when absent.
|
/* This is the values of some QUIC transport parameters when absent.
|
||||||
* Should be used to initialize any transport parameters (local or remote)
|
* Should be used to initialize any transport parameters (local or remote)
|
||||||
* before updating them with customized values.
|
* before updating them with customized values.
|
||||||
@ -3136,8 +3144,6 @@ static inline int quic_packet_read_long_header(unsigned char **buf, const unsign
|
|||||||
if (!quic_read_uint32(&pkt->version, (const unsigned char **)buf, end))
|
if (!quic_read_uint32(&pkt->version, (const unsigned char **)buf, end))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if (!pkt->version) { /* XXX TO DO XXX Version negotiation packet */ };
|
|
||||||
|
|
||||||
/* Destination Connection ID Length */
|
/* Destination Connection ID Length */
|
||||||
dcid_len = *(*buf)++;
|
dcid_len = *(*buf)++;
|
||||||
/* We want to be sure we can read <dcid_len> bytes and one more for <scid_len> value */
|
/* We want to be sure we can read <dcid_len> bytes and one more for <scid_len> value */
|
||||||
@ -3313,6 +3319,24 @@ static inline void qc_parse_hd_form(struct quic_rx_packet *pkt,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check if the QUIC version in packet <pkt> is supported. Returns a boolean.
|
||||||
|
*/
|
||||||
|
static inline int qc_pkt_is_supported_version(struct quic_rx_packet *pkt)
|
||||||
|
{
|
||||||
|
int j = 0, version;
|
||||||
|
|
||||||
|
do {
|
||||||
|
version = quic_supported_version[j];
|
||||||
|
if (version == pkt->version)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
version = quic_supported_version[++j];
|
||||||
|
} while(version);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
__attribute__((unused))
|
__attribute__((unused))
|
||||||
static ssize_t qc_srv_pkt_rcv(unsigned char **buf, const unsigned char *end,
|
static ssize_t qc_srv_pkt_rcv(unsigned char **buf, const unsigned char *end,
|
||||||
struct quic_rx_packet *pkt,
|
struct quic_rx_packet *pkt,
|
||||||
@ -3351,6 +3375,12 @@ static ssize_t qc_srv_pkt_rcv(unsigned char **buf, const unsigned char *end,
|
|||||||
if (!quic_packet_read_long_header(buf, end, pkt))
|
if (!quic_packet_read_long_header(buf, end, pkt))
|
||||||
goto err;
|
goto err;
|
||||||
|
|
||||||
|
/* unsupported QUIC version */
|
||||||
|
if (!qc_pkt_is_supported_version(pkt)) {
|
||||||
|
TRACE_PROTO("Null QUIC version, packet dropped", QUIC_EV_CONN_LPKT);
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
/* For Initial packets, and for servers (QUIC clients connections),
|
/* For Initial packets, and for servers (QUIC clients connections),
|
||||||
* there is no Initial connection IDs storage.
|
* there is no Initial connection IDs storage.
|
||||||
*/
|
*/
|
||||||
@ -3467,6 +3497,60 @@ static ssize_t qc_srv_pkt_rcv(unsigned char **buf, const unsigned char *end,
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Send a Version Negotiation packet on response to <pkt> on socket <fd> to
|
||||||
|
* address <addr>.
|
||||||
|
* Implementation of RFC9000 6. Version Negotiation
|
||||||
|
*
|
||||||
|
* TODO implement a rate-limiting sending of Version Negotiation packets
|
||||||
|
*
|
||||||
|
* Returns 0 on success else non-zero
|
||||||
|
*/
|
||||||
|
static int qc_send_version_negotiation(int fd, struct sockaddr_storage *addr,
|
||||||
|
struct quic_rx_packet *pkt)
|
||||||
|
{
|
||||||
|
char buf[256];
|
||||||
|
int i = 0, j, version;
|
||||||
|
const socklen_t addrlen = get_addr_len(addr);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* header form
|
||||||
|
* long header, fixed bit to 0 for Version Negotiation
|
||||||
|
*/
|
||||||
|
buf[i++] = '\x80';
|
||||||
|
|
||||||
|
/* null version for Version Negotiation */
|
||||||
|
buf[i++] = '\x00';
|
||||||
|
buf[i++] = '\x00';
|
||||||
|
buf[i++] = '\x00';
|
||||||
|
buf[i++] = '\x00';
|
||||||
|
|
||||||
|
/* source connection id */
|
||||||
|
buf[i++] = pkt->scid.len;
|
||||||
|
memcpy(buf, pkt->scid.data, pkt->scid.len);
|
||||||
|
i += pkt->scid.len;
|
||||||
|
|
||||||
|
/* destination connection id */
|
||||||
|
buf[i++] = pkt->dcid.len;
|
||||||
|
memcpy(buf, pkt->dcid.data, pkt->dcid.len);
|
||||||
|
i += pkt->dcid.len;
|
||||||
|
|
||||||
|
/* supported version */
|
||||||
|
j = 0;
|
||||||
|
do {
|
||||||
|
version = htonl(quic_supported_version[j]);
|
||||||
|
memcpy(&buf[i], &version, sizeof(version));
|
||||||
|
i += sizeof(version);
|
||||||
|
|
||||||
|
version = quic_supported_version[++j];
|
||||||
|
} while (version);
|
||||||
|
|
||||||
|
if (sendto(fd, buf, i, 0, (struct sockaddr *)addr, addrlen) < 0)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static ssize_t qc_lstnr_pkt_rcv(unsigned char **buf, const unsigned char *end,
|
static ssize_t qc_lstnr_pkt_rcv(unsigned char **buf, const unsigned char *end,
|
||||||
struct quic_rx_packet *pkt,
|
struct quic_rx_packet *pkt,
|
||||||
struct quic_dgram_ctx *dgram_ctx,
|
struct quic_dgram_ctx *dgram_ctx,
|
||||||
@ -3508,6 +3592,26 @@ static ssize_t qc_lstnr_pkt_rcv(unsigned char **buf, const unsigned char *end,
|
|||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* RFC9000 6. Version Negotiation */
|
||||||
|
if (!qc_pkt_is_supported_version(pkt)) {
|
||||||
|
/* do not send Version Negotiation in response to a
|
||||||
|
* Version Negotiation packet.
|
||||||
|
*/
|
||||||
|
if (!pkt->version) {
|
||||||
|
TRACE_PROTO("Null QUIC version, packet dropped", QUIC_EV_CONN_LPKT);
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* unsupported version, send Negotiation packet */
|
||||||
|
if (qc_send_version_negotiation(l->rx.fd, saddr, pkt)) {
|
||||||
|
TRACE_PROTO("Error on Version Negotiation sending", QUIC_EV_CONN_LPKT);
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
TRACE_PROTO("Unsupported QUIC version, send Version Negotiation packet", QUIC_EV_CONN_LPKT);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
dcid_len = pkt->dcid.len;
|
dcid_len = pkt->dcid.len;
|
||||||
/* For Initial packets, and for servers (QUIC clients connections),
|
/* For Initial packets, and for servers (QUIC clients connections),
|
||||||
* there is no Initial connection IDs storage.
|
* there is no Initial connection IDs storage.
|
||||||
@ -3721,6 +3825,7 @@ static ssize_t qc_lstnr_pkt_rcv(unsigned char **buf, const unsigned char *end,
|
|||||||
if (conn_ctx && HA_ATOMIC_LOAD(&conn_ctx->conn->ctx))
|
if (conn_ctx && HA_ATOMIC_LOAD(&conn_ctx->conn->ctx))
|
||||||
tasklet_wakeup(conn_ctx->wait_event.tasklet);
|
tasklet_wakeup(conn_ctx->wait_event.tasklet);
|
||||||
|
|
||||||
|
out:
|
||||||
TRACE_LEAVE(QUIC_EV_CONN_LPKT, qc->conn, pkt);
|
TRACE_LEAVE(QUIC_EV_CONN_LPKT, qc->conn, pkt);
|
||||||
|
|
||||||
return pkt->len;
|
return pkt->len;
|
||||||
|
Loading…
Reference in New Issue
Block a user