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 */