/* * include/haproxy/xprt_quic.h * This file contains QUIC xprt function prototypes * * Copyright 2020 HAProxy Technologies, Frédéric Lécaille * * 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 #include #include #include #include #include #include #include #include #include #include #include #include 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 QUIC connection ID. */ static inline size_t sizeof_quic_cid(const struct quic_cid *cid) { return sizeof cid->len + cid->len; } /* Copy QUIC CID to . * This is the responsibility of the caller to check there is enough room in * to copy . * 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 to QUIC connection ID. * Returns the number of bytes concatenated to . */ 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 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 new connection ID information 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 as sequence number and attach it to * ebtree. * Returns the new CID if succeeded, 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 from QUIC packet with as address. * Makes 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 as address. * Make point to the data after this 32-buts value if succeeded. * Note that these 32-bits integers are networkg bytes ordered. * Returns 0 if failed (not enough room in the buffer), 1 if succeeded. */ static inline int quic_write_uint32(unsigned char **buf, const unsigned char *end, uint32_t val) { if (end - *buf < sizeof val) return 0; *(uint32_t *)*buf = htonl(val); *buf += sizeof val; return 1; } /* Returns enough log2 of first powers of two to encode QUIC variable length * integers. * Returns -1 if 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 and the encoded * length of . */ 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 and the encoded * length of . */ 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 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 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> + = . */ 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 >= <*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 as * size prefixed with a variable-length field "Length" whose value is the * remaining data length, already filled of 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. * is the size of the buffer to fill. * the number of bytes already put after the "Length" field. * 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 which match our need. * As we must put bytes in our buffer, the minimum value for * is the number of bytes required to encode . */ 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 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 buffer into . * 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 ), 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 into buffer with 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 packet number depending on * 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 packet number with as length in byte into a buffer with * as current copy address and as pointer to one past the end of * this buffer. This is the responsibility of the caller to check there is * enough room in the buffer to copy 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 field value from ACK frame for * 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 transport parameters from . * 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

transport parameters depending 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 preferred address transport parameter in 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 responsibility of the caller to check there is enough room in 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 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

struct a transport parameter found in <*buf> buffer with * as type and as length, depending on 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 and variable length values in . * 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 and 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 bytes stream with as type and as length into buf. * Returns 1 if succeeded, 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 64-bits value as variable length integer into . * 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 preferred address into . * 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

transport parameter into depending on 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 buffer into

, depending on * 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 buffer into QUIC connection * depending on 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; pktns->rx.arngs.root = EB_ROOT_UNIQUE; pktns->rx.arngs.sz = 0; pktns->rx.arngs.enc_sz = 0; pktns->flags = 0; } /* Discard packet number space attached to 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

QUIC network path depending on 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->prep_in_flight = 0; path->in_flight = 0; path->ifae_pkts = 0; quic_cc_init(&path->cc, algo, qc); } /* Return the remaining available on QUIC path. In fact this this *the remaining number of bytes available in the congestion controller window. */ static inline size_t quic_path_room(struct quic_path *path) { if (path->in_flight > path->cwnd) return 0; return path->cwnd - path->in_flight; } /* Return the remaining available on QUIC path for prepared data * (before being sent). Almost the same that for the QUIC path room, except that * here this is the data which have been prepared which are taken into an account. */ static inline size_t quic_path_prep_data(struct quic_path *path) { if (path->in_flight > path->cwnd) return 0; return path->cwnd - path->prep_in_flight; } /* Return 1 if matches with the Application packet number space of * 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 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 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 buffer. */ static inline const unsigned char *q_buf_end(struct q_buf *buf) { return buf->end; } /* Set the position of buffer to 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 buffer. */ static inline ssize_t q_buf_room(struct q_buf *buf) { return q_buf_end(buf) - q_buf_getpos(buf); } /* Reset (or empty) buffer to prepare it for the next writing. */ static inline void q_buf_reset(struct q_buf *buf) { buf->pos = buf->area; buf->data = 0; } /* Returns 1 if is empty, 0 if not. */ static inline int q_buf_empty(struct q_buf *buf) { return !buf->data; } /* Return 1 if 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 */ static inline void quic_rx_packet_refinc(struct quic_rx_packet *pkt) { pkt->refcnt++; } /* Decrement the reference counter of */ static inline void quic_rx_packet_refdec(struct quic_rx_packet *pkt) { if (!--pkt->refcnt) pool_free(pool_head_quic_rx_packet, pkt); } /* Add RX packet to , 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 RX packet from , 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 RX packet to 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 RX packet from 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 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 */