BUG/MINOR: quic: adjust restriction for stateless reset emission

Review RFC 9000 and ensure restriction on Stateless reset are properly
enforced. After careful examination, several changes are introduced.

First, redefine minimal Stateless Reset emitted packet length to 21
bytes (5 random bytes + a token). This is the new default length used in
every case, unless received packet which triggered it is 43 bytes or
smaller.

Ensure every Stateless Reset packets emitted are at 1 byte shorter than
the received packet which triggered it. No Stateless reset will be
emitted if this falls under the above limit of 21 bytes. Thus this
should prevent looping issues.

This should be backported up to 2.6.
This commit is contained in:
Amaury Denoyelle 2024-05-22 15:55:58 +02:00
parent f55748a422
commit 5764bc50b5
3 changed files with 38 additions and 14 deletions

View File

@ -176,8 +176,15 @@ enum quic_pkt_type {
*/
#define QUIC_CONN_MAX_PACKET 64
#define QUIC_STATELESS_RESET_PACKET_HEADER_LEN 5
#define QUIC_STATELESS_RESET_PACKET_MINLEN (22 + QUIC_HAP_CID_LEN)
/* RFC 9000 10.3. Stateless Reset
*
* To entities other than its intended recipient, a Stateless Reset will
* appear to be a packet with a short header. For the Stateless Reset to
* appear as a valid QUIC packet, the Unpredictable Bits field needs to
* include at least 38 bits of data (or 5 bytes, less the two fixed
* bits).
*/
#define QUIC_STATELESS_RESET_PACKET_MINLEN (5 + QUIC_STATELESS_RESET_TOKEN_LEN)
/* Similar to kernel min()/max() definitions. */
#define QUIC_MIN(a, b) ({ \

View File

@ -1687,6 +1687,9 @@ static struct quic_conn *quic_rx_pkt_retrieve_conn(struct quic_rx_packet *pkt,
}
}
else if (!qc) {
/* Stateless Reset sent even for Long header packets as haproxy
* emits stateless_reset_token in its TPs.
*/
TRACE_PROTO("RX non Initial pkt without connection", QUIC_EV_CONN_LPKT, NULL, NULL, NULL, pkt->version);
if (!send_stateless_reset(l, &dgram->saddr, pkt))
TRACE_ERROR("stateless reset not sent", QUIC_EV_CONN_LPKT, qc);

View File

@ -981,24 +981,38 @@ int send_stateless_reset(struct listener *l, struct sockaddr_storage *dstaddr,
TRACE_ENTER(QUIC_EV_STATELESS_RST);
/* RFC 9000 10.3. Stateless Reset
*
* Endpoints MUST discard packets that are too small to be valid QUIC
* packets. To give an example, with the set of AEAD functions defined
* in [QUIC-TLS], short header packets that are smaller than 21 bytes
* are never valid.
*
* [...]
*
* RFC 9000 10.3.3. Looping
*
* An endpoint MUST ensure that every Stateless Reset that it sends is
* smaller than the packet that triggered it, unless it maintains state
* sufficient to prevent looping. In the event of a loop, this results
* in packets eventually being too small to trigger a response.
*/
if (rxpkt->len <= QUIC_STATELESS_RESET_PACKET_MINLEN) {
TRACE_DEVEL("rxpkt too short", QUIC_EV_STATELESS_RST);
goto leave;
}
prx = l->bind_conf->frontend;
prx_counters = EXTRA_COUNTERS_GET(prx->extra_counters_fe, &quic_stats_module);
/* 10.3 Stateless Reset (https://www.rfc-editor.org/rfc/rfc9000.html#section-10.3)
* The resulting minimum size of 21 bytes does not guarantee that a Stateless
* Reset is difficult to distinguish from other packets if the recipient requires
* the use of a connection ID. To achieve that end, the endpoint SHOULD ensure
* that all packets it sends are at least 22 bytes longer than the minimum
* connection ID length that it requests the peer to include in its packets,
* adding PADDING frames as necessary. This ensures that any Stateless Reset
* sent by the peer is indistinguishable from a valid packet sent to the endpoint.
/* RFC 9000 10.3. Stateless Reset
*
* An endpoint that sends a Stateless Reset in response to a packet that is
* 43 bytes or shorter SHOULD send a Stateless Reset that is one byte shorter
* than the packet it responds to.
*/
/* Note that we build at most a 42 bytes QUIC packet to mimic a short packet */
pktlen = rxpkt->len <= 43 ? rxpkt->len - 1 : 0;
pktlen = QUIC_MAX(QUIC_STATELESS_RESET_PACKET_MINLEN, pktlen);
pktlen = rxpkt->len <= 43 ? rxpkt->len - 1 :
QUIC_STATELESS_RESET_PACKET_MINLEN;
rndlen = pktlen - QUIC_STATELESS_RESET_TOKEN_LEN;
/* Put a header of random bytes */