From: Frederic Lecaille Date: Wed, 10 Jan 2024 15:48:51 +0000 (+0100) Subject: MINOR: quic-be: Datagrams and packet parsing support X-Git-Tag: v3.3-dev2~94 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=43d88a44f1df486dc37bb4937aa7b0960d4963de;p=thirdparty%2Fhaproxy.git 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. --- diff --git a/include/haproxy/quic_conn.h b/include/haproxy/quic_conn.h index 77d9f69be..409ebf2c3 100644 --- a/include/haproxy/quic_conn.h +++ b/include/haproxy/quic_conn.h @@ -164,6 +164,29 @@ static inline void quic_free_ncbuf(struct ncbuf *ncbuf) *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 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 quic_set_connection_close(struct quic_conn *qc, const struct quic_err err); void quic_set_tls_alert(struct quic_conn *qc, int alert); diff --git a/include/haproxy/quic_rx.h b/include/haproxy/quic_rx.h index 3e65acb08..af575b548 100644 --- a/include/haproxy/quic_rx.h +++ b/include/haproxy/quic_rx.h @@ -26,7 +26,7 @@ #include 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_parse_hd_form(struct quic_rx_packet *pkt, unsigned char **pos, const unsigned char *end); diff --git a/src/quic_rx.c b/src/quic_rx.c index 2398cd8cb..b1b0a39ab 100644 --- a/src/quic_rx.c +++ b/src/quic_rx.c @@ -1877,8 +1877,8 @@ static struct quic_conn *quic_rx_pkt_retrieve_conn(struct quic_rx_packet *pkt, /* Parse a QUIC packet starting at . Data won't be read after even * if the packet is incomplete. This function will populate fields of * instance, most notably its length. is the UDP datagram which - * contains the parsed packet. is the listener instance on which it was - * received. + * contains the parsed packet. is the address object type address of the + * object which receives this received packet. * * 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 and : 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, 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; - struct proxy *prx; struct quic_counters *prx_counters; + struct listener *l = objt_listener(o); TRACE_ENTER(QUIC_EV_CONN_LPKT); - prx = l->bind_conf->frontend; - prx_counters = EXTRA_COUNTERS_GET(prx->extra_counters_fe, &quic_stats_module); + prx_counters = qc_counters(o, &quic_stats_module); if (end <= pos) { 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; } - /* RFC9000 6. Version Negotiation */ - if (!pkt->version) { + /* RFC9000 6. Version Negotiation. A Version Negotiation packet is + * sent only by servers. + */ + if (l && !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); @@ -1976,6 +1977,13 @@ static int quic_rx_pkt_parse(struct quic_rx_packet *pkt, 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_len = 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->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 * * 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; } - /* Interrupt parsing after packet length retrieval : this - * ensures that only the packet is dropped but not the whole - * datagram. - */ - if (pkt->type == QUIC_PACKET_TYPE_0RTT && !l->bind_conf->ssl_conf.early_data) { + /* O-RTT packet are not sent by servers. */ + if (pkt->type == QUIC_PACKET_TYPE_0RTT && (!l || !l->bind_conf->ssl_conf.early_data)) { TRACE_PROTO("RX 0-RTT packet not supported", QUIC_EV_CONN_LPKT); goto drop; } @@ -2242,7 +2252,8 @@ static void qc_rx_pkt_handle(struct quic_conn *qc, struct quic_rx_packet *pkt, * this function. * * If datagram has been received on a quic-conn owned FD, must be set - * to the connection instance.
  • is the attached listener. The caller is + * to the connection instance. 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 * 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. */ 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_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; 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; /* 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) { 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 is NULL if receiving a non Initial packet for an diff --git a/src/quic_sock.c b/src/quic_sock.c index 6aabb24a1..f3c6341ce 100644 --- a/src/quic_sock.c +++ b/src/quic_sock.c @@ -928,7 +928,7 @@ int qc_rcv_buf(struct quic_conn *qc) 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(). */ BUG_ON(new_dgram->buf); } while (ret > 0);