]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
REORG: quic: Move several inlined functions from quic_conn.h
authorFrédéric Lécaille <flecaille@haproxy.com>
Mon, 27 Nov 2023 10:34:03 +0000 (11:34 +0100)
committerFrédéric Lécaille <flecaille@haproxy.com>
Tue, 28 Nov 2023 14:37:47 +0000 (15:37 +0100)
Move quic_pkt_type(), quic_saddr_cpy(), quic_write_uint32(), max_available_room(),
max_stream_data_size(), quic_packet_number_length(), quic_packet_number_encode()
and quic_compute_ack_delay_us() to quic_tx.c because only used in this file.
Also move quic_ack_delay_ms() and quic_read_uint32() to quic_tx.c because they
are used only in this file.

Move quic_rx_packet_refinc() and quic_rx_packet_refdec() to quic_rx.h header.
Move qc_el_rx_pkts(), qc_el_rx_pkts_del() and qc_list_qel_rx_pkts() to quic_tls.h
header.

include/haproxy/quic_conn.h
include/haproxy/quic_rx.h
include/haproxy/quic_tls.h
include/haproxy/quic_tx.h
src/quic_rx.c
src/quic_tx.c

index ec8d4b24ea833b8ca8c33bc8f879f8487b4711af..9ff559646ced2e12d27be31e51f26e9924e52223 100644 (file)
@@ -45,7 +45,7 @@
 #include <haproxy/quic_enc.h>
 #include <haproxy/quic_frame.h>
 #include <haproxy/quic_loss.h>
-#include <haproxy/quic_rx-t.h>
+#include <haproxy/quic_rx.h>
 #include <haproxy/mux_quic.h>
 
 #include <openssl/rand.h>
@@ -85,64 +85,11 @@ void qc_check_close_on_released_mux(struct quic_conn *qc);
 int quic_stateless_reset_token_cpy(unsigned char *pos, size_t len,
                                    const unsigned char *salt, size_t saltlen);
 
-/* Return the long packet type matching with <qv> version and <type> */
-static inline int quic_pkt_type(int type, uint32_t version)
-{
-       if (version != QUIC_PROTOCOL_VERSION_2)
-               return type;
-
-       switch (type) {
-       case QUIC_PACKET_TYPE_INITIAL:
-               return 1;
-       case QUIC_PACKET_TYPE_0RTT:
-               return 2;
-       case QUIC_PACKET_TYPE_HANDSHAKE:
-               return 3;
-       case QUIC_PACKET_TYPE_RETRY:
-               return 0;
-       }
-
-       return -1;
-}
-
 static inline int qc_is_listener(struct quic_conn *qc)
 {
        return qc->flags & QUIC_FL_CONN_LISTENER;
 }
 
-/* Copy <saddr> socket address data into <buf> buffer.
- * This is the responsibility of the caller to check the output buffer is big
- * enough to contain these socket address data.
- * Return the number of bytes copied.
- */
-static inline size_t quic_saddr_cpy(unsigned char *buf,
-                                    const struct sockaddr_storage *saddr)
-{
-       void *port, *addr;
-       unsigned char *p;
-       size_t port_len, addr_len;
-
-       p = buf;
-       if (saddr->ss_family == AF_INET6) {
-               port = &((struct sockaddr_in6 *)saddr)->sin6_port;
-               addr = &((struct sockaddr_in6 *)saddr)->sin6_addr;
-               port_len = sizeof ((struct sockaddr_in6 *)saddr)->sin6_port;
-               addr_len = sizeof ((struct sockaddr_in6 *)saddr)->sin6_addr;
-       }
-       else {
-               port = &((struct sockaddr_in *)saddr)->sin_port;
-               addr = &((struct sockaddr_in *)saddr)->sin_addr;
-               port_len = sizeof ((struct sockaddr_in *)saddr)->sin_port;
-               addr_len = sizeof ((struct sockaddr_in *)saddr)->sin_addr;
-       }
-       memcpy(p, port, port_len);
-       p += port_len;
-       memcpy(p, addr, addr_len);
-       p += addr_len;
-
-       return p - buf;
-}
-
 /* Free the CIDs attached to <conn> QUIC connection. */
 static inline void free_quic_conn_cids(struct quic_conn *conn)
 {
@@ -199,201 +146,6 @@ static inline void quic_connection_id_to_frm_cpy(struct quic_frame *dst,
        ncid_frm->stateless_reset_token = src->stateless_reset_token;
 }
 
-/* Return a 32-bits integer in <val> from QUIC packet with <buf> as address.
- * Makes <buf> point to the data after this 32-bits value if succeeded.
- * Note that these 32-bits integers are network bytes ordered.
- * Returns 0 if failed (not enough data in the buffer), 1 if succeeded.
- */
-static inline int quic_read_uint32(uint32_t *val,
-                                   const unsigned char **buf,
-                                   const unsigned char *end)
-{
-       if (end - *buf < sizeof *val)
-               return 0;
-
-       *val = ntohl(*(uint32_t *)*buf);
-       *buf += sizeof *val;
-
-       return 1;
-}
-
-/* Write a 32-bits integer to a buffer with <buf> as address.
- * Make <buf> point to the data after this 32-buts value if succeeded.
- * Note that these 32-bits integers are networkg bytes ordered.
- * Returns 0 if failed (not enough room in the buffer), 1 if succeeded.
- */
-static inline int quic_write_uint32(unsigned char **buf,
-                                    const unsigned char *end, uint32_t val)
-{
-       if (end - *buf < sizeof val)
-               return 0;
-
-       *(uint32_t *)*buf = htonl(val);
-       *buf += sizeof val;
-
-       return 1;
-}
-
-
-/* Return the maximum number of bytes we must use to completely fill a
- * buffer with <sz> as size for a data field of bytes prefixed by its QUIC
- * variable-length (may be 0).
- * Also put in <*len_sz> the size of this QUIC variable-length.
- * So after returning from this function we have : <*len_sz> + <ret> <= <sz>
- * (<*len_sz> = { max(i), i + ret <= <sz> }) .
- */
-static inline size_t max_available_room(size_t sz, size_t *len_sz)
-{
-       size_t sz_sz, ret;
-       size_t diff;
-
-       sz_sz = quic_int_getsize(sz);
-       if (sz <= sz_sz)
-               return 0;
-
-       ret = sz - sz_sz;
-       *len_sz = quic_int_getsize(ret);
-       /* Difference between the two sizes. Note that <sz_sz> >= <*len_sz>. */
-       diff = sz_sz - *len_sz;
-       if (unlikely(diff > 0)) {
-               /* Let's try to take into an account remaining bytes.
-                *
-                *                  <----------------> <sz_sz>
-                *  <--------------><-------->  +----> <max_int>
-                *       <ret>       <len_sz>   |
-                *  +---------------------------+-----------....
-                *  <--------------------------------> <sz>
-                */
-               size_t max_int = quic_max_int(*len_sz);
-
-               if (max_int + *len_sz <= sz)
-                       ret = max_int;
-               else
-                       ret = sz - diff;
-       }
-
-       return ret;
-}
-
-/* This function computes the maximum data we can put into a buffer with <sz> as
- * size prefixed with a variable-length field "Length" whose value is the
- * remaining data length, already filled of <ilen> bytes which must be taken
- * into an account by "Length" field, and finally followed by the data we want
- * to put in this buffer prefixed again by a variable-length field.
- * <sz> is the size of the buffer to fill.
- * <ilen> the number of bytes already put after the "Length" field.
- * <dlen> the number of bytes we want to at most put in the buffer.
- * Also set <*dlen_sz> to the size of the data variable-length we want to put in
- * the buffer. This is typically this function which must be used to fill as
- * much as possible a QUIC packet made of only one CRYPTO or STREAM frames.
- * Returns this computed size if there is enough room in the buffer, 0 if not.
- */
-static inline size_t max_stream_data_size(size_t sz, size_t ilen, size_t dlen)
-{
-       size_t ret, len_sz, dlen_sz;
-
-       /*
-        * The length of variable-length QUIC integers are powers of two.
-        * Look for the first 3length" field value <len_sz> which match our need.
-        * As we must put <ilen> bytes in our buffer, the minimum value for
-        * <len_sz> is the number of bytes required to encode <ilen>.
-        */
-       for (len_sz = quic_int_getsize(ilen);
-            len_sz <= QUIC_VARINT_MAX_SIZE;
-            len_sz <<= 1) {
-               if (sz < len_sz + ilen)
-                       return 0;
-
-               ret = max_available_room(sz - len_sz - ilen, &dlen_sz);
-               if (!ret)
-                       return 0;
-
-               /* Check that <*len_sz> matches <ret> value */
-               if (len_sz + ilen + dlen_sz + ret <= quic_max_int(len_sz))
-                       return ret < dlen ? ret : dlen;
-       }
-
-       return 0;
-}
-
-/* Return the length in bytes of <pn> packet number depending on
- * <largest_acked_pn> the largest ackownledged packet number.
- */
-static inline size_t quic_packet_number_length(int64_t pn,
-                                               int64_t largest_acked_pn)
-{
-       int64_t max_nack_pkts;
-
-       /* About packet number encoding, the RFC says:
-        * The sender MUST use a packet number size able to represent more than
-        * twice as large a range than the difference between the largest
-        * acknowledged packet and packet number being sent.
-        */
-       max_nack_pkts = 2 * (pn - largest_acked_pn) + 1;
-       if (max_nack_pkts > 0xffffff)
-               return 4;
-       if (max_nack_pkts > 0xffff)
-               return 3;
-       if (max_nack_pkts > 0xff)
-               return 2;
-
-       return 1;
-}
-
-/* Encode <pn> packet number with <pn_len> as length in byte into a buffer with
- * <buf> as current copy address and <end> as pointer to one past the end of
- * this buffer. This is the responsibility of the caller to check there is
- * enough room in the buffer to copy <pn_len> bytes.
- * Never fails.
- */
-static inline int quic_packet_number_encode(unsigned char **buf,
-                                            const unsigned char *end,
-                                            uint64_t pn, size_t pn_len)
-{
-       if (end - *buf < pn_len)
-               return 0;
-
-       /* Encode the packet number. */
-       switch (pn_len) {
-       case 1:
-               **buf = pn;
-               break;
-       case 2:
-               write_n16(*buf, pn);
-               break;
-       case 3:
-               (*buf)[0] = pn >> 16;
-               (*buf)[1] = pn >> 8;
-               (*buf)[2] = pn;
-               break;
-       case 4:
-               write_n32(*buf, pn);
-               break;
-       }
-       *buf += pn_len;
-
-       return 1;
-}
-
-/* Returns the <ack_delay> field value in milliseconds from <ack_frm> ACK frame for
- * <conn> QUIC connection. Note that the value of <ack_delay> coming from
- * ACK frame is in microseconds.
- */
-static inline unsigned int quic_ack_delay_ms(struct qf_ack *ack_frm,
-                                             struct quic_conn *conn)
-{
-       return (ack_frm->ack_delay << conn->tx.params.ack_delay_exponent) / 1000;
-}
-
-/* Returns the <ack_delay> field value in microsecond to be set in an ACK frame
- * depending on the time the packet with a new largest packet number was received.
- */
-static inline uint64_t quic_compute_ack_delay_us(unsigned int time_received,
-                                                 struct quic_conn *conn)
-{
-       return ((now_ms - time_received) * 1000) >> conn->tx.params.ack_delay_exponent;
-}
-
 /* Initialize <p> QUIC network path depending on <ipv4> boolean
  * which is true for an IPv4 path, if not false for an IPv6 path.
  */
@@ -427,76 +179,12 @@ static inline size_t quic_path_prep_data(struct quic_path *path)
        return path->cwnd - path->prep_in_flight;
 }
 
-/* Return the number of bytes which may be sent from <qc> connection when
- * it has not already been validated. Note that this is the responsibility
- * of the caller to check that the case with quic_peer_validated_addr().
- * This latter BUG_ON() if 3 * qc->rx.bytes < qc->tx.prep_bytes.
- */
-static inline size_t quic_may_send_bytes(struct quic_conn *qc)
-{
-       return 3 * qc->bytes.rx - qc->bytes.prep;
-}
-
 /* Return 1 if <pkt> header form is long, 0 if not. */
 static inline int qc_pkt_long(const struct quic_rx_packet *pkt)
 {
        return pkt->type != QUIC_PACKET_TYPE_SHORT;
 }
 
-/* Return 1 if there is RX packets for <qel> QUIC encryption level, 0 if not */
-static inline int qc_el_rx_pkts(struct quic_enc_level *qel)
-{
-       int ret;
-
-       ret = !eb_is_empty(&qel->rx.pkts);
-
-       return ret;
-}
-
-/* Increment the reference counter of <pkt> */
-static inline void quic_rx_packet_refinc(struct quic_rx_packet *pkt)
-{
-       pkt->refcnt++;
-}
-
-/* Decrement the reference counter of <pkt> while remaining positive */
-static inline void quic_rx_packet_refdec(struct quic_rx_packet *pkt)
-{
-       if (pkt->refcnt)
-               pkt->refcnt--;
-}
-
-/* Delete all RX packets for <qel> QUIC encryption level */
-static inline void qc_el_rx_pkts_del(struct quic_enc_level *qel)
-{
-       struct eb64_node *node;
-
-       node = eb64_first(&qel->rx.pkts);
-       while (node) {
-               struct quic_rx_packet *pkt =
-                       eb64_entry(node, struct quic_rx_packet, pn_node);
-
-               node = eb64_next(node);
-               eb64_delete(&pkt->pn_node);
-               quic_rx_packet_refdec(pkt);
-       }
-}
-
-static inline void qc_list_qel_rx_pkts(struct quic_enc_level *qel)
-{
-       struct eb64_node *node;
-
-       node = eb64_first(&qel->rx.pkts);
-       while (node) {
-               struct quic_rx_packet *pkt;
-
-               pkt = eb64_entry(node, struct quic_rx_packet, pn_node);
-               fprintf(stderr, "pkt@%p type=%d pn=%llu\n",
-                       pkt, pkt->type, (ull)pkt->pn_node.key);
-               node = eb64_next(node);
-       }
-}
-
 void chunk_frm_appendf(struct buffer *buf, const struct quic_frame *frm);
 
 void quic_set_connection_close(struct quic_conn *qc, const struct quic_err err);
index aea208c6cd8f7c6c01b69ae246b4cd3c7c1483c4..8c017724e3bdce8c0659ef469993eab56c0772dc 100644 (file)
@@ -36,4 +36,17 @@ int qc_release_lost_pkts(struct quic_conn *qc, struct quic_pktns *pktns,
 int qc_treat_rx_crypto_frms(struct quic_conn *qc, struct quic_enc_level *el,
                             struct ssl_sock_ctx *ctx);
 
+/* Increment the reference counter of <pkt> */
+static inline void quic_rx_packet_refinc(struct quic_rx_packet *pkt)
+{
+       pkt->refcnt++;
+}
+
+/* Decrement the reference counter of <pkt> while remaining positive */
+static inline void quic_rx_packet_refdec(struct quic_rx_packet *pkt)
+{
+       if (pkt->refcnt)
+               pkt->refcnt--;
+}
+
 #endif /* _HAPROXY_QUIC_RX_H */
index e747f778f2730c6858c576df1b7b7944673a7d7d..473545fefea8a9c475a43f81c743d11db1dcae91 100644 (file)
@@ -1049,6 +1049,57 @@ static inline int quic_tls_has_tx_sec(const struct quic_enc_level *qel)
        return qel && !!qel->tls_ctx.tx.key;
 }
 
+/* Return 1 if there is RX packets for <qel> QUIC encryption level, 0 if not */
+static inline int qc_el_rx_pkts(struct quic_enc_level *qel)
+{
+       int ret;
+
+       ret = !eb_is_empty(&qel->rx.pkts);
+
+       return ret;
+}
+
+/* Delete all RX packets for <qel> QUIC encryption level */
+static inline void qc_el_rx_pkts_del(struct quic_enc_level *qel)
+{
+       struct eb64_node *node;
+
+       node = eb64_first(&qel->rx.pkts);
+       while (node) {
+               struct quic_rx_packet *pkt =
+                       eb64_entry(node, struct quic_rx_packet, pn_node);
+
+               node = eb64_next(node);
+               eb64_delete(&pkt->pn_node);
+               quic_rx_packet_refdec(pkt);
+       }
+}
+
+static inline void qc_list_qel_rx_pkts(struct quic_enc_level *qel)
+{
+       struct eb64_node *node;
+
+       node = eb64_first(&qel->rx.pkts);
+       while (node) {
+               struct quic_rx_packet *pkt;
+
+               pkt = eb64_entry(node, struct quic_rx_packet, pn_node);
+               fprintf(stderr, "pkt@%p type=%d pn=%llu\n",
+                       pkt, pkt->type, (ull)pkt->pn_node.key);
+               node = eb64_next(node);
+       }
+}
+
+/* Returns a boolean if <qc> needs to emit frames for <qel> encryption level. */
+static inline int qc_need_sending(struct quic_conn *qc, struct quic_enc_level *qel)
+{
+       return (qc->flags & QUIC_FL_CONN_IMMEDIATE_CLOSE) ||
+              (qel->pktns->flags & QUIC_FL_PKTNS_ACK_REQUIRED) ||
+              qel->pktns->tx.pto_probe ||
+              !LIST_ISEMPTY(&qel->pktns->tx.frms);
+}
+
+
 #endif /* USE_QUIC */
 #endif /* _PROTO_QUIC_TLS_H */
 
index 66541b208a8cdcc88e795eaf5025f46b7178e7d1..dfe44a39a13517f234fc9b740d9e77f11d23866b 100644 (file)
@@ -33,7 +33,6 @@ void qc_txb_release(struct quic_conn *qc);
 int qc_purge_txbuf(struct quic_conn *qc, struct buffer *buf);
 struct buffer *qc_get_txb(struct quic_conn *qc);
 
-int qc_need_sending(struct quic_conn *qc, struct quic_enc_level *qel);
 int qc_prep_hpkts(struct quic_conn *qc, struct buffer *buf, struct list *qels);
 int qc_send_ppkts(struct buffer *buf, struct ssl_sock_ctx *ctx);
 int qc_may_probe_ipktns(struct quic_conn *qc);
@@ -86,4 +85,15 @@ static inline void quic_tx_packet_refdec(struct quic_tx_packet *pkt)
        }
 }
 
+/* Return the number of bytes which may be sent from <qc> connection when
+ * it has not already been validated. Note that this is the responsability
+ * of the caller to check that the case with quic_peer_validated_addr().
+ * This latter BUG_ON() if 3 * qc->rx.bytes < qc->tx.prep_bytes.
+ */
+static inline size_t quic_may_send_bytes(struct quic_conn *qc)
+{
+       return 3 * qc->bytes.rx - qc->bytes.prep;
+}
+
+
 #endif /* _HAPROXY_QUIC_TX_H */
index c717b73e1a4ddbc02676f8f26bd1248035887feb..5bb130160f6e6ec9aea80a49e767c993fc3c1675 100644 (file)
@@ -940,6 +940,16 @@ static int qc_handle_retire_connection_id_frm(struct quic_conn *qc,
        goto leave;
 }
 
+/* Returns the <ack_delay> field value in milliseconds from <ack_frm> ACK frame for
+ * <conn> QUIC connection. Note that the value of <ack_delay> coming from
+ * ACK frame is in microseconds.
+ */
+static inline unsigned int quic_ack_delay_ms(struct qf_ack *ack_frm,
+                                             struct quic_conn *conn)
+{
+       return (ack_frm->ack_delay << conn->tx.params.ack_delay_exponent) / 1000;
+}
+
 /* Parse all the frames of <pkt> QUIC packet for QUIC connection <qc> and <qel>
  * as encryption level.
  * Returns 1 if succeeded, 0 if failed.
@@ -1670,6 +1680,24 @@ static int qc_try_rm_hp(struct quic_conn *qc, struct quic_rx_packet *pkt,
        return ret;
 }
 
+/* Return a 32-bits integer in <val> from QUIC packet with <buf> as address.
+ * Makes <buf> point to the data after this 32-bits value if succeeded.
+ * Note that these 32-bits integers are network bytes ordered.
+ * Returns 0 if failed (not enough data in the buffer), 1 if succeeded.
+ */
+static inline int quic_read_uint32(uint32_t *val,
+                                   const unsigned char **buf,
+                                   const unsigned char *end)
+{
+       if (end - *buf < sizeof *val)
+               return 0;
+
+       *val = ntohl(*(uint32_t *)*buf);
+       *buf += sizeof *val;
+
+       return 1;
+}
+
 /* Parse a QUIC packet header starting at <pos> position without exceeding <end>.
  * Version and type are stored in <pkt> packet instance. Type is set to unknown
  * on two occasions : for unsupported version, in this case version field is
index 7953c04817f88526640d27fc7ac88f2251cdb0c4..49dfd6914c7d9239debe62c63b7fd3cc601e47f5 100644 (file)
@@ -1429,15 +1429,6 @@ int qc_dgrams_retransmit(struct quic_conn *qc)
        return ret;
 }
 
-/* Returns a boolean if <qc> needs to emit frames for <qel> encryption level. */
-int qc_need_sending(struct quic_conn *qc, struct quic_enc_level *qel)
-{
-       return (qc->flags & QUIC_FL_CONN_IMMEDIATE_CLOSE) ||
-              (qel->pktns->flags & QUIC_FL_PKTNS_ACK_REQUIRED) ||
-              qel->pktns->tx.pto_probe ||
-              !LIST_ISEMPTY(&qel->pktns->tx.frms);
-}
-
 /* Return 1 if <qc> connection may probe the Initial packet number space, 0 if not.
  * This is not the case if the remote peer address is not validated and if
  * it cannot send at least QUIC_INITIAL_PACKET_MINLEN bytes.
@@ -1568,6 +1559,40 @@ int send_stateless_reset(struct listener *l, struct sockaddr_storage *dstaddr,
        return ret;
 }
 
+/* Copy <saddr> socket address data into <buf> buffer.
+ * This is the responsibility of the caller to check the output buffer is big
+ * enough to contain these socket address data.
+ * Return the number of bytes copied.
+ */
+static inline size_t quic_saddr_cpy(unsigned char *buf,
+                                    const struct sockaddr_storage *saddr)
+{
+       void *port, *addr;
+       unsigned char *p;
+       size_t port_len, addr_len;
+
+       p = buf;
+       if (saddr->ss_family == AF_INET6) {
+               port = &((struct sockaddr_in6 *)saddr)->sin6_port;
+               addr = &((struct sockaddr_in6 *)saddr)->sin6_addr;
+               port_len = sizeof ((struct sockaddr_in6 *)saddr)->sin6_port;
+               addr_len = sizeof ((struct sockaddr_in6 *)saddr)->sin6_addr;
+       }
+       else {
+               port = &((struct sockaddr_in *)saddr)->sin_port;
+               addr = &((struct sockaddr_in *)saddr)->sin_addr;
+               port_len = sizeof ((struct sockaddr_in *)saddr)->sin_port;
+               addr_len = sizeof ((struct sockaddr_in *)saddr)->sin_addr;
+       }
+       memcpy(p, port, port_len);
+       p += port_len;
+       memcpy(p, addr, addr_len);
+       p += addr_len;
+
+       return p - buf;
+}
+
+
 /* QUIC server only function.
  * Add AAD to <add> buffer from <cid> connection ID and <addr> socket address.
  * This is the responsibility of the caller to check <aad> size is big enough
@@ -1676,6 +1701,27 @@ static int quic_generate_retry_token(unsigned char *token, size_t len,
        goto leave;
 }
 
+/* Return the long packet type matching with <qv> version and <type> */
+static inline int quic_pkt_type(int type, uint32_t version)
+{
+       if (version != QUIC_PROTOCOL_VERSION_2)
+               return type;
+
+       switch (type) {
+       case QUIC_PACKET_TYPE_INITIAL:
+               return 1;
+       case QUIC_PACKET_TYPE_0RTT:
+               return 2;
+       case QUIC_PACKET_TYPE_HANDSHAKE:
+               return 3;
+       case QUIC_PACKET_TYPE_RETRY:
+               return 0;
+       }
+
+       return -1;
+}
+
+
 /* Generate a Retry packet and send it on <fd> socket to <addr> in response to
  * the Initial <pkt> packet.
  *
@@ -1747,6 +1793,163 @@ int send_retry(int fd, struct sockaddr_storage *addr,
        return !ret;
 }
 
+/* Write a 32-bits integer to a buffer with <buf> as address.
+ * Make <buf> point to the data after this 32-buts value if succeeded.
+ * Note that these 32-bits integers are networkg bytes ordered.
+ * Returns 0 if failed (not enough room in the buffer), 1 if succeeded.
+ */
+static inline int quic_write_uint32(unsigned char **buf,
+                                    const unsigned char *end, uint32_t val)
+{
+       if (end - *buf < sizeof val)
+               return 0;
+
+       *(uint32_t *)*buf = htonl(val);
+       *buf += sizeof val;
+
+       return 1;
+}
+
+/* Return the maximum number of bytes we must use to completely fill a
+ * buffer with <sz> as size for a data field of bytes prefixed by its QUIC
+ * variable-length (may be 0).
+ * Also put in <*len_sz> the size of this QUIC variable-length.
+ * So after returning from this function we have : <*len_sz> + <ret> <= <sz>
+ * (<*len_sz> = { max(i), i + ret <= <sz> }) .
+ */
+static inline size_t max_available_room(size_t sz, size_t *len_sz)
+{
+       size_t sz_sz, ret;
+       size_t diff;
+
+       sz_sz = quic_int_getsize(sz);
+       if (sz <= sz_sz)
+               return 0;
+
+       ret = sz - sz_sz;
+       *len_sz = quic_int_getsize(ret);
+       /* Difference between the two sizes. Note that <sz_sz> >= <*len_sz>. */
+       diff = sz_sz - *len_sz;
+       if (unlikely(diff > 0)) {
+               /* Let's try to take into an account remaining bytes.
+                *
+                *                  <----------------> <sz_sz>
+                *  <--------------><-------->  +----> <max_int>
+                *       <ret>       <len_sz>   |
+                *  +---------------------------+-----------....
+                *  <--------------------------------> <sz>
+                */
+               size_t max_int = quic_max_int(*len_sz);
+
+               if (max_int + *len_sz <= sz)
+                       ret = max_int;
+               else
+                       ret = sz - diff;
+       }
+
+       return ret;
+}
+
+/* This function computes the maximum data we can put into a buffer with <sz> as
+ * size prefixed with a variable-length field "Length" whose value is the
+ * remaining data length, already filled of <ilen> bytes which must be taken
+ * into an account by "Length" field, and finally followed by the data we want
+ * to put in this buffer prefixed again by a variable-length field.
+ * <sz> is the size of the buffer to fill.
+ * <ilen> the number of bytes already put after the "Length" field.
+ * <dlen> the number of bytes we want to at most put in the buffer.
+ * Also set <*dlen_sz> to the size of the data variable-length we want to put in
+ * the buffer. This is typically this function which must be used to fill as
+ * much as possible a QUIC packet made of only one CRYPTO or STREAM frames.
+ * Returns this computed size if there is enough room in the buffer, 0 if not.
+ */
+static inline size_t max_stream_data_size(size_t sz, size_t ilen, size_t dlen)
+{
+       size_t ret, len_sz, dlen_sz;
+
+       /*
+        * The length of variable-length QUIC integers are powers of two.
+        * Look for the first 3length" field value <len_sz> which match our need.
+        * As we must put <ilen> bytes in our buffer, the minimum value for
+        * <len_sz> is the number of bytes required to encode <ilen>.
+        */
+       for (len_sz = quic_int_getsize(ilen);
+            len_sz <= QUIC_VARINT_MAX_SIZE;
+            len_sz <<= 1) {
+               if (sz < len_sz + ilen)
+                       return 0;
+
+               ret = max_available_room(sz - len_sz - ilen, &dlen_sz);
+               if (!ret)
+                       return 0;
+
+               /* Check that <*len_sz> matches <ret> value */
+               if (len_sz + ilen + dlen_sz + ret <= quic_max_int(len_sz))
+                       return ret < dlen ? ret : dlen;
+       }
+
+       return 0;
+}
+
+/* Return the length in bytes of <pn> packet number depending on
+ * <largest_acked_pn> the largest ackownledged packet number.
+ */
+static inline size_t quic_packet_number_length(int64_t pn,
+                                               int64_t largest_acked_pn)
+{
+       int64_t max_nack_pkts;
+
+       /* About packet number encoding, the RFC says:
+        * The sender MUST use a packet number size able to represent more than
+        * twice as large a range than the difference between the largest
+        * acknowledged packet and packet number being sent.
+        */
+       max_nack_pkts = 2 * (pn - largest_acked_pn) + 1;
+       if (max_nack_pkts > 0xffffff)
+               return 4;
+       if (max_nack_pkts > 0xffff)
+               return 3;
+       if (max_nack_pkts > 0xff)
+               return 2;
+
+       return 1;
+}
+
+/* Encode <pn> packet number with <pn_len> as length in byte into a buffer with
+ * <buf> as current copy address and <end> as pointer to one past the end of
+ * this buffer. This is the responsibility of the caller to check there is
+ * enough room in the buffer to copy <pn_len> bytes.
+ * Never fails.
+ */
+static inline int quic_packet_number_encode(unsigned char **buf,
+                                            const unsigned char *end,
+                                            uint64_t pn, size_t pn_len)
+{
+       if (end - *buf < pn_len)
+               return 0;
+
+       /* Encode the packet number. */
+       switch (pn_len) {
+       case 1:
+               **buf = pn;
+               break;
+       case 2:
+               write_n16(*buf, pn);
+               break;
+       case 3:
+               (*buf)[0] = pn >> 16;
+               (*buf)[1] = pn >> 8;
+               (*buf)[2] = pn;
+               break;
+       case 4:
+               write_n32(*buf, pn);
+               break;
+       }
+       *buf += pn_len;
+
+       return 1;
+}
+
 /* This function builds into a buffer at <pos> position a QUIC long packet header,
  * <end> being one byte past the end of this buffer.
  * Return 1 if enough room to build this header, 0 if not.
@@ -2186,6 +2389,15 @@ static void qc_build_cc_frm(struct quic_conn *qc, struct quic_enc_level *qel,
 
 }
 
+/* Returns the <ack_delay> field value in microsecond to be set in an ACK frame
+ * depending on the time the packet with a new largest packet number was received.
+ */
+static inline uint64_t quic_compute_ack_delay_us(unsigned int time_received,
+                                                 struct quic_conn *conn)
+{
+       return ((now_ms - time_received) * 1000) >> conn->tx.params.ack_delay_exponent;
+}
+
 /* This function builds a clear packet from <pkt> information (its type)
  * into a buffer with <pos> as position pointer and <qel> as QUIC TLS encryption
  * level for <conn> QUIC connection and <qel> as QUIC TLS encryption level,