]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MINOR: quic: Add definitions for QUIC protocol.
authorFrédéric Lécaille <flecaille@haproxy.com>
Mon, 23 Nov 2020 13:10:37 +0000 (14:10 +0100)
committerWilly Tarreau <w@1wt.eu>
Wed, 23 Dec 2020 10:57:26 +0000 (11:57 +0100)
This patch imports all the definitions for QUIC protocol with few modifications
from 20200720-quic branch of quic-dev repository found at
https://github.com/haproxytech/quic-dev.

include/haproxy/quic_cc-t.h [new file with mode: 0644]
include/haproxy/quic_cc.h [new file with mode: 0644]
include/haproxy/quic_frame-t.h [new file with mode: 0644]
include/haproxy/quic_frame.h [new file with mode: 0644]
include/haproxy/quic_loss-t.h [new file with mode: 0644]
include/haproxy/quic_loss.h [new file with mode: 0644]
include/haproxy/quic_sock.h
include/haproxy/quic_tls-t.h [new file with mode: 0644]
include/haproxy/quic_tls.h [new file with mode: 0644]
include/haproxy/xprt_quic-t.h [new file with mode: 0644]
include/haproxy/xprt_quic.h [new file with mode: 0644]

diff --git a/include/haproxy/quic_cc-t.h b/include/haproxy/quic_cc-t.h
new file mode 100644 (file)
index 0000000..2c11011
--- /dev/null
@@ -0,0 +1,101 @@
+/*
+ * include/haproxy/quic_cc-t.h
+ * This file contains definitions for QUIC congestion control.
+ *
+ * Copyright 2020 HAProxy Technologies, Frédéric Lécaille <flecaille@haproxy.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, version 2.1
+ * exclusively.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifndef _HAPROXY_QUIC_CC_H
+#define _HAPROXY_QUIC_CC_H
+#ifdef USE_QUIC
+#ifndef USE_OPENSSL
+#error "Must define USE_OPENSSL"
+#endif
+
+#include <stddef.h> /* size_t */
+#include <stdint.h>
+
+#include <haproxy/buf-t.h>
+
+#define QUIC_CC_INFINITE_SSTHESH ((uint64_t)-1)
+
+extern struct quic_cc_algo quic_cc_algo_nr;
+extern struct quic_cc_algo *default_quic_cc_algo;
+
+enum quic_cc_algo_state_type {
+       /* Slow start. */
+       QUIC_CC_ST_SS,
+       /* Congestion avoidance. */
+       QUIC_CC_ST_CA,
+};
+
+enum quic_cc_event_type {
+       /* ACK receipt. */
+       QUIC_CC_EVT_ACK,
+       /* Packet loss. */
+       QUIC_CC_EVT_LOSS,
+       /* ECN-CE. */
+       QUIC_CC_EVT_ECN_CE,
+};
+
+struct quic_cc_event {
+       enum quic_cc_event_type type;
+       union {
+               struct ack {
+                       uint64_t acked;
+                       unsigned int time_sent;
+               } ack;
+               struct loss {
+                       unsigned int now_ms;
+                       unsigned int max_ack_delay;
+                       size_t lost_bytes;
+                       unsigned int newest_time_sent;
+                       unsigned int period;
+               } loss;
+       };
+};
+
+enum quic_cc_algo_type {
+       QUIC_CC_ALGO_TP_NEWRENO,
+};
+
+union quic_cc_algo_state {
+       /* NewReno */
+       struct nr {
+               enum quic_cc_algo_state_type state;
+               uint64_t cwnd;
+               uint64_t ssthresh;
+               uint64_t recovery_start_time;
+       } nr;
+};
+
+struct quic_cc {
+       /* <conn> is there ony for debugging purpose. */
+       struct quic_conn *qc;
+       struct quic_cc_algo *algo;
+       union quic_cc_algo_state algo_state;
+};
+
+struct quic_cc_algo {
+       enum quic_cc_algo_type type;
+       int (*init)(struct quic_cc *cc);
+       void (*event)(struct quic_cc *cc, struct quic_cc_event *ev);
+       void (*state_trace)(struct buffer *buf, const struct quic_cc *cc);
+};
+
+#endif /* USE_QUIC */
+#endif /* _HAPROXY_QUIC_CC_H */
diff --git a/include/haproxy/quic_cc.h b/include/haproxy/quic_cc.h
new file mode 100644 (file)
index 0000000..d250af9
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * include/proto/quic_cc.h
+ * This file contains prototypes for QUIC congestion control.
+ *
+ * Copyright 2019 HAProxy Technologies, Frédéric Lécaille <flecaille@haproxy.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, version 2.1
+ * exclusively.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifndef _PROTO_QUIC_CC_H
+#define _PROTO_QUIC_CC_H
+#ifdef USE_QUIC
+#ifndef USE_OPENSSL
+#error "Must define USE_OPENSSL"
+#endif
+
+#include <haproxy/api.h>
+#include <haproxy/chunk.h>
+#include <haproxy/quic_cc-t.h>
+#include <haproxy/xprt_quic-t.h>
+
+void quic_cc_init(struct quic_cc *cc, struct quic_cc_algo *algo, struct quic_conn *qc);
+void quic_cc_event(struct quic_cc *cc, struct quic_cc_event *ev);
+void quic_cc_state_trace(struct buffer *buf, const struct quic_cc *cc);
+
+static inline const char *quic_cc_state_str(enum quic_cc_algo_state_type state)
+{
+       switch (state) {
+       case QUIC_CC_ST_SS:
+               return "ss";
+       case QUIC_CC_ST_CA:
+               return "ca";
+       default:
+               return "unknown";
+       }
+}
+
+/* Return a human readable string from <ev> control congestion event type. */
+static inline void quic_cc_event_trace(struct buffer *buf, const struct quic_cc_event *ev)
+{
+       chunk_appendf(buf, " event type=");
+       switch (ev->type) {
+       case QUIC_CC_EVT_ACK:
+               chunk_appendf(buf, "ack acked=%llu time_sent:%u",
+                             (unsigned long long)ev->ack.acked, ev->ack.time_sent);
+               break;
+       case QUIC_CC_EVT_LOSS:
+               chunk_appendf(buf, "loss now_ms=%u max_ack_delay=%u lost_bytes=%llu"
+                             " time_sent=%u period=%u",
+                             ev->loss.now_ms, ev->loss.max_ack_delay,
+                             (unsigned long long)ev->loss.lost_bytes,
+                             ev->loss.newest_time_sent, ev->loss.period);
+               break;
+       case QUIC_CC_EVT_ECN_CE:
+               chunk_appendf(buf, "ecn_ce");
+               break;
+       }
+}
+
+#endif /* USE_QUIC */
+#endif /* _PROTO_QUIC_CC_H */
diff --git a/include/haproxy/quic_frame-t.h b/include/haproxy/quic_frame-t.h
new file mode 100644 (file)
index 0000000..a2404ef
--- /dev/null
@@ -0,0 +1,235 @@
+/*
+ * include/types/quic_frame.h
+ * This file contains QUIC frame definitions.
+ *
+ * Copyright 2019 HAProxy Technologies, Frédéric Lécaille <flecaille@haproxy.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, version 2.1
+ * exclusively.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifndef _TYPES_QUIC_FRAME_H
+#define _TYPES_QUIC_FRAME_H
+#ifdef USE_QUIC
+#ifndef USE_OPENSSL
+#error "Must define USE_OPENSSL"
+#endif
+
+#include <stdint.h>
+#include <stdlib.h>
+
+#include <haproxy/list.h>
+
+/* QUIC frame types. */
+enum quic_frame_type {
+       QUIC_FT_PADDING      = 0x00,
+       QUIC_FT_PING         = 0x01,
+       QUIC_FT_ACK          = 0x02,
+       QUIC_FT_ACK_ECN      = 0x03,
+       QUIC_FT_RESET_STREAM = 0x04,
+       QUIC_FT_STOP_SENDING = 0x05,
+       QUIC_FT_CRYPTO       = 0x06,
+       QUIC_FT_NEW_TOKEN    = 0x07,
+
+       QUIC_FT_STREAM_8     = 0x08,
+       QUIC_FT_STREAM_9     = 0x09,
+       QUIC_FT_STREAM_A     = 0x0a,
+       QUIC_FT_STREAM_B     = 0x0b,
+       QUIC_FT_STREAM_C     = 0x0c,
+       QUIC_FT_STREAM_D     = 0x0d,
+       QUIC_FT_STREAM_E     = 0x0e,
+       QUIC_FT_STREAM_F     = 0x0f,
+
+       QUIC_FT_MAX_DATA             = 0x10,
+       QUIC_FT_MAX_STREAM_DATA      = 0x11,
+       QUIC_FT_MAX_STREAMS_BIDI     = 0x12,
+       QUIC_FT_MAX_STREAMS_UNI      = 0x13,
+       QUIC_FT_DATA_BLOCKED         = 0x14,
+       QUIC_FT_STREAM_DATA_BLOCKED  = 0x15,
+       QUIC_FT_STREAMS_BLOCKED_BIDI = 0x16,
+       QUIC_FT_STREAMS_BLOCKED_UNI  = 0x17,
+       QUIC_FT_NEW_CONNECTION_ID    = 0x18,
+       QUIC_FT_RETIRE_CONNECTION_ID = 0x19,
+       QUIC_FT_PATH_CHALLENGE       = 0x1a,
+       QUIC_FT_PATH_RESPONSE        = 0x1b,
+       QUIC_FT_CONNECTION_CLOSE     = 0x1c,
+       QUIC_FT_CONNECTION_CLOSE_APP = 0x1d,
+       QUIC_FT_HANDSHAKE_DONE       = 0x1e,
+       /* Do not insert enums after the following one. */
+       QUIC_FT_MAX
+};
+
+#define QUIC_FT_PKT_TYPE_I_BITMASK (1 << QUIC_PACKET_TYPE_INITIAL)
+#define QUIC_FT_PKT_TYPE_0_BITMASK (1 << QUIC_PACKET_TYPE_0RTT)
+#define QUIC_FT_PKT_TYPE_H_BITMASK (1 << QUIC_PACKET_TYPE_HANDSHAKE)
+#define QUIC_FT_PKT_TYPE_1_BITMASK (1 << QUIC_PACKET_TYPE_SHORT)
+
+#define QUIC_FT_PKT_TYPE_IH01_BITMASK \
+       (QUIC_FT_PKT_TYPE_I_BITMASK | QUIC_FT_PKT_TYPE_H_BITMASK | \
+        QUIC_FT_PKT_TYPE_0_BITMASK | QUIC_FT_PKT_TYPE_1_BITMASK)
+
+#define QUIC_FT_PKT_TYPE_IH_1_BITMASK \
+       (QUIC_FT_PKT_TYPE_I_BITMASK | QUIC_FT_PKT_TYPE_H_BITMASK | \
+        QUIC_FT_PKT_TYPE_1_BITMASK)
+
+#define QUIC_FT_PKT_TYPE___01_BITMASK \
+       (QUIC_FT_PKT_TYPE_0_BITMASK | QUIC_FT_PKT_TYPE_1_BITMASK)
+
+#define QUIC_FT_PKT_TYPE____1_BITMASK QUIC_FT_PKT_TYPE_1_BITMASK
+
+#define QUIC_STREAM_FRAME_FIN_BIT    0x01
+#define QUIC_STREAM_FRAME_LEN_BIT    0x02
+#define QUIC_STREAM_FRAME_OFF_BIT    0x04
+
+#define QUIC_PATH_CHALLENGE_LEN         8
+
+struct quic_padding {
+       size_t len;
+};
+
+struct quic_ack {
+       uint64_t largest_ack;
+       uint64_t ack_delay;
+       uint64_t ack_range_num;
+       uint64_t first_ack_range;
+};
+
+/* Structure used when emitting ACK frames. */
+struct quic_tx_ack {
+       uint64_t ack_delay;
+       struct quic_ack_ranges *ack_ranges;
+};
+
+struct quic_reset_stream {
+       uint64_t id;
+       uint64_t app_error_code;
+       uint64_t final_size;
+};
+
+struct quic_stop_sending_frame {
+       uint64_t id;
+       uint64_t app_error_code;
+};
+
+struct quic_crypto {
+       uint64_t offset;
+       uint64_t len;
+       const struct quic_enc_level *qel;
+       const unsigned char *data;
+};
+
+struct quic_new_token {
+       uint64_t len;
+       const unsigned char *data;
+};
+
+struct quic_stream {
+       uint64_t id;
+       uint64_t offset;
+       uint64_t len;
+       const unsigned char *data;
+};
+
+struct quic_max_data {
+       uint64_t max_data;
+};
+
+struct quic_max_stream_data {
+       uint64_t id;
+       uint64_t max_stream_data;
+};
+
+struct quic_max_streams {
+       uint64_t max_streams;
+};
+
+struct quic_data_blocked {
+       uint64_t limit;
+};
+
+struct quic_stream_data_blocked {
+       uint64_t id;
+       uint64_t limit;
+};
+
+struct quic_streams_blocked {
+       uint64_t limit;
+};
+
+struct quic_new_connection_id {
+       uint64_t seq_num;
+       uint64_t retire_prior_to;
+       struct {
+               unsigned char len;
+               const unsigned char *data;
+       } cid;
+       const unsigned char *stateless_reset_token;
+};
+
+struct quic_retire_connection_id {
+       uint64_t seq_num;
+};
+
+struct quic_path_challenge {
+       unsigned char data[QUIC_PATH_CHALLENGE_LEN];
+};
+
+struct quic_path_challenge_response {
+       unsigned char data[QUIC_PATH_CHALLENGE_LEN];
+};
+
+struct quic_connection_close {
+       uint64_t error_code;
+       uint64_t frame_type;
+       uint64_t reason_phrase_len;
+       unsigned char *reason_phrase;
+};
+
+struct quic_connection_close_app {
+       uint64_t error_code;
+       uint64_t reason_phrase_len;
+       unsigned char *reason_phrase;
+};
+
+struct quic_frame {
+       struct list list;
+       unsigned char type;
+       union {
+               struct quic_padding padding;
+               struct quic_ack ack;
+               struct quic_tx_ack tx_ack;
+               struct quic_crypto crypto;
+               struct quic_reset_stream reset_stream;
+               struct quic_stop_sending_frame stop_sending_frame;
+               struct quic_new_token new_token;
+               struct quic_stream stream;
+               struct quic_max_data max_data;
+               struct quic_max_stream_data max_stream_data;
+               struct quic_max_streams max_streams_bidi;
+               struct quic_max_streams max_streams_uni;
+               struct quic_data_blocked data_blocked;
+               struct quic_stream_data_blocked stream_data_blocked;
+               struct quic_streams_blocked streams_blocked_bidi;
+               struct quic_streams_blocked streams_blocked_uni;
+               struct quic_new_connection_id new_connection_id;
+               struct quic_retire_connection_id retire_connection_id;
+               struct quic_path_challenge path_challenge;
+               struct quic_path_challenge_response path_challenge_response;
+               struct quic_connection_close connection_close;
+               struct quic_connection_close_app connection_close_app;
+       };
+};
+
+#endif /* USE_QUIC */
+#endif /* _TYPES_QUIC_FRAME_H */
diff --git a/include/haproxy/quic_frame.h b/include/haproxy/quic_frame.h
new file mode 100644 (file)
index 0000000..123f508
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * include/haproxy/quic_frame.h
+ * This file contains prototypes for QUIC frames.
+ *
+ * Copyright 2020 HAProxy Technologies, Frédéric Lécaille <flecaille@haproxy.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, version 2.1
+ * exclusively.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifndef _HAPROXY_QUIC_FRAME_H
+#define _HAPROXY_QUIC_FRAME_H
+#ifdef USE_QUIC
+#ifndef USE_OPENSSL
+#error "Must define USE_OPENSSL"
+#endif
+
+#include <haproxy/quic_frame-t.h>
+#include <haproxy/xprt_quic-t.h>
+
+const char *quic_frame_type_string(enum quic_frame_type ft);
+
+int qc_build_frm(unsigned char **buf, const unsigned char *end,
+                 struct quic_frame *frm, struct quic_tx_packet *pkt,
+                 struct quic_conn *conn);
+
+int qc_parse_frm(struct quic_frame *frm, struct quic_rx_packet *pkt,
+                 const unsigned char **buf, const unsigned char *end,
+                 struct quic_conn *conn);
+
+#endif /* USE_QUIC */
+#endif /* _HAPROXY_QUIC_FRAME_H */
diff --git a/include/haproxy/quic_loss-t.h b/include/haproxy/quic_loss-t.h
new file mode 100644 (file)
index 0000000..5ca9b3a
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * include/types/quic_loss.h
+ * This file contains definitions for QUIC loss detection.
+ *
+ * Copyright 2019 HAProxy Technologies, Frédéric Lécaille <flecaille@haproxy.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, version 2.1
+ * exclusively.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifndef _TYPES_QUIC_LOSS_H
+#define _TYPES_QUIC_LOSS_H
+#ifdef USE_QUIC
+#ifndef USE_OPENSSL
+#error "Must define USE_OPENSSL"
+#endif
+
+#include <stdint.h>
+
+/* Maximum reordering in packets. */
+#define QUIC_LOSS_PACKET_THRESHOLD         3
+#define QUIC_TIMER_GRANULARITY            1U /* 1ms   */
+#define QUIC_LOSS_INITIAL_RTT           333U /* 333ms */
+
+/* Note that all the unit of variables for QUIC LOSS dectections
+ * is the tick.
+ */
+
+struct quic_loss {
+       /* The most recent RTT measurement. */
+       unsigned int latest_rtt;
+       /* Smoothed RTT << 4*/
+       unsigned int srtt;
+       /* RTT variation << 2 */
+       unsigned int rtt_var;
+       /* Minimum RTT. */
+       unsigned int rtt_min;
+       /* Number of NACKed sent PTO. */
+       unsigned int pto_count;
+};
+
+#endif /* USE_QUIC */
+#endif /* _TYPES_QUIC_LOSS_H */
diff --git a/include/haproxy/quic_loss.h b/include/haproxy/quic_loss.h
new file mode 100644 (file)
index 0000000..ee7649c
--- /dev/null
@@ -0,0 +1,190 @@
+/*
+ * include/proto/quic_loss.h
+ * This file provides interface definition for QUIC loss detection.
+ *
+ * Copyright 2019 HAProxy Technologies, Frédéric Lécaille <flecaille@haproxy.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, version 2.1
+ * exclusively.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifndef _PROTO_QUIC_LOSS_H
+#define _PROTO_QUIC_LOSS_H
+#ifdef USE_QUIC
+#ifndef USE_OPENSSL
+#error "Must define USE_OPENSSL"
+#endif
+
+#include <haproxy/ticks.h>
+#include <haproxy/time.h>
+#include <haproxy/xprt_quic-t.h>
+
+#include <haproxy/trace.h>
+
+#define TRACE_SOURCE &trace_quic
+
+static inline void quic_loss_init(struct quic_loss *ql)
+{
+       ql->srtt = QUIC_LOSS_INITIAL_RTT << 4;
+       ql->rtt_var = (QUIC_LOSS_INITIAL_RTT >> 1) << 2;
+       ql->rtt_min = 0;
+       ql->pto_count = 0;
+}
+
+/* Update <ql> QUIC loss information with new <rtt> measurement and <ack_delay>
+ * on ACK frame receipt which MUST be min(ack->ack_delay, max_ack_delay) for
+ * non handshake packets.
+ */
+static inline void quic_loss_srtt_update(struct quic_loss *ql,
+                                         unsigned int rtt, unsigned int ack_delay,
+                                         struct quic_conn *conn)
+{
+       TRACE_PROTO("Loss info update", QUIC_EV_CONN_RTTUPDT, conn->conn, &rtt, &ack_delay, ql);
+       ql->latest_rtt = rtt;
+       if (!ql->rtt_min) {
+               /* No previous measurement. */
+               ql->srtt = rtt << 3;
+               /* rttval <- rtt / 2 or 4*rttval <- 2*rtt. */
+               ql->rtt_var = rtt << 1;
+               ql->rtt_min = rtt;
+       }
+       else {
+               int diff;
+
+               ql->rtt_min = QUIC_MIN(rtt, ql->rtt_min);
+               /* Specific to QUIC (RTT adjustment). */
+               if (ack_delay && rtt > ql->rtt_min + ack_delay)
+                       rtt -= ack_delay;
+               diff = ql->srtt - rtt;
+               if (diff < 0)
+                       diff = -diff;
+               /* 4*rttvar = 3*rttvar + |diff| */
+               ql->rtt_var += diff - (ql->rtt_var >> 2);
+               /* 8*srtt = 7*srtt + rtt */
+               ql->srtt += rtt - (ql->srtt >> 3);
+       }
+       TRACE_PROTO("Loss info update", QUIC_EV_CONN_RTTUPDT, conn->conn,,, ql);
+}
+
+/* Return 1 if a persitent congestion is observed for a list of
+ * lost packets sent during <period> period depending on <ql> loss information,
+ * <now_us> the current time and <max_ack_delay_us> the maximum ACK delay of the connection
+ * experiencing a packet loss. Return 0 on the contrary.
+ */
+static inline int quic_loss_persistent_congestion(struct quic_loss *ql,
+                                                  unsigned int period,
+                                                  unsigned int now_us,
+                                                  unsigned int max_ack_delay)
+{
+       unsigned int congestion_period;
+
+       if (!period)
+               return 0;
+
+       congestion_period = (ql->srtt >> 3) +
+               QUIC_MAX(ql->rtt_var, QUIC_TIMER_GRANULARITY) + max_ack_delay;
+       congestion_period *= QUIC_LOSS_PACKET_THRESHOLD;
+
+       return period >= congestion_period;
+}
+
+/* Returns for <qc> QUIC connection the first packet number space which
+ * experienced packet loss, if any or a packet number space with
+ * TICK_ETERNITY as packet loss time if not.
+ */
+static inline struct quic_pktns *quic_loss_pktns(struct quic_conn *qc)
+{
+       enum quic_tls_pktns i;
+       struct quic_pktns *pktns;
+
+       pktns = &qc->pktns[QUIC_TLS_PKTNS_INITIAL];
+       for (i = QUIC_TLS_PKTNS_01RTT; i < QUIC_TLS_PKTNS_MAX; i++)
+               if (qc->pktns[i].tx.loss_time < pktns->tx.loss_time)
+                       pktns = &qc->pktns[i];
+
+       return pktns;
+}
+
+/* Returns for <qc> QUIC connection the first packet number space to
+ * arm the PTO for if any or a packet number space with TICK_ETERNITY
+ * as PTO value if not.
+ */
+static inline struct quic_pktns *quic_pto_pktns(struct quic_conn *qc,
+                                                int handshake_completed,
+                                                unsigned int *pto)
+{
+       int i;
+       unsigned int duration, lpto, time_of_last_eliciting;
+       struct quic_loss *ql = &qc->path->loss;
+       struct quic_pktns *pktns;
+
+       TRACE_ENTER(QUIC_EV_CONN_SPTO, qc->conn);
+       duration =
+               (ql->srtt >> 3) +
+               (QUIC_MAX(ql->rtt_var, QUIC_TIMER_GRANULARITY) << ql->pto_count);
+
+       if (!qc->path->in_flight) {
+               struct quic_enc_level *hel;
+
+               hel = &qc->els[QUIC_TLS_ENC_LEVEL_HANDSHAKE];
+               if (hel->tls_ctx.tx.flags & QUIC_FL_TLS_SECRETS_SET) {
+                       pktns = &qc->pktns[QUIC_TLS_PKTNS_HANDSHAKE];
+               }
+               else {
+                       pktns = &qc->pktns[QUIC_TLS_PKTNS_INITIAL];
+               }
+               lpto = tick_add(now_ms, duration);
+               goto out;
+       }
+
+       lpto = TICK_ETERNITY;
+       pktns = &qc->pktns[QUIC_TLS_PKTNS_INITIAL];
+
+       for (i = QUIC_TLS_PKTNS_INITIAL; i < QUIC_TLS_PKTNS_MAX; i++) {
+               unsigned int tmp_pto;
+               struct quic_pktns *p;
+
+               p = &qc->pktns[i];
+               if (!p->tx.in_flight)
+                       continue;
+
+               if (i == QUIC_TLS_PKTNS_01RTT) {
+                       if (!handshake_completed) {
+                               pktns = p;
+                               goto out;
+                       }
+
+                       duration += qc->max_ack_delay << ql->pto_count;
+               }
+
+               time_of_last_eliciting = p->tx.time_of_last_eliciting;
+               tmp_pto =
+                       tick_first(lpto, tick_add(time_of_last_eliciting, duration));
+               if (!tick_isset(lpto) || tmp_pto < lpto) {
+                       lpto = tmp_pto;
+                       pktns = p;
+               }
+               TRACE_PROTO("pktns", QUIC_EV_CONN_SPTO, qc->conn, p);
+       }
+
+ out:
+       if (pto)
+               *pto = lpto;
+       TRACE_LEAVE(QUIC_EV_CONN_SPTO, qc->conn, pktns, &duration);
+
+       return pktns;
+}
+
+#endif /* USE_QUIC */
+#endif /* _PROTO_QUIC_LOSS_H */
index 78623e8179b90f4a26019441edd5f741e8d31d26..672255ed300b171295a1becae8ad32d0c5141665 100644 (file)
 
 #ifndef _HAPROXY_QUIC_SOCK_H
 #define _HAPROXY_QUIC_SOCK_H
+#ifdef USE_QUIC
+#ifndef USE_OPENSSL
+#error "Must define USE_OPENSSL"
+#endif
 
 #include <sys/socket.h>
 #include <sys/types.h>
@@ -33,6 +37,7 @@ int quic_sock_accepting_conn(const struct receiver *rx);
 struct connection *quic_sock_accept_conn(struct listener *l, int *status);
 void quic_sock_fd_iocb(int fd);
 
+#endif /* USE_QUIC */
 #endif /* _HAPROXY_QUIC_SOCK_H */
 
 /*
diff --git a/include/haproxy/quic_tls-t.h b/include/haproxy/quic_tls-t.h
new file mode 100644 (file)
index 0000000..20efc28
--- /dev/null
@@ -0,0 +1,110 @@
+/*
+ * include/types/quic_tls.h
+ * This file provides definitions for QUIC-TLS.
+ *
+ * Copyright 2019 HAProxy Technologies, Frédéric Lécaille <flecaille@haproxy.com>
+ *
+ * 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 _TYPES_QUIC_TLS_H
+#define _TYPES_QUIC_TLS_H
+#ifdef USE_QUIC
+#ifndef USE_OPENSSL
+#error "Must define USE_OPENSSL"
+#endif
+
+#include <openssl/evp.h>
+
+/* It seems TLS 1.3 ciphersuites macros differ between openssl and boringssl */
+
+#if defined(OPENSSL_IS_BORINGSSL)
+#if !defined(TLS1_3_CK_AES_128_GCM_SHA256)
+#define TLS1_3_CK_AES_128_GCM_SHA256       TLS1_CK_AES_128_GCM_SHA256
+#endif
+#if !defined(TLS1_3_CK_AES_256_GCM_SHA384)
+#define TLS1_3_CK_AES_256_GCM_SHA384       TLS1_CK_AES_256_GCM_SHA384
+#endif
+#if !defined(TLS1_3_CK_CHACHA20_POLY1305_SHA256)
+#define TLS1_3_CK_CHACHA20_POLY1305_SHA256 TLS1_CK_CHACHA20_POLY1305_SHA256
+#endif
+#if !defined(TLS1_3_CK_AES_128_CCM_SHA256)
+/* Note that TLS1_CK_AES_128_CCM_SHA256 is not defined in boringssl */
+#define TLS1_3_CK_AES_128_CCM_SHA256       0x03001304
+#endif
+#endif
+
+/* The TLS extension (enum) for QUIC transport parameters */
+#define TLS_EXTENSION_QUIC_TRANSPORT_PARAMETERS 0xffa5
+
+/* QUIC handshake states for both clients and servers. */
+enum quic_handshake_state {
+       QUIC_HS_ST_CLIENT_INITIAL,
+       QUIC_HS_ST_CLIENT_HANDSHAKE,
+       QUIC_HS_ST_CLIENT_HANDSHAKE_FAILED,
+
+       QUIC_HS_ST_SERVER_INITIAL,
+       QUIC_HS_ST_SERVER_HANDSHAKE,
+       QUIC_HS_ST_SERVER_HANDSHAKE_FAILED,
+
+       /* Common to servers and clients */
+       QUIC_HS_ST_COMPLETE,
+       QUIC_HS_ST_CONFIRMED,
+};
+
+/* QUIC TLS level encryption */
+enum quic_tls_enc_level {
+       QUIC_TLS_ENC_LEVEL_NONE = -1,
+       QUIC_TLS_ENC_LEVEL_INITIAL,
+       QUIC_TLS_ENC_LEVEL_EARLY_DATA,
+       QUIC_TLS_ENC_LEVEL_HANDSHAKE,
+       QUIC_TLS_ENC_LEVEL_APP,
+       /* Please do not insert any value after this following one */
+       QUIC_TLS_ENC_LEVEL_MAX,
+};
+
+/* QUIC packet number spaces */
+enum quic_tls_pktns {
+       QUIC_TLS_PKTNS_INITIAL,
+       QUIC_TLS_PKTNS_01RTT,
+       QUIC_TLS_PKTNS_HANDSHAKE,
+       /* Please do not insert any value after this following one */
+       QUIC_TLS_PKTNS_MAX,
+};
+
+/* The ciphersuites for AEAD QUIC-TLS have 16-bytes authentication tag */
+#define QUIC_TLS_TAG_LEN             16
+
+extern unsigned char initial_salt[20];
+
+/* Flag to be used when TLS secrets have been set. */
+#define QUIC_FL_TLS_SECRETS_SET  (1 << 0)
+/* Flag to be used when TLS secrets have been discarded. */
+#define QUIC_FL_TLS_SECRETS_DCD  (1 << 1)
+
+struct quic_tls_secrets {
+       const EVP_CIPHER *aead;
+       const EVP_MD *md;
+       const EVP_CIPHER *hp;
+       unsigned char key[32];
+       unsigned char iv[12];
+       /* Header protection key.
+       * Note: the header protection is applied after packet protection.
+       * As the header belong to the data, its protection must be removed before removing
+       * the packet protection.
+       */
+       unsigned char hp_key[32];
+       char flags;
+};
+
+struct quic_tls_ctx {
+       struct quic_tls_secrets rx;
+       struct quic_tls_secrets tx;
+};
+
+#endif /* USE_QUIC */
+#endif /* _TYPES_QUIC_TLS_H */
+
diff --git a/include/haproxy/quic_tls.h b/include/haproxy/quic_tls.h
new file mode 100644 (file)
index 0000000..94b978b
--- /dev/null
@@ -0,0 +1,441 @@
+/*
+ * include/proto/quic_tls.h
+ * This file provides definitions for QUIC-TLS.
+ *
+ * Copyright 2019 HAProxy Technologies, Frédéric Lécaille <flecaille@haproxy.com>
+ *
+ * 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 _PROTO_QUIC_TLS_H
+#define _PROTO_QUIC_TLS_H
+#ifdef USE_QUIC
+#ifndef USE_OPENSSL
+#error "Must define USE_OPENSSL"
+#endif
+
+#define TRACE_SOURCE &trace_quic
+
+#include <stdlib.h>
+#include <openssl/ssl.h>
+
+#include <haproxy/dynbuf.h>
+#include <haproxy/quic_tls-t.h>
+#include <haproxy/trace.h>
+#include <haproxy/xprt_quic.h>
+
+void quic_tls_keys_hexdump(struct buffer *buf, struct quic_tls_secrets *secs);
+
+void quic_tls_secret_hexdump(struct buffer *buf,
+                             const unsigned char *secret, size_t secret_len);
+
+int quic_derive_initial_secret(const EVP_MD *md,
+                               unsigned char *initial_secret, size_t initial_secret_sz,
+                               const unsigned char *secret, size_t secret_sz);
+
+int quic_tls_derive_initial_secrets(const EVP_MD *md,
+                                    unsigned char *rx, size_t rx_sz,
+                                    unsigned char *tx, size_t tx_sz,
+                                    const unsigned char *secret, size_t secret_sz,
+                                    int server);
+
+int quic_tls_encrypt(unsigned char *buf, size_t len,
+                     const unsigned char *aad, size_t aad_len,
+                     const EVP_CIPHER *aead,
+                     const unsigned char *key, const unsigned char *iv);
+
+int quic_tls_decrypt(unsigned char *buf, size_t len,
+                     unsigned char *aad, size_t aad_len,
+                     const EVP_CIPHER *aead,
+                     const unsigned char *key, const unsigned char *iv);
+
+int quic_tls_derive_keys(const EVP_CIPHER *aead, const EVP_CIPHER *hp,
+                         const EVP_MD *md,
+                         unsigned char *key, size_t keylen,
+                         unsigned char *iv, size_t ivlen,
+                         unsigned char *hp_key, size_t hp_keylen,
+                         const unsigned char *secret, size_t secretlen);
+
+int quic_aead_iv_build(unsigned char *iv, size_t ivlen,
+                       unsigned char *aead_iv, size_t aead_ivlen, uint64_t pn);
+
+static inline const EVP_CIPHER *tls_aead(const SSL_CIPHER *cipher)
+{
+       switch (SSL_CIPHER_get_id(cipher)) {
+       case TLS1_3_CK_AES_128_GCM_SHA256:
+               return EVP_aes_128_gcm();
+       case TLS1_3_CK_AES_256_GCM_SHA384:
+               return EVP_aes_256_gcm();
+#ifndef OPENSSL_IS_BORINGSSL
+       /* XXX TO DO XXX */
+    /* Note that for chacha20_poly1305, there exists EVP_AEAD_chacha20_poly135() function
+     * which returns a pointer to const EVP_AEAD.
+     */
+       case TLS1_3_CK_CHACHA20_POLY1305_SHA256:
+               return EVP_chacha20_poly1305();
+       case TLS1_3_CK_AES_128_CCM_SHA256:
+               return EVP_aes_128_ccm();
+#endif
+       default:
+               return NULL;
+       }
+}
+
+static inline const EVP_MD *tls_md(const SSL_CIPHER *cipher)
+{
+       switch (SSL_CIPHER_get_id(cipher)) {
+       case TLS1_3_CK_AES_128_GCM_SHA256:
+#ifndef OPENSSL_IS_BORINGSSL
+       /* XXX TO DO XXX */
+    /* Note that for chacha20_poly1305, there exists EVP_AEAD_chacha20_poly135() function
+     * which returns a pointer to const EVP_AEAD.
+     */
+       case TLS1_3_CK_AES_128_CCM_SHA256:
+       case TLS1_3_CK_CHACHA20_POLY1305_SHA256:
+#endif
+               return EVP_sha256();
+       case TLS1_3_CK_AES_256_GCM_SHA384:
+               return EVP_sha384();
+       default:
+               return NULL;
+       }
+}
+
+static inline const EVP_CIPHER *tls_hp(const SSL_CIPHER *cipher)
+{
+       switch (SSL_CIPHER_get_id(cipher)) {
+#ifndef OPENSSL_IS_BORINGSSL
+       /* XXX TO DO XXX */
+    /* Note that for chacha20_poly1305, there exists EVP_AEAD_chacha20_poly135() function
+     * which returns a pointer to const EVP_AEAD.
+     */
+       case TLS1_3_CK_CHACHA20_POLY1305_SHA256:
+               return EVP_chacha20();
+       case TLS1_3_CK_AES_128_CCM_SHA256:
+#endif
+       case TLS1_3_CK_AES_128_GCM_SHA256:
+               return EVP_aes_128_ctr();
+       case TLS1_3_CK_AES_256_GCM_SHA384:
+               return EVP_aes_256_ctr();
+       default:
+               return NULL;
+       }
+
+}
+
+/* These following functions map TLS implementation encryption level to ours */
+static inline enum quic_tls_enc_level ssl_to_quic_enc_level(enum ssl_encryption_level_t level)
+{
+       switch (level) {
+       case ssl_encryption_initial:
+               return QUIC_TLS_ENC_LEVEL_INITIAL;
+       case ssl_encryption_early_data:
+               return QUIC_TLS_ENC_LEVEL_EARLY_DATA;
+       case ssl_encryption_handshake:
+               return QUIC_TLS_ENC_LEVEL_HANDSHAKE;
+       case ssl_encryption_application:
+               return QUIC_TLS_ENC_LEVEL_APP;
+       default:
+               return -1;
+       }
+}
+
+/* These two following functions map our encryption level to the TLS implementation ones. */
+static inline enum quic_tls_enc_level quic_to_ssl_enc_level(enum quic_tls_enc_level level)
+{
+       switch (level) {
+       case QUIC_TLS_ENC_LEVEL_INITIAL:
+               return ssl_encryption_initial;
+       case QUIC_TLS_ENC_LEVEL_EARLY_DATA:
+               return ssl_encryption_early_data;
+       case QUIC_TLS_ENC_LEVEL_HANDSHAKE:
+               return ssl_encryption_handshake;
+       case QUIC_TLS_ENC_LEVEL_APP:
+               return ssl_encryption_application;
+       default:
+               return -1;
+       }
+}
+
+/* Return a human readable string from <state> QUIC handshake state of NULL
+ * for unknown state values (for debug purpose).
+ */
+static inline char *quic_hdshk_state_str(const enum quic_handshake_state state)
+{
+       switch (state) {
+       case QUIC_HS_ST_CLIENT_INITIAL:
+               return "CI";
+       case QUIC_HS_ST_CLIENT_HANDSHAKE:
+               return "CH";
+       case QUIC_HS_ST_CLIENT_HANDSHAKE_FAILED:
+               return "CF";
+       case QUIC_HS_ST_SERVER_INITIAL:
+               return "SI";
+       case QUIC_HS_ST_SERVER_HANDSHAKE:
+               return "SH";
+       case QUIC_HS_ST_SERVER_HANDSHAKE_FAILED:
+               return "SF";
+       case QUIC_HS_ST_COMPLETE:
+               return "CP";
+       case QUIC_HS_ST_CONFIRMED:
+               return "CF";
+       }
+
+       return NULL;
+}
+
+/* Return a human readable string from <err> SSL error (returned from
+ * SSL_get_error())
+ */
+static inline const char *ssl_error_str(int err)
+{
+       switch (err) {
+       case SSL_ERROR_NONE:
+               return "NONE";
+       case SSL_ERROR_SSL:
+               return "SSL";
+       case SSL_ERROR_WANT_READ:
+               return "WANT_READ";
+       case SSL_ERROR_WANT_WRITE:
+               return "WANT_WRITE";
+       case SSL_ERROR_WANT_X509_LOOKUP:
+               return "X509_LOOKUP";
+       case SSL_ERROR_SYSCALL:
+               return "SYSCALL";
+       case SSL_ERROR_ZERO_RETURN:
+               return "ZERO_RETURN";
+       case SSL_ERROR_WANT_CONNECT:
+               return "WANT_CONNECT";
+       case SSL_ERROR_WANT_ACCEPT:
+               return "WANT_ACCEPT";
+#ifndef OPENSSL_IS_BORINGSSL
+       case SSL_ERROR_WANT_ASYNC:
+               return "WANT_ASYNC";
+       case SSL_ERROR_WANT_ASYNC_JOB:
+               return "WANT_ASYNC_JOB";
+       case SSL_ERROR_WANT_CLIENT_HELLO_CB:
+               return "WANT_CLIENT_HELLO_CB";
+#endif
+       default:
+               return "UNKNWON";
+       }
+}
+
+
+/* Return a character identifying the encryption level from <level> QUIC TLS
+ * encryption level (for debug purpose).
+ * Initial -> 'I', Early Data -> 'E', Handshake -> 'H', Application -> 'A' and
+ * '-' if undefined.
+ */
+static inline char quic_enc_level_char(enum quic_tls_enc_level level)
+{
+       switch (level) {
+       case QUIC_TLS_ENC_LEVEL_INITIAL:
+               return 'I';
+       case QUIC_TLS_ENC_LEVEL_EARLY_DATA:
+               return 'E';
+       case QUIC_TLS_ENC_LEVEL_HANDSHAKE:
+               return 'H';
+       case QUIC_TLS_ENC_LEVEL_APP:
+               return 'A';
+       default:
+               return '-';
+       }
+}
+
+/* Return a character identifying <qel> encryption level from <qc> QUIC connection
+ * (for debug purpose).
+ * Initial -> 'I', Early Data -> 'E', Handshake -> 'H', Application -> 'A' and
+ * '-' if undefined.
+ */
+static inline char quic_enc_level_char_from_qel(const struct quic_enc_level *qel,
+                                                const struct quic_conn *qc)
+{
+       if (qel == &qc->els[QUIC_TLS_ENC_LEVEL_INITIAL])
+               return 'I';
+       else if (qel == &qc->els[QUIC_TLS_ENC_LEVEL_EARLY_DATA])
+               return 'E';
+       else if (qel == &qc->els[QUIC_TLS_ENC_LEVEL_HANDSHAKE])
+               return 'H';
+       else if (qel == &qc->els[QUIC_TLS_ENC_LEVEL_APP])
+               return 'A';
+       return '-';
+}
+
+/* Return a character identifying the encryption level of a packet depending on
+ * its <type> type, and its <long_header> header length (for debug purpose).
+ * Initial -> 'I', ORTT -> '0', Handshake -> 'H', Application -> 'A' and
+ * '-' if undefined.
+ */
+static inline char quic_packet_type_enc_level_char(int packet_type)
+{
+       switch (packet_type) {
+       case QUIC_PACKET_TYPE_INITIAL:
+               return 'I';
+       case QUIC_PACKET_TYPE_0RTT:
+               return '0';
+       case QUIC_PACKET_TYPE_HANDSHAKE:
+               return 'H';
+       case QUIC_PACKET_TYPE_SHORT:
+               return 'A';
+       default:
+               return '-';
+       }
+}
+
+/* Return the TLS encryption level to be used for <packet_type>
+ * QUIC packet type.
+ * Returns -1 if there is no TLS encryption level for <packet_type>
+ * packet type.
+ */
+static inline enum quic_tls_enc_level quic_packet_type_enc_level(enum quic_pkt_type packet_type)
+{
+       switch (packet_type) {
+       case QUIC_PACKET_TYPE_INITIAL:
+               return QUIC_TLS_ENC_LEVEL_INITIAL;
+       case QUIC_PACKET_TYPE_0RTT:
+               return QUIC_TLS_ENC_LEVEL_EARLY_DATA;
+       case QUIC_PACKET_TYPE_HANDSHAKE:
+               return QUIC_TLS_ENC_LEVEL_HANDSHAKE;
+       case QUIC_PACKET_TYPE_RETRY:
+               return QUIC_TLS_ENC_LEVEL_NONE;
+       case QUIC_PACKET_TYPE_SHORT:
+               return QUIC_TLS_ENC_LEVEL_APP;
+       default:
+               return QUIC_TLS_ENC_LEVEL_NONE;
+       }
+}
+
+static inline enum quic_tls_pktns quic_tls_pktns(enum quic_tls_enc_level level)
+{
+       switch (level) {
+       case QUIC_TLS_ENC_LEVEL_INITIAL:
+               return QUIC_TLS_PKTNS_INITIAL;
+       case QUIC_TLS_ENC_LEVEL_EARLY_DATA:
+       case QUIC_TLS_ENC_LEVEL_APP:
+               return QUIC_TLS_PKTNS_01RTT;
+       case QUIC_TLS_ENC_LEVEL_HANDSHAKE:
+               return QUIC_TLS_PKTNS_HANDSHAKE;
+       default:
+               return -1;
+       }
+}
+
+/* Initialize a TLS cryptographic context for the Initial encryption level. */
+static inline void quic_initial_tls_ctx_init(struct quic_tls_ctx *ctx)
+{
+       ctx->rx.aead = ctx->tx.aead = EVP_aes_128_gcm();
+       ctx->rx.md   = ctx->tx.md   = EVP_sha256();
+       ctx->rx.hp   = ctx->tx.hp   = EVP_aes_128_ctr();
+}
+
+static inline int quic_tls_level_pkt_type(enum quic_tls_enc_level level)
+{
+       switch (level) {
+       case QUIC_TLS_ENC_LEVEL_INITIAL:
+               return QUIC_PACKET_TYPE_INITIAL;
+       case QUIC_TLS_ENC_LEVEL_EARLY_DATA:
+               return QUIC_PACKET_TYPE_0RTT;
+       case QUIC_TLS_ENC_LEVEL_HANDSHAKE:
+               return QUIC_PACKET_TYPE_HANDSHAKE;
+       default:
+               return -1;
+       }
+}
+
+/* Set <*level> and <*next_level> depending on <state> QUIC handshake state. */
+static inline int quic_get_tls_enc_levels(enum quic_tls_enc_level *level,
+                                          enum quic_tls_enc_level *next_level,
+                                          enum quic_handshake_state state)
+{
+       switch (state) {
+       case QUIC_HS_ST_SERVER_INITIAL:
+       case QUIC_HS_ST_CLIENT_INITIAL:
+               *level = QUIC_TLS_ENC_LEVEL_INITIAL;
+               *next_level = QUIC_TLS_ENC_LEVEL_HANDSHAKE;
+               break;
+       case QUIC_HS_ST_SERVER_HANDSHAKE:
+       case QUIC_HS_ST_CLIENT_HANDSHAKE:
+       case QUIC_HS_ST_COMPLETE:
+       case QUIC_HS_ST_CONFIRMED:
+               *level = QUIC_TLS_ENC_LEVEL_HANDSHAKE;
+               *next_level = QUIC_TLS_ENC_LEVEL_APP;
+               break;
+       default:
+               return 0;
+       }
+
+       return 1;
+}
+
+/* Flag the keys at <qel> encryption level as discarded. */
+static inline void quic_tls_discard_keys(struct quic_enc_level *qel)
+{
+       qel->tls_ctx.rx.flags |= QUIC_FL_TLS_SECRETS_DCD;
+       qel->tls_ctx.tx.flags |= QUIC_FL_TLS_SECRETS_DCD;
+}
+
+/* Derive the initial secrets with <ctx> as QUIC TLS context which is the
+ * cryptographic context for the first encryption level (Initial) from
+ * <cid> connection ID with <cidlen> as length (in bytes) for a server or not
+ * depending on <server> boolean value.
+ * Return 1 if succeeded or 0 if not.
+ */
+static inline int qc_new_isecs(struct connection *conn,
+                               const unsigned char *cid, size_t cidlen, int server)
+{
+       unsigned char initial_secret[32];
+       /* Initial secret to be derived for incoming packets */
+       unsigned char rx_init_sec[32];
+       /* Initial secret to be derived for outgoing packets */
+       unsigned char tx_init_sec[32];
+       struct quic_tls_secrets *rx_ctx, *tx_ctx;
+       struct quic_tls_ctx *ctx;
+
+       TRACE_ENTER(QUIC_EV_CONN_ISEC, conn);
+       ctx = &conn->qc->els[QUIC_TLS_ENC_LEVEL_INITIAL].tls_ctx;
+       quic_initial_tls_ctx_init(ctx);
+       if (!quic_derive_initial_secret(ctx->rx.md,
+                                       initial_secret, sizeof initial_secret,
+                                       cid, cidlen))
+               goto err;
+
+       if (!quic_tls_derive_initial_secrets(ctx->rx.md,
+                                            rx_init_sec, sizeof rx_init_sec,
+                                            tx_init_sec, sizeof tx_init_sec,
+                                            initial_secret, sizeof initial_secret, server))
+               goto err;
+
+       rx_ctx = &ctx->rx;
+       tx_ctx = &ctx->tx;
+       if (!quic_tls_derive_keys(ctx->rx.aead, ctx->rx.hp, ctx->rx.md,
+                                 rx_ctx->key, sizeof rx_ctx->key,
+                                 rx_ctx->iv, sizeof rx_ctx->iv,
+                                 rx_ctx->hp_key, sizeof rx_ctx->hp_key,
+                                 rx_init_sec, sizeof rx_init_sec))
+               goto err;
+
+       rx_ctx->flags |= QUIC_FL_TLS_SECRETS_SET;
+       if (!quic_tls_derive_keys(ctx->tx.aead, ctx->tx.hp, ctx->tx.md,
+                                 tx_ctx->key, sizeof tx_ctx->key,
+                                 tx_ctx->iv, sizeof tx_ctx->iv,
+                                 tx_ctx->hp_key, sizeof tx_ctx->hp_key,
+                                 tx_init_sec, sizeof tx_init_sec))
+               goto err;
+
+       tx_ctx->flags |= QUIC_FL_TLS_SECRETS_SET;
+       TRACE_LEAVE(QUIC_EV_CONN_ISEC, conn, rx_init_sec, tx_init_sec);
+
+       return 1;
+
+ err:
+       TRACE_DEVEL("leaving in error", QUIC_EV_CONN_EISEC, conn);
+       return 0;
+}
+
+#endif /* USE_QUIC */
+#endif /* _PROTO_QUIC_TLS_H */
+
diff --git a/include/haproxy/xprt_quic-t.h b/include/haproxy/xprt_quic-t.h
new file mode 100644 (file)
index 0000000..b25c3eb
--- /dev/null
@@ -0,0 +1,640 @@
+/*
+ * include/haproxy/xprt_quic-t.h
+ * This file contains applet function prototypes
+ *
+ * Copyright 2019 HAProxy Technologies, Frédéric Lécaille <flecaille@haproxy.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, version 2.1
+ * exclusively.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifndef _HAPROXY_XPRT_QUIC_T_H
+#define _HAPROXY_XPRT_QUIC_T_H
+#ifdef USE_QUIC
+#ifndef USE_OPENSSL
+#error "Must define USE_OPENSSL"
+#endif
+
+#include <sys/socket.h>
+#include <openssl/ssl.h>
+
+#include <haproxy/list.h>
+
+#include <haproxy/quic_cc-t.h>
+#include <haproxy/quic_frame-t.h>
+#include <haproxy/quic_tls-t.h>
+#include <haproxy/quic_loss-t.h>
+#include <haproxy/task.h>
+
+#include <import/eb64tree.h>
+#include <import/ebmbtree.h>
+
+#define QUIC_PROTOCOL_VERSION_DRAFT_28   0xff00001c /* draft-28 */
+
+#define QUIC_INITIAL_IPV4_MTU      1252 /* (bytes) */
+#define QUIC_INITIAL_IPV6_MTU      1232
+/* XXX TO DO XXX */
+/* Maximum packet length during handshake */
+#define QUIC_PACKET_MAXLEN     QUIC_INITIAL_IPV4_MTU
+
+/* The minimum length of Initial packets. */
+#define QUIC_INITIAL_PACKET_MINLEN 1200
+
+/*
+ * QUIC CID lengths. This the length of the connection IDs for this QUIC
+ * implementation.
+ */
+#define QUIC_CID_LEN               8
+
+/* Common definitions for short and long QUIC packet headers. */
+/* QUIC connection ID maximum length for version 1. */
+#define QUIC_CID_MAXLEN               20 /* bytes */
+/*
+ * All QUIC packets with long headers are made of at least (in bytes):
+ * flags(1), version(4), DCID length(1), DCID(0..20), SCID length(1), SCID(0..20)
+ */
+#define QUIC_LONG_PACKET_MINLEN            7
+/*
+ * All QUIC packets with short headers are made of at least (in bytes):
+ * flags(1), DCID length(1), DCID(0..20)
+ */
+#define QUIC_SHORT_PACKET_MINLEN           2
+/* Byte 0 of QUIC packets. */
+#define QUIC_PACKET_LONG_HEADER_BIT  0x80 /* Long header format if set, short if not. */
+#define QUIC_PACKET_FIXED_BIT        0x40 /* Must always be set for all the headers. */
+
+/*
+ *  0                   1                   2                   3
+ *  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+
+ * |1|1|T|T|X|X|X|X|
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |                         Version (32)                          |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | DCID Len (8)  |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |               Destination Connection ID (0..160)            ...
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | SCID Len (8)  |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |                 Source Connection ID (0..160)               ...
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *                      Long Header Packet Format
+ */
+
+/* Two bits (T) for QUIC packet types. */
+#define QUIC_PACKET_TYPE_BITMASK     0x03
+#define QUIC_PACKET_TYPE_SHIFT       4
+
+enum quic_pkt_type {
+       QUIC_PACKET_TYPE_INITIAL,
+       QUIC_PACKET_TYPE_0RTT,
+       QUIC_PACKET_TYPE_HANDSHAKE,
+       QUIC_PACKET_TYPE_RETRY,
+       /*
+        * The following one is not defined by the RFC but we define it for our
+        * own convenience.
+        */
+       QUIC_PACKET_TYPE_SHORT,
+};
+
+/* Packet number field length. */
+#define QUIC_PACKET_PNL_BITMASK      0x03
+#define QUIC_PACKET_PN_MAXLEN        4
+
+/*
+ *  0                   1                   2                   3
+ *  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+
+ * |0|1|S|R|R|K|P|P|
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |                Destination Connection ID (0..160)           ...
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |                     Packet Number (8/16/24/32)              ...
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |                     Protected Payload (*)                   ...
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *                      Short Header Packet Format
+ */
+
+/* Bit (S) of short header. */
+#define QUIC_PACKET_SPIN_BIT         0x20
+
+/* Reserved Bits (R):  The next two bits of byte 0 are reserved.
+ * These bits are protected using header protection
+ * (see Section 5.4 of [QUIC-TLS]). The value included
+ * prior to protection MUST be set to 0. An endpoint MUST treat
+ * receipt of a packet that has a non-zero value for these bits,
+ * after removing both packet and header protection, as a connection
+ * error of type PROTOCOL_VIOLATION. Discarding such a packet after
+ * only removing header protection can expose the endpoint to attacks
+ * (see Section 9.3 of [QUIC-TLS]).
+ */
+#define QUIC_PACKET_RESERVED_BITS    0x18 /* (protected) */
+
+#define QUIC_PACKET_KEY_PHASE_BIT    0x04 /* (protected) */
+
+/*
+ * Tranport level error codes.
+ */
+#define NO_ERROR                     0x00
+#define INTERNAL_ERROR               0x01
+#define CONNECTION_REFUSED_ERROR     0x02
+#define FLOW_CONTROL_ERROR           0x03
+#define STREAM_LIMIT_ERROR           0x04
+#define STREAM_STATE_ERROR           0x05
+#define FINAL_SIZE_ERROR             0x06
+#define FRAME_ENCODING_ERROR         0x07
+#define TRANSPORT_PARAMETER_ERROR    0x08
+#define CONNECTION_ID_LIMIT_ERROR    0x09
+#define PROTOCOL_VIOLATION           0x0a
+#define INVALID_TOKEN                0x0b
+#define APPLICATION_ERROR            0x0c
+#define CRYPTO_BUFFER_EXCEEDED       0x0d
+
+/* XXX TODO: check/complete this remaining part (256 crypto reserved errors). */
+#define CRYPTO_ERROR                0x100
+
+/* The maximum number of QUIC packets stored by the fd I/O handler by QUIC
+ * connection. Must be a power of two.
+ */
+#define QUIC_CONN_MAX_PACKET  64
+
+#define QUIC_STATELESS_RESET_TOKEN_LEN 16
+
+#define           QUIC_EV_CONN_NEW       (1ULL << 0)
+#define           QUIC_EV_CONN_INIT      (1ULL << 1)
+#define           QUIC_EV_CONN_ISEC      (1ULL << 2)
+#define           QUIC_EV_CONN_RSEC      (1ULL << 3)
+#define           QUIC_EV_CONN_WSEC      (1ULL << 4)
+#define           QUIC_EV_CONN_RWSEC     (1ULL << 5)
+#define           QUIC_EV_CONN_LPKT      (1ULL << 6)
+#define           QUIC_EV_CONN_SPKT      (1ULL << 7)
+#define           QUIC_EV_CONN_CHPKT     (1ULL << 8)
+#define           QUIC_EV_CONN_HPKT      (1ULL << 9)
+#define           QUIC_EV_CONN_PAPKT     (1ULL << 10)
+#define           QUIC_EV_CONN_PAPKTS    (1ULL << 11)
+#define           QUIC_EV_CONN_HDSHK     (1ULL << 12)
+#define           QUIC_EV_CONN_RMHP      (1ULL << 13)
+#define           QUIC_EV_CONN_PRSHPKT   (1ULL << 14)
+#define           QUIC_EV_CONN_PRSAPKT   (1ULL << 15)
+#define           QUIC_EV_CONN_PRSFRM    (1ULL << 16)
+#define           QUIC_EV_CONN_PRSAFRM   (1ULL << 17)
+#define           QUIC_EV_CONN_BFRM      (1ULL << 18)
+#define           QUIC_EV_CONN_PHPKTS    (1ULL << 19)
+#define           QUIC_EV_CONN_TRMHP     (1ULL << 20)
+#define           QUIC_EV_CONN_ELRMHP    (1ULL << 21)
+#define           QUIC_EV_CONN_ELRXPKTS  (1ULL << 22)
+#define           QUIC_EV_CONN_SSLDATA   (1ULL << 23)
+#define           QUIC_EV_CONN_RXCDATA   (1ULL << 24)
+#define           QUIC_EV_CONN_ADDDATA   (1ULL << 25)
+#define           QUIC_EV_CONN_FFLIGHT   (1ULL << 26)
+#define           QUIC_EV_CONN_SSLALERT  (1ULL << 27)
+#define           QUIC_EV_CONN_CPAPKT    (1ULL << 28)
+#define           QUIC_EV_CONN_RTTUPDT   (1ULL << 29)
+#define           QUIC_EV_CONN_CC        (1ULL << 30)
+#define           QUIC_EV_CONN_SPPKTS    (1ULL << 31)
+#define           QUIC_EV_CONN_PKTLOSS   (1ULL << 32)
+#define           QUIC_EV_CONN_STIMER    (1ULL << 33)
+#define           QUIC_EV_CONN_PTIMER    (1ULL << 34)
+#define           QUIC_EV_CONN_SPTO      (1ULL << 35)
+
+#define           QUIC_EV_CONN_ENEW      (1ULL << 32)
+#define           QUIC_EV_CONN_EISEC     (1ULL << 33)
+#define           QUIC_EV_CONN_ERSEC     (1ULL << 34)
+#define           QUIC_EV_CONN_EWSEC     (1ULL << 35)
+#define           QUIC_EV_CONN_ELPKT     (1ULL << 36)
+#define           QUIC_EV_CONN_ESPKT     (1ULL << 37)
+#define           QUIC_EV_CONN_ECHPKT    (1ULL << 38)
+#define           QUIC_EV_CONN_EHPKT     (1ULL << 39)
+#define           QUIC_EV_CONN_EPAPKT    (1ULL << 40)
+
+/* Similar to kernel min()/max() definitions. */
+#define QUIC_MIN(a, b) ({ \
+    typeof(a) _a = (a);   \
+    typeof(b) _b = (b);   \
+    (void) (&_a == &_b);  \
+    _a < _b ? _a : _b; })
+
+#define QUIC_MAX(a, b) ({ \
+    typeof(a) _a = (a);   \
+    typeof(b) _b = (b);   \
+    (void) (&_a == &_b);  \
+    _a > _b ? _a : _b; })
+
+extern struct trace_source trace_quic;
+extern struct pool_head *pool_head_quic_rx_packet;
+extern struct pool_head *pool_head_quic_tx_packet;
+extern struct pool_head *pool_head_quic_tx_frm;
+
+/*
+ * This struct is used by ebmb_node structs as last member of flexible arrays.
+ * So do not change the order of the member of quic_cid struct.
+ * <data> member must be the first one.
+ */
+struct quic_cid {
+       unsigned char data[QUIC_CID_MAXLEN + sizeof(in_port_t) + sizeof(struct in6_addr)];
+       unsigned char len;
+};
+
+/* The data structure used to build a set of connection IDs for each connection. */
+struct quic_connection_id {
+       struct eb64_node seq_num;
+       uint64_t retire_prior_to;
+       struct quic_cid cid;
+       unsigned char stateless_reset_token[QUIC_STATELESS_RESET_TOKEN_LEN];
+};
+
+struct preferred_address {
+       uint16_t ipv4_port;
+       uint16_t ipv6_port;
+       uint8_t ipv4_addr[4];
+       uint8_t ipv6_addr[16];
+       struct quic_cid cid;
+       uint8_t stateless_reset_token[QUIC_STATELESS_RESET_TOKEN_LEN];
+};
+
+/* Default values for some of transport parameters */
+#define QUIC_DFLT_MAX_PACKET_SIZE     65527
+#define QUIC_DFLT_ACK_DELAY_COMPONENT     3 /* milliseconds */
+#define QUIC_DFLT_MAX_ACK_DELAY          25 /* milliseconds */
+
+/* Types of QUIC transport parameters */
+#define QUIC_TP_ORIGINAL_DESTINATION_CONNECTION_ID   0
+#define QUIC_TP_IDLE_TIMEOUT                         1
+#define QUIC_TP_STATELESS_RESET_TOKEN                2
+#define QUIC_TP_MAX_PACKET_SIZE                      3
+#define QUIC_TP_INITIAL_MAX_DATA                     4
+#define QUIC_TP_INITIAL_MAX_STREAM_DATA_BIDI_LOCAL   5
+#define QUIC_TP_INITIAL_MAX_STREAM_DATA_BIDI_REMOTE  6
+#define QUIC_TP_INITIAL_MAX_STREAM_DATA_UNI          7
+#define QUIC_TP_INITIAL_MAX_STREAMS_BIDI             8
+#define QUIC_TP_INITIAL_MAX_STREAMS_UNI              9
+#define QUIC_TP_ACK_DELAY_EXPONENT                  10
+#define QUIC_TP_MAX_ACK_DELAY                       11
+#define QUIC_TP_DISABLE_ACTIVE_MIGRATION            12
+#define QUIC_TP_PREFERRED_ADDRESS                   13
+#define QUIC_TP_ACTIVE_CONNECTION_ID_LIMIT          14
+#define QUIC_TP_INITIAL_SOURCE_CONNECTION_ID        15
+
+/*
+ * These defines are not for transport parameter type, but the maximum accepted value for
+ * transport parameter types.
+ */
+#define QUIC_TP_ACK_DELAY_EXPONENT_LIMIT 20
+#define QUIC_TP_MAX_ACK_DELAY_LIMIT      (1UL << 14)
+
+/* The maximum length of encoded transport parameters for any QUIC peer. */
+#define QUIC_TP_MAX_ENCLEN    128
+/*
+ * QUIC transport parameters.
+ * Note that forbidden parameters sent by clients MUST generate TRANSPORT_PARAMETER_ERROR errors.
+ */
+struct quic_transport_params {
+       uint64_t idle_timeout;
+       uint64_t max_packet_size;                                      /* Default: 65527 (max of UDP payload for IPv6) */
+       uint64_t initial_max_data;
+       uint64_t initial_max_stream_data_bidi_local;
+       uint64_t initial_max_stream_data_bidi_remote;
+       uint64_t initial_max_stream_data_uni;
+       uint64_t initial_max_streams_bidi;
+       uint64_t initial_max_streams_uni;
+       uint64_t ack_delay_exponent;                                   /* Default: 3, max: 20 */
+       uint64_t max_ack_delay;                                        /* Default: 3ms, max: 2^14ms*/
+       uint64_t active_connection_id_limit;
+
+       /* Booleans */
+       uint8_t disable_active_migration;
+       uint8_t with_stateless_reset_token;
+       uint8_t with_preferred_address;
+       uint8_t original_destination_connection_id_present;
+       uint8_t initial_source_connection_id_present;
+
+       uint8_t stateless_reset_token[QUIC_STATELESS_RESET_TOKEN_LEN]; /* Forbidden for clients */
+       /*
+        * MUST be sent by servers.
+        * When received by clients, must be set to 1 if present.
+        */
+       struct quic_cid original_destination_connection_id;            /* Forbidden for clients */
+       /* MUST be present both for servers and clients. */
+       struct quic_cid initial_source_connection_id;
+       struct preferred_address preferred_address;                    /* Forbidden for clients */
+};
+
+/* Structure for ACK ranges sent in ACK frames. */
+struct quic_ack_range {
+       struct list list;
+       int64_t first;
+       int64_t last;
+};
+
+struct quic_ack_ranges {
+       /* list of ACK ranges. */
+       struct list list;
+       /* The number of ACK ranges is this lists */
+       size_t sz;
+       /* The number of bytes required to encode this ACK ranges lists. */
+       size_t enc_sz;
+};
+
+/* Flag the packet number space as requiring an ACK frame to be sent. */
+#define QUIC_FL_PKTNS_ACK_REQUIRED  (1UL << 0)
+#define QUIC_FL_PKTNS_ACK_RECEIVED  (1UL << 1)
+
+/* The maximum number of dgrams which may be sent upon PTO expirations. */
+#define QUIC_MAX_NB_PTO_DGRAMS         2
+
+/* QUIC packet number space */
+struct quic_pktns {
+       struct {
+               /* List of frames to send. */
+               struct list frms;
+               /* Next packet number to use for transmissions. */
+               int64_t next_pn;
+               /* Largest acked sent packet. */
+               int64_t largest_acked_pn;
+               /* The packet which has been sent. */
+               struct eb_root pkts;
+               /* The time the most recent ack-eliciting packer was sent. */
+               unsigned int time_of_last_eliciting;
+               /* The time this packet number space has experienced packet loss. */
+               unsigned int loss_time;
+               /* Boolean to denote if we must send probe packet. */
+               unsigned int pto_probe;
+               /* In flight bytes for this packet number space. */
+               size_t in_flight;
+       } tx;
+       struct {
+               /* Largest packet number */
+               int64_t largest_pn;
+               /* Number of ack-eliciting packets. */
+               size_t nb_ack_eliciting;
+               struct quic_ack_ranges ack_ranges;
+       } rx;
+       unsigned int flags;
+};
+
+/* The QUIC packet numbers are 62-bits integers */
+#define QUIC_MAX_PACKET_NUM      ((1ULL << 62) - 1)
+
+/* Default QUIC connection transport parameters */
+extern struct quic_transport_params quic_dflt_transport_params;
+
+/* Flag a received packet as being an ack-eliciting packet. */
+#define QUIC_FL_RX_PACKET_ACK_ELICITING (1UL << 0)
+
+struct quic_rx_packet {
+       struct list list;
+       struct list rx_list;
+       struct quic_conn *qc;
+       unsigned char type;
+       uint32_t version;
+       /* Initial desctination connection ID. */
+       struct quic_cid dcid;
+       struct quic_cid scid;
+       size_t odcid_len;
+       size_t pn_offset;
+       /* Packet number */
+       int64_t pn;
+       /* Packet number length */
+       uint32_t pnl;
+       uint64_t token_len;
+       /* Packet length */
+       uint64_t len;
+       /* Additional authenticated data length */
+       size_t aad_len;
+       unsigned char data[QUIC_PACKET_MAXLEN];
+       struct eb64_node pn_node;
+       volatile unsigned int refcnt;
+       /* Source address of this packet. */
+       struct sockaddr_storage saddr;
+       unsigned int flags;
+};
+
+/* UDP datagram context used by the I/O handler receiver callbacks.
+ * Useful to store the connection
+ */
+struct quic_dgram_ctx {
+       struct quic_conn *qc;
+       struct ebmb_node *dcid_node;
+       void *owner;
+};
+
+/* QUIC packet reader. */
+typedef ssize_t qpkt_read_func(unsigned char **buf,
+                               const unsigned char *end,
+                               struct quic_rx_packet *qpkt,
+                               struct quic_dgram_ctx *dgram_ctx,
+                               struct sockaddr_storage *saddr);
+
+/* Structure to store enough information about the RX CRYPTO frames. */
+struct quic_rx_crypto_frm {
+       struct eb64_node offset_node;
+       uint64_t len;
+       const unsigned char *data;
+       struct quic_rx_packet *pkt;
+};
+
+/* Flag a sent packet as being an ack-eliciting packet. */
+#define QUIC_FL_TX_PACKET_ACK_ELICITING (1UL << 0)
+/* Flag a sent packet as containing a PADDING frame. */
+#define QUIC_FL_TX_PACKET_PADDING       (1UL << 1)
+/* Flag a sent packet as being in flight. */
+#define QUIC_FL_TX_PACKET_IN_FLIGHT     (QUIC_FL_TX_PACKET_ACK_ELICITING | QUIC_FL_TX_PACKET_PADDING)
+
+/* Structure to store enough information about TX QUIC packets. */
+struct quic_tx_packet {
+       /* List entry point. */
+       struct list list;
+       /* This is not the packet length but the length of outstanding data
+        * for in flight TX packet.
+        */
+       size_t in_flight_len;
+       struct eb64_node pn_node;
+       /* The number of bytes of CRYPTO data in this packet. */
+       unsigned int cdata_len;
+       /* The list of frames of this packet. */
+       struct list frms;
+       /* The time this packet was sent (usec). */
+       unsigned int time_sent;
+       /* Packet number spakce. */
+       struct quic_pktns *pktns;
+       /* Flags. */
+       unsigned int flags;
+};
+
+/* Structure to stora enough information about the TX frames. */
+struct quic_tx_frm {
+       struct list list;
+       unsigned char type;
+       union {
+               struct quic_crypto crypto;
+               struct quic_new_connection_id new_connection_id;
+       };
+};
+
+
+#define QUIC_CRYPTO_BUF_SHIFT  10
+#define QUIC_CRYPTO_BUF_MASK   ((1UL << QUIC_CRYPTO_BUF_SHIFT) - 1)
+/* The maximum allowed size of CRYPTO data buffer provided by the TLS stack. */
+#define QUIC_CRYPTO_BUF_SZ    (1UL << QUIC_CRYPTO_BUF_SHIFT) /* 1 KB */
+
+/* The maximum number of bytes of CRYPTO data in flight during handshakes. */
+#define QUIC_CRYPTO_IN_FLIGHT_MAX 4096
+
+/*
+ * CRYPTO buffer struct.
+ * Such buffers are used to send CRYPTO data.
+ */
+struct quic_crypto_buf {
+       unsigned char data[QUIC_CRYPTO_BUF_SZ];
+       size_t sz;
+};
+
+/* QUIC buffer structure used to build outgoing packets. */
+struct q_buf {
+       /* Points to the data in this buffer. */
+       unsigned char *area;
+       /* Points to the current position to write into this buffer. */
+       unsigned char *pos;
+       /* Point to the end of this buffer past one. */
+       const unsigned char *end;
+       /* The number of data bytes in this buffer. */
+       size_t data;
+       /* The list of packets attached to this buffer which have not been already sent. */
+       struct list pkts;
+};
+
+struct quic_enc_level {
+       enum ssl_encryption_level_t level;
+       struct quic_tls_ctx tls_ctx;
+       struct {
+               /* The packets received by the listener I/O handler
+                  with header protection removed. */
+               struct eb_root pkts;
+               /* Liste of QUIC packets with protected header. */
+               struct list pqpkts;
+               /* Crypto frames */
+               struct {
+                       uint64_t offset;
+                       struct eb_root frms; /* XXX TO CHECK XXX */
+               } crypto;
+       } rx;
+       struct {
+               struct {
+                       struct quic_crypto_buf **bufs;
+                       /* The number of element in use in the previous array. */
+                       size_t nb_buf;
+                       /* The total size of the CRYPTO data stored in the CRYPTO buffers. */
+                       size_t sz;
+                       /* The offset of the CRYPT0 data stream. */
+                       uint64_t offset;
+               } crypto;
+       } tx;
+       struct quic_pktns *pktns;
+};
+
+struct quic_path {
+       /* Control congestion. */
+       struct quic_cc cc;
+       /* Packet loss detection information. */
+       struct quic_loss loss;
+
+       /* MTU. */
+       size_t mtu;
+       /* Congestion window. */
+       uint64_t cwnd;
+       /* Minimum congestion window. */
+       uint64_t min_cwnd;
+       /* Prepared data to be sent (in bytes). */
+       uint64_t prep_in_flight;
+       /* Outstanding data (in bytes). */
+       uint64_t in_flight;
+       /* Number of in flight ack-eliciting packets. */
+       uint64_t in_flight_ae_pkts;
+};
+
+/* The number of buffers for outgoing packets (must be a power of two). */
+#define QUIC_CONN_TX_BUFS_NB 8
+#define QUIC_CONN_TX_BUF_SZ  QUIC_PACKET_MAXLEN
+
+struct quic_conn {
+       uint32_t version;
+
+       /* Transport parameters. */
+       struct quic_transport_params params;
+       unsigned char enc_params[QUIC_TP_MAX_ENCLEN]; /* encoded QUIC transport parameters */
+       size_t enc_params_len;
+
+       /*
+        * Original Destination Connection ID  (comming with first client Initial packets).
+        * Used only by servers.
+        */
+       struct ebmb_node odcid_node;
+       struct quic_cid odcid;
+
+       struct quic_cid dcid;
+       struct ebmb_node scid_node;
+       struct quic_cid scid;
+       struct eb_root cids;
+
+       struct quic_enc_level els[QUIC_TLS_ENC_LEVEL_MAX];
+
+       struct quic_transport_params rx_tps;
+
+       struct quic_pktns pktns[QUIC_TLS_PKTNS_MAX];
+
+       /* Used only to reach the tasklet for the I/O handler from this quic_conn object. */
+       struct connection *conn;
+       /* Output buffer used during the handshakes. */
+       struct {
+               unsigned char data[QUIC_PACKET_MAXLEN];
+               unsigned char *pos;
+       } obuf;
+
+       struct {
+               /* The remaining frames to send. */
+               struct list frms_to_send;
+
+               /* Array of buffers. */
+               struct q_buf **bufs;
+               /* The size of the previous array. */
+               size_t nb_buf;
+               /* Writer index. */
+               int wbuf;
+               /* Reader index. */
+               int rbuf;
+               /* Number of sent bytes. */
+               uint64_t bytes;
+               /* The number of datagrams which may be sent
+                * when sending probe packets.
+                */
+               int nb_pto_dgrams;
+       } tx;
+       struct {
+               /* Number of received bytes. */
+               uint64_t bytes;
+       } rx;
+       /* In flight CRYPTO data counter. */
+       size_t ifcdata;
+       unsigned int max_ack_delay;
+       struct quic_path paths[1];
+       struct quic_path *path;
+
+       struct task *timer_task;
+       unsigned int timer;
+};
+
+#endif /* USE_QUIC */
+#endif /* _HAPROXY_XPRT_QUIC_T_H */
diff --git a/include/haproxy/xprt_quic.h b/include/haproxy/xprt_quic.h
new file mode 100644 (file)
index 0000000..5b31c67
--- /dev/null
@@ -0,0 +1,1209 @@
+/*
+ * include/haproxy/xprt_quic.h
+ * This file contains QUIC xprt function prototypes
+ *
+ * Copyright 2020 HAProxy Technologies, Frédéric Lécaille <flecaille@haproxy.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, version 2.1
+ * exclusively.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifndef _HAPROXY_XPRT_QUIC_H
+#define _HAPROXY_XPRT_QUIC_H
+#ifdef USE_QUIC
+#ifndef USE_OPENSSL
+#error "Must define USE_OPENSSL"
+#endif
+
+#include <stdint.h>
+
+#include <haproxy/buf.h>
+#include <haproxy/chunk.h>
+#include <haproxy/net_helper.h>
+#include <haproxy/openssl-compat.h>
+#include <haproxy/ticks.h>
+#include <haproxy/time.h>
+
+#include <haproxy/listener.h>
+#include <haproxy/quic_cc.h>
+#include <haproxy/quic_frame.h>
+#include <haproxy/quic_loss.h>
+#include <haproxy/xprt_quic-t.h>
+
+#include <openssl/rand.h>
+
+extern struct pool_head *pool_head_quic_connection_id;
+
+int ssl_quic_initial_ctx(struct bind_conf *bind_conf);
+
+/* Returns the required length in bytes to encode <cid> QUIC connection ID. */
+static inline size_t sizeof_quic_cid(const struct quic_cid *cid)
+{
+       return sizeof cid->len + cid->len;
+}
+
+/* Copy <src> QUIC CID to <dst>.
+ * This is the responsability of the caller to check there is enough room in
+ * <dst> to copy <src>.
+ * Always succeeds.
+ */
+static inline void quic_cid_cpy(struct quic_cid *dst, const struct quic_cid *src)
+{
+       memcpy(dst->data, src->data, src->len);
+       dst->len = src->len;
+}
+
+/* Concatenate the port and address of <saddr> to <cid> QUIC connection ID.
+ * Returns the number of bytes concatenated to <cid>.
+ */
+static inline size_t quic_cid_saddr_cat(struct quic_cid *cid, struct sockaddr_storage *saddr)
+{
+       void *port, *addr;
+       size_t port_len, addr_len;
+
+       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(cid->data + cid->len, port, port_len);
+       cid->len += port_len;
+       memcpy(cid->data + cid->len, addr, addr_len);
+       cid->len += addr_len;
+
+       return port_len + addr_len;
+}
+
+
+/* Dump the QUIC connection ID value if present (non null length). Used only for
+ * debugging purposes.
+ * Always succeeds.
+ */
+static inline void quic_cid_dump(struct buffer *buf, struct quic_cid *cid)
+{
+       int i;
+
+       chunk_appendf(buf, "(%d", cid->len);
+       if (cid->len)
+               chunk_appendf(buf, ",");
+       for (i = 0; i < cid->len; i++)
+               chunk_appendf(buf, "%02x", cid->data[i]);
+       chunk_appendf(buf, ")");
+}
+
+/* Free the CIDs attached to <conn> QUIC connection.
+ * Always succeeds.
+ */
+static inline void free_quic_conn_cids(struct quic_conn *conn)
+{
+       struct eb64_node *node;
+
+       node = eb64_first(&conn->cids);
+       while (node) {
+               struct quic_connection_id *cid;
+
+               cid = eb64_entry(&node->node, struct quic_connection_id, seq_num);
+               node = eb64_next(node);
+               eb64_delete(&cid->seq_num);
+               pool_free(pool_head_quic_connection_id, cid);
+       }
+}
+
+/* Copy <src> new connection ID information to <to> NEW_CONNECTION_ID frame data.
+ * Always succeeds.
+ */
+static inline void quic_connection_id_to_frm_cpy(struct quic_frame *dst,
+                                                 struct quic_connection_id *src)
+{
+       struct quic_new_connection_id *to = &dst->new_connection_id;
+
+       dst->type = QUIC_FT_NEW_CONNECTION_ID;
+       to->seq_num = src->seq_num.key;
+       to->retire_prior_to = src->retire_prior_to;
+       to->cid.len = src->cid.len;
+       to->cid.data = src->cid.data;
+       to->stateless_reset_token = src->stateless_reset_token;
+}
+
+/* Allocate a new CID with <seq_num> as sequence number and attach it to <root>
+ * ebtree.
+ * Returns the new CID if succedded, NULL if not.
+ */
+static inline struct quic_connection_id *new_quic_cid(struct eb_root *root,
+                                                      int seq_num)
+{
+       struct quic_connection_id *cid;
+
+       cid = pool_alloc(pool_head_quic_connection_id);
+       if (!cid)
+               return NULL;
+
+       cid->cid.len = QUIC_CID_LEN;
+       if (RAND_bytes(cid->cid.data, cid->cid.len) != 1 ||
+           RAND_bytes(cid->stateless_reset_token,
+                      sizeof cid->stateless_reset_token) != 1) {
+               fprintf(stderr, "Could not generate %d random bytes\n", cid->cid.len);
+               goto err;
+       }
+
+       cid->seq_num.key = seq_num;
+       cid->retire_prior_to = 0;
+       eb64_insert(root, &cid->seq_num);
+
+       return cid;
+
+ err:
+       pool_free(pool_head_quic_connection_id, cid);
+       return NULL;
+}
+
+/* The maximum size of a variable-length QUIC integer encoded with 1 byte */
+#define QUIC_VARINT_1_BYTE_MAX       ((1UL <<  6) - 1)
+/* The maximum size of a variable-length QUIC integer encoded with 2 bytes */
+#define QUIC_VARINT_2_BYTE_MAX       ((1UL <<  14) - 1)
+/* The maximum size of a variable-length QUIC integer encoded with 4 bytes */
+#define QUIC_VARINT_4_BYTE_MAX       ((1UL <<  30) - 1)
+/* The maximum size of a variable-length QUIC integer encoded with 8 bytes */
+#define QUIC_VARINT_8_BYTE_MAX       ((1ULL <<  62) - 1)
+
+/* The maximum size of a variable-length QUIC integer */
+#define QUIC_VARINT_MAX_SIZE       8
+
+/* The two most significant bits of byte #0 from a QUIC packet gives the 2 
+ * logarithm of the length of a variable length encoded integer.
+ */
+#define QUIC_VARINT_BYTE_0_BITMASK 0x3f
+#define QUIC_VARINT_BYTE_0_SHIFT   6
+
+/* 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 thes 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;
+}
+
+
+/* Returns enough log2 of first powers of two to encode QUIC variable length
+ * integers.
+ * Returns -1 if <val> if out of the range of lengths supported by QUIC.
+ */
+static inline int quic_log2(unsigned int val)
+{
+       switch (val) {
+       case 8:
+               return 3;
+       case 4:
+               return 2;
+       case 2:
+               return 1;
+       case 1:
+               return 0;
+       default:
+               return -1;
+       }
+}
+
+/* Returns the size in bytes required to encode a 64bits integer if
+ * not out of range (< (1 << 62)), or 0 if out of range.
+ */
+static inline size_t quic_int_getsize(uint64_t val)
+{
+       switch (val) {
+       case 0 ... QUIC_VARINT_1_BYTE_MAX:
+               return 1;
+       case QUIC_VARINT_1_BYTE_MAX + 1 ... QUIC_VARINT_2_BYTE_MAX:
+               return 2;
+       case QUIC_VARINT_2_BYTE_MAX + 1 ... QUIC_VARINT_4_BYTE_MAX:
+               return 4;
+       case QUIC_VARINT_4_BYTE_MAX + 1 ... QUIC_VARINT_8_BYTE_MAX:
+               return 8;
+       default:
+               return 0;
+       }
+}
+
+/* Return the difference between the encoded length of <val> and the encoded
+ * length of <val+1>.
+ */
+static inline size_t quic_incint_size_diff(uint64_t val)
+{
+       switch (val) {
+       case QUIC_VARINT_1_BYTE_MAX:
+               return 1;
+       case QUIC_VARINT_2_BYTE_MAX:
+               return 2;
+       case QUIC_VARINT_4_BYTE_MAX:
+               return 4;
+       default:
+               return 0;
+       }
+}
+
+/* Return the difference between the encoded length of <val> and the encoded
+ * length of <val-1>.
+ */
+static inline size_t quic_decint_size_diff(uint64_t val)
+{
+       switch (val) {
+       case QUIC_VARINT_1_BYTE_MAX + 1:
+               return 1;
+       case QUIC_VARINT_2_BYTE_MAX + 1:
+               return 2;
+       case QUIC_VARINT_4_BYTE_MAX + 1:
+               return 4;
+       default:
+               return 0;
+       }
+}
+
+
+/* Returns the maximum value of a QUIC variable-length integer with <sz> as size */
+static inline uint64_t quic_max_int(size_t sz)
+{
+       switch (sz) {
+       case 1:
+               return QUIC_VARINT_1_BYTE_MAX;
+       case 2:
+               return QUIC_VARINT_2_BYTE_MAX;
+       case 4:
+               return QUIC_VARINT_4_BYTE_MAX;
+       case 8:
+               return QUIC_VARINT_8_BYTE_MAX;
+       }
+
+       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>.
+ */
+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))
+               ret += 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;
+}
+
+/* Decode a QUIC variable-length integer from <buf> buffer into <val>.
+ * Note that the result is a 64-bits integer but with the less significant
+ * 62 bits as relevant information. The most significant 2 remaining bits encode
+ * the length of the integer.
+ * Returns 1 if succeeded there was enough data in <buf>), 0 if not.
+ */
+static inline int quic_dec_int(uint64_t *val,
+                               const unsigned char **buf,
+                               const unsigned char *end)
+{
+       size_t len;
+
+       if (*buf >= end)
+               return 0;
+
+       len = 1 << (**buf >> QUIC_VARINT_BYTE_0_SHIFT);
+       if (*buf + len > end)
+               return 0;
+
+       *val = *(*buf)++ & QUIC_VARINT_BYTE_0_BITMASK;
+       while (--len)
+               *val = (*val << 8) | *(*buf)++;
+
+       return 1;
+}
+
+/* Encode a QUIC variable-length integer from <val> into <buf> buffer with <end> as first
+ * byte address after the end of this buffer.
+ * Returns 1 if succeeded (there was enough room in buf), 0 if not.
+ */
+static inline int quic_enc_int(unsigned char **buf, const unsigned char *end, uint64_t val)
+{
+       size_t len;
+       unsigned int shift;
+       unsigned char size_bits, *head;
+
+       len = quic_int_getsize(val);
+       if (!len || end - *buf < len)
+               return 0;
+
+       shift = (len - 1) * 8;
+       /* set the bits of byte#0 which gives the length of the encoded integer */
+       size_bits = quic_log2(len) << QUIC_VARINT_BYTE_0_SHIFT;
+       head = *buf;
+       while (len--) {
+               *(*buf)++ = val >> shift;
+               shift -= 8;
+       }
+       *head |= size_bits;
+
+       return 1;
+}
+
+/* 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 responsability of the caller to check there is
+ * enough room in the buffer to copy <pn_len> bytes.
+ * Never fails.
+ */
+static inline void quic_packet_number_encode(unsigned char **buf,
+                                             const unsigned char *end,
+                                             uint64_t pn, size_t pn_len)
+{
+       /* 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;
+}
+
+/* Returns the <ack_delay> field value from <ack_frm> ACK frame for
+ * <conn> QUIC connection.
+ */
+static inline unsigned int quic_ack_delay_ms(struct quic_ack *ack_frm,
+                                             struct quic_conn *conn)
+{
+       return ack_frm->ack_delay << conn->rx_tps.ack_delay_exponent;
+}
+
+/* Initialize <dst> transport parameters from <quic_dflt_trasports_parame>.
+ * Never fails.
+ */
+static inline void quic_dflt_transport_params_cpy(struct quic_transport_params *dst)
+{
+       dst->max_packet_size    = quic_dflt_transport_params.max_packet_size;
+       dst->ack_delay_exponent = quic_dflt_transport_params.ack_delay_exponent;
+       dst->max_ack_delay      = quic_dflt_transport_params.max_ack_delay;
+}
+
+/* Initialize <p> transport parameters depending <server> boolean value which
+ * must be set to 1 for a server (haproxy listener), 0 for a client (connection
+ * to haproxy server).
+ * Never fails.
+ */
+static inline void quic_transport_params_init(struct quic_transport_params *p,
+                                              int server)
+{
+       quic_dflt_transport_params_cpy(p);
+
+       p->idle_timeout                        = 30000;
+
+       p->initial_max_data                    = 1 * 1024 * 1024;
+       p->initial_max_stream_data_bidi_local  = 256 * 1024;
+       p->initial_max_stream_data_bidi_remote = 256 * 1024;
+       p->initial_max_stream_data_uni         = 256 * 1024;
+       p->initial_max_streams_bidi            = 100;
+       p->initial_max_streams_uni             = 3;
+
+       if (server)
+               p->with_stateless_reset_token      = 1;
+       p->active_connection_id_limit          = 8;
+
+}
+
+/* Encode <addr> preferred address transport parameter in <buf> without its
+ * "type+len" prefix. Note that the IP addresses must be encoded in network byte
+ * order.
+ * So ->ipv4_addr and ->ipv6_addr, which are buffers, must contained values
+ * already encoded in network byte order.
+ * It is the responsability of the caller to check there is enough room in <buf> to encode
+ * this address.
+ * Never fails.
+ */
+static inline void quic_transport_param_enc_pref_addr_val(unsigned char **buf,
+                                                          const unsigned char *end,
+                                                          struct preferred_address *addr)
+{
+       write_n16(*buf, addr->ipv4_port);
+       *buf += sizeof addr->ipv4_port;
+
+       memcpy(*buf, addr->ipv4_addr, sizeof addr->ipv4_addr);
+       *buf += sizeof addr->ipv4_addr;
+
+       write_n16(*buf, addr->ipv6_port);
+       *buf += sizeof addr->ipv6_port;
+
+       memcpy(*buf, addr->ipv6_addr, sizeof addr->ipv6_addr);
+       *buf += sizeof addr->ipv6_addr;
+
+       *(*buf)++ = addr->cid.len;
+       if (addr->cid.len) {
+               memcpy(*buf, addr->cid.data, addr->cid.len);
+               *buf += addr->cid.len;
+       }
+
+       memcpy(*buf, addr->stateless_reset_token, sizeof addr->stateless_reset_token);
+       *buf += sizeof addr->stateless_reset_token;
+}
+
+/* Decode into <addr> preferred address transport parameter found in <*buf> buffer.
+ * Returns 1 if succeeded, 0 if not.
+ */
+static inline int quic_transport_param_dec_pref_addr(struct preferred_address *addr,
+                                                     const unsigned char **buf,
+                                                     const unsigned char *end)
+{
+       ssize_t addr_len;
+
+       addr_len = sizeof addr->ipv4_port + sizeof addr->ipv4_addr;
+       addr_len += sizeof addr->ipv6_port + sizeof addr->ipv6_addr;
+       addr_len += sizeof addr->cid.len;
+
+       if (end - *buf < addr_len)
+               return 0;
+
+       addr->ipv4_port = read_n16(*buf);
+       *buf += sizeof addr->ipv4_port;
+
+       memcpy(addr->ipv4_addr, *buf, sizeof addr->ipv4_addr);
+       *buf += sizeof addr->ipv4_addr;
+
+       addr->ipv6_port = read_n16(*buf);
+       *buf += sizeof addr->ipv6_port;
+
+       memcpy(addr->ipv6_addr, *buf, sizeof addr->ipv6_addr);
+       *buf += sizeof addr->ipv6_addr;
+
+       addr->cid.len = *(*buf)++;
+       if (addr->cid.len) {
+               if (end - *buf > addr->cid.len || addr->cid.len > sizeof addr->cid.data)
+                       return 0;
+               memcpy(addr->cid.data, *buf, addr->cid.len);
+               *buf += addr->cid.len;
+       }
+
+       if (end - *buf != sizeof addr->stateless_reset_token)
+               return 0;
+
+       memcpy(addr->stateless_reset_token, *buf, end - *buf);
+       *buf += sizeof addr->stateless_reset_token;
+
+       return *buf == end;
+}
+
+/* Decode into <p> struct a transport parameter found in <*buf> buffer with
+ * <type> as type and <len> as length, depending on <server> boolean value which
+ * must be set to 1 for a server (haproxy listener) or 0 for a client (connection
+ * to an haproxy server).
+ */
+static inline int quic_transport_param_decode(struct quic_transport_params *p,
+                                              int server, uint64_t type,
+                                              const unsigned char **buf, size_t len)
+{
+       const unsigned char *end = *buf + len;
+
+       switch (type) {
+       case QUIC_TP_ORIGINAL_DESTINATION_CONNECTION_ID:
+               if (!server || len >= sizeof p->original_destination_connection_id.data)
+                       return 0;
+
+               if (len)
+                       memcpy(p->original_destination_connection_id.data, *buf, len);
+               p->original_destination_connection_id.len = len;
+               *buf += len;
+               p->original_destination_connection_id_present = 1;
+               break;
+       case QUIC_TP_INITIAL_SOURCE_CONNECTION_ID:
+               if (len >= sizeof p->initial_source_connection_id.data)
+                       return 0;
+
+               if (len)
+                       memcpy(p->initial_source_connection_id.data, *buf, len);
+               p->initial_source_connection_id.len = len;
+               *buf += len;
+               p->initial_source_connection_id_present = 1;
+               break;
+       case QUIC_TP_STATELESS_RESET_TOKEN:
+               if (!server || len != sizeof p->stateless_reset_token)
+                       return 0;
+               memcpy(p->stateless_reset_token, *buf, len);
+               *buf += len;
+               p->with_stateless_reset_token = 1;
+               break;
+       case QUIC_TP_PREFERRED_ADDRESS:
+               if (!server)
+                       return 0;
+               if (!quic_transport_param_dec_pref_addr(&p->preferred_address, buf, *buf + len))
+                       return 0;
+               p->with_preferred_address = 1;
+               break;
+       case QUIC_TP_IDLE_TIMEOUT:
+               if (!quic_dec_int(&p->idle_timeout, buf, end))
+                       return 0;
+               break;
+       case QUIC_TP_MAX_PACKET_SIZE:
+               if (!quic_dec_int(&p->max_packet_size, buf, end))
+                       return 0;
+               break;
+       case QUIC_TP_INITIAL_MAX_DATA:
+               if (!quic_dec_int(&p->initial_max_data, buf, end))
+                       return 0;
+               break;
+       case QUIC_TP_INITIAL_MAX_STREAM_DATA_BIDI_LOCAL:
+               if (!quic_dec_int(&p->initial_max_stream_data_bidi_local, buf, end))
+                       return 0;
+               break;
+       case QUIC_TP_INITIAL_MAX_STREAM_DATA_BIDI_REMOTE:
+               if (!quic_dec_int(&p->initial_max_stream_data_bidi_remote, buf, end))
+                       return 0;
+               break;
+       case QUIC_TP_INITIAL_MAX_STREAM_DATA_UNI:
+               if (!quic_dec_int(&p->initial_max_stream_data_uni, buf, end))
+                       return 0;
+               break;
+       case QUIC_TP_INITIAL_MAX_STREAMS_BIDI:
+               if (!quic_dec_int(&p->initial_max_streams_bidi, buf, end))
+                       return 0;
+               break;
+       case QUIC_TP_INITIAL_MAX_STREAMS_UNI:
+               if (!quic_dec_int(&p->initial_max_streams_uni, buf, end))
+                       return 0;
+               break;
+       case QUIC_TP_ACK_DELAY_EXPONENT:
+               if (!quic_dec_int(&p->ack_delay_exponent, buf, end) ||
+                       p->ack_delay_exponent > QUIC_TP_ACK_DELAY_EXPONENT_LIMIT)
+                       return 0;
+               break;
+       case QUIC_TP_MAX_ACK_DELAY:
+               if (!quic_dec_int(&p->max_ack_delay, buf, end) ||
+                       p->max_ack_delay > QUIC_TP_MAX_ACK_DELAY_LIMIT)
+                       return 0;
+               break;
+       case QUIC_TP_DISABLE_ACTIVE_MIGRATION:
+               /* Zero-length parameter type. */
+               if (len != 0)
+                       return 0;
+               p->disable_active_migration = 1;
+               break;
+       case QUIC_TP_ACTIVE_CONNECTION_ID_LIMIT:
+               if (!quic_dec_int(&p->active_connection_id_limit, buf, end))
+                       return 0;
+               break;
+       default:
+               *buf += len;
+       };
+
+       return *buf == end;
+}
+
+/* Encode <type> and <len> variable length values in <buf>.
+ * Returns 1 if succeeded, 0 if not.
+ */
+static inline int quic_transport_param_encode_type_len(unsigned char **buf,
+                                                       const unsigned char *end,
+                                                       uint64_t type, uint64_t len)
+{
+       return quic_enc_int(buf, end, type) && quic_enc_int(buf, end, len);
+}
+
+/* Decode variable length type and length values of a QUIC transport parameter
+ * into <type> and <len> found in <*buf> buffer.
+ * Returns 1 if succeeded, 0 if not.
+ */
+static inline int quic_transport_param_decode_type_len(uint64_t *type, uint64_t *len,
+                                                       const unsigned char **buf,
+                                                       const unsigned char *end)
+{
+       return quic_dec_int(type, buf, end) && quic_dec_int(len, buf, end);
+}
+
+/* Encode <param> bytes stream with <type> as type and <length> as length into buf.
+ * Returns 1 if succeded, 0 if not.
+ */
+static inline int quic_transport_param_enc_mem(unsigned char **buf, const unsigned char *end,
+                                               uint64_t type, void *param, uint64_t length)
+{
+       if (!quic_transport_param_encode_type_len(buf, end, type, length))
+               return 0;
+
+       if (end - *buf < length)
+               return 0;
+
+       if (length)
+               memcpy(*buf, param, length);
+       *buf += length;
+
+       return 1;
+}
+
+/* Encode <val> 64-bits value as variable length integer into <buf>.
+ * Returns 1 if succeeded, 0 if not.
+ */
+static inline int quic_transport_param_enc_int(unsigned char **buf,
+                                               const unsigned char *end,
+                                               uint64_t type, uint64_t val)
+{
+       size_t len;
+
+       len = quic_int_getsize(val);
+
+       return len && quic_transport_param_encode_type_len(buf, end, type, len) &&
+               quic_enc_int(buf, end, val);
+}
+
+/* Encode <addr> preferred address into <buf>.
+ * Note that the IP addresses must be encoded in network byte order.
+ * So ->ipv4_addr and ->ipv6_addr, which are buffers, must contained
+ * values already encoded in network byte order.
+ * Returns 1 if succeeded, 0 if not.
+ */
+static inline int quic_transport_param_enc_pref_addr(unsigned char **buf,
+                                                     const unsigned char *end,
+                                                     struct preferred_address *addr)
+{
+       uint64_t addr_len = 0;
+
+       addr_len += sizeof addr->ipv4_port + sizeof addr->ipv4_addr;
+       addr_len += sizeof addr->ipv6_port + sizeof addr->ipv6_addr;
+       addr_len += sizeof_quic_cid(&addr->cid);
+       addr_len += sizeof addr->stateless_reset_token;
+
+       if (!quic_transport_param_encode_type_len(buf, end, QUIC_TP_PREFERRED_ADDRESS, addr_len))
+               return 0;
+
+       if (end - *buf < addr_len)
+               return 0;
+
+       quic_transport_param_enc_pref_addr_val(buf, end, addr);
+
+       return 1;
+}
+
+/* Encode <p> transport parameter into <buf> depending on <server> value which
+ * must be set to 1 for a server (haproxy listener) or 0 for a client
+ * (connection to a haproxy server).
+ * Return the number of bytes consumed if succeeded, 0 if not.
+ */
+static inline int quic_transport_params_encode(unsigned char *buf,
+                                               const unsigned char *end,
+                                               struct quic_transport_params *p,
+                                               int server)
+{
+       unsigned char *head;
+       unsigned char *pos;
+
+       head = pos = buf;
+       if (server) {
+               if (!quic_transport_param_enc_mem(&pos, end,
+                                                 QUIC_TP_ORIGINAL_DESTINATION_CONNECTION_ID,
+                                                 p->original_destination_connection_id.data,
+                                                 p->original_destination_connection_id.len))
+                       return 0;
+               if (p->with_stateless_reset_token &&
+                       !quic_transport_param_enc_mem(&pos, end, QUIC_TP_STATELESS_RESET_TOKEN,
+                                                     p->stateless_reset_token,
+                                                     sizeof p->stateless_reset_token))
+                       return 0;
+               if (p->with_preferred_address &&
+                       !quic_transport_param_enc_pref_addr(&pos, end, &p->preferred_address))
+                       return 0;
+       }
+
+       if (!quic_transport_param_enc_mem(&pos, end,
+                                         QUIC_TP_INITIAL_SOURCE_CONNECTION_ID,
+                                         p->initial_source_connection_id.data,
+                                         p->initial_source_connection_id.len))
+               return 0;
+
+       if (p->idle_timeout &&
+           !quic_transport_param_enc_int(&pos, end, QUIC_TP_IDLE_TIMEOUT, p->idle_timeout))
+               return 0;
+
+       /*
+        * "max_packet_size" transport parameter must be transmitted only if different
+        * of the default value.
+        */
+       if (p->max_packet_size != QUIC_DFLT_MAX_PACKET_SIZE &&
+           !quic_transport_param_enc_int(&pos, end, QUIC_TP_MAX_PACKET_SIZE, p->max_packet_size))
+               return 0;
+
+       if (p->initial_max_data &&
+           !quic_transport_param_enc_int(&pos, end, QUIC_TP_INITIAL_MAX_DATA, p->initial_max_data))
+           return 0;
+
+       if (p->initial_max_stream_data_bidi_local &&
+           !quic_transport_param_enc_int(&pos, end, QUIC_TP_INITIAL_MAX_STREAM_DATA_BIDI_LOCAL,
+                                                 p->initial_max_stream_data_bidi_local))
+           return 0;
+
+       if (p->initial_max_stream_data_bidi_remote &&
+           !quic_transport_param_enc_int(&pos, end, QUIC_TP_INITIAL_MAX_STREAM_DATA_BIDI_REMOTE,
+                                                 p->initial_max_stream_data_bidi_remote))
+           return 0;
+
+       if (p->initial_max_stream_data_uni &&
+           !quic_transport_param_enc_int(&pos, end, QUIC_TP_INITIAL_MAX_STREAM_DATA_UNI,
+                                                 p->initial_max_stream_data_uni))
+           return 0;
+
+       if (p->initial_max_streams_bidi &&
+           !quic_transport_param_enc_int(&pos, end, QUIC_TP_INITIAL_MAX_STREAMS_BIDI,
+                                                 p->initial_max_streams_bidi))
+           return 0;
+
+       if (p->initial_max_streams_uni &&
+           !quic_transport_param_enc_int(&pos, end, QUIC_TP_INITIAL_MAX_STREAMS_UNI,
+                                                 p->initial_max_streams_uni))
+           return 0;
+
+       /*
+        * "ack_delay_exponent" transport parameter must be transmitted only if different
+        * of the default value.
+        */
+       if (p->ack_delay_exponent != QUIC_DFLT_ACK_DELAY_COMPONENT  &&
+           !quic_transport_param_enc_int(&pos, end, QUIC_TP_ACK_DELAY_EXPONENT, p->ack_delay_exponent))
+           return 0;
+
+       /*
+        * "max_ack_delay" transport parameter must be transmitted only if different
+        * of the default value.
+        */
+       if (p->max_ack_delay != QUIC_DFLT_MAX_ACK_DELAY &&
+           !quic_transport_param_enc_int(&pos, end, QUIC_TP_MAX_ACK_DELAY, p->max_ack_delay))
+           return 0;
+
+       /* 0-length value */
+       if (p->disable_active_migration &&
+           !quic_transport_param_encode_type_len(&pos, end, QUIC_TP_DISABLE_ACTIVE_MIGRATION, 0))
+               return 0;
+
+       if (p->active_connection_id_limit &&
+           !quic_transport_param_enc_int(&pos, end, QUIC_TP_ACTIVE_CONNECTION_ID_LIMIT,
+                                         p->active_connection_id_limit))
+           return 0;
+
+       return pos - head;
+}
+
+/* Decode transport parameters found in <buf> buffer into <p>, depending on
+ * <server> boolean value which must be set to 1 for a server (haproxy listener)
+ * or 0 for a client (connection to a haproxy server).
+ * Returns 1 if succeeded, 0 if not.
+ */
+static inline int quic_transport_params_decode(struct quic_transport_params *p, int server,
+                                               const unsigned char *buf,
+                                               const unsigned char *end)
+{
+       const unsigned char *pos;
+
+       pos = buf;
+
+       quic_transport_params_init(p, server);
+       while (pos != end) {
+               uint64_t type, len;
+
+               if (!quic_transport_param_decode_type_len(&type, &len, &pos, end))
+                       return 0;
+
+               if (end - pos < len)
+                       return 0;
+
+               if (!quic_transport_param_decode(p, server, type, &pos, len))
+                       return 0;
+       }
+
+       /*
+        * A server MUST send original_destination_connection_id transport parameter.
+        * initial_source_connection_id must be present both for server and client.
+        */
+       if ((server && !p->original_destination_connection_id_present) ||
+           !p->initial_source_connection_id_present)
+               return 0;
+
+       return 1;
+}
+
+/* Store transport parameters found in <buf> buffer into <conn> QUIC connection
+ * depending on <server> value which must be 1 for a server (haproxy listener)
+ * or 0 for a client (connection to a haproxy server).
+ * Returns 1 if succeeded, 0 if not.
+ */
+static inline int quic_transport_params_store(struct quic_conn *conn, int server,
+                                              const unsigned char *buf,
+                                              const unsigned char *end)
+{
+       if (!quic_transport_params_decode(&conn->rx_tps, server, buf, end))
+               return 0;
+
+       if (conn->rx_tps.max_ack_delay)
+               conn->max_ack_delay = conn->rx_tps.max_ack_delay;
+
+       return 1;
+}
+
+/* Initialize a QUIC packet number space.
+ * Never fails.
+ */
+static inline void quic_pktns_init(struct quic_pktns *pktns)
+{
+       LIST_INIT(&pktns->tx.frms);
+       pktns->tx.next_pn = -1;
+       pktns->tx.pkts = EB_ROOT_UNIQUE;
+       pktns->tx.largest_acked_pn = -1;
+       pktns->tx.time_of_last_eliciting = 0;
+       pktns->tx.loss_time = TICK_ETERNITY;
+       pktns->tx.in_flight = 0;
+
+       pktns->rx.largest_pn = -1;
+       pktns->rx.nb_ack_eliciting = 0;
+       LIST_INIT(&pktns->rx.ack_ranges.list);
+       pktns->rx.ack_ranges.sz = 0;
+       pktns->rx.ack_ranges.enc_sz = 0;
+
+       pktns->flags = 0;
+}
+
+/* Discard <pktns> packet number space attached to <qc> QUIC connection.
+ * Its loss information are reset. Deduce the outstanding bytes for this
+ * packet number space from the outstanding bytes for the path of this
+ * connection§.
+ * Note that all the non acknowledged TX packets and their frames are freed.
+ * Always succeeds. 
+ */
+static inline void quic_pktns_discard(struct quic_pktns *pktns,
+                                      struct quic_conn *qc)
+{
+       struct eb64_node *node;
+
+       pktns->tx.time_of_last_eliciting = 0;
+       pktns->tx.loss_time = TICK_ETERNITY;
+       pktns->tx.pto_probe = 0;
+       pktns->tx.in_flight = 0;
+       qc->path->loss.pto_count = 0;
+       qc->path->in_flight -= pktns->tx.in_flight;
+
+       node = eb64_first(&pktns->tx.pkts);
+       while (node) {
+               struct quic_tx_packet *pkt;
+               struct quic_tx_frm *frm, *frmbak;
+
+               pkt = eb64_entry(&node->node, struct quic_tx_packet, pn_node);
+               node = eb64_next(node);
+               list_for_each_entry_safe(frm, frmbak, &pkt->frms, list) {
+                       LIST_DEL(&frm->list);
+                       pool_free(pool_head_quic_tx_frm, frm);
+               }
+               eb64_delete(&pkt->pn_node);
+               pool_free(pool_head_quic_tx_packet, pkt);
+       }
+}
+
+/* Initialize <p> QUIC network path depending on <ipv4> boolean
+ * which is true for an IPv4 path, if not false for an IPv6 path.
+ */
+static inline void quic_path_init(struct quic_path *path, int ipv4,
+                                  struct quic_cc_algo *algo, struct quic_conn *qc)
+{
+       unsigned int max_dgram_sz;
+
+       max_dgram_sz = ipv4 ? QUIC_INITIAL_IPV4_MTU : QUIC_INITIAL_IPV6_MTU;
+       quic_loss_init(&path->loss);
+       path->mtu = max_dgram_sz;
+       path->cwnd = QUIC_MIN(10 * max_dgram_sz, QUIC_MAX(max_dgram_sz << 1, 14720U));
+       path->min_cwnd = max_dgram_sz << 1;
+       path->in_flight = 0;
+       path->in_flight_ae_pkts = 0;
+       quic_cc_init(&path->cc, algo, qc);
+}
+
+/* Return 1 if <pktns> matches with the Application packet number space of
+ * <conn> connection which is common to the 0-RTT and 1-RTT encryption levels, 0
+ * if not (handshake packets).
+ */
+static inline int quic_application_pktns(struct quic_pktns *pktns, struct quic_conn *conn)
+{
+       return pktns == &conn->pktns[QUIC_TLS_PKTNS_01RTT];
+}
+
+/* CRYPTO data buffer handling functions. */
+static inline unsigned char *c_buf_getpos(struct quic_enc_level *qel, uint64_t offset)
+{
+       int idx;
+       unsigned char *data;
+
+       idx = offset >> QUIC_CRYPTO_BUF_SHIFT;
+       data = qel->tx.crypto.bufs[idx]->data;
+       return data + (offset & QUIC_CRYPTO_BUF_MASK);
+}
+
+/* Returns 1 if the CRYPTO buffer at <qel> encryption level has been
+ * consumed (sent to the peer), 0 if not.
+ */
+static inline int c_buf_consumed(struct quic_enc_level *qel)
+{
+       return qel->tx.crypto.offset == qel->tx.crypto.sz;
+}
+
+
+/* QUIC buffer handling functions */
+
+/* Returns the current buffer which may be used to build outgoing packets. */
+static inline struct q_buf *q_wbuf(struct quic_conn *qc)
+{
+       return qc->tx.bufs[qc->tx.wbuf];
+}
+
+static inline struct q_buf *q_rbuf(struct quic_conn *qc)
+{
+       return qc->tx.bufs[qc->tx.rbuf];
+}
+
+/* Returns the next buffer to be used to send packets from. */
+static inline struct q_buf *q_next_rbuf(struct quic_conn *qc)
+{
+       qc->tx.rbuf = (qc->tx.rbuf + 1) & (QUIC_CONN_TX_BUFS_NB - 1);
+       return q_rbuf(qc);
+}
+
+/* Return the next buffer which may be used to build outgoing packets.
+ * Also decrement by one the number of remaining probing datagrams
+ * which may be sent.
+ */
+static inline struct q_buf *q_next_wbuf(struct quic_conn *qc)
+{
+       qc->tx.wbuf = (qc->tx.wbuf + 1) & (QUIC_CONN_TX_BUFS_NB - 1);
+       /* Decrement the number of prepared datagrams (only when probing). */
+       if (qc->tx.nb_pto_dgrams)
+               --qc->tx.nb_pto_dgrams;
+       return q_wbuf(qc);
+}
+
+/* Return the position of <buf> buffer to be used to write outgoing packets. */
+static inline unsigned char *q_buf_getpos(struct q_buf *buf)
+{
+       return buf->pos;
+}
+
+/* Return the pointer to one past the end of <buf> buffer. */
+static inline const unsigned char *q_buf_end(struct q_buf *buf)
+{
+       return buf->end;
+}
+
+/* Set the position of <buf> buffer to <pos> value. */
+static inline void q_buf_setpos(struct q_buf *buf, unsigned char *pos)
+{
+       buf->pos = pos;
+}
+
+/* Returns the remaining amount of room left in <buf> buffer. */
+static inline ssize_t q_buf_room(struct q_buf *buf)
+{
+       return q_buf_end(buf) - q_buf_getpos(buf);
+}
+
+/* Reset (or empty) <buf> buffer to prepare it for the next writting. */
+static inline void q_buf_reset(struct q_buf *buf)
+{
+       buf->pos = buf->area;
+       buf->data = 0;
+}
+
+/* Returns 1 if <buf> is empty, 0 if not. */
+static inline int q_buf_empty(struct q_buf *buf)
+{
+       return !buf->data;
+}
+
+/* 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;
+}
+
+/* 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> */
+static inline void quic_rx_packet_refdec(struct quic_rx_packet *pkt)
+{
+       if (!--pkt->refcnt)
+               pool_free(pool_head_quic_rx_packet, pkt);
+}
+
+/* Add <pkt> RX packet to <list>, incrementing its reference counter. */
+static inline void quic_rx_packet_list_addq(struct list *list,
+                                            struct quic_rx_packet *pkt)
+{
+       LIST_ADDQ(list, &pkt->list);
+       quic_rx_packet_refinc(pkt);
+}
+
+/* Remove <pkt> RX packet from <list>, decrementing its reference counter. */
+static inline void quic_rx_packet_list_del(struct quic_rx_packet *pkt)
+{
+       LIST_DEL(&pkt->list);
+       quic_rx_packet_refdec(pkt);
+}
+
+/* Add <pkt> RX packet to <root> tree, incrementing its reference counter. */
+static inline void quic_rx_packet_eb64_insert(struct eb_root *root,
+                                              struct eb64_node *node)
+{
+       eb64_insert(root, node);
+       quic_rx_packet_refinc(eb64_entry(node, struct quic_rx_packet, pn_node));
+}
+
+/* Delete <pkt> RX packet from <root> tree, decrementing its reference counter. */
+static inline void quic_rx_packet_eb64_delete(struct eb64_node *node)
+{
+       eb64_delete(node);
+       quic_rx_packet_refdec(eb64_entry(node, struct quic_rx_packet, pn_node));
+}
+
+/* Release the memory allocated for <pkt> RX packet. */
+static inline void free_quic_rx_packet(struct quic_rx_packet *pkt)
+{
+       quic_rx_packet_refdec(pkt);
+}
+
+int qc_new_conn_init(struct quic_conn *conn, int ipv4,
+                     struct eb_root *quic_initial_clients,
+                     struct eb_root *quic_clients,
+                     unsigned char *dcid, size_t dcid_len,
+                     unsigned char *scid, size_t scid_len);
+ssize_t quic_lstnr_dgram_read(char *buf, size_t len, void *owner,
+                              struct sockaddr_storage *saddr);
+ssize_t quic_srv_dgram_read(char *buf, size_t len, void *owner,
+                            struct sockaddr_storage *saddr);
+#endif /* USE_QUIC */
+#endif /* _HAPROXY_XPRT_QUIC_H */