From 7008f16d57ecf23072e6a58f43927c31059201dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20L=C3=A9caille?= Date: Tue, 25 Jul 2023 07:09:24 +0200 Subject: [PATCH] MINOR: quic: Add a new quic_ack.c C module for QUIC acknowledgements Extract the code in relation with the QUIC acknowledgements from quic_conn.c to quic_ack.c to accelerate the compilation of quic_conn.c. --- Makefile | 2 +- include/haproxy/quic_ack-t.h | 43 ++++++ include/haproxy/quic_ack.h | 23 +++ include/haproxy/quic_conn-t.h | 14 -- include/haproxy/quic_tls-t.h | 14 +- src/quic_ack.c | 259 ++++++++++++++++++++++++++++++++++ src/quic_conn.c | 253 +-------------------------------- src/quic_tls.c | 1 + 8 files changed, 330 insertions(+), 279 deletions(-) create mode 100644 include/haproxy/quic_ack-t.h create mode 100644 include/haproxy/quic_ack.h create mode 100644 src/quic_ack.c diff --git a/Makefile b/Makefile index 56df8c121..7ed76929a 100644 --- a/Makefile +++ b/Makefile @@ -607,7 +607,7 @@ OPTIONS_OBJS += src/quic_conn.o src/mux_quic.o src/h3.o src/xprt_quic.o \ 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 diff --git a/include/haproxy/quic_ack-t.h b/include/haproxy/quic_ack-t.h new file mode 100644 index 000000000..95b77f1c9 --- /dev/null +++ b/include/haproxy/quic_ack-t.h @@ -0,0 +1,43 @@ +/* + * 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 */ diff --git a/include/haproxy/quic_ack.h b/include/haproxy/quic_ack.h new file mode 100644 index 000000000..540e2c08c --- /dev/null +++ b/include/haproxy/quic_ack.h @@ -0,0 +1,23 @@ +/* + * 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 */ diff --git a/include/haproxy/quic_conn-t.h b/include/haproxy/quic_conn-t.h index c311d8984..2a05f3b83 100644 --- a/include/haproxy/quic_conn-t.h +++ b/include/haproxy/quic_conn-t.h @@ -309,20 +309,6 @@ struct quic_connection_id { 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. */ diff --git a/include/haproxy/quic_tls-t.h b/include/haproxy/quic_tls-t.h index 70cbf673b..3a0c27ac5 100644 --- a/include/haproxy/quic_tls-t.h +++ b/include/haproxy/quic_tls-t.h @@ -21,6 +21,7 @@ #include +#include #include /* It seems TLS 1.3 ciphersuites macros differ between openssl and boringssl */ @@ -123,19 +124,6 @@ extern const unsigned char initial_salt_draft_29[20]; 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; diff --git a/src/quic_ack.c b/src/quic_ack.c new file mode 100644 index 000000000..9334e77ee --- /dev/null +++ b/src/quic_ack.c @@ -0,0 +1,259 @@ +#include + +#include + +#include +#include +#include + +#define TRACE_SOURCE &trace_quic + +DECLARE_STATIC_POOL(pool_head_quic_arng, "quic_arng", sizeof(struct quic_arng_node)); + +/* Deallocate 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

and ACK ranges where follows

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 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 ack range into 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 tree of ACK ranges with 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 + * packet number space tree below possibly + * updating the range which contains . + * 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); +} + diff --git a/src/quic_conn.c b/src/quic_conn.c index 0f6879c36..250c53f56 100644 --- a/src/quic_conn.c +++ b/src/quic_conn.c @@ -45,6 +45,7 @@ #include #include #include +#include #include #include #include @@ -135,7 +136,6 @@ DECLARE_POOL(pool_head_quic_tx_packet, "quic_tx_packet", sizeof(struct quic_tx_p 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, @@ -865,54 +865,6 @@ static inline void free_quic_tx_pkts(struct quic_conn *qc, struct list *pkts) TRACE_LEAVE(QUIC_EV_CONN_TXPKT, qc); } -/* Remove already sent ranges of acknowledged packet numbers from - * packet number space tree below possibly - * updating the range which contains . - * 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 * list and free them. * Always succeeds. @@ -936,7 +888,7 @@ static inline void qc_treat_newly_acked_pkts(struct quic_conn *qc, * 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); @@ -2687,207 +2639,6 @@ static int quic_build_post_handshake_frames(struct quic_conn *qc) goto leave; } -/* Deallocate 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

and ACK ranges where follows

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 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 ack range into 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 tree of ACK ranges with 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) diff --git a/src/quic_tls.c b/src/quic_tls.c index 34f211920..ae5207887 100644 --- a/src/quic_tls.c +++ b/src/quic_tls.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include