src/quic_cc_newreno.o src/quic_cc_cubic.o src/qpack-tbl.o \
src/qpack-dec.o src/hq_interop.o src/quic_stream.o \
src/h3_stats.o src/qmux_http.o src/cfgparse-quic.o \
- src/cbuf.o src/quic_cc.o src/quic_cc_nocc.o \
+ src/cbuf.o src/quic_cc.o src/quic_cc_nocc.o src/quic_ack.o \
src/quic_trace.o src/quic_cli.o src/quic_ssl.o
endif
--- /dev/null
+/*
+ * include/haproxy/quic_ack-t.h
+ * Definitions for QUIC acknowledgements internal types, constants and flags.
+ *
+ * Copyright (C) 2023
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ */
+#ifndef _HAPROXY_QUIC_ACK_T_H
+#define _HAPROXY_QUIC_ACK_T_H
+
+/* The maximum number of ack ranges to be built in ACK frames */
+#define QUIC_MAX_ACK_RANGES 32
+
+/* Structure to maintain a set of ACK ranges to be used to build ACK frames. */
+struct quic_arngs {
+ /* ebtree of ACK ranges organized by their first value. */
+ struct eb_root root;
+ /* The number of ACK ranges is this tree */
+ size_t sz;
+ /* The number of bytes required to encode this ACK ranges lists. */
+ size_t enc_sz;
+};
+
+/* Structure to hold a range of ACKs sent in ACK frames. */
+struct quic_arng {
+ int64_t first;
+ int64_t last;
+};
+
+/* Structure to hold a range of ACKs to be store as a node in a tree of
+ * ACK ranges.
+ */
+struct quic_arng_node {
+ struct eb64_node first;
+ uint64_t last;
+};
+
+#endif /* _HAPROXY_QUIC_ACK_T_H */
--- /dev/null
+/*
+ * include/proto/quic_ack.h
+ * This file provides definitions for QUIC acknowledgements.
+ *
+ * Copyright (C) 2023
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#ifndef _HAPROXY_QUIC_ACK_H
+#define _HAPROXY_QUIC_ACK_H
+
+void quic_free_arngs(struct quic_conn *qc, struct quic_arngs *arngs);
+int quic_update_ack_ranges_list(struct quic_conn *qc,
+ struct quic_arngs *arngs,
+ struct quic_arng *ar);
+void qc_treat_ack_of_ack(struct quic_conn *qc, struct quic_arngs *arngs,
+ int64_t largest_acked_pn);
+
+#endif /* _HAPROXY_QUIC_ACK_H */
uint tid; /* Attached Thread ID for the connection. */
};
-/* Structure to hold a range of ACKs sent in ACK frames. */
-struct quic_arng {
- int64_t first;
- int64_t last;
-};
-
-/* Structure to hold a range of ACKs to be store as a node in a tree of
- * ACK ranges.
- */
-struct quic_arng_node {
- struct eb64_node first;
- uint64_t last;
-};
-
/* Flag the packet number space as having received a packet */
#define QUIC_FL_PKTNS_PKT_RECEIVED (1UL << 0)
/* Flag the packet number space as requiring an ACK frame to be sent. */
#include <import/ebtree.h>
+#include <haproxy/quic_ack-t.h>
#include <haproxy/openssl-compat.h>
/* It seems TLS 1.3 ciphersuites macros differ between openssl and boringssl */
extern const unsigned char initial_salt_v1[20];
extern const unsigned char initial_salt_v2[20];
-/* The maximum number of ack ranges to be built in ACK frames */
-#define QUIC_MAX_ACK_RANGES 32
-
-/* Structure to maintain a set of ACK ranges to be used to build ACK frames. */
-struct quic_arngs {
- /* ebtree of ACK ranges organized by their first value. */
- struct eb_root root;
- /* The number of ACK ranges is this tree */
- size_t sz;
- /* The number of bytes required to encode this ACK ranges lists. */
- size_t enc_sz;
-};
-
/* QUIC packet number space */
struct quic_pktns {
struct list list;
--- /dev/null
+#include <inttypes.h>
+
+#include <import/eb64tree.h>
+
+#include <haproxy/quic_conn-t.h>
+#include <haproxy/quic_enc.h>
+#include <haproxy/trace.h>
+
+#define TRACE_SOURCE &trace_quic
+
+DECLARE_STATIC_POOL(pool_head_quic_arng, "quic_arng", sizeof(struct quic_arng_node));
+
+/* Deallocate <l> list of ACK ranges. */
+void quic_free_arngs(struct quic_conn *qc, struct quic_arngs *arngs)
+{
+ struct eb64_node *n;
+ struct quic_arng_node *ar;
+
+ TRACE_ENTER(QUIC_EV_CONN_CLOSE, qc);
+
+ n = eb64_first(&arngs->root);
+ while (n) {
+ struct eb64_node *next;
+
+ ar = eb64_entry(n, struct quic_arng_node, first);
+ next = eb64_next(n);
+ eb64_delete(n);
+ pool_free(pool_head_quic_arng, ar);
+ n = next;
+ }
+
+ TRACE_LEAVE(QUIC_EV_CONN_CLOSE, qc);
+}
+
+/* Return the gap value between <p> and <q> ACK ranges where <q> follows <p> in
+ * descending order.
+ */
+static inline size_t sack_gap(struct quic_arng_node *p,
+ struct quic_arng_node *q)
+{
+ return p->first.key - q->last - 2;
+}
+
+/* Set the encoded size of <arngs> QUIC ack ranges. */
+static void quic_arngs_set_enc_sz(struct quic_conn *qc, struct quic_arngs *arngs)
+{
+ struct eb64_node *node, *next;
+ struct quic_arng_node *ar, *ar_next;
+
+ TRACE_ENTER(QUIC_EV_CONN_TXPKT, qc);
+
+ node = eb64_last(&arngs->root);
+ if (!node)
+ goto leave;
+
+ ar = eb64_entry(node, struct quic_arng_node, first);
+ arngs->enc_sz = quic_int_getsize(ar->last) +
+ quic_int_getsize(ar->last - ar->first.key) + quic_int_getsize(arngs->sz - 1);
+
+ while ((next = eb64_prev(node))) {
+ ar_next = eb64_entry(next, struct quic_arng_node, first);
+ arngs->enc_sz += quic_int_getsize(sack_gap(ar, ar_next)) +
+ quic_int_getsize(ar_next->last - ar_next->first.key);
+ node = next;
+ ar = eb64_entry(node, struct quic_arng_node, first);
+ }
+
+ leave:
+ TRACE_LEAVE(QUIC_EV_CONN_TXPKT, qc);
+}
+
+/* Insert <ar> ack range into <argns> tree of ack ranges.
+ * Returns the ack range node which has been inserted if succeeded, NULL if not.
+ */
+static inline
+struct quic_arng_node *quic_insert_new_range(struct quic_conn *qc,
+ struct quic_arngs *arngs,
+ struct quic_arng *ar)
+{
+ struct quic_arng_node *new_ar;
+
+ TRACE_ENTER(QUIC_EV_CONN_RXPKT, qc);
+
+ if (arngs->sz >= QUIC_MAX_ACK_RANGES) {
+ struct eb64_node *last;
+
+ last = eb64_last(&arngs->root);
+ BUG_ON(last == NULL);
+ eb64_delete(last);
+ pool_free(pool_head_quic_arng, last);
+ arngs->sz--;
+ }
+
+ new_ar = pool_alloc(pool_head_quic_arng);
+ if (!new_ar) {
+ TRACE_ERROR("ack range allocation failed", QUIC_EV_CONN_RXPKT, qc);
+ goto leave;
+ }
+
+ new_ar->first.key = ar->first;
+ new_ar->last = ar->last;
+ eb64_insert(&arngs->root, &new_ar->first);
+ arngs->sz++;
+
+ leave:
+ TRACE_LEAVE(QUIC_EV_CONN_RXPKT, qc);
+ return new_ar;
+}
+
+/* Update <arngs> tree of ACK ranges with <ar> as new ACK range value.
+ * Note that this function computes the number of bytes required to encode
+ * this tree of ACK ranges in descending order.
+ *
+ * Descending order
+ * ------------->
+ * range1 range2
+ * ..........|--------|..............|--------|
+ * ^ ^ ^ ^
+ * | | | |
+ * last1 first1 last2 first2
+ * ..........+--------+--------------+--------+......
+ * diff1 gap12 diff2
+ *
+ * To encode the previous list of ranges we must encode integers as follows in
+ * descending order:
+ * enc(last2),enc(diff2),enc(gap12),enc(diff1)
+ * with diff1 = last1 - first1
+ * diff2 = last2 - first2
+ * gap12 = first1 - last2 - 2 (>= 0)
+ *
+
+returns 0 on error
+
+ */
+int quic_update_ack_ranges_list(struct quic_conn *qc,
+ struct quic_arngs *arngs,
+ struct quic_arng *ar)
+{
+ int ret = 0;
+ struct eb64_node *le;
+ struct quic_arng_node *new_node;
+ struct eb64_node *new;
+
+ TRACE_ENTER(QUIC_EV_CONN_RXPKT, qc);
+
+ new = NULL;
+ if (eb_is_empty(&arngs->root)) {
+ new_node = quic_insert_new_range(qc, arngs, ar);
+ if (new_node)
+ ret = 1;
+
+ goto leave;
+ }
+
+ le = eb64_lookup_le(&arngs->root, ar->first);
+ if (!le) {
+ new_node = quic_insert_new_range(qc, arngs, ar);
+ if (!new_node)
+ goto leave;
+
+ new = &new_node->first;
+ }
+ else {
+ struct quic_arng_node *le_ar =
+ eb64_entry(le, struct quic_arng_node, first);
+
+ /* Already existing range */
+ if (le_ar->last >= ar->last) {
+ ret = 1;
+ }
+ else if (le_ar->last + 1 >= ar->first) {
+ le_ar->last = ar->last;
+ new = le;
+ new_node = le_ar;
+ }
+ else {
+ new_node = quic_insert_new_range(qc, arngs, ar);
+ if (!new_node)
+ goto leave;
+
+ new = &new_node->first;
+ }
+ }
+
+ /* Verify that the new inserted node does not overlap the nodes
+ * which follow it.
+ */
+ if (new) {
+ struct eb64_node *next;
+ struct quic_arng_node *next_node;
+
+ while ((next = eb64_next(new))) {
+ next_node =
+ eb64_entry(next, struct quic_arng_node, first);
+ if (new_node->last + 1 < next_node->first.key)
+ break;
+
+ if (next_node->last > new_node->last)
+ new_node->last = next_node->last;
+ eb64_delete(next);
+ pool_free(pool_head_quic_arng, next_node);
+ /* Decrement the size of these ranges. */
+ arngs->sz--;
+ }
+ }
+
+ ret = 1;
+ leave:
+ quic_arngs_set_enc_sz(qc, arngs);
+ TRACE_LEAVE(QUIC_EV_CONN_RXPKT, qc);
+ return ret;
+}
+
+/* Remove already sent ranges of acknowledged packet numbers from
+ * <pktns> packet number space tree below <largest_acked_pn> possibly
+ * updating the range which contains <largest_acked_pn>.
+ * Never fails.
+ */
+void qc_treat_ack_of_ack(struct quic_conn *qc, struct quic_arngs *arngs,
+ int64_t largest_acked_pn)
+{
+ struct eb64_node *ar, *next_ar;
+
+ TRACE_ENTER(QUIC_EV_CONN_PRSAFRM, qc);
+
+ ar = eb64_first(&arngs->root);
+ while (ar) {
+ struct quic_arng_node *ar_node;
+
+ next_ar = eb64_next(ar);
+ ar_node = eb64_entry(ar, struct quic_arng_node, first);
+
+ if ((int64_t)ar_node->first.key > largest_acked_pn) {
+ TRACE_DEVEL("first.key > largest", QUIC_EV_CONN_PRSAFRM, qc);
+ break;
+ }
+
+ if (largest_acked_pn < ar_node->last) {
+ eb64_delete(ar);
+ ar_node->first.key = largest_acked_pn + 1;
+ eb64_insert(&arngs->root, ar);
+ break;
+ }
+
+ /* Do not empty the tree: the first ACK range contains the
+ * largest acknowledged packet number.
+ */
+ if (arngs->sz == 1)
+ break;
+
+ eb64_delete(ar);
+ pool_free(pool_head_quic_arng, ar_node);
+ arngs->sz--;
+ ar = next_ar;
+ }
+
+ TRACE_LEAVE(QUIC_EV_CONN_PRSAFRM, qc);
+}
+
#include <haproxy/ncbuf.h>
#include <haproxy/pipe.h>
#include <haproxy/proxy.h>
+#include <haproxy/quic_ack.h>
#include <haproxy/quic_cc.h>
#include <haproxy/quic_cli-t.h>
#include <haproxy/quic_frame.h>
DECLARE_POOL(pool_head_quic_crypto_buf, "quic_crypto_buf", sizeof(struct quic_crypto_buf));
DECLARE_STATIC_POOL(pool_head_quic_cstream, "quic_cstream", sizeof(struct quic_cstream));
DECLARE_POOL(pool_head_quic_frame, "quic_frame", sizeof(struct quic_frame));
-DECLARE_STATIC_POOL(pool_head_quic_arng, "quic_arng", sizeof(struct quic_arng_node));
static struct quic_connection_id *new_quic_cid(struct eb_root *root,
struct quic_conn *qc,
TRACE_LEAVE(QUIC_EV_CONN_TXPKT, qc);
}
-/* Remove already sent ranges of acknowledged packet numbers from
- * <pktns> packet number space tree below <largest_acked_pn> possibly
- * updating the range which contains <largest_acked_pn>.
- * Never fails.
- */
-static void qc_treat_ack_of_ack(struct quic_conn *qc,
- struct quic_pktns *pktns,
- int64_t largest_acked_pn)
-{
- struct eb64_node *ar, *next_ar;
- struct quic_arngs *arngs = &pktns->rx.arngs;
-
- TRACE_ENTER(QUIC_EV_CONN_PRSAFRM, qc);
-
- ar = eb64_first(&arngs->root);
- while (ar) {
- struct quic_arng_node *ar_node;
-
- next_ar = eb64_next(ar);
- ar_node = eb64_entry(ar, struct quic_arng_node, first);
-
- if ((int64_t)ar_node->first.key > largest_acked_pn) {
- TRACE_DEVEL("first.key > largest", QUIC_EV_CONN_PRSAFRM, qc);
- break;
- }
-
- if (largest_acked_pn < ar_node->last) {
- eb64_delete(ar);
- ar_node->first.key = largest_acked_pn + 1;
- eb64_insert(&arngs->root, ar);
- break;
- }
-
- /* Do not empty the tree: the first ACK range contains the
- * largest acknowledged packet number.
- */
- if (arngs->sz == 1)
- break;
-
- eb64_delete(ar);
- pool_free(pool_head_quic_arng, ar_node);
- arngs->sz--;
- ar = next_ar;
- }
-
- TRACE_LEAVE(QUIC_EV_CONN_PRSAFRM, qc);
-}
-
/* Send a packet ack event nofication for each newly acked packet of
* <newly_acked_pkts> list and free them.
* Always succeeds.
* packet number which was sent in an ACK frame by this packet.
*/
if (pkt->largest_acked_pn != -1)
- qc_treat_ack_of_ack(qc, pkt->pktns, pkt->largest_acked_pn);
+ qc_treat_ack_of_ack(qc, &pkt->pktns->rx.arngs, pkt->largest_acked_pn);
ev.ack.acked = pkt->in_flight_len;
ev.ack.time_sent = pkt->time_sent;
quic_cc_event(&qc->path->cc, &ev);
goto leave;
}
-/* Deallocate <l> list of ACK ranges. */
-void quic_free_arngs(struct quic_conn *qc, struct quic_arngs *arngs)
-{
- struct eb64_node *n;
- struct quic_arng_node *ar;
-
- TRACE_ENTER(QUIC_EV_CONN_CLOSE, qc);
-
- n = eb64_first(&arngs->root);
- while (n) {
- struct eb64_node *next;
-
- ar = eb64_entry(n, struct quic_arng_node, first);
- next = eb64_next(n);
- eb64_delete(n);
- pool_free(pool_head_quic_arng, ar);
- n = next;
- }
-
- TRACE_LEAVE(QUIC_EV_CONN_CLOSE, qc);
-}
-
-/* Return the gap value between <p> and <q> ACK ranges where <q> follows <p> in
- * descending order.
- */
-static inline size_t sack_gap(struct quic_arng_node *p,
- struct quic_arng_node *q)
-{
- return p->first.key - q->last - 2;
-}
-
-/* Set the encoded size of <arngs> QUIC ack ranges. */
-static void quic_arngs_set_enc_sz(struct quic_conn *qc, struct quic_arngs *arngs)
-{
- struct eb64_node *node, *next;
- struct quic_arng_node *ar, *ar_next;
-
- TRACE_ENTER(QUIC_EV_CONN_TXPKT, qc);
-
- node = eb64_last(&arngs->root);
- if (!node)
- goto leave;
-
- ar = eb64_entry(node, struct quic_arng_node, first);
- arngs->enc_sz = quic_int_getsize(ar->last) +
- quic_int_getsize(ar->last - ar->first.key) + quic_int_getsize(arngs->sz - 1);
-
- while ((next = eb64_prev(node))) {
- ar_next = eb64_entry(next, struct quic_arng_node, first);
- arngs->enc_sz += quic_int_getsize(sack_gap(ar, ar_next)) +
- quic_int_getsize(ar_next->last - ar_next->first.key);
- node = next;
- ar = eb64_entry(node, struct quic_arng_node, first);
- }
-
- leave:
- TRACE_LEAVE(QUIC_EV_CONN_TXPKT, qc);
-}
-
-/* Insert <ar> ack range into <argns> tree of ack ranges.
- * Returns the ack range node which has been inserted if succeeded, NULL if not.
- */
-static inline
-struct quic_arng_node *quic_insert_new_range(struct quic_conn *qc,
- struct quic_arngs *arngs,
- struct quic_arng *ar)
-{
- struct quic_arng_node *new_ar;
-
- TRACE_ENTER(QUIC_EV_CONN_RXPKT, qc);
-
- if (arngs->sz >= QUIC_MAX_ACK_RANGES) {
- struct eb64_node *last;
-
- last = eb64_last(&arngs->root);
- BUG_ON(last == NULL);
- eb64_delete(last);
- pool_free(pool_head_quic_arng, last);
- arngs->sz--;
- }
-
- new_ar = pool_alloc(pool_head_quic_arng);
- if (!new_ar) {
- TRACE_ERROR("ack range allocation failed", QUIC_EV_CONN_RXPKT, qc);
- goto leave;
- }
-
- new_ar->first.key = ar->first;
- new_ar->last = ar->last;
- eb64_insert(&arngs->root, &new_ar->first);
- arngs->sz++;
-
- leave:
- TRACE_LEAVE(QUIC_EV_CONN_RXPKT, qc);
- return new_ar;
-}
-
-/* Update <arngs> tree of ACK ranges with <ar> as new ACK range value.
- * Note that this function computes the number of bytes required to encode
- * this tree of ACK ranges in descending order.
- *
- * Descending order
- * ------------->
- * range1 range2
- * ..........|--------|..............|--------|
- * ^ ^ ^ ^
- * | | | |
- * last1 first1 last2 first2
- * ..........+--------+--------------+--------+......
- * diff1 gap12 diff2
- *
- * To encode the previous list of ranges we must encode integers as follows in
- * descending order:
- * enc(last2),enc(diff2),enc(gap12),enc(diff1)
- * with diff1 = last1 - first1
- * diff2 = last2 - first2
- * gap12 = first1 - last2 - 2 (>= 0)
- *
-
-returns 0 on error
-
- */
-int quic_update_ack_ranges_list(struct quic_conn *qc,
- struct quic_arngs *arngs,
- struct quic_arng *ar)
-{
- int ret = 0;
- struct eb64_node *le;
- struct quic_arng_node *new_node;
- struct eb64_node *new;
-
- TRACE_ENTER(QUIC_EV_CONN_RXPKT, qc);
-
- new = NULL;
- if (eb_is_empty(&arngs->root)) {
- new_node = quic_insert_new_range(qc, arngs, ar);
- if (new_node)
- ret = 1;
-
- goto leave;
- }
-
- le = eb64_lookup_le(&arngs->root, ar->first);
- if (!le) {
- new_node = quic_insert_new_range(qc, arngs, ar);
- if (!new_node)
- goto leave;
-
- new = &new_node->first;
- }
- else {
- struct quic_arng_node *le_ar =
- eb64_entry(le, struct quic_arng_node, first);
-
- /* Already existing range */
- if (le_ar->last >= ar->last) {
- ret = 1;
- }
- else if (le_ar->last + 1 >= ar->first) {
- le_ar->last = ar->last;
- new = le;
- new_node = le_ar;
- }
- else {
- new_node = quic_insert_new_range(qc, arngs, ar);
- if (!new_node)
- goto leave;
-
- new = &new_node->first;
- }
- }
-
- /* Verify that the new inserted node does not overlap the nodes
- * which follow it.
- */
- if (new) {
- struct eb64_node *next;
- struct quic_arng_node *next_node;
-
- while ((next = eb64_next(new))) {
- next_node =
- eb64_entry(next, struct quic_arng_node, first);
- if (new_node->last + 1 < next_node->first.key)
- break;
-
- if (next_node->last > new_node->last)
- new_node->last = next_node->last;
- eb64_delete(next);
- pool_free(pool_head_quic_arng, next_node);
- /* Decrement the size of these ranges. */
- arngs->sz--;
- }
- }
-
- ret = 1;
- leave:
- quic_arngs_set_enc_sz(qc, arngs);
- TRACE_LEAVE(QUIC_EV_CONN_RXPKT, qc);
- return ret;
-}
-
/* Detect the value of the spin bit to be used. */
static inline void qc_handle_spin_bit(struct quic_conn *qc, struct quic_rx_packet *pkt,
struct quic_enc_level *qel)
#include <haproxy/buf.h>
#include <haproxy/chunk.h>
#include <haproxy/pool.h>
+#include <haproxy/quic_ack.h>
#include <haproxy/quic_conn.h>
#include <haproxy/quic_trace-t.h>