MINOR: quic-be: Datagrams and packet parsing support

Modify quic_dgram_parse() to stop passing it a listener as third parameter.
In place the object type address of the connection socket owner is passed
to support the haproxy servers with QUIC as transport protocol.
qc_owner_obj_type() is implemented to return this address.
qc_counters() is also implemented to return the QUIC specific counters of
the proxy of owner of the connection.
quic_rx_pkt_parse() called by quic_dgram_parse() is also modify to use
the object type address used by this latter as last parameter. It is
also modified to send Retry packet only from listeners. A QUIC client
(connection to haproxy QUIC servers) must drop the Initial packets with
non null token length. It is also not supposed to receive O-RTT packets
which are dropped.
This commit is contained in:
Frederic Lecaille 2024-01-10 16:48:51 +01:00 committed by Amaury Denoyelle
parent 266b10b8a4
commit 43d88a44f1
4 changed files with 53 additions and 18 deletions

View File

@ -164,6 +164,29 @@ static inline void quic_free_ncbuf(struct ncbuf *ncbuf)
*ncbuf = NCBUF_NULL; *ncbuf = NCBUF_NULL;
} }
/* Return the address of the connection owner object type. */
static inline enum obj_type *qc_owner_obj_type(struct quic_conn *qc)
{
return qc_is_listener(qc) ? &qc->li->obj_type :
&objt_server(qc->conn->target)->obj_type;
}
/* Return the address of the QUIC counters attached to the proxy of
* the owner of the connection whose object type address is <o> for
* listener and servers, or NULL for others object type.
*/
static inline void *qc_counters(enum obj_type *o, const struct stats_module *m)
{
struct proxy *p;
struct listener *l = objt_listener(o);
struct server *s = objt_server(o);
p = l ? l->bind_conf->frontend :
s ? s->proxy : NULL;
return p ? EXTRA_COUNTERS_GET(p->extra_counters_fe, m) : NULL;
}
void chunk_frm_appendf(struct buffer *buf, const struct quic_frame *frm); void chunk_frm_appendf(struct buffer *buf, const struct quic_frame *frm);
void quic_set_connection_close(struct quic_conn *qc, const struct quic_err err); void quic_set_connection_close(struct quic_conn *qc, const struct quic_err err);
void quic_set_tls_alert(struct quic_conn *qc, int alert); void quic_set_tls_alert(struct quic_conn *qc, int alert);

View File

@ -26,7 +26,7 @@
#include <haproxy/quic_rx-t.h> #include <haproxy/quic_rx-t.h>
int quic_dgram_parse(struct quic_dgram *dgram, struct quic_conn *from_qc, int quic_dgram_parse(struct quic_dgram *dgram, struct quic_conn *from_qc,
struct listener *li); enum obj_type *obj_type);
int qc_treat_rx_pkts(struct quic_conn *qc); int qc_treat_rx_pkts(struct quic_conn *qc);
int qc_parse_hd_form(struct quic_rx_packet *pkt, int qc_parse_hd_form(struct quic_rx_packet *pkt,
unsigned char **pos, const unsigned char *end); unsigned char **pos, const unsigned char *end);

View File

@ -1877,8 +1877,8 @@ static struct quic_conn *quic_rx_pkt_retrieve_conn(struct quic_rx_packet *pkt,
/* Parse a QUIC packet starting at <pos>. Data won't be read after <end> even /* Parse a QUIC packet starting at <pos>. Data won't be read after <end> even
* if the packet is incomplete. This function will populate fields of <pkt> * if the packet is incomplete. This function will populate fields of <pkt>
* instance, most notably its length. <dgram> is the UDP datagram which * instance, most notably its length. <dgram> is the UDP datagram which
* contains the parsed packet. <l> is the listener instance on which it was * contains the parsed packet. <o> is the address object type address of the
* received. * object which receives this received packet.
* *
* Returns 0 on success else non-zero. Packet length is guaranteed to be set to * Returns 0 on success else non-zero. Packet length is guaranteed to be set to
* the real packet value or to cover all data between <pos> and <end> : this is * the real packet value or to cover all data between <pos> and <end> : this is
@ -1886,16 +1886,15 @@ static struct quic_conn *quic_rx_pkt_retrieve_conn(struct quic_rx_packet *pkt,
*/ */
static int quic_rx_pkt_parse(struct quic_rx_packet *pkt, static int quic_rx_pkt_parse(struct quic_rx_packet *pkt,
unsigned char *pos, const unsigned char *end, unsigned char *pos, const unsigned char *end,
struct quic_dgram *dgram, struct listener *l) struct quic_dgram *dgram, enum obj_type *o)
{ {
const unsigned char *beg = pos; const unsigned char *beg = pos;
struct proxy *prx;
struct quic_counters *prx_counters; struct quic_counters *prx_counters;
struct listener *l = objt_listener(o);
TRACE_ENTER(QUIC_EV_CONN_LPKT); TRACE_ENTER(QUIC_EV_CONN_LPKT);
prx = l->bind_conf->frontend; prx_counters = qc_counters(o, &quic_stats_module);
prx_counters = EXTRA_COUNTERS_GET(prx->extra_counters_fe, &quic_stats_module);
if (end <= pos) { if (end <= pos) {
TRACE_PROTO("Packet dropped", QUIC_EV_CONN_LPKT); TRACE_PROTO("Packet dropped", QUIC_EV_CONN_LPKT);
@ -1951,8 +1950,10 @@ static int quic_rx_pkt_parse(struct quic_rx_packet *pkt,
goto drop; goto drop;
} }
/* RFC9000 6. Version Negotiation */ /* RFC9000 6. Version Negotiation. A Version Negotiation packet is
if (!pkt->version) { * sent only by servers.
*/
if (l && !pkt->version) {
/* unsupported version, send Negotiation packet */ /* unsupported version, send Negotiation packet */
if (send_version_negotiation(l->rx.fd, &dgram->saddr, pkt)) { if (send_version_negotiation(l->rx.fd, &dgram->saddr, pkt)) {
TRACE_ERROR("VN packet not sent", QUIC_EV_CONN_LPKT); TRACE_ERROR("VN packet not sent", QUIC_EV_CONN_LPKT);
@ -1976,6 +1977,13 @@ static int quic_rx_pkt_parse(struct quic_rx_packet *pkt,
goto drop; goto drop;
} }
if (!l && pkt->token_len) {
/* A server must sent Initial packets with a null token length. */
TRACE_PROTO("Packet dropped",
QUIC_EV_CONN_LPKT, NULL, NULL, NULL, pkt->version);
goto drop;
}
pkt->token = pos; pkt->token = pos;
pkt->token_len = token_len; pkt->token_len = token_len;
pos += pkt->token_len; pos += pkt->token_len;
@ -2001,6 +2009,11 @@ static int quic_rx_pkt_parse(struct quic_rx_packet *pkt,
pkt->pn_offset = pos - beg; pkt->pn_offset = pos - beg;
pkt->len = pkt->pn_offset + len; pkt->len = pkt->pn_offset + len;
/* Interrupt parsing after packet length retrieval : this
* ensures that only the packet is dropped but not the whole
* datagram.
*/
/* RFC 9000. Initial Datagram Size /* RFC 9000. Initial Datagram Size
* *
* A server MUST discard an Initial packet that is carried in a UDP datagram * A server MUST discard an Initial packet that is carried in a UDP datagram
@ -2014,11 +2027,8 @@ static int quic_rx_pkt_parse(struct quic_rx_packet *pkt,
goto drop; goto drop;
} }
/* Interrupt parsing after packet length retrieval : this /* O-RTT packet are not sent by servers. */
* ensures that only the packet is dropped but not the whole if (pkt->type == QUIC_PACKET_TYPE_0RTT && (!l || !l->bind_conf->ssl_conf.early_data)) {
* datagram.
*/
if (pkt->type == QUIC_PACKET_TYPE_0RTT && !l->bind_conf->ssl_conf.early_data) {
TRACE_PROTO("RX 0-RTT packet not supported", QUIC_EV_CONN_LPKT); TRACE_PROTO("RX 0-RTT packet not supported", QUIC_EV_CONN_LPKT);
goto drop; goto drop;
} }
@ -2242,7 +2252,8 @@ static void qc_rx_pkt_handle(struct quic_conn *qc, struct quic_rx_packet *pkt,
* this function. * this function.
* *
* If datagram has been received on a quic-conn owned FD, <from_qc> must be set * If datagram has been received on a quic-conn owned FD, <from_qc> must be set
* to the connection instance. <li> is the attached listener. The caller is * to the connection instance. <o> is the object type address of the object
* (listener or server) receiving the datagram. The caller is
* responsible to ensure that the first packet is destined to this connection * responsible to ensure that the first packet is destined to this connection
* by comparing CIDs. * by comparing CIDs.
* *
@ -2254,7 +2265,7 @@ static void qc_rx_pkt_handle(struct quic_conn *qc, struct quic_rx_packet *pkt,
* the datagram may not have been parsed. * the datagram may not have been parsed.
*/ */
int quic_dgram_parse(struct quic_dgram *dgram, struct quic_conn *from_qc, int quic_dgram_parse(struct quic_dgram *dgram, struct quic_conn *from_qc,
struct listener *li) enum obj_type *o)
{ {
struct quic_rx_packet *pkt; struct quic_rx_packet *pkt;
struct quic_conn *qc = NULL; struct quic_conn *qc = NULL;
@ -2292,7 +2303,7 @@ int quic_dgram_parse(struct quic_dgram *dgram, struct quic_conn *from_qc,
pkt->flags |= QUIC_FL_RX_PACKET_DGRAM_FIRST; pkt->flags |= QUIC_FL_RX_PACKET_DGRAM_FIRST;
quic_rx_packet_refinc(pkt); quic_rx_packet_refinc(pkt);
if (quic_rx_pkt_parse(pkt, pos, end, dgram, li)) if (quic_rx_pkt_parse(pkt, pos, end, dgram, o))
goto next; goto next;
/* Search quic-conn instance for first packet of the datagram. /* Search quic-conn instance for first packet of the datagram.
@ -2301,6 +2312,7 @@ int quic_dgram_parse(struct quic_dgram *dgram, struct quic_conn *from_qc,
*/ */
if (!qc) { if (!qc) {
int new_tid = -1; int new_tid = -1;
struct listener *li = objt_listener(o);
qc = from_qc ? from_qc : quic_rx_pkt_retrieve_conn(pkt, dgram, li, &new_tid); qc = from_qc ? from_qc : quic_rx_pkt_retrieve_conn(pkt, dgram, li, &new_tid);
/* qc is NULL if receiving a non Initial packet for an /* qc is NULL if receiving a non Initial packet for an

View File

@ -928,7 +928,7 @@ int qc_rcv_buf(struct quic_conn *qc)
continue; continue;
} }
quic_dgram_parse(new_dgram, qc, qc->li); quic_dgram_parse(new_dgram, qc, qc_owner_obj_type(qc));
/* A datagram must always be consumed after quic_parse_dgram(). */ /* A datagram must always be consumed after quic_parse_dgram(). */
BUG_ON(new_dgram->buf); BUG_ON(new_dgram->buf);
} while (ret > 0); } while (ret > 0);