Skip to content

Commit 275d064

Browse files
a-denoyellecapflam
authored andcommitted
BUG/MINOR: quic: fix padding of INITIAL packets
API for sending has been extended to support emission on more than 2 QEL instances. However, this has rendered the PADDING emission for INITIAL packets less previsible. Indeed, if qc_send() is used with empty QEL instances, a padding frame may be generated before handling the last QEL registered, which could cause unnecessary padding to be emitted. This commit simplify PADDING by only activating it for the last QEL registered. This ensures that no superfluous padding is generated as if the minimal INITIAL datagram length is reached, padding is resetted before handling last QEL instance. This bug is labelled as minor as haproxy already emit big enough INITIAL packets coalesced with HANDSHAKE one without needing padding. This however render the padding code difficult to test. Thus, it may be useful to force emission on INITIAL qel only without coalescing HANDSHAKE packet. Here is a sample to reproduce it : --- a/src/quic_conn.c +++ b/src/quic_conn.c @@ -794,6 +794,14 @@ struct task *quic_conn_io_cb(struct task *t, void *context, unsigned int state) } } + if (qc->iel && qel_need_sending(qc->iel, qc)) { + struct list empty = LIST_HEAD_INIT(empty); + qel_register_send(&send_list, qc->iel, &qc->iel->pktns->tx.frms); + if (qc->hel) + qel_register_send(&send_list, qc->hel, &empty); + qc_send(qc, 0, &send_list); + } + /* Insert each QEL into sending list if needed. */ list_for_each_entry(qel, &qc->qel_list, list) { if (qel_need_sending(qel, qc)) This should be backported up to 3.0. (cherry picked from commit a60609f) Signed-off-by: Christopher Faulet <[email protected]>
1 parent c9b7482 commit 275d064

File tree

1 file changed

+37
-40
lines changed

1 file changed

+37
-40
lines changed

src/quic_tx.c

Lines changed: 37 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -155,16 +155,27 @@ static void qc_txb_store(struct buffer *buf, uint16_t length,
155155
const size_t hdlen = sizeof(uint16_t) + sizeof(void *);
156156
BUG_ON_HOT(b_contig_space(buf) < hdlen); /* this must not happen */
157157

158+
/* If first packet is INITIAL, ensure datagram is sufficiently padded. */
159+
BUG_ON(first_pkt->type == QUIC_PACKET_TYPE_INITIAL &&
160+
(first_pkt->flags & QUIC_FL_TX_PACKET_ACK_ELICITING) &&
161+
length < QUIC_INITIAL_PACKET_MINLEN);
162+
158163
write_u16(b_tail(buf), length);
159164
write_ptr(b_tail(buf) + sizeof(length), first_pkt);
160165
b_add(buf, hdlen + length);
161166
}
162167

163-
/* Returns 1 if a packet may be built for <qc> from <qel> encryption level
164-
* with <frms> as ack-eliciting frame list to send, 0 if not.
165-
* <cc> must equal to 1 if an immediate close was asked, 0 if not.
166-
* <probe> must equalt to 1 if a probing packet is required, 0 if not.
167-
* Also set <*must_ack> to inform the caller if an acknowledgement should be sent.
168+
/* Reports if data are ready to be sent for <qel> encryption level on <qc>
169+
* connection.
170+
*
171+
* <frms> is the ack-eliciting frames list to send, if any. Other parameters
172+
* can be set individually for some special frame types : <cc> for immediate
173+
* close, <probe> to emit probing frames.
174+
*
175+
* This function will also set <must_ack> to inform the caller that an
176+
* acknowledgement should be sent.
177+
*
178+
* Returns true if data to emit else false.
168179
*/
169180
static int qc_may_build_pkt(struct quic_conn *qc, struct list *frms,
170181
struct quic_enc_level *qel, int cc, int probe,
@@ -330,15 +341,7 @@ static int qc_send_ppkts(struct buffer *buf, struct ssl_sock_ctx *ctx)
330341

331342
for (pkt = first_pkt; pkt; pkt = next_pkt) {
332343
struct quic_cc *cc = &qc->path->cc;
333-
/* RFC 9000 14.1 Initial datagram size
334-
* a server MUST expand the payload of all UDP datagrams carrying ack-eliciting
335-
* Initial packets to at least the smallest allowed maximum datagram size of
336-
* 1200 bytes.
337-
*/
338344
qc->cntrs.sent_pkt++;
339-
BUG_ON_HOT(pkt->type == QUIC_PACKET_TYPE_INITIAL &&
340-
(pkt->flags & QUIC_FL_TX_PACKET_ACK_ELICITING) &&
341-
dglen < QUIC_INITIAL_PACKET_MINLEN);
342345

343346
pkt->time_sent = time_sent;
344347
if (pkt->flags & QUIC_FL_TX_PACKET_ACK_ELICITING) {
@@ -510,7 +513,7 @@ static int qc_prep_pkts(struct quic_conn *qc, struct buffer *buf,
510513
list_for_each_entry_safe(qel, tmp_qel, qels, el_send) {
511514
struct quic_tls_ctx *tls_ctx;
512515
const struct quic_version *ver;
513-
struct list *frms = qel->send_frms, *next_frms;
516+
struct list *frms = qel->send_frms;
514517
struct quic_enc_level *next_qel;
515518

516519
if (qel == qc->eel) {
@@ -521,14 +524,9 @@ static int qc_prep_pkts(struct quic_conn *qc, struct buffer *buf,
521524
qc_select_tls_ver(qc, qel, &tls_ctx, &ver);
522525

523526
/* Retrieve next QEL. Set it to NULL if on qels last element. */
524-
if (qel->el_send.n != qels) {
525-
next_qel = LIST_ELEM(qel->el_send.n, struct quic_enc_level *, el_send);
526-
next_frms = next_qel->send_frms;
527-
}
528-
else {
529-
next_qel = NULL;
530-
next_frms = NULL;
531-
}
527+
next_qel = LIST_NEXT(&qel->el_send, struct quic_enc_level *, el_send);
528+
if (&next_qel->el_send == qels)
529+
next_qel = NULL;
532530

533531
/* Build as much as datagrams at <qel> encryption level.
534532
* Each datagram is prepended with its length followed by the address
@@ -546,7 +544,9 @@ static int qc_prep_pkts(struct quic_conn *qc, struct buffer *buf,
546544
if (!cc)
547545
probe = qel->pktns->tx.pto_probe;
548546

549-
if (!qc_may_build_pkt(qc, frms, qel, cc, probe, &must_ack)) {
547+
/* Remove QEL if nothing to send anymore. Padding is only emitted for last QEL. */
548+
if (!qc_may_build_pkt(qc, frms, qel, cc, probe, &must_ack) &&
549+
(!padding || next_qel)) {
550550
/* Remove qel from send_list if nothing to send. */
551551
LIST_DEL_INIT(&qel->el_send);
552552
qel->send_frms = NULL;
@@ -577,30 +577,28 @@ static int qc_prep_pkts(struct quic_conn *qc, struct buffer *buf,
577577
}
578578

579579
/* RFC 9000 14.1 Initial datagram size
580-
* a server MUST expand the payload of all UDP datagrams carrying ack-eliciting
581-
* Initial packets to at least the smallest allowed maximum datagram size of
582-
* 1200 bytes.
583580
*
584-
* Ensure that no ack-eliciting packets are sent into too small datagrams
581+
* Similarly, a server MUST expand the payload of all UDP
582+
* datagrams carrying ack-eliciting Initial packets to at least the
583+
* smallest allowed maximum datagram size of 1200 bytes.
585584
*/
586-
if (qel == qc->iel && !LIST_ISEMPTY(frms)) {
585+
if (qel == qc->iel && (!LIST_ISEMPTY(frms) || probe)) {
586+
/* Ensure that no ack-eliciting packets are sent into too small datagrams */
587587
if (end - pos < QUIC_INITIAL_PACKET_MINLEN) {
588588
TRACE_PROTO("No more enough room to build an Initial packet",
589589
QUIC_EV_CONN_PHPKTS, qc);
590590
break;
591591
}
592592

593-
/* Pad this Initial packet if there is no ack-eliciting frames to send from
594-
* the next packet number space.
595-
*/
596-
if (!next_frms || LIST_ISEMPTY(next_frms))
597-
padding = 1;
593+
/* padding will be set for last QEL */
594+
padding = 1;
598595
}
599596

600597
pkt_type = quic_enc_level_pkt_type(qc, qel);
601598
cur_pkt = qc_build_pkt(&pos, end, qel, tls_ctx, frms,
602599
qc, ver, dglen, pkt_type,
603-
must_ack, padding, probe, cc, &err);
600+
must_ack, padding && !next_qel,
601+
probe, cc, &err);
604602
switch (err) {
605603
case -3:
606604
if (first_pkt)
@@ -628,6 +626,10 @@ static int qc_prep_pkts(struct quic_conn *qc, struct buffer *buf,
628626
total += cur_pkt->len;
629627
dglen += cur_pkt->len;
630628

629+
/* Reset padding if datagram is big enough. */
630+
if (dglen >= QUIC_INITIAL_PACKET_MINLEN)
631+
padding = 0;
632+
631633
if (qc->flags & QUIC_FL_CONN_RETRANS_OLD_DATA)
632634
cur_pkt->flags |= QUIC_FL_TX_PACKET_PROBE_WITH_OLD_DATA;
633635

@@ -647,12 +649,7 @@ static int qc_prep_pkts(struct quic_conn *qc, struct buffer *buf,
647649
* the same datagram, except if <qel> is the Application data
648650
* encryption level which cannot be selected to do that.
649651
*/
650-
if (LIST_ISEMPTY(frms) && qel != qc->ael && next_qel) {
651-
if (qel == qc->iel &&
652-
(!qc_is_listener(qc) ||
653-
cur_pkt->flags & QUIC_FL_TX_PACKET_ACK_ELICITING))
654-
padding = 1;
655-
652+
if (LIST_ISEMPTY(frms) && next_qel) {
656653
prv_pkt = cur_pkt;
657654
}
658655
else {

0 commit comments

Comments
 (0)