MINOR: quic: Add definitions for QUIC protocol.

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.
diff --git a/include/haproxy/quic_cc-t.h b/include/haproxy/quic_cc-t.h
new file mode 100644
index 0000000..2c11011
--- /dev/null
+++ b/include/haproxy/quic_cc-t.h
@@ -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
index 0000000..d250af9
--- /dev/null
+++ b/include/haproxy/quic_cc.h
@@ -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
index 0000000..a2404ef
--- /dev/null
+++ b/include/haproxy/quic_frame-t.h
@@ -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
index 0000000..123f508
--- /dev/null
+++ b/include/haproxy/quic_frame.h
@@ -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
index 0000000..5ca9b3a
--- /dev/null
+++ b/include/haproxy/quic_loss-t.h
@@ -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
index 0000000..ee7649c
--- /dev/null
+++ b/include/haproxy/quic_loss.h
@@ -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 */
diff --git a/include/haproxy/quic_sock.h b/include/haproxy/quic_sock.h
index 78623e8..672255e 100644
--- a/include/haproxy/quic_sock.h
+++ b/include/haproxy/quic_sock.h
@@ -21,6 +21,10 @@
 
 #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 @@
 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
index 0000000..20efc28
--- /dev/null
+++ b/include/haproxy/quic_tls-t.h
@@ -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
index 0000000..94b978b
--- /dev/null
+++ b/include/haproxy/quic_tls.h
@@ -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
index 0000000..b25c3eb
--- /dev/null
+++ b/include/haproxy/xprt_quic-t.h
@@ -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
index 0000000..5b31c67
--- /dev/null
+++ b/include/haproxy/xprt_quic.h
@@ -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 */