MINOR: quic: adjust Rx packet type parsing
qc_parse_hd_form() is the function used to parse the first byte of a
packet and return its type and version. Its API has been simplified with
the following changes :
* extra out paremeters are removed (long_header and version). All infos
are now stored directly in quic_rx_packet instance
* a new dummy version is declared in quic_versions array with a 0 number
code. This can be used to match Version negotiation packets.
* a new default packet type is defined QUIC_PACKET_TYPE_UNKNOWN to be
used as an initial value.
Also, the function has been exported to an include file. This will be
useful to be able to reuse on quic-sock to parse the first packet of a
datagram.
This should be backported up to 2.7.
diff --git a/include/haproxy/quic_conn-t.h b/include/haproxy/quic_conn-t.h
index 1efb8f9..5e6b5d6 100644
--- a/include/haproxy/quic_conn-t.h
+++ b/include/haproxy/quic_conn-t.h
@@ -131,6 +131,9 @@
* own convenience.
*/
QUIC_PACKET_TYPE_SHORT,
+
+ /* unknown type */
+ QUIC_PACKET_TYPE_UNKNOWN
};
/* Packet number field length. */
diff --git a/include/haproxy/quic_conn.h b/include/haproxy/quic_conn.h
index ade8a81..78a9755 100644
--- a/include/haproxy/quic_conn.h
+++ b/include/haproxy/quic_conn.h
@@ -687,6 +687,8 @@
void qc_kill_conn(struct quic_conn *qc);
+int qc_parse_hd_form(struct quic_rx_packet *pkt,
+ unsigned char **buf, const unsigned char *end);
int quic_dgram_parse(struct quic_dgram *dgram, struct quic_conn *qc,
struct listener *li);
diff --git a/src/quic_conn.c b/src/quic_conn.c
index 1796227..788ba18 100644
--- a/src/quic_conn.c
+++ b/src/quic_conn.c
@@ -119,6 +119,11 @@
const size_t quic_versions_nb = sizeof quic_versions / sizeof *quic_versions;
/* Listener only preferred version */
const struct quic_version *preferred_version;
+/* RFC 8999 5.4. Version
+ * A Version field with a
+ * value of 0x00000000 is reserved for version negotiation
+ */
+const struct quic_version quic_version_VN_reserved = { .num = 0, };
/* trace source and events */
static void quic_trace(enum trace_level level, uint64_t mask, \
@@ -5884,7 +5889,8 @@
/* Check that the length of this received DCID matches the CID lengths
* of our implementation for non Initials packets only.
*/
- if (pkt->type != QUIC_PACKET_TYPE_INITIAL &&
+ if (pkt->version && pkt->version->num &&
+ pkt->type != QUIC_PACKET_TYPE_INITIAL &&
pkt->type != QUIC_PACKET_TYPE_0RTT &&
dcid_len != QUIC_HAP_CID_LEN) {
TRACE_ERROR("wrong DCID length", QUIC_EV_CONN_RXPKT);
@@ -6015,48 +6021,72 @@
return ret;
}
+/* Return the QUIC version (quic_version struct) with <version> as version number
+ * if supported or NULL if not.
+ */
+static inline const struct quic_version *qc_supported_version(uint32_t version)
+{
+ int i;
+
+ if (unlikely(!version))
+ return &quic_version_VN_reserved;
+
+ for (i = 0; i < quic_versions_nb; i++)
+ if (quic_versions[i].num == version)
+ return &quic_versions[i];
+
+ return NULL;
+}
+
-/* Parse the header form from <byte0> first byte of <pkt> packet to set its type.
- * Also set <*long_header> to 1 if this form is long, 0 if not and the version
- * of this packet into <*version>.
+/* Parse a QUIC packet header starting at <buf> without exceeding <end>.
+ * Version and type are stored in <pkt> packet instance. Type is set to unknown
+ * on two occasions : for unsupported version, in this case version field is
+ * set to NULL; for Version Negotiation packet with version number set to 0.
+ *
+ * Returns 1 on success else 0.
*/
-static inline int qc_parse_hd_form(struct quic_rx_packet *pkt,
- unsigned char **buf, const unsigned char *end,
- int *long_header, uint32_t *version)
+int qc_parse_hd_form(struct quic_rx_packet *pkt,
+ unsigned char **buf, const unsigned char *end)
{
+ uint32_t version;
int ret = 0;
const unsigned char byte0 = **buf;
TRACE_ENTER(QUIC_EV_CONN_RXPKT);
+ pkt->version = NULL;
+ pkt->type = QUIC_PACKET_TYPE_UNKNOWN;
(*buf)++;
if (byte0 & QUIC_PACKET_LONG_HEADER_BIT) {
unsigned char type =
(byte0 >> QUIC_PACKET_TYPE_SHIFT) & QUIC_PACKET_TYPE_BITMASK;
- *long_header = 1;
/* Version */
- if (!quic_read_uint32(version, (const unsigned char **)buf, end)) {
+ if (!quic_read_uint32(&version, (const unsigned char **)buf, end)) {
TRACE_ERROR("could not read the packet version", QUIC_EV_CONN_RXPKT);
goto out;
}
- if (*version != QUIC_PROTOCOL_VERSION_2) {
- pkt->type = type;
- }
- else {
- switch (type) {
- case 0:
- pkt->type = QUIC_PACKET_TYPE_RETRY;
- break;
- case 1:
- pkt->type = QUIC_PACKET_TYPE_INITIAL;
- break;
- case 2:
- pkt->type = QUIC_PACKET_TYPE_0RTT;
- break;
- case 3:
- pkt->type = QUIC_PACKET_TYPE_HANDSHAKE;
- break;
+ pkt->version = qc_supported_version(version);
+ if (version && pkt->version) {
+ if (version != QUIC_PROTOCOL_VERSION_2) {
+ pkt->type = type;
+ }
+ else {
+ switch (type) {
+ case 0:
+ pkt->type = QUIC_PACKET_TYPE_RETRY;
+ break;
+ case 1:
+ pkt->type = QUIC_PACKET_TYPE_INITIAL;
+ break;
+ case 2:
+ pkt->type = QUIC_PACKET_TYPE_0RTT;
+ break;
+ case 3:
+ pkt->type = QUIC_PACKET_TYPE_HANDSHAKE;
+ break;
+ }
}
}
}
@@ -6064,7 +6094,6 @@
if (byte0 & QUIC_PACKET_SPIN_BIT)
pkt->flags |= QUIC_FL_RX_PACKET_SPIN_BIT;
pkt->type = QUIC_PACKET_TYPE_SHORT;
- *long_header = 0;
}
ret = 1;
@@ -6073,20 +6102,6 @@
return ret;
}
-/* Return the QUIC version (quic_version struct) with <version> as version number
- * if supported or NULL if not.
- */
-static inline const struct quic_version *qc_supported_version(uint32_t version)
-{
- int i;
-
- for (i = 0; i < quic_versions_nb; i++)
- if (quic_versions[i].num == version)
- return &quic_versions[i];
-
- return NULL;
-}
-
/*
* Send a Version Negotiation packet on response to <pkt> on socket <fd> to
* address <addr>.
@@ -6743,8 +6758,6 @@
const unsigned char *beg = buf;
struct proxy *prx;
struct quic_counters *prx_counters;
- int long_header = 0;
- uint32_t version = 0;
const struct quic_version *qv = NULL;
TRACE_ENTER(QUIC_EV_CONN_LPKT);
@@ -6778,15 +6791,15 @@
}
/* Header form */
- if (!qc_parse_hd_form(pkt, &buf, end, &long_header, &version)) {
+ if (!qc_parse_hd_form(pkt, &buf, end)) {
TRACE_PROTO("Packet dropped", QUIC_EV_CONN_LPKT);
goto drop;
}
- if (long_header) {
+ if (pkt->type != QUIC_PACKET_TYPE_SHORT) {
uint64_t len;
-
TRACE_PROTO("long header packet received", QUIC_EV_CONN_LPKT);
+
if (!quic_packet_read_long_header(&buf, end, pkt)) {
TRACE_PROTO("Packet dropped", QUIC_EV_CONN_LPKT);
goto drop;
@@ -6803,14 +6816,14 @@
}
/* Retry of Version Negotiation packets are only sent by servers */
- if (pkt->type == QUIC_PACKET_TYPE_RETRY || !version) {
+ if (pkt->type == QUIC_PACKET_TYPE_RETRY ||
+ (pkt->version && !pkt->version->num)) {
TRACE_PROTO("Packet dropped", QUIC_EV_CONN_LPKT);
goto drop;
}
/* RFC9000 6. Version Negotiation */
- qv = qc_supported_version(version);
- if (!qv) {
+ if (!pkt->version) {
/* unsupported version, send Negotiation packet */
if (send_version_negotiation(l->rx.fd, &dgram->saddr, pkt)) {
TRACE_ERROR("VN packet not sent", QUIC_EV_CONN_LPKT);
@@ -6820,7 +6833,6 @@
TRACE_PROTO("VN packet sent", QUIC_EV_CONN_LPKT);
goto drop_silent;
}
- pkt->version = qv;
/* For Initial packets, and for servers (QUIC clients connections),
* there is no Initial connection IDs storage.