]> git.kaiwu.me - haproxy.git/commitdiff
BUG/MINOR: quic: rearm packet on packet received even non ack-eliciting
authorAmaury Denoyelle <adenoyelle@haproxy.com>
Wed, 14 Oct 2026 13:40:56 +0000 (15:40 +0200)
committerAmaury Denoyelle <adenoyelle@haproxy.com>
Thu, 15 Oct 2026 12:24:19 +0000 (14:24 +0200)
QUIC idle timeout must be refreshed for each valid packet received.
Previously, this was only performed for ack-eliciting packets. In case
the client only emit non ack-eliciting frames, the connection would be
closed despite being still active.

Fix idle timeout mechanism by calling qc_idle_timer_rearm() on every
packets, outside of QUIC_FL_RX_PACKET_ACK_ELICITING flag. As previously,
in case of an ack-eliciting packet, both idle and ack expire are
refreshed, but the latter will be queued as it is smaller.

This issue was detected with SSE running on top of an HTTP/3 connection,
with the server pushing updates and the client emitting only ACKs. This
should fix github issue #3534.

This must be backported up to 2.6, after a period of observation.

src/quic_rx.c

index 1bfe78fbb7fec9b16462d2b508b9d36c0971205f..c4411c9cd318c85d56eaf7303fa56aa217d902e4 100644 (file)
@@ -1387,6 +1387,7 @@ int qc_treat_rx_pkts(struct quic_conn *qc)
        int64_t largest_pn = -1;
        unsigned int largest_pn_time_received = 0;
        struct quic_enc_level *qel, *qelbak;
+       int arm_ack_timer;
 
        TRACE_ENTER(QUIC_EV_CONN_RXPKT, qc);
 
@@ -1434,16 +1435,17 @@ int qc_treat_rx_pkts(struct quic_conn *qc)
                                                HA_ATOMIC_DEC(&qc->prx_counters->half_open_conn);
                                        }
 
+                                       arm_ack_timer = 0;
+
                                        /* Update the list of ranges to acknowledge. */
                                        if (quic_update_ack_ranges_list(qc, &qel->pktns->rx.arngs, &ar)) {
                                                if (pkt->flags & QUIC_FL_RX_PACKET_ACK_ELICITING) {
-                                                       int arm_ack_timer =
-                                                               qc->state >= QUIC_HS_ST_COMPLETE &&
-                                                               qel->pktns == qc->apktns;
+                                                       arm_ack_timer =
+                                                         qc->state >= QUIC_HS_ST_COMPLETE &&
+                                                         qel->pktns == qc->apktns;
 
                                                        qel->pktns->flags |= QUIC_FL_PKTNS_ACK_REQUIRED;
                                                        qel->pktns->rx.nb_aepkts_since_last_ack++;
-                                                       qc_idle_timer_rearm(qc, 1, arm_ack_timer);
                                                }
 
                                                if (pkt->pn > largest_pn) {
@@ -1455,6 +1457,13 @@ int qc_treat_rx_pkts(struct quic_conn *qc)
                                                TRACE_ERROR("Could not update ack range list",
                                                            QUIC_EV_CONN_RXPKT, qc);
                                        }
+
+                                       /* RFC 9000 10.1. Idle Timeout
+                                        *
+                                        * An endpoint restarts its idle timer when a packet from its peer is
+                                        * received and processed successfully.
+                                        */
+                                       qc_idle_timer_rearm(qc, 1, arm_ack_timer);
                                }
                        }
                        node = eb64_next(node);