Commit 35dc6c353b for openssl.org
commit 35dc6c353bfeac6f94ba294cc336e1455d6b4453
Author: Nikolas Gauder <nikolas.gauder@tum.de>
Date: Sat Jan 17 20:56:41 2026 +0100
QUIC: Make more transport parameters configurable
The following QUIC transport parameters are now configurable via
SSL_get_value_uint() / SSL_set_value_uint():
max_udp_payload_size
initial_max_data
initial_max_stream_data_{uni, bidi_local, bidi_remote}
initial_max_streams_{uni, bidi}
ack_delay_exponent
max_ack_delay
disable_active_migration
active_connection_id_limit
Reviewed-by: Neil Horman <nhorman@openssl.org>
Reviewed-by: Saša NedvÄ›dický <sashan@openssl.org>
MergeDate: Tue Mar 17 13:15:29 2026
(Merged from https://github.com/openssl/openssl/pull/29664)
diff --git a/CHANGES.md b/CHANGES.md
index df79d148be..24b68d6d06 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -47,6 +47,14 @@ OpenSSL Releases
*Tomáš Mráz*
+ * Made more QUIC transport parameters configurable via the
+ `SSL_get_value_uint`/`SSL_set_value_uint` functions. Now also configurable:
+ `max_udp_payload_size`, `initial_max_data`,
+ `initial_max_stream_data_bidi_local`, `initial_max_stream_data_uni`,
+ `ack_delay_exponent`, `max_ack_delay`.
+
+ *Nikolas Gauder*
+
### Changes between 3.6 and 4.0 [xx XXX xxxx]
* Added `-expected-rpks` option to the `openssl s_client`
diff --git a/doc/man3/SSL_get_value_uint.pod b/doc/man3/SSL_get_value_uint.pod
index df0ee6176b..c757f1ba25 100644
--- a/doc/man3/SSL_get_value_uint.pod
+++ b/doc/man3/SSL_get_value_uint.pod
@@ -12,6 +12,9 @@ SSL_VALUE_CLASS_FEATURE_REQUEST, SSL_VALUE_CLASS_FEATURE_PEER_REQUEST,
SSL_VALUE_CLASS_FEATURE_NEGOTIATED, SSL_VALUE_QUIC_STREAM_BIDI_LOCAL_AVAIL,
SSL_VALUE_QUIC_STREAM_BIDI_REMOTE_AVAIL, SSL_VALUE_QUIC_STREAM_UNI_LOCAL_AVAIL,
SSL_VALUE_QUIC_STREAM_UNI_REMOTE_AVAIL, SSL_VALUE_QUIC_IDLE_TIMEOUT,
+SSL_VALUE_QUIC_UDP_PAYLOAD_SIZE_MAX, SSL_VALUE_QUIC_WINDOWCON,
+SSL_VALUE_QUIC_WINDOWBSTR, SSL_VALUE_QUIC_WINDOWUSTR,
+SSL_VALUE_QUIC_ACK_DELAY_EXPONENT, SSL_VALUE_QUIC_ACK_DELAY_MAX,
SSL_VALUE_EVENT_HANDLING_MODE,
SSL_VALUE_EVENT_HANDLING_MODE_INHERIT,
SSL_VALUE_EVENT_HANDLING_MODE_EXPLICIT,
@@ -45,6 +48,12 @@ manage negotiable features and configuration values for an SSL object
#define SSL_VALUE_QUIC_STREAM_UNI_LOCAL_AVAIL
#define SSL_VALUE_QUIC_STREAM_UNI_REMOTE_AVAIL
#define SSL_VALUE_QUIC_IDLE_TIMEOUT
+ #define SSL_VALUE_QUIC_UDP_PAYLOAD_SIZE_MAX
+ #define SSL_VALUE_QUIC_WINDOWCON
+ #define SSL_VALUE_QUIC_WINDOWBSTR
+ #define SSL_VALUE_QUIC_WINDOWUSTR
+ #define SSL_VALUE_QUIC_ACK_DELAY_EXPONENT
+ #define SSL_VALUE_QUIC_ACK_DELAY_MAX
#define SSL_VALUE_EVENT_HANDLING_MODE
#define SSL_VALUE_EVENT_HANDLING_MODE_INHERIT
@@ -158,7 +167,7 @@ documentation for a specific value specifies otherwise.
=over 4
-=item B<SSL_VALUE_QUIC_IDLE_TIMEOUT> (connection object)
+=item B<SSL_VALUE_QUIC_IDLE_TIMEOUT> (connection/listener object)
Negotiated feature value. This configures the desired QUIC idle timeout in
milliseconds, where 0 represents a lack of an idle timeout. This feature can
@@ -168,6 +177,65 @@ changed.
This release of OpenSSL uses a default value of 30 seconds. This default value
may change between releases of OpenSSL.
+=item B<SSL_VALUE_QUIC_UDP_PAYLOAD_SIZE_MAX> (connection/listener object)
+
+Feature (peer) request value. This configures the maximum UDP payload size in
+bytes, corresponding to the QUIC max_udp_payload_size transport parameter
+as defined in RFC 9000, Section 18.2. It limits the size of UDP datagrams the
+local QUIC stack is willing to receive from the peer. It does not change the
+maximum datagram payload size used on the transmit path. This feature can only
+be configured prior to connection establishment and cannot be subsequently
+changed. This value will be sent to the peer in the transport parameters.
+
+This release of OpenSSL uses a default value of 1200 bytes. This default value
+may change between releases of OpenSSL.
+
+=item B<SSL_VALUE_QUIC_WINDOWCON> (connection/listener object)
+
+Feature (peer) request value. This configures the initial QUIC connection-level
+flow control receive window in bytes. This feature can only be configured prior
+to connection establishment and cannot be subsequently changed. This value will
+be sent to the peer in the transport parameters.
+
+This release of OpenSSL uses a default value of 768 * 1024 bytes. This default
+value may change between releases of OpenSSL.
+
+=item B<SSL_VALUE_QUIC_WINDOWBSTR> (connection/listener object)
+
+Feature (peer) request value. This sets the initial receive window size, in
+bytes, for QUIC stream flow control on a bidirectional stream created by the
+local endpoint. This feature can only be configured prior to connection
+establishment and cannot be subsequently changed. This value will be sent to the
+peer in the transport parameters.
+
+This release of OpenSSL uses a default value of 512 * 1024 bytes. This default
+value may change between releases of OpenSSL.
+
+=item B<SSL_VALUE_QUIC_WINDOWUSTR> (connection/listener object)
+
+As above, but configures the initial receive window size for QUIC stream flow
+control on a unidirectional stream created by the remote endpoint.
+
+=item B<SSL_VALUE_QUIC_ACK_DELAY_EXPONENT> (connection/listener object)
+
+Feature (peer) request value. This configures the ACK delay exponent. This
+feature can only be configured prior to connection establishment and cannot be
+subsequently changed. This value will be sent to the peer in the transport
+parameters.
+
+This release of OpenSSL uses a default value of 3. This default value may change
+between releases of OpenSSL.
+
+=item B<SSL_VALUE_QUIC_ACK_DELAY_MAX> (connection/listener object)
+
+Feature (peer) request value. This configures the maximum ACK delay in
+milliseconds. This feature can only be configured prior to connection
+establishment and cannot be subsequently changed. This value will be sent to the
+peer in the transport parameters.
+
+This release of OpenSSL uses a default value of 25 milliseconds. This default
+value may change between releases of OpenSSL.
+
=item B<SSL_VALUE_QUIC_STREAM_BIDI_LOCAL_AVAIL> (connection object)
Generic read-only statistical value. The number of bidirectional,
@@ -339,11 +407,16 @@ L<SSL_set_default_stream_mode(3)>, L<SSL_set_incoming_stream_policy(3)>
=head1 HISTORY
-These functions were added in OpenSSL 3.3.
+The values SSL_VALUE_QUIC_UDP_PAYLOAD_SIZE_MAX, SSL_VALUE_QUIC_WINDOWCON,
+SSL_VALUE_QUIC_WINDOWBSTR, SSL_VALUE_QUIC_WINDOWUSTR,
+SSL_VALUE_QUIC_ACK_DELAY_EXPONENT, and SSL_VALUE_QUIC_ACK_DELAY_MAX
+were added in OpenSSL 4.1.
+
+The remaining functions and values described here were all added in OpenSSL 3.3.
=head1 COPYRIGHT
-Copyright 2002-2024 The OpenSSL Project Authors. All Rights Reserved.
+Copyright 2002-2026 The OpenSSL Project Authors. All Rights Reserved.
Licensed under the Apache License 2.0 (the "License"). You may not use
this file except in compliance with the License. You can obtain a copy
diff --git a/include/internal/quic_channel.h b/include/internal/quic_channel.h
index 26b23b1fa2..147642d0fb 100644
--- a/include/internal/quic_channel.h
+++ b/include/internal/quic_channel.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2022-2025 The OpenSSL Project Authors. All Rights Reserved.
+ * Copyright 2022-2026 The OpenSSL Project Authors. All Rights Reserved.
*
* Licensed under the Apache License 2.0 (the "License"). You may not use
* this file except in compliance with the License. You can obtain a copy
@@ -128,6 +128,20 @@ typedef struct quic_channel_args_st {
/* Title to use for the qlog session, or NULL. */
const char *qlog_title;
+
+ /* Transport parameter values for the channel. */
+ uint64_t max_idle_timeout;
+ uint64_t max_udp_payload_size;
+ uint64_t init_max_data;
+ uint64_t init_max_stream_data_bidi_local;
+ uint64_t init_max_stream_data_bidi_remote;
+ uint64_t init_max_stream_data_uni;
+ uint64_t init_max_streams_bidi;
+ uint64_t init_max_streams_uni;
+ uint64_t max_ack_delay;
+ uint64_t active_conn_id_limit;
+ unsigned char ack_delay_exponent;
+ unsigned char disable_active_migration;
} QUIC_CHANNEL_ARGS;
/* Represents the cause for a connection's termination. */
@@ -461,14 +475,70 @@ uint64_t ossl_quic_channel_get_remote_stream_count_avail(const QUIC_CHANNEL *ch,
int ossl_quic_channel_have_generated_transport_params(const QUIC_CHANNEL *ch);
/* Configures the idle timeout to request from peer (milliseconds, 0=no timeout). */
-void ossl_quic_channel_set_max_idle_timeout_request(QUIC_CHANNEL *ch, uint64_t ms);
-/* Get the configured idle timeout to request from peer. */
+int ossl_quic_channel_set_max_idle_timeout_request(QUIC_CHANNEL *ch, uint64_t ms);
+/* Gets the configured idle timeout to request from peer. */
uint64_t ossl_quic_channel_get_max_idle_timeout_request(const QUIC_CHANNEL *ch);
-/* Get the idle timeout requested by the peer. */
+/* Gets the idle timeout requested by the peer. */
uint64_t ossl_quic_channel_get_max_idle_timeout_peer_request(const QUIC_CHANNEL *ch);
-/* Get the idle timeout actually negotiated. */
+/* Gets the idle timeout actually negotiated. */
uint64_t ossl_quic_channel_get_max_idle_timeout_actual(const QUIC_CHANNEL *ch);
+/* Configures the maximum UDP payload size to advertise to the peer (bytes). */
+int ossl_quic_channel_set_max_udp_payload_size_request(QUIC_CHANNEL *ch, uint64_t size);
+/* Gets the configured maximum UDP payload size to advertise to the peer. */
+uint64_t ossl_quic_channel_get_max_udp_payload_size_request(const QUIC_CHANNEL *ch);
+/* Gets the maximum UDP payload size advertised by the peer. */
+uint64_t ossl_quic_channel_get_max_udp_payload_size_peer_request(const QUIC_CHANNEL *ch);
+
+/* Configures the maximum data to advertise to the peer (bytes). */
+int ossl_quic_channel_set_max_data_request(QUIC_CHANNEL *ch, uint64_t max_data);
+/* Gets the configured maximum data to advertise to the peer. */
+uint64_t ossl_quic_channel_get_max_data_request(const QUIC_CHANNEL *ch);
+/* Gets the maximum data advertised by the peer. */
+uint64_t ossl_quic_channel_get_max_data_peer_request(const QUIC_CHANNEL *ch);
+
+/* Configures the maximum stream data for a bidi/uni remote/local stream to advertise to the peer (bytes). */
+int ossl_quic_channel_set_max_stream_data_request(QUIC_CHANNEL *ch, uint64_t max_data, int is_uni, int is_remote);
+/* Gets the configured maximum stream data for a bidi/uni remote/local stream to advertise to the peer. */
+uint64_t ossl_quic_channel_get_max_stream_data_request(const QUIC_CHANNEL *ch, int is_uni, int is_remote);
+/* Gets the maximum stream data for a bidi/uni remote/local stream advertised by the peer. */
+uint64_t ossl_quic_channel_get_max_stream_data_peer_request(const QUIC_CHANNEL *ch, int is_uni, int is_remote);
+
+/* Configures the maximum bidi/uni streams to advertise to the peer. */
+int ossl_quic_channel_set_max_streams_request(QUIC_CHANNEL *ch, uint64_t max_streams, int is_uni);
+/* Gets the configured maximum bidi/uni streams to advertise to the peer. */
+uint64_t ossl_quic_channel_get_max_streams_request(const QUIC_CHANNEL *ch, int is_uni);
+/* Gets the maximum bidi/uni streams advertised by the peer. */
+uint64_t ossl_quic_channel_get_max_streams_peer_request(const QUIC_CHANNEL *ch, int is_uni);
+
+/* Configures the ACK delay exponent to advertise to the peer. */
+int ossl_quic_channel_set_ack_delay_exponent_request(QUIC_CHANNEL *ch, uint64_t exp);
+/* Gets the configured ACK delay exponent to advertise to the peer. */
+uint64_t ossl_quic_channel_get_ack_delay_exponent_request(const QUIC_CHANNEL *ch);
+/* Gets the ACK delay exponent advertised by the peer. */
+uint64_t ossl_quic_channel_get_ack_delay_exponent_peer_request(const QUIC_CHANNEL *ch);
+
+/* Configures the maximum ACK delay to advertise to the peer (milliseconds). */
+int ossl_quic_channel_set_max_ack_delay_request(QUIC_CHANNEL *ch, uint64_t ms);
+/* Gets the configured maximum ACK delay to advertise to the peer. */
+uint64_t ossl_quic_channel_get_max_ack_delay_request(const QUIC_CHANNEL *ch);
+/* Gets the maximum ACK delay advertised by the peer. */
+uint64_t ossl_quic_channel_get_max_ack_delay_peer_request(const QUIC_CHANNEL *ch);
+
+/* Configures the disable active migration flag to advertise to the peer. */
+int ossl_quic_channel_set_disable_active_migration_request(QUIC_CHANNEL *ch, uint64_t disable);
+/* Gets the configured disable active migration flag to advertise to the peer. */
+uint64_t ossl_quic_channel_get_disable_active_migration_request(const QUIC_CHANNEL *ch);
+/* Gets the disable active migration flag advertised by the peer. */
+uint64_t ossl_quic_channel_get_disable_active_migration_peer_request(const QUIC_CHANNEL *ch);
+
+/* Configures the active connection ID limit to advertise to the peer. */
+int ossl_quic_channel_set_active_conn_id_limit_request(QUIC_CHANNEL *ch, uint64_t limit);
+/* Gets the configured active connection ID limit to advertise to the peer. */
+uint64_t ossl_quic_channel_get_active_conn_id_limit_request(const QUIC_CHANNEL *ch);
+/* Gets the active connection ID limit advertised by the peer. */
+uint64_t ossl_quic_channel_get_active_conn_id_limit_peer_request(const QUIC_CHANNEL *ch);
+
int ossl_quic_bind_channel(QUIC_CHANNEL *ch, const BIO_ADDR *peer,
const QUIC_CONN_ID *scid, const QUIC_CONN_ID *dcid,
const QUIC_CONN_ID *odcid);
diff --git a/include/internal/quic_port.h b/include/internal/quic_port.h
index ad3ba738ae..5ddbdd4cd1 100644
--- a/include/internal/quic_port.h
+++ b/include/internal/quic_port.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2023-2025 The OpenSSL Project Authors. All Rights Reserved.
+ * Copyright 2023-2026 The OpenSSL Project Authors. All Rights Reserved.
*
* Licensed under the Apache License 2.0 (the "License"). You may not use
* this file except in compliance with the License. You can obtain a copy
@@ -140,6 +140,51 @@ OSSL_TIME ossl_quic_port_get_time(QUIC_PORT *port);
int ossl_quic_port_get_rx_short_dcid_len(const QUIC_PORT *port);
int ossl_quic_port_get_tx_init_dcid_len(const QUIC_PORT *port);
+/* Configures the idle timeout to request from peer (milliseconds, 0=no timeout). */
+void ossl_quic_port_set_max_idle_timeout(QUIC_PORT *port, uint64_t ms);
+/* Gets the configured idle timeout to request from peer. */
+uint64_t ossl_quic_port_get_max_idle_timeout(const QUIC_PORT *port);
+
+/* Configures the maximum UDP payload size to advertise to the peer (bytes). */
+void ossl_quic_port_set_max_udp_payload_size(QUIC_PORT *port, uint64_t size);
+/* Gets the configured maximum UDP payload size to advertise to the peer. */
+uint64_t ossl_quic_port_get_max_udp_payload_size(const QUIC_PORT *port);
+
+/* Configures the maximum data to advertise to the peer (bytes). */
+void ossl_quic_port_set_init_max_data(QUIC_PORT *port, uint64_t max_data);
+/* Gets the configured maximum data to advertise to the peer. */
+uint64_t ossl_quic_port_get_init_max_data(const QUIC_PORT *port);
+
+/* Configures the maximum stream data for a bidi/uni remote/local stream to advertise to the peer (bytes). */
+void ossl_quic_port_set_init_max_stream_data(QUIC_PORT *port, uint64_t max_data, int is_uni, int is_remote);
+/* Gets the configured maximum stream data for a bidi/uni remote/local stream to advertise to the peer. */
+uint64_t ossl_quic_port_get_init_max_stream_data(const QUIC_PORT *port, int is_uni, int is_remote);
+
+/* Configures the maximum bidi/uni streams to advertise to the peer. */
+void ossl_quic_port_set_init_max_streams(QUIC_PORT *port, uint64_t max_streams, int is_uni);
+/* Gets the configured maximum bidi/uni streams to advertise to the peer. */
+uint64_t ossl_quic_port_get_init_max_streams(const QUIC_PORT *port, int is_uni);
+
+/* Configures the ACK delay exponent to advertise to the peer. */
+void ossl_quic_port_set_ack_delay_exponent(QUIC_PORT *port, uint64_t exp);
+/* Gets the configured ACK delay exponent to advertise to the peer. */
+uint64_t ossl_quic_port_get_ack_delay_exponent(const QUIC_PORT *port);
+
+/* Configures the maximum ACK delay to advertise to the peer (milliseconds). */
+void ossl_quic_port_set_max_ack_delay(QUIC_PORT *port, uint64_t ms);
+/* Gets the configured maximum ACK delay to advertise to the peer. */
+uint64_t ossl_quic_port_get_max_ack_delay(const QUIC_PORT *port);
+
+/* Configures the disable active migration flag to advertise to the peer. */
+void ossl_quic_port_set_disable_active_migration(QUIC_PORT *port, uint64_t disable);
+/* Gets the configured disable active migration flag to advertise to the peer. */
+uint64_t ossl_quic_port_get_disable_active_migration(const QUIC_PORT *port);
+
+/* Configures the active connection ID limit to advertise to the peer. */
+void ossl_quic_port_set_active_conn_id_limit(QUIC_PORT *port, uint64_t limit);
+/* Gets the configured active connection ID limit to advertise to the peer. */
+uint64_t ossl_quic_port_get_active_conn_id_limit(const QUIC_PORT *port);
+
/* Returns 1 if the port is running/healthy, 0 if it has failed. */
int ossl_quic_port_is_running(const QUIC_PORT *port);
diff --git a/include/internal/quic_record_tx.h b/include/internal/quic_record_tx.h
index b2be046f8c..d983d8bc79 100644
--- a/include/internal/quic_record_tx.h
+++ b/include/internal/quic_record_tx.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2022-2024 The OpenSSL Project Authors. All Rights Reserved.
+ * Copyright 2022-2026 The OpenSSL Project Authors. All Rights Reserved.
*
* Licensed under the Apache License 2.0 (the "License"). You may not use
* this file except in compliance with the License. You can obtain a copy
@@ -306,6 +306,9 @@ size_t ossl_qtx_get_unflushed_pkt_count(OSSL_QTX *qtx);
*/
void ossl_qtx_set_bio(OSSL_QTX *qtx, BIO *bio);
+/* Changes the MTU in bytes we use to send datagrams. */
+int ossl_qtx_set_mtu(OSSL_QTX *qtx, unsigned int mtu);
+
/* Changes the MDPL. */
int ossl_qtx_set_mdpl(OSSL_QTX *qtx, size_t mdpl);
diff --git a/include/internal/quic_txp.h b/include/internal/quic_txp.h
index d94a79620d..ef1faff547 100644
--- a/include/internal/quic_txp.h
+++ b/include/internal/quic_txp.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2022-2025 The OpenSSL Project Authors. All Rights Reserved.
+ * Copyright 2022-2026 The OpenSSL Project Authors. All Rights Reserved.
*
* Licensed under the Apache License 2.0 (the "License"). You may not use
* this file except in compliance with the License. You can obtain a copy
@@ -155,6 +155,10 @@ int ossl_quic_tx_packetiser_set_cur_scid(OSSL_QUIC_TX_PACKETISER *txp,
int ossl_quic_tx_packetiser_set_peer(OSSL_QUIC_TX_PACKETISER *txp,
const BIO_ADDR *peer);
+/* Change the ACK delay exponent the TXP uses to encode ACK frames. */
+int ossl_quic_tx_packetiser_set_ack_delay_exponent(OSSL_QUIC_TX_PACKETISER *txp,
+ uint32_t exp);
+
/*
* Change the QLOG instance retrieval function in use after instantiation.
*/
diff --git a/include/internal/quic_types.h b/include/internal/quic_types.h
index 8636912cd9..944b430ae9 100644
--- a/include/internal/quic_types.h
+++ b/include/internal/quic_types.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2022-2025 The OpenSSL Project Authors. All Rights Reserved.
+ * Copyright 2022-2026 The OpenSSL Project Authors. All Rights Reserved.
*
* Licensed under the Apache License 2.0 (the "License"). You may not use
* this file except in compliance with the License. You can obtain a copy
@@ -96,16 +96,17 @@ int ossl_quic_gen_rand_conn_id(OSSL_LIB_CTX *libctx, size_t len,
#define QUIC_MIN_INITIAL_DGRAM_LEN 1200
+#define QUIC_MAX_MAX_UDP_PAYLOAD_SIZE 65527 /* RFC 9000 s. 18.2 */
+#define QUIC_DEFAULT_MAX_UDP_PAYLOAD_SIZE QUIC_MAX_MAX_UDP_PAYLOAD_SIZE
+
#define QUIC_DEFAULT_ACK_DELAY_EXP 3
#define QUIC_MAX_ACK_DELAY_EXP 20
#define QUIC_DEFAULT_MAX_ACK_DELAY 25
+#define QUIC_MAX_MAX_ACK_DELAY 16384
#define QUIC_MIN_ACTIVE_CONN_ID_LIMIT 2
-/* Arbitrary choice of default idle timeout (not an RFC value). */
-#define QUIC_DEFAULT_IDLE_TIMEOUT 30000
-
#define QUIC_STATELESS_RESET_TOKEN_LEN 16
typedef struct {
diff --git a/include/openssl/ssl.h.in b/include/openssl/ssl.h.in
index 95640d1d2c..d7915db1aa 100644
--- a/include/openssl/ssl.h.in
+++ b/include/openssl/ssl.h.in
@@ -2437,6 +2437,12 @@ __owur int SSL_get_conn_close_info(SSL *ssl,
#define SSL_VALUE_STREAM_WRITE_BUF_SIZE 7
#define SSL_VALUE_STREAM_WRITE_BUF_USED 8
#define SSL_VALUE_STREAM_WRITE_BUF_AVAIL 9
+#define SSL_VALUE_QUIC_UDP_PAYLOAD_SIZE_MAX 10
+#define SSL_VALUE_QUIC_WINDOWCON 11
+#define SSL_VALUE_QUIC_WINDOWBSTR 12
+#define SSL_VALUE_QUIC_WINDOWUSTR 13
+#define SSL_VALUE_QUIC_ACK_DELAY_EXPONENT 14
+#define SSL_VALUE_QUIC_ACK_DELAY_MAX 15
#define SSL_VALUE_EVENT_HANDLING_MODE_INHERIT 0
#define SSL_VALUE_EVENT_HANDLING_MODE_IMPLICIT 1
diff --git a/ssl/quic/quic_channel.c b/ssl/quic/quic_channel.c
index 6e6e55a870..8727202793 100644
--- a/ssl/quic/quic_channel.c
+++ b/ssl/quic/quic_channel.c
@@ -35,14 +35,6 @@
*/
#define MAX_NAT_INTERVAL (ossl_ms2time(25000))
-/*
- * Our maximum ACK delay on the TX side. This is up to us to choose. Note that
- * this could differ from QUIC_DEFAULT_MAX_DELAY in future as that is a protocol
- * value which determines the value of the maximum ACK delay if the
- * max_ack_delay transport parameter is not set.
- */
-#define DEFAULT_MAX_ACK_DELAY QUIC_DEFAULT_MAX_ACK_DELAY
-
DEFINE_LIST_OF_IMPL(ch, QUIC_CHANNEL);
static void ch_save_err_state(QUIC_CHANNEL *ch);
@@ -146,14 +138,10 @@ static QLOG *ch_get_qlog_cb(void *arg)
* QUIC Channel Initialization and Teardown
* ========================================
*/
-#define DEFAULT_INIT_CONN_RXFC_WND (768 * 1024)
#define DEFAULT_CONN_RXFC_MAX_WND_MUL 20
-#define DEFAULT_INIT_STREAM_RXFC_WND (512 * 1024)
#define DEFAULT_STREAM_RXFC_MAX_WND_MUL 12
-#define DEFAULT_INIT_CONN_MAX_STREAMS 100
-
static int ch_init(QUIC_CHANNEL *ch)
{
OSSL_QUIC_TX_PACKETISER_ARGS txp_args = { 0 };
@@ -200,20 +188,6 @@ static int ch_init(QUIC_CHANNEL *ch)
if (!ossl_quic_txfc_init(&ch->conn_txfc, NULL))
goto err;
- /*
- * Note: The TP we transmit governs what the peer can transmit and thus
- * applies to the RXFC.
- */
- ch->tx_init_max_stream_data_bidi_local = DEFAULT_INIT_STREAM_RXFC_WND;
- ch->tx_init_max_stream_data_bidi_remote = DEFAULT_INIT_STREAM_RXFC_WND;
- ch->tx_init_max_stream_data_uni = DEFAULT_INIT_STREAM_RXFC_WND;
-
- if (!ossl_quic_rxfc_init(&ch->conn_rxfc, NULL,
- DEFAULT_INIT_CONN_RXFC_WND,
- DEFAULT_CONN_RXFC_MAX_WND_MUL * DEFAULT_INIT_CONN_RXFC_WND,
- get_time, ch))
- goto err;
-
for (pn_space = QUIC_PN_SPACE_INITIAL; pn_space < QUIC_PN_SPACE_NUM; ++pn_space)
if (!ossl_quic_rxfc_init_standalone(&ch->crypto_rxfc[pn_space],
INIT_CRYPTO_RECV_BUF_LEN,
@@ -221,12 +195,12 @@ static int ch_init(QUIC_CHANNEL *ch)
goto err;
if (!ossl_quic_rxfc_init_standalone(&ch->max_streams_bidi_rxfc,
- DEFAULT_INIT_CONN_MAX_STREAMS,
+ ch->tx_init_max_streams_bidi,
get_time, ch))
goto err;
if (!ossl_quic_rxfc_init_standalone(&ch->max_streams_uni_rxfc,
- DEFAULT_INIT_CONN_MAX_STREAMS,
+ ch->tx_init_max_streams_uni,
get_time, ch))
goto err;
@@ -256,9 +230,11 @@ static int ch_init(QUIC_CHANNEL *ch)
&& !ossl_quic_lcidm_generate_initial(ch->lcidm, ch, &ch->init_scid))
goto err;
+ ch->rx_ack_delay_exp = QUIC_DEFAULT_ACK_DELAY_EXP;
+
txp_args.cur_scid = ch->init_scid;
txp_args.cur_dcid = ch->init_dcid;
- txp_args.ack_delay_exponent = 3;
+ txp_args.ack_delay_exponent = ch->tx_ack_delay_exp;
txp_args.qtx = ch->qtx;
txp_args.txpim = ch->txpim;
txp_args.cfq = ch->cfq;
@@ -360,16 +336,12 @@ static int ch_init(QUIC_CHANNEL *ch)
if ((ch->qtls = ossl_quic_tls_new(&tls_args)) == NULL)
goto err;
- ch->tx_max_ack_delay = DEFAULT_MAX_ACK_DELAY;
ch->rx_max_ack_delay = QUIC_DEFAULT_MAX_ACK_DELAY;
- ch->rx_ack_delay_exp = QUIC_DEFAULT_ACK_DELAY_EXP;
ch->rx_active_conn_id_limit = QUIC_MIN_ACTIVE_CONN_ID_LIMIT;
ch->tx_enc_level = QUIC_ENC_LEVEL_INITIAL;
ch->rx_enc_level = QUIC_ENC_LEVEL_INITIAL;
ch->txku_threshold_override = UINT64_MAX;
- ch->max_idle_timeout_local_req = QUIC_DEFAULT_IDLE_TIMEOUT;
- ch->max_idle_timeout_remote_req = 0;
ch->max_idle_timeout = ch->max_idle_timeout_local_req;
ossl_ackm_set_tx_max_ack_delay(ch->ackm, ossl_ms2time(ch->tx_max_ack_delay));
@@ -485,6 +457,25 @@ QUIC_CHANNEL *ossl_quic_channel_alloc(const QUIC_CHANNEL_ARGS *args)
}
#endif
+ ch->max_idle_timeout_local_req = args->max_idle_timeout;
+ ch->tx_max_udp_payload_size = args->max_udp_payload_size;
+ ch->tx_init_max_data = args->init_max_data;
+ ch->tx_init_max_stream_data_bidi_local = args->init_max_stream_data_bidi_local;
+ ch->tx_init_max_stream_data_bidi_remote = args->init_max_stream_data_bidi_remote;
+ ch->tx_init_max_stream_data_uni = args->init_max_stream_data_uni;
+ ch->tx_init_max_streams_bidi = args->init_max_streams_bidi;
+ ch->tx_init_max_streams_uni = args->init_max_streams_uni;
+ ch->tx_ack_delay_exp = args->ack_delay_exponent;
+ ch->tx_max_ack_delay = args->max_ack_delay;
+ ch->tx_disable_active_migration = args->disable_active_migration;
+ ch->tx_active_conn_id_limit = args->active_conn_id_limit;
+
+ if (!ossl_quic_rxfc_init(&ch->conn_rxfc, NULL,
+ ch->tx_init_max_data,
+ DEFAULT_CONN_RXFC_MAX_WND_MUL * ch->tx_init_max_data,
+ get_time, ch))
+ return NULL;
+
return ch;
}
@@ -1380,7 +1371,6 @@ static int ch_on_transport_params(const unsigned char *params,
int got_disable_active_migration = 0;
QUIC_CONN_ID cid;
const char *reason = "bad transport parameter";
- ossl_unused uint64_t rx_max_idle_timeout = 0;
ossl_unused const void *stateless_reset_token_p = NULL;
QUIC_PREFERRED_ADDR pfa;
SSL_CONNECTION *sc = SSL_CONNECTION_FROM_SSL(ch->tls);
@@ -1506,6 +1496,8 @@ static int ch_on_transport_params(const unsigned char *params,
goto malformed;
}
+ ch->rx_init_max_data = v;
+
ossl_quic_txfc_bump_cwm(&ch->conn_txfc, v);
got_initial_max_data = 1;
break;
@@ -1624,6 +1616,7 @@ static int ch_on_transport_params(const unsigned char *params,
assert(ch->max_local_streams_bidi == 0);
ch->max_local_streams_bidi = v;
+ ch->rx_init_max_streams_bidi = v;
got_initial_max_streams_bidi = 1;
break;
@@ -1642,6 +1635,7 @@ static int ch_on_transport_params(const unsigned char *params,
assert(ch->max_local_streams_uni == 0);
ch->max_local_streams_uni = v;
+ ch->rx_init_max_streams_uni = v;
got_initial_max_streams_uni = 1;
break;
@@ -1664,7 +1658,6 @@ static int ch_on_transport_params(const unsigned char *params,
ch_update_idle(ch);
got_max_idle_timeout = 1;
- rx_max_idle_timeout = v;
break;
case QUIC_TPARAM_MAX_UDP_PAYLOAD_SIZE:
@@ -1675,7 +1668,8 @@ static int ch_on_transport_params(const unsigned char *params,
}
if (!ossl_quic_wire_decode_transport_param_int(&pkt, &id, &v)
- || v < QUIC_MIN_INITIAL_DGRAM_LEN) {
+ || v < QUIC_MIN_INITIAL_DGRAM_LEN
+ || v > QUIC_MAX_MAX_UDP_PAYLOAD_SIZE) {
reason = TP_REASON_MALFORMED("MAX_UDP_PAYLOAD_SIZE");
goto malformed;
}
@@ -1783,6 +1777,7 @@ static int ch_on_transport_params(const unsigned char *params,
goto malformed;
}
+ ch->rx_disable_active_migration = 1;
got_disable_active_migration = 1;
break;
@@ -1851,10 +1846,10 @@ static int ch_on_transport_params(const unsigned char *params,
ch->rx_init_max_stream_data_uni);
if (got_initial_max_streams_bidi)
QLOG_U64("initial_max_streams_bidi",
- ch->max_local_streams_bidi);
+ ch->rx_init_max_streams_bidi);
if (got_initial_max_streams_uni)
QLOG_U64("initial_max_streams_uni",
- ch->max_local_streams_uni);
+ ch->rx_init_max_streams_uni);
if (got_ack_delay_exp)
QLOG_U64("ack_delay_exponent", ch->rx_ack_delay_exp);
if (got_max_ack_delay)
@@ -1862,7 +1857,7 @@ static int ch_on_transport_params(const unsigned char *params,
if (got_max_udp_payload_size)
QLOG_U64("max_udp_payload_size", ch->rx_max_udp_payload_size);
if (got_max_idle_timeout)
- QLOG_U64("max_idle_timeout", rx_max_idle_timeout);
+ QLOG_U64("max_idle_timeout", ch->max_idle_timeout_remote_req);
if (got_active_conn_id_limit)
QLOG_U64("active_connection_id_limit", ch->rx_active_conn_id_limit);
if (got_stateless_reset_token)
@@ -1879,11 +1874,12 @@ static int ch_on_transport_params(const unsigned char *params,
QLOG_CID("connection_id", &pfa.cid);
QLOG_END()
}
- QLOG_BOOL("disable_active_migration", got_disable_active_migration);
+ QLOG_BOOL("disable_active_migration", ch->rx_disable_active_migration);
QLOG_EVENT_END()
#endif
- if (got_initial_max_data || got_initial_max_stream_data_bidi_remote
+ if (got_initial_max_data
+ || got_initial_max_stream_data_bidi_remote || got_initial_max_stream_data_uni
|| got_initial_max_streams_bidi || got_initial_max_streams_uni)
/*
* If FC credit was bumped, we may now be able to send. Update all
@@ -1945,9 +1941,7 @@ static int ch_generate_transport_params(QUIC_CHANNEL *ch)
wpkt_valid = 1;
- if (ossl_quic_wire_encode_transport_param_bytes(&wpkt, QUIC_TPARAM_DISABLE_ACTIVE_MIGRATION,
- NULL, 0)
- == NULL)
+ if (ch->tx_disable_active_migration != 0 && ossl_quic_wire_encode_transport_param_bytes(&wpkt, QUIC_TPARAM_DISABLE_ACTIVE_MIGRATION, NULL, 0) == NULL)
goto err;
if (ch->is_server) {
@@ -1974,11 +1968,16 @@ static int ch_generate_transport_params(QUIC_CHANNEL *ch)
goto err;
if (!ossl_quic_wire_encode_transport_param_int(&wpkt, QUIC_TPARAM_MAX_UDP_PAYLOAD_SIZE,
- QUIC_MIN_INITIAL_DGRAM_LEN))
+ ch->tx_max_udp_payload_size))
goto err;
if (!ossl_quic_wire_encode_transport_param_int(&wpkt, QUIC_TPARAM_ACTIVE_CONN_ID_LIMIT,
- QUIC_MIN_ACTIVE_CONN_ID_LIMIT))
+ ch->tx_active_conn_id_limit))
+ goto err;
+
+ if (ch->tx_ack_delay_exp != QUIC_DEFAULT_ACK_DELAY_EXP
+ && !ossl_quic_wire_encode_transport_param_int(&wpkt, QUIC_TPARAM_ACK_DELAY_EXP,
+ ch->tx_ack_delay_exp))
goto err;
if (ch->tx_max_ack_delay != QUIC_DEFAULT_MAX_ACK_DELAY
@@ -2029,17 +2028,20 @@ static int ch_generate_transport_params(QUIC_CHANNEL *ch)
#ifndef OPENSSL_NO_QLOG
QLOG_EVENT_BEGIN(ch_get_qlog(ch), transport, parameters_set)
QLOG_STR("owner", "local");
- QLOG_BOOL("disable_active_migration", 1);
+ QLOG_BOOL("disable_active_migration", ch->tx_disable_active_migration);
if (ch->is_server) {
QLOG_CID("original_destination_connection_id", &ch->init_dcid);
QLOG_CID("initial_source_connection_id", &ch->cur_local_cid);
} else {
QLOG_STR("initial_source_connection_id", "");
}
- QLOG_U64("max_idle_timeout", ch->max_idle_timeout);
- QLOG_U64("max_udp_payload_size", QUIC_MIN_INITIAL_DGRAM_LEN);
- QLOG_U64("active_connection_id_limit", QUIC_MIN_ACTIVE_CONN_ID_LIMIT);
- QLOG_U64("max_ack_delay", ch->tx_max_ack_delay);
+ QLOG_U64("max_idle_timeout", ch->max_idle_timeout_local_req);
+ QLOG_U64("max_udp_payload_size", ch->tx_max_udp_payload_size);
+ QLOG_U64("active_connection_id_limit", ch->tx_active_conn_id_limit);
+ if (ch->tx_ack_delay_exp != QUIC_DEFAULT_ACK_DELAY_EXP)
+ QLOG_U64("ack_delay_exponent", ch->tx_ack_delay_exp);
+ if (ch->tx_max_ack_delay != QUIC_DEFAULT_MAX_ACK_DELAY)
+ QLOG_U64("max_ack_delay", ch->tx_max_ack_delay);
QLOG_U64("initial_max_data", ossl_quic_rxfc_get_cwm(&ch->conn_rxfc));
QLOG_U64("initial_max_stream_data_bidi_local",
ch->tx_init_max_stream_data_bidi_local);
@@ -4095,10 +4097,15 @@ int ossl_quic_channel_have_generated_transport_params(const QUIC_CHANNEL *ch)
return ch->got_local_transport_params;
}
-void ossl_quic_channel_set_max_idle_timeout_request(QUIC_CHANNEL *ch, uint64_t ms)
+int ossl_quic_channel_set_max_idle_timeout_request(QUIC_CHANNEL *ch, uint64_t ms)
{
+ if (ossl_quic_channel_have_generated_transport_params(ch))
+ return 0;
+
ch->max_idle_timeout_local_req = ms;
+ return 1;
}
+
uint64_t ossl_quic_channel_get_max_idle_timeout_request(const QUIC_CHANNEL *ch)
{
return ch->max_idle_timeout_local_req;
@@ -4113,3 +4120,181 @@ uint64_t ossl_quic_channel_get_max_idle_timeout_actual(const QUIC_CHANNEL *ch)
{
return ch->max_idle_timeout;
}
+
+int ossl_quic_channel_set_max_udp_payload_size_request(QUIC_CHANNEL *ch, uint64_t size)
+{
+ if (ossl_quic_channel_have_generated_transport_params(ch))
+ return 0;
+
+ ch->tx_max_udp_payload_size = size;
+ return 1;
+}
+
+uint64_t ossl_quic_channel_get_max_udp_payload_size_request(const QUIC_CHANNEL *ch)
+{
+ return ch->tx_max_udp_payload_size;
+}
+
+uint64_t ossl_quic_channel_get_max_udp_payload_size_peer_request(const QUIC_CHANNEL *ch)
+{
+ return ch->rx_max_udp_payload_size;
+}
+
+int ossl_quic_channel_set_max_data_request(QUIC_CHANNEL *ch, uint64_t max_data)
+{
+ if (ossl_quic_channel_have_generated_transport_params(ch))
+ return 0;
+
+ ch->tx_init_max_data = max_data;
+ ossl_quic_rxfc_init(&ch->conn_rxfc, NULL,
+ max_data, DEFAULT_CONN_RXFC_MAX_WND_MUL * max_data, get_time, ch);
+ return 1;
+}
+
+uint64_t ossl_quic_channel_get_max_data_request(const QUIC_CHANNEL *ch)
+{
+ return ch->tx_init_max_data;
+}
+
+uint64_t ossl_quic_channel_get_max_data_peer_request(const QUIC_CHANNEL *ch)
+{
+ return ch->rx_init_max_data;
+}
+
+int ossl_quic_channel_set_max_stream_data_request(QUIC_CHANNEL *ch, uint64_t max_data, int is_uni, int is_remote)
+{
+ if (ossl_quic_channel_have_generated_transport_params(ch))
+ return 0;
+
+ /* no need to update fc here since no stream is created yet */
+ if (is_uni) {
+ ch->tx_init_max_stream_data_uni = max_data;
+ } else {
+ if (is_remote)
+ ch->tx_init_max_stream_data_bidi_remote = max_data;
+ else
+ ch->tx_init_max_stream_data_bidi_local = max_data;
+ }
+
+ return 1;
+}
+
+uint64_t ossl_quic_channel_get_max_stream_data_request(const QUIC_CHANNEL *ch, int is_uni, int is_remote)
+{
+ if (is_uni)
+ return ch->tx_init_max_stream_data_uni;
+ else
+ return is_remote ? ch->tx_init_max_stream_data_bidi_remote : ch->tx_init_max_stream_data_bidi_local;
+}
+
+uint64_t ossl_quic_channel_get_max_stream_data_peer_request(const QUIC_CHANNEL *ch, int is_uni, int is_remote)
+{
+ if (is_uni)
+ return ch->rx_init_max_stream_data_uni;
+ else
+ return is_remote ? ch->rx_init_max_stream_data_bidi_remote : ch->rx_init_max_stream_data_bidi_local;
+}
+
+int ossl_quic_channel_set_max_streams_request(QUIC_CHANNEL *ch, uint64_t max_streams, int is_uni)
+{
+ if (ossl_quic_channel_have_generated_transport_params(ch))
+ return 0;
+
+ if (is_uni) {
+ ch->tx_init_max_streams_uni = max_streams;
+ ossl_quic_rxfc_init_standalone(&ch->max_streams_uni_rxfc, max_streams, get_time, ch);
+ } else {
+ ch->tx_init_max_streams_bidi = max_streams;
+ ossl_quic_rxfc_init_standalone(&ch->max_streams_bidi_rxfc, max_streams, get_time, ch);
+ }
+
+ return 1;
+}
+
+uint64_t ossl_quic_channel_get_max_streams_request(const QUIC_CHANNEL *ch, int is_uni)
+{
+ return is_uni ? ch->tx_init_max_streams_uni : ch->tx_init_max_streams_bidi;
+}
+
+uint64_t ossl_quic_channel_get_max_streams_peer_request(const QUIC_CHANNEL *ch, int is_uni)
+{
+ return is_uni ? ch->rx_init_max_streams_uni : ch->rx_init_max_streams_bidi;
+}
+
+int ossl_quic_channel_set_ack_delay_exponent_request(QUIC_CHANNEL *ch, uint64_t exp)
+{
+ if (ossl_quic_channel_have_generated_transport_params(ch))
+ return 0;
+
+ ch->tx_ack_delay_exp = (unsigned char)exp;
+ ossl_quic_tx_packetiser_set_ack_delay_exponent(ch->txp, (uint32_t)exp);
+ return 1;
+}
+
+uint64_t ossl_quic_channel_get_ack_delay_exponent_request(const QUIC_CHANNEL *ch)
+{
+ return ch->tx_ack_delay_exp;
+}
+
+uint64_t ossl_quic_channel_get_ack_delay_exponent_peer_request(const QUIC_CHANNEL *ch)
+{
+ return ch->rx_ack_delay_exp;
+}
+
+int ossl_quic_channel_set_max_ack_delay_request(QUIC_CHANNEL *ch, uint64_t ms)
+{
+ if (ossl_quic_channel_have_generated_transport_params(ch))
+ return 0;
+
+ ch->tx_max_ack_delay = ms;
+ ossl_ackm_set_tx_max_ack_delay(ch->ackm, ossl_ms2time(ch->tx_max_ack_delay));
+ return 1;
+}
+
+uint64_t ossl_quic_channel_get_max_ack_delay_request(const QUIC_CHANNEL *ch)
+{
+ return ch->tx_max_ack_delay;
+}
+
+uint64_t ossl_quic_channel_get_max_ack_delay_peer_request(const QUIC_CHANNEL *ch)
+{
+ return ch->rx_max_ack_delay;
+}
+
+int ossl_quic_channel_set_disable_active_migration_request(QUIC_CHANNEL *ch, uint64_t disable)
+{
+ if (ossl_quic_channel_have_generated_transport_params(ch))
+ return 0;
+
+ ch->tx_disable_active_migration = (unsigned char)disable;
+ return 1;
+}
+
+uint64_t ossl_quic_channel_get_disable_active_migration_request(const QUIC_CHANNEL *ch)
+{
+ return ch->tx_disable_active_migration;
+}
+
+uint64_t ossl_quic_channel_get_disable_active_migration_peer_request(const QUIC_CHANNEL *ch)
+{
+ return ch->rx_disable_active_migration;
+}
+
+int ossl_quic_channel_set_active_conn_id_limit_request(QUIC_CHANNEL *ch, uint64_t limit)
+{
+ if (ossl_quic_channel_have_generated_transport_params(ch))
+ return 0;
+
+ ch->tx_active_conn_id_limit = limit;
+ return 1;
+}
+
+uint64_t ossl_quic_channel_get_active_conn_id_limit_request(const QUIC_CHANNEL *ch)
+{
+ return ch->tx_active_conn_id_limit;
+}
+
+uint64_t ossl_quic_channel_get_active_conn_id_limit_peer_request(const QUIC_CHANNEL *ch)
+{
+ return ch->rx_active_conn_id_limit;
+}
diff --git a/ssl/quic/quic_channel_local.h b/ssl/quic/quic_channel_local.h
index ae443fccca..6dea6fdf1b 100644
--- a/ssl/quic/quic_channel_local.h
+++ b/ssl/quic/quic_channel_local.h
@@ -1,3 +1,12 @@
+/*
+ * Copyright 2023-2026 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
#ifndef OSSL_QUIC_CHANNEL_LOCAL_H
#define OSSL_QUIC_CHANNEL_LOCAL_H
@@ -148,17 +157,27 @@ struct quic_channel_st {
uint64_t cur_retire_prior_to;
/* Transport parameter values we send to our peer. */
+ uint64_t tx_init_max_data;
uint64_t tx_init_max_stream_data_bidi_local;
uint64_t tx_init_max_stream_data_bidi_remote;
uint64_t tx_init_max_stream_data_uni;
+ uint64_t tx_init_max_streams_bidi;
+ uint64_t tx_init_max_streams_uni;
uint64_t tx_max_ack_delay; /* ms */
+ unsigned char tx_ack_delay_exp;
+ unsigned char tx_disable_active_migration;
+ uint64_t tx_active_conn_id_limit;
- /* Transport parameter values received from server. */
+ /* Transport parameter values received from peer. */
+ uint64_t rx_init_max_data;
uint64_t rx_init_max_stream_data_bidi_local;
uint64_t rx_init_max_stream_data_bidi_remote;
uint64_t rx_init_max_stream_data_uni;
+ uint64_t rx_init_max_streams_bidi;
+ uint64_t rx_init_max_streams_uni;
uint64_t rx_max_ack_delay; /* ms */
unsigned char rx_ack_delay_exp;
+ unsigned char rx_disable_active_migration;
/* Diagnostic counters for testing purposes only. May roll over. */
uint16_t diag_num_rx_ack; /* Number of ACK frames received */
@@ -188,6 +207,11 @@ struct quic_channel_st {
* negotiated by transport parameters.
*/
uint64_t rx_max_udp_payload_size;
+ /*
+ * Maximum payload size in bytes for datagrams received from our peer, as
+ * negotiated by transport parameters.
+ */
+ uint64_t tx_max_udp_payload_size;
/* Maximum active CID limit, as negotiated by transport parameters. */
uint64_t rx_active_conn_id_limit;
diff --git a/ssl/quic/quic_impl.c b/ssl/quic/quic_impl.c
index ea76430009..8fb6091171 100644
--- a/ssl/quic/quic_impl.c
+++ b/ssl/quic/quic_impl.c
@@ -415,6 +415,11 @@ static int expect_quic_c(const SSL *s, QCTX *ctx)
return expect_quic_as(s, ctx, QCTX_C);
}
+static int expect_quic_cl(const SSL *s, QCTX *ctx)
+{
+ return expect_quic_as(s, ctx, QCTX_C | QCTX_L);
+}
+
static int expect_quic_csl(const SSL *s, QCTX *ctx)
{
return expect_quic_as(s, ctx, QCTX_C | QCTX_S | QCTX_L);
@@ -3633,7 +3638,9 @@ static int qc_getset_idle_timeout(QCTX *ctx, uint32_t class_,
switch (class_) {
case SSL_VALUE_CLASS_FEATURE_REQUEST:
- value_out = ossl_quic_channel_get_max_idle_timeout_request(ctx->qc->ch);
+ value_out = ctx->is_listener
+ ? ossl_quic_port_get_max_idle_timeout(ctx->ql->port)
+ : ossl_quic_channel_get_max_idle_timeout_request(ctx->qc->ch);
if (p_value_in != NULL) {
value_in = *p_value_in;
@@ -3643,13 +3650,15 @@ static int qc_getset_idle_timeout(QCTX *ctx, uint32_t class_,
goto err;
}
- if (ossl_quic_channel_have_generated_transport_params(ctx->qc->ch)) {
- QUIC_RAISE_NON_NORMAL_ERROR(ctx, SSL_R_FEATURE_NOT_RENEGOTIABLE,
- NULL);
- goto err;
+ if (ctx->is_listener) {
+ ossl_quic_port_set_max_idle_timeout(ctx->ql->port, value_in);
+ } else {
+ if (!ossl_quic_channel_set_max_idle_timeout_request(ctx->qc->ch, value_in)) {
+ QUIC_RAISE_NON_NORMAL_ERROR(ctx, SSL_R_FEATURE_NOT_RENEGOTIABLE,
+ NULL);
+ goto err;
+ }
}
-
- ossl_quic_channel_set_max_idle_timeout_request(ctx->qc->ch, value_in);
}
break;
@@ -3661,6 +3670,12 @@ static int qc_getset_idle_timeout(QCTX *ctx, uint32_t class_,
goto err;
}
+ if (ctx->is_listener) {
+ QUIC_RAISE_NON_NORMAL_ERROR(ctx, SSL_R_UNSUPPORTED_CONFIG_VALUE_OP,
+ NULL);
+ goto err;
+ }
+
if (!ossl_quic_channel_is_handshake_complete(ctx->qc->ch)) {
QUIC_RAISE_NON_NORMAL_ERROR(ctx, SSL_R_FEATURE_NEGOTIATION_NOT_COMPLETE,
NULL);
@@ -3687,6 +3702,366 @@ err:
return ret;
}
+QUIC_TAKES_LOCK
+static int qc_getset_max_udp_payload_size(QCTX *ctx, uint32_t class_,
+ uint64_t *p_value_out, uint64_t *p_value_in)
+{
+ int ret = 0;
+ uint64_t value_out = 0, value_in;
+
+ qctx_lock(ctx);
+
+ switch (class_) {
+ case SSL_VALUE_CLASS_FEATURE_REQUEST:
+ value_out = ctx->is_listener
+ ? ossl_quic_port_get_max_udp_payload_size(ctx->ql->port)
+ : ossl_quic_channel_get_max_udp_payload_size_request(ctx->qc->ch);
+
+ if (p_value_in != NULL) {
+ value_in = *p_value_in;
+ if (value_in > QUIC_DEFAULT_MAX_UDP_PAYLOAD_SIZE || value_in < QUIC_MIN_INITIAL_DGRAM_LEN) {
+ QUIC_RAISE_NON_NORMAL_ERROR(ctx, ERR_R_PASSED_INVALID_ARGUMENT,
+ NULL);
+ goto err;
+ }
+
+ if (ctx->is_listener) {
+ ossl_quic_port_set_max_udp_payload_size(ctx->ql->port, value_in);
+ } else {
+ if (!ossl_quic_channel_set_max_udp_payload_size_request(ctx->qc->ch, value_in)) {
+ QUIC_RAISE_NON_NORMAL_ERROR(ctx, SSL_R_FEATURE_NOT_RENEGOTIABLE,
+ NULL);
+ goto err;
+ }
+ }
+ }
+ break;
+
+ case SSL_VALUE_CLASS_FEATURE_PEER_REQUEST:
+ if (p_value_in != NULL) {
+ QUIC_RAISE_NON_NORMAL_ERROR(ctx, SSL_R_UNSUPPORTED_CONFIG_VALUE_OP,
+ NULL);
+ goto err;
+ }
+
+ if (ctx->is_listener) {
+ QUIC_RAISE_NON_NORMAL_ERROR(ctx, SSL_R_UNSUPPORTED_CONFIG_VALUE_OP,
+ NULL);
+ goto err;
+ }
+
+ if (!ossl_quic_channel_is_handshake_complete(ctx->qc->ch)) {
+ QUIC_RAISE_NON_NORMAL_ERROR(ctx, SSL_R_FEATURE_NEGOTIATION_NOT_COMPLETE,
+ NULL);
+ goto err;
+ }
+
+ value_out = ossl_quic_channel_get_max_udp_payload_size_peer_request(ctx->qc->ch);
+ break;
+
+ default:
+ QUIC_RAISE_NON_NORMAL_ERROR(ctx, SSL_R_UNSUPPORTED_CONFIG_VALUE_CLASS,
+ NULL);
+ goto err;
+ }
+
+ ret = 1;
+err:
+ qctx_unlock(ctx);
+ if (ret && p_value_out != NULL)
+ *p_value_out = value_out;
+
+ return ret;
+}
+
+QUIC_TAKES_LOCK
+static int qc_getset_max_data(QCTX *ctx, uint32_t class_,
+ uint64_t *p_value_out, uint64_t *p_value_in)
+{
+ int ret = 0;
+ uint64_t value_out = 0, value_in;
+
+ qctx_lock(ctx);
+
+ switch (class_) {
+ case SSL_VALUE_CLASS_FEATURE_REQUEST:
+ value_out = ctx->is_listener
+ ? ossl_quic_port_get_init_max_data(ctx->ql->port)
+ : ossl_quic_channel_get_max_data_request(ctx->qc->ch);
+
+ if (p_value_in != NULL) {
+ value_in = *p_value_in;
+ if (value_in > OSSL_QUIC_VLINT_MAX) {
+ QUIC_RAISE_NON_NORMAL_ERROR(ctx, ERR_R_PASSED_INVALID_ARGUMENT,
+ NULL);
+ goto err;
+ }
+
+ if (ctx->is_listener) {
+ ossl_quic_port_set_init_max_data(ctx->ql->port, value_in);
+ } else {
+ if (!ossl_quic_channel_set_max_data_request(ctx->qc->ch, value_in)) {
+ QUIC_RAISE_NON_NORMAL_ERROR(ctx, SSL_R_FEATURE_NOT_RENEGOTIABLE,
+ NULL);
+ goto err;
+ }
+ }
+ }
+ break;
+
+ case SSL_VALUE_CLASS_FEATURE_PEER_REQUEST:
+ if (p_value_in != NULL) {
+ QUIC_RAISE_NON_NORMAL_ERROR(ctx, SSL_R_UNSUPPORTED_CONFIG_VALUE_OP,
+ NULL);
+ goto err;
+ }
+
+ if (ctx->is_listener) {
+ QUIC_RAISE_NON_NORMAL_ERROR(ctx, SSL_R_UNSUPPORTED_CONFIG_VALUE_OP,
+ NULL);
+ goto err;
+ }
+
+ if (!ossl_quic_channel_is_handshake_complete(ctx->qc->ch)) {
+ QUIC_RAISE_NON_NORMAL_ERROR(ctx, SSL_R_FEATURE_NEGOTIATION_NOT_COMPLETE,
+ NULL);
+ goto err;
+ }
+
+ value_out = ossl_quic_channel_get_max_data_peer_request(ctx->qc->ch);
+ break;
+
+ default:
+ QUIC_RAISE_NON_NORMAL_ERROR(ctx, SSL_R_UNSUPPORTED_CONFIG_VALUE_CLASS,
+ NULL);
+ goto err;
+ }
+
+ ret = 1;
+err:
+ qctx_unlock(ctx);
+ if (ret && p_value_out != NULL)
+ *p_value_out = value_out;
+
+ return ret;
+}
+
+QUIC_TAKES_LOCK
+static int qc_getset_max_stream_data(QCTX *ctx, uint32_t class_,
+ uint64_t *p_value_out, int is_uni, int is_remote, uint64_t *p_value_in)
+{
+ int ret = 0;
+ uint64_t value_out = 0, value_in;
+
+ qctx_lock(ctx);
+
+ switch (class_) {
+ case SSL_VALUE_CLASS_FEATURE_REQUEST:
+ value_out = ctx->is_listener
+ ? ossl_quic_port_get_init_max_stream_data(ctx->ql->port, is_uni, is_remote)
+ : ossl_quic_channel_get_max_stream_data_request(ctx->qc->ch, is_uni, is_remote);
+
+ if (p_value_in != NULL) {
+ value_in = *p_value_in;
+ if (value_in > OSSL_QUIC_VLINT_MAX) {
+ QUIC_RAISE_NON_NORMAL_ERROR(ctx, ERR_R_PASSED_INVALID_ARGUMENT,
+ NULL);
+ goto err;
+ }
+
+ if (ctx->is_listener) {
+ ossl_quic_port_set_init_max_stream_data(ctx->ql->port, value_in, is_uni, is_remote);
+ } else {
+ if (!ossl_quic_channel_set_max_stream_data_request(ctx->qc->ch, value_in, is_uni, is_remote)) {
+ QUIC_RAISE_NON_NORMAL_ERROR(ctx, SSL_R_FEATURE_NOT_RENEGOTIABLE,
+ NULL);
+ goto err;
+ }
+ }
+ }
+ break;
+
+ case SSL_VALUE_CLASS_FEATURE_PEER_REQUEST:
+ if (p_value_in != NULL) {
+ QUIC_RAISE_NON_NORMAL_ERROR(ctx, SSL_R_UNSUPPORTED_CONFIG_VALUE_OP,
+ NULL);
+ goto err;
+ }
+
+ if (ctx->is_listener) {
+ QUIC_RAISE_NON_NORMAL_ERROR(ctx, SSL_R_UNSUPPORTED_CONFIG_VALUE_OP,
+ NULL);
+ goto err;
+ }
+
+ if (!ossl_quic_channel_is_handshake_complete(ctx->qc->ch)) {
+ QUIC_RAISE_NON_NORMAL_ERROR(ctx, SSL_R_FEATURE_NEGOTIATION_NOT_COMPLETE,
+ NULL);
+ goto err;
+ }
+
+ value_out = ossl_quic_channel_get_max_stream_data_peer_request(ctx->qc->ch, is_uni, is_remote);
+ break;
+
+ default:
+ QUIC_RAISE_NON_NORMAL_ERROR(ctx, SSL_R_UNSUPPORTED_CONFIG_VALUE_CLASS,
+ NULL);
+ goto err;
+ }
+
+ ret = 1;
+err:
+ qctx_unlock(ctx);
+ if (ret && p_value_out != NULL)
+ *p_value_out = value_out;
+
+ return ret;
+}
+
+QUIC_TAKES_LOCK
+static int qc_getset_ack_delay_exponent(QCTX *ctx, uint32_t class_,
+ uint64_t *p_value_out, uint64_t *p_value_in)
+{
+ int ret = 0;
+ uint64_t value_out = 0, value_in;
+
+ qctx_lock(ctx);
+
+ switch (class_) {
+ case SSL_VALUE_CLASS_FEATURE_REQUEST:
+ value_out = ctx->is_listener
+ ? ossl_quic_port_get_ack_delay_exponent(ctx->ql->port)
+ : ossl_quic_channel_get_ack_delay_exponent_request(ctx->qc->ch);
+
+ if (p_value_in != NULL) {
+ value_in = *p_value_in;
+ if (value_in > QUIC_MAX_ACK_DELAY_EXP) {
+ QUIC_RAISE_NON_NORMAL_ERROR(ctx, ERR_R_PASSED_INVALID_ARGUMENT,
+ NULL);
+ goto err;
+ }
+
+ if (ctx->is_listener) {
+ ossl_quic_port_set_ack_delay_exponent(ctx->ql->port, value_in);
+ } else {
+ if (!ossl_quic_channel_set_ack_delay_exponent_request(ctx->qc->ch, value_in)) {
+ QUIC_RAISE_NON_NORMAL_ERROR(ctx, SSL_R_FEATURE_NOT_RENEGOTIABLE,
+ NULL);
+ goto err;
+ }
+ }
+ }
+ break;
+
+ case SSL_VALUE_CLASS_FEATURE_PEER_REQUEST:
+ if (p_value_in != NULL) {
+ QUIC_RAISE_NON_NORMAL_ERROR(ctx, SSL_R_UNSUPPORTED_CONFIG_VALUE_OP,
+ NULL);
+ goto err;
+ }
+
+ if (ctx->is_listener) {
+ QUIC_RAISE_NON_NORMAL_ERROR(ctx, SSL_R_UNSUPPORTED_CONFIG_VALUE_OP,
+ NULL);
+ goto err;
+ }
+
+ if (!ossl_quic_channel_is_handshake_complete(ctx->qc->ch)) {
+ QUIC_RAISE_NON_NORMAL_ERROR(ctx, SSL_R_FEATURE_NEGOTIATION_NOT_COMPLETE,
+ NULL);
+ goto err;
+ }
+
+ value_out = ossl_quic_channel_get_ack_delay_exponent_peer_request(ctx->qc->ch);
+ break;
+
+ default:
+ QUIC_RAISE_NON_NORMAL_ERROR(ctx, SSL_R_UNSUPPORTED_CONFIG_VALUE_CLASS,
+ NULL);
+ goto err;
+ }
+
+ ret = 1;
+err:
+ qctx_unlock(ctx);
+ if (ret && p_value_out != NULL)
+ *p_value_out = value_out;
+
+ return ret;
+}
+
+QUIC_TAKES_LOCK
+static int qc_getset_max_ack_delay(QCTX *ctx, uint32_t class_,
+ uint64_t *p_value_out, uint64_t *p_value_in)
+{
+ int ret = 0;
+ uint64_t value_out = 0, value_in;
+
+ qctx_lock(ctx);
+
+ switch (class_) {
+ case SSL_VALUE_CLASS_FEATURE_REQUEST:
+ value_out = ctx->is_listener
+ ? ossl_quic_port_get_max_ack_delay(ctx->ql->port)
+ : ossl_quic_channel_get_max_ack_delay_request(ctx->qc->ch);
+
+ if (p_value_in != NULL) {
+ value_in = *p_value_in;
+ if (value_in > QUIC_MAX_MAX_ACK_DELAY) {
+ QUIC_RAISE_NON_NORMAL_ERROR(ctx, ERR_R_PASSED_INVALID_ARGUMENT,
+ NULL);
+ goto err;
+ }
+
+ if (ctx->is_listener) {
+ ossl_quic_port_set_max_ack_delay(ctx->ql->port, value_in);
+ } else {
+ if (!ossl_quic_channel_set_max_ack_delay_request(ctx->qc->ch, value_in)) {
+ QUIC_RAISE_NON_NORMAL_ERROR(ctx, SSL_R_FEATURE_NOT_RENEGOTIABLE,
+ NULL);
+ goto err;
+ }
+ }
+ }
+ break;
+
+ case SSL_VALUE_CLASS_FEATURE_PEER_REQUEST:
+ if (p_value_in != NULL) {
+ QUIC_RAISE_NON_NORMAL_ERROR(ctx, SSL_R_UNSUPPORTED_CONFIG_VALUE_OP,
+ NULL);
+ goto err;
+ }
+
+ if (ctx->is_listener) {
+ QUIC_RAISE_NON_NORMAL_ERROR(ctx, SSL_R_UNSUPPORTED_CONFIG_VALUE_OP,
+ NULL);
+ goto err;
+ }
+
+ if (!ossl_quic_channel_is_handshake_complete(ctx->qc->ch)) {
+ QUIC_RAISE_NON_NORMAL_ERROR(ctx, SSL_R_FEATURE_NEGOTIATION_NOT_COMPLETE,
+ NULL);
+ goto err;
+ }
+
+ value_out = ossl_quic_channel_get_max_ack_delay_peer_request(ctx->qc->ch);
+ break;
+
+ default:
+ QUIC_RAISE_NON_NORMAL_ERROR(ctx, SSL_R_UNSUPPORTED_CONFIG_VALUE_CLASS,
+ NULL);
+ goto err;
+ }
+
+ ret = 1;
+err:
+ qctx_unlock(ctx);
+ if (ret && p_value_out != NULL)
+ *p_value_out = value_out;
+
+ return ret;
+}
+
QUIC_TAKES_LOCK
static int qc_get_stream_avail(QCTX *ctx, uint32_t class_,
int is_uni, int is_remote,
@@ -3822,6 +4197,14 @@ static int expect_quic_for_value(SSL *s, QCTX *ctx, uint32_t id)
case SSL_VALUE_STREAM_WRITE_BUF_USED:
case SSL_VALUE_STREAM_WRITE_BUF_AVAIL:
return expect_quic_cs(s, ctx);
+ case SSL_VALUE_QUIC_IDLE_TIMEOUT:
+ case SSL_VALUE_QUIC_UDP_PAYLOAD_SIZE_MAX:
+ case SSL_VALUE_QUIC_WINDOWCON:
+ case SSL_VALUE_QUIC_WINDOWBSTR:
+ case SSL_VALUE_QUIC_WINDOWUSTR:
+ case SSL_VALUE_QUIC_ACK_DELAY_EXPONENT:
+ case SSL_VALUE_QUIC_ACK_DELAY_MAX:
+ return expect_quic_cl(s, ctx);
default:
return expect_quic_conn_only(s, ctx);
}
@@ -3843,6 +4226,18 @@ int ossl_quic_get_value_uint(SSL *s, uint32_t class_, uint32_t id,
switch (id) {
case SSL_VALUE_QUIC_IDLE_TIMEOUT:
return qc_getset_idle_timeout(&ctx, class_, value, NULL);
+ case SSL_VALUE_QUIC_UDP_PAYLOAD_SIZE_MAX:
+ return qc_getset_max_udp_payload_size(&ctx, class_, value, NULL);
+ case SSL_VALUE_QUIC_WINDOWCON:
+ return qc_getset_max_data(&ctx, class_, value, NULL);
+ case SSL_VALUE_QUIC_WINDOWBSTR:
+ return qc_getset_max_stream_data(&ctx, class_, value, /*uni=*/0, /*remote=*/0, NULL);
+ case SSL_VALUE_QUIC_WINDOWUSTR:
+ return qc_getset_max_stream_data(&ctx, class_, value, /*uni=*/1, /*remote=*/1, NULL);
+ case SSL_VALUE_QUIC_ACK_DELAY_EXPONENT:
+ return qc_getset_ack_delay_exponent(&ctx, class_, value, NULL);
+ case SSL_VALUE_QUIC_ACK_DELAY_MAX:
+ return qc_getset_max_ack_delay(&ctx, class_, value, NULL);
case SSL_VALUE_QUIC_STREAM_BIDI_LOCAL_AVAIL:
return qc_get_stream_avail(&ctx, class_, /*uni=*/0, /*remote=*/0, value);
@@ -3884,12 +4279,24 @@ int ossl_quic_set_value_uint(SSL *s, uint32_t class_, uint32_t id,
return 0;
switch (id) {
- case SSL_VALUE_QUIC_IDLE_TIMEOUT:
- return qc_getset_idle_timeout(&ctx, class_, NULL, &value);
-
case SSL_VALUE_EVENT_HANDLING_MODE:
return qc_getset_event_handling(&ctx, class_, NULL, &value);
+ case SSL_VALUE_QUIC_IDLE_TIMEOUT:
+ return qc_getset_idle_timeout(&ctx, class_, NULL, &value);
+ case SSL_VALUE_QUIC_UDP_PAYLOAD_SIZE_MAX:
+ return qc_getset_max_udp_payload_size(&ctx, class_, NULL, &value);
+ case SSL_VALUE_QUIC_WINDOWCON:
+ return qc_getset_max_data(&ctx, class_, NULL, &value);
+ case SSL_VALUE_QUIC_WINDOWBSTR:
+ return qc_getset_max_stream_data(&ctx, class_, NULL, /*uni=*/0, /*remote=*/0, &value);
+ case SSL_VALUE_QUIC_WINDOWUSTR:
+ return qc_getset_max_stream_data(&ctx, class_, NULL, /*uni=*/1, /*remote=*/1, &value);
+ case SSL_VALUE_QUIC_ACK_DELAY_EXPONENT:
+ return qc_getset_ack_delay_exponent(&ctx, class_, NULL, &value);
+ case SSL_VALUE_QUIC_ACK_DELAY_MAX:
+ return qc_getset_max_ack_delay(&ctx, class_, NULL, &value);
+
default:
return QUIC_RAISE_NON_NORMAL_ERROR(&ctx,
SSL_R_UNSUPPORTED_CONFIG_VALUE, NULL);
diff --git a/ssl/quic/quic_port.c b/ssl/quic/quic_port.c
index 780206a4f9..c6184623bc 100644
--- a/ssl/quic/quic_port.c
+++ b/ssl/quic/quic_port.c
@@ -93,6 +93,14 @@ typedef struct validation_token {
*/
#define ENCRYPTED_TOKEN_MAX_LEN (MARSHALLED_TOKEN_MAX_LEN + 16 + 12)
+/* Arbitrary choice of default idle timeout (not an RFC value). */
+#define DEFAULT_IDLE_TIMEOUT 30000
+
+#define DEFAULT_INIT_CONN_RXFC_WND (768 * 1024)
+#define DEFAULT_INIT_STREAM_RXFC_WND (512 * 1024)
+
+#define DEFAULT_INIT_CONN_MAX_STREAMS 100
+
DEFINE_LIST_OF_IMPL(ch, QUIC_CHANNEL);
DEFINE_LIST_OF_IMPL(incoming_ch, QUIC_CHANNEL);
DEFINE_LIST_OF_IMPL(port, QUIC_PORT);
@@ -164,6 +172,33 @@ static int port_init(QUIC_PORT *port)
port->rx_short_dcid_len = (unsigned char)rx_short_dcid_len;
port->tx_init_dcid_len = INIT_DCID_LEN;
+
+ port->max_idle_timeout = DEFAULT_IDLE_TIMEOUT;
+
+ /*
+ * We tell the peer we can handle at most this many bytes in a datagram payload.
+ * However, currently the QUIC_DEMUX in the QRX uses the BIO's MTU as upper bound
+ * on an incoming datagram size.
+ */
+ port->max_udp_payload_size = QUIC_MIN_INITIAL_DGRAM_LEN;
+ port->init_max_data = DEFAULT_INIT_CONN_RXFC_WND;
+ port->init_max_stream_data_bidi_local = DEFAULT_INIT_STREAM_RXFC_WND;
+ port->init_max_stream_data_bidi_remote = DEFAULT_INIT_STREAM_RXFC_WND;
+ port->init_max_stream_data_uni = DEFAULT_INIT_STREAM_RXFC_WND;
+ port->init_max_streams_bidi = DEFAULT_INIT_CONN_MAX_STREAMS;
+ port->init_max_streams_uni = DEFAULT_INIT_CONN_MAX_STREAMS;
+ port->ack_delay_exponent = QUIC_DEFAULT_ACK_DELAY_EXP;
+
+ /*
+ * Our maximum ACK delay on the TX side. This is up to us to choose. Note that
+ * this could differ from QUIC_DEFAULT_MAX_DELAY in future as that is a protocol
+ * value which determines the value of the maximum ACK delay if the
+ * max_ack_delay transport parameter is not set.
+ */
+ port->max_ack_delay = QUIC_DEFAULT_MAX_ACK_DELAY;
+ port->disable_active_migration = 1;
+ port->active_conn_id_limit = QUIC_MIN_ACTIVE_CONN_ID_LIMIT;
+
port->state = QUIC_PORT_STATE_RUNNING;
ossl_list_port_insert_tail(&port->engine->port_list, port);
@@ -522,6 +557,19 @@ static QUIC_CHANNEL *port_make_channel(QUIC_PORT *port, SSL *tls, OSSL_QRX *qrx,
args.qrx = qrx;
args.is_tserver_ch = is_tserver;
+ args.max_idle_timeout = port->max_idle_timeout;
+ args.max_udp_payload_size = port->max_udp_payload_size;
+ args.init_max_data = port->init_max_data;
+ args.init_max_stream_data_bidi_local = port->init_max_stream_data_bidi_local;
+ args.init_max_stream_data_bidi_remote = port->init_max_stream_data_bidi_remote;
+ args.init_max_stream_data_uni = port->init_max_stream_data_uni;
+ args.init_max_streams_bidi = port->init_max_streams_bidi;
+ args.init_max_streams_uni = port->init_max_streams_uni;
+ args.ack_delay_exponent = port->ack_delay_exponent;
+ args.max_ack_delay = port->max_ack_delay;
+ args.disable_active_migration = port->disable_active_migration;
+ args.active_conn_id_limit = port->active_conn_id_limit;
+
/*
* Creating a new channel is made a bit tricky here as there is a
* bit of a circular dependency. Initializing a channel requires that
@@ -1788,3 +1836,107 @@ void ossl_quic_port_restore_err_state(const QUIC_PORT *port)
ERR_clear_error();
OSSL_ERR_STATE_restore(port->err_state);
}
+
+void ossl_quic_port_set_max_idle_timeout(QUIC_PORT *port, uint64_t ms)
+{
+ port->max_idle_timeout = ms;
+}
+
+uint64_t ossl_quic_port_get_max_idle_timeout(const QUIC_PORT *port)
+{
+ return port->max_idle_timeout;
+}
+
+void ossl_quic_port_set_max_udp_payload_size(QUIC_PORT *port, uint64_t size)
+{
+ port->max_udp_payload_size = size;
+}
+
+uint64_t ossl_quic_port_get_max_udp_payload_size(const QUIC_PORT *port)
+{
+ return port->max_udp_payload_size;
+}
+
+void ossl_quic_port_set_init_max_data(QUIC_PORT *port, uint64_t max_data)
+{
+ port->init_max_data = max_data;
+}
+
+uint64_t ossl_quic_port_get_init_max_data(const QUIC_PORT *port)
+{
+ return port->init_max_data;
+}
+
+void ossl_quic_port_set_init_max_stream_data(QUIC_PORT *port, uint64_t max_data, int is_uni, int is_remote)
+{
+ if (is_uni) {
+ port->init_max_stream_data_uni = max_data;
+ } else {
+ if (is_remote)
+ port->init_max_stream_data_bidi_remote = max_data;
+ else
+ port->init_max_stream_data_bidi_local = max_data;
+ }
+}
+
+uint64_t ossl_quic_port_get_init_max_stream_data(const QUIC_PORT *port, int is_uni, int is_remote)
+{
+ if (is_uni)
+ return port->init_max_stream_data_uni;
+ else
+ return is_remote ? port->init_max_stream_data_bidi_remote : port->init_max_stream_data_bidi_local;
+}
+
+void ossl_quic_port_set_init_max_streams(QUIC_PORT *port, uint64_t max_streams, int is_uni)
+{
+ if (is_uni) {
+ port->init_max_streams_uni = max_streams;
+ } else {
+ port->init_max_streams_bidi = max_streams;
+ }
+}
+
+uint64_t ossl_quic_port_get_init_max_streams(const QUIC_PORT *port, int is_uni)
+{
+ return is_uni ? port->init_max_streams_uni : port->init_max_streams_bidi;
+}
+
+void ossl_quic_port_set_ack_delay_exponent(QUIC_PORT *port, uint64_t exp)
+{
+ port->ack_delay_exponent = (unsigned char)exp;
+}
+
+uint64_t ossl_quic_port_get_ack_delay_exponent(const QUIC_PORT *port)
+{
+ return port->ack_delay_exponent;
+}
+
+void ossl_quic_port_set_max_ack_delay(QUIC_PORT *port, uint64_t ms)
+{
+ port->max_ack_delay = ms;
+}
+
+uint64_t ossl_quic_port_get_max_ack_delay(const QUIC_PORT *port)
+{
+ return port->max_ack_delay;
+}
+
+void ossl_quic_port_set_disable_active_migration(QUIC_PORT *port, uint64_t disable)
+{
+ port->disable_active_migration = (unsigned char)disable;
+}
+
+uint64_t ossl_quic_port_get_disable_active_migration(const QUIC_PORT *port)
+{
+ return port->disable_active_migration;
+}
+
+void ossl_quic_port_set_active_conn_id_limit(QUIC_PORT *port, uint64_t limit)
+{
+ port->active_conn_id_limit = limit;
+}
+
+uint64_t ossl_quic_port_get_active_conn_id_limit(const QUIC_PORT *port)
+{
+ return port->active_conn_id_limit;
+}
diff --git a/ssl/quic/quic_port_local.h b/ssl/quic/quic_port_local.h
index 521d9b0fe3..3343f879cd 100644
--- a/ssl/quic/quic_port_local.h
+++ b/ssl/quic/quic_port_local.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2023-2025 The OpenSSL Project Authors. All Rights Reserved.
+ * Copyright 2023-2026 The OpenSSL Project Authors. All Rights Reserved.
*
* Licensed under the Apache License 2.0 (the "License"). You may not use
* this file except in compliance with the License. You can obtain a copy
@@ -121,6 +121,20 @@ struct quic_port_st {
/* AES-256 GCM context for token encryption */
EVP_CIPHER_CTX *token_ctx;
+
+ /* Transport parameter values for the port. */
+ uint64_t max_idle_timeout;
+ uint64_t max_udp_payload_size;
+ uint64_t init_max_data;
+ uint64_t init_max_stream_data_bidi_local;
+ uint64_t init_max_stream_data_bidi_remote;
+ uint64_t init_max_stream_data_uni;
+ uint64_t init_max_streams_bidi;
+ uint64_t init_max_streams_uni;
+ uint64_t max_ack_delay;
+ uint64_t active_conn_id_limit;
+ unsigned char ack_delay_exponent;
+ unsigned char disable_active_migration;
};
#endif
diff --git a/ssl/quic/quic_record_tx.c b/ssl/quic/quic_record_tx.c
index b8058f900a..8714437232 100644
--- a/ssl/quic/quic_record_tx.c
+++ b/ssl/quic/quic_record_tx.c
@@ -1,5 +1,5 @@
/*
- * Copyright 2022-2025 The OpenSSL Project Authors. All Rights Reserved.
+ * Copyright 2022-2026 The OpenSSL Project Authors. All Rights Reserved.
*
* Licensed under the Apache License 2.0 (the "License"). You may not use
* this file except in compliance with the License. You can obtain a copy
@@ -15,6 +15,8 @@
#include "internal/list.h"
#include "../ssl_local.h"
+#define QTX_DEFAULT_MTU 1500
+
/*
* TXE
* ===
@@ -68,6 +70,12 @@ struct ossl_qtx_st {
/* TX maximum datagram payload length. */
size_t mdpl;
+ /*
+ * Our current understanding of the upper bound on an outgoing datagram size
+ * in bytes.
+ */
+ size_t mtu;
+
/*
* List of TXEs which are not currently in use. These are moved to the
* pending list (possibly via tx_cons first) as they are filled.
@@ -125,6 +133,8 @@ OSSL_QTX *ossl_qtx_new(const OSSL_QTX_ARGS *args)
qtx->propq = args->propq;
qtx->bio = args->bio;
qtx->mdpl = args->mdpl;
+ /* We update this if possible when we get a BIO. */
+ qtx->mtu = QTX_DEFAULT_MTU;
qtx->get_qlog_cb = args->get_qlog_cb;
qtx->get_qlog_cb_arg = args->get_qlog_cb_arg;
@@ -839,7 +849,8 @@ int ossl_qtx_write_pkt(OSSL_QTX *qtx, const OSSL_QTX_PKT *pkt)
* Ensure TXE has at least MDPL bytes allocated. This should only be
* possible if the MDPL has increased.
*/
- if (!qtx_reserve_txe(qtx, NULL, txe, qtx->mdpl))
+ txe = qtx_reserve_txe(qtx, NULL, txe, qtx->mdpl);
+ if (txe == NULL)
return 0;
if (!was_coalescing) {
@@ -1018,15 +1029,40 @@ int ossl_qtx_pop_net(OSSL_QTX *qtx, BIO_MSG *msg)
void ossl_qtx_set_bio(OSSL_QTX *qtx, BIO *bio)
{
+ unsigned int mtu;
+
qtx->bio = bio;
+
+ if (bio != NULL) {
+ /*
+ * Try to determine our MTU if possible. The BIO is not required to
+ * support this, in which case we remain at the last known MTU, or our
+ * initial default.
+ */
+ mtu = BIO_dgram_get_mtu(bio);
+ if (mtu >= QUIC_MIN_INITIAL_DGRAM_LEN)
+ ossl_qtx_set_mtu(qtx, mtu); /* best effort */
+ }
+}
+
+int ossl_qtx_set_mtu(OSSL_QTX *qtx, unsigned int mtu)
+{
+ if (mtu < QUIC_MIN_INITIAL_DGRAM_LEN)
+ return 0;
+
+ qtx->mtu = mtu;
+ return 1;
}
int ossl_qtx_set_mdpl(OSSL_QTX *qtx, size_t mdpl)
{
+ size_t mtu_limit;
+
if (mdpl < QUIC_MIN_INITIAL_DGRAM_LEN)
return 0;
- qtx->mdpl = mdpl;
+ mtu_limit = qtx->mtu - BIO_dgram_get_mtu_overhead(qtx->bio);
+ qtx->mdpl = mdpl > mtu_limit ? mtu_limit : mdpl;
return 1;
}
diff --git a/ssl/quic/quic_txp.c b/ssl/quic/quic_txp.c
index b8fa50e19d..72ac08c95d 100644
--- a/ssl/quic/quic_txp.c
+++ b/ssl/quic/quic_txp.c
@@ -1,5 +1,5 @@
/*
- * Copyright 2022-2025 The OpenSSL Project Authors. All Rights Reserved.
+ * Copyright 2022-2026 The OpenSSL Project Authors. All Rights Reserved.
*
* Licensed under the Apache License 2.0 (the "License"). You may not use
* this file except in compliance with the License. You can obtain a copy
@@ -708,6 +708,18 @@ int ossl_quic_tx_packetiser_set_peer(OSSL_QUIC_TX_PACKETISER *txp,
return BIO_ADDR_copy(&txp->args.peer, peer);
}
+int ossl_quic_tx_packetiser_set_ack_delay_exponent(OSSL_QUIC_TX_PACKETISER *txp,
+ uint32_t exp)
+{
+ if (exp > QUIC_MAX_ACK_DELAY_EXP) {
+ ERR_raise(ERR_LIB_SSL, ERR_R_PASSED_INVALID_ARGUMENT);
+ return 0;
+ }
+
+ txp->args.ack_delay_exponent = exp;
+ return 1;
+}
+
void ossl_quic_tx_packetiser_set_ack_tx_cb(OSSL_QUIC_TX_PACKETISER *txp,
void (*cb)(const OSSL_QUIC_FRAME_ACK *ack,
uint32_t pn_space,
diff --git a/test/quic_multistream_test.c b/test/quic_multistream_test.c
index a3b46e57d0..9ae95e379b 100644
--- a/test/quic_multistream_test.c
+++ b/test/quic_multistream_test.c
@@ -5350,25 +5350,24 @@ static const struct script_op script_80[] = {
OP_END
};
-/* 81. Idle timeout configuration */
-static int modify_idle_timeout(struct helper *h, struct helper_local *hl)
+static int modify_static_tp(struct helper *h, struct helper_local *hl, int ssl_value)
{
uint64_t v = 0;
/* Test bad value is rejected. */
if (!TEST_false(SSL_set_feature_request_uint(h->c_conn,
- SSL_VALUE_QUIC_IDLE_TIMEOUT,
- (1ULL << 62))))
+ ssl_value,
+ OSSL_QUIC_VLINT_MAX + 1)))
return 0;
/* Set value. */
if (!TEST_true(SSL_set_feature_request_uint(h->c_conn,
- SSL_VALUE_QUIC_IDLE_TIMEOUT,
+ ssl_value,
hl->check_op->arg2)))
return 0;
if (!TEST_true(SSL_get_feature_request_uint(h->c_conn,
- SSL_VALUE_QUIC_IDLE_TIMEOUT,
+ ssl_value,
&v)))
return 0;
@@ -5378,12 +5377,12 @@ static int modify_idle_timeout(struct helper *h, struct helper_local *hl)
return 1;
}
-static int check_idle_timeout(struct helper *h, struct helper_local *hl)
+static int check_static_tp(struct helper *h, struct helper_local *hl, int ssl_value)
{
uint64_t v = 0;
if (!TEST_true(SSL_get_value_uint(h->c_conn, (uint32_t)hl->check_op->arg1,
- SSL_VALUE_QUIC_IDLE_TIMEOUT,
+ ssl_value,
&v)))
return 0;
@@ -5393,6 +5392,42 @@ static int check_idle_timeout(struct helper *h, struct helper_local *hl)
return 1;
}
+static int cannot_change_static_tp(struct helper *h, struct helper_local *hl, int ssl_value)
+{
+ uint64_t v = 0;
+
+ if (!TEST_true(SSL_get_feature_request_uint(h->c_conn,
+ ssl_value,
+ &v)))
+ return 0;
+
+ if (!TEST_uint64_t_eq(v, hl->check_op->arg1))
+ return 0;
+
+ if (!TEST_false(SSL_set_feature_request_uint(h->c_conn,
+ ssl_value,
+ hl->check_op->arg2)))
+ return 0;
+
+ return 1;
+}
+
+static int modify_idle_timeout(struct helper *h, struct helper_local *hl)
+{
+ return modify_static_tp(h, hl, SSL_VALUE_QUIC_IDLE_TIMEOUT);
+}
+
+static int check_idle_timeout(struct helper *h, struct helper_local *hl)
+{
+ return check_static_tp(h, hl, SSL_VALUE_QUIC_IDLE_TIMEOUT);
+}
+
+static int cannot_change_idle_timeout(struct helper *h, struct helper_local *hl)
+{
+ return cannot_change_static_tp(h, hl, SSL_VALUE_QUIC_IDLE_TIMEOUT);
+}
+
+/* 81. Idle timeout configuration */
static const struct script_op script_81[] = {
OP_C_SET_ALPN("ossltest"),
OP_CHECK(modify_idle_timeout, 25000),
@@ -5420,33 +5455,13 @@ static const struct script_op script_82[] = {
};
/* 83. No late changes to idle timeout */
-static int cannot_change_idle_timeout(struct helper *h, struct helper_local *hl)
-{
- uint64_t v = 0;
-
- if (!TEST_true(SSL_get_feature_request_uint(h->c_conn,
- SSL_VALUE_QUIC_IDLE_TIMEOUT,
- &v)))
- return 0;
-
- if (!TEST_uint64_t_eq(v, 30000))
- return 0;
-
- if (!TEST_false(SSL_set_feature_request_uint(h->c_conn,
- SSL_VALUE_QUIC_IDLE_TIMEOUT,
- 5000)))
- return 0;
-
- return 1;
-}
-
static const struct script_op script_83[] = {
OP_C_SET_ALPN("ossltest"),
OP_C_CONNECT_WAIT(),
OP_C_SET_DEFAULT_STREAM_MODE(SSL_DEFAULT_STREAM_MODE_NONE),
- OP_CHECK(cannot_change_idle_timeout, 0),
+ OP_CHECK2(cannot_change_idle_timeout, 30000, 5000),
OP_CHECK2(check_idle_timeout, SSL_VALUE_CLASS_FEATURE_PEER_REQUEST, 30000),
OP_CHECK2(check_idle_timeout, SSL_VALUE_CLASS_FEATURE_NEGOTIATED, 30000),
@@ -5996,6 +6011,375 @@ static const struct script_op script_87[] = {
OP_END
};
+static int modify_udp_payload_size_max(struct helper *h, struct helper_local *hl)
+{
+ if (!TEST_false(SSL_set_feature_request_uint(h->c_conn,
+ SSL_VALUE_QUIC_UDP_PAYLOAD_SIZE_MAX,
+ QUIC_MIN_INITIAL_DGRAM_LEN - 1)))
+ return 0;
+
+ if (!TEST_false(SSL_set_feature_request_uint(h->c_conn,
+ SSL_VALUE_QUIC_UDP_PAYLOAD_SIZE_MAX,
+ QUIC_DEFAULT_MAX_UDP_PAYLOAD_SIZE + 1)))
+ return 0;
+
+ return modify_static_tp(h, hl, SSL_VALUE_QUIC_UDP_PAYLOAD_SIZE_MAX);
+}
+
+static int check_udp_payload_size_max(struct helper *h, struct helper_local *hl)
+{
+ return check_static_tp(h, hl, SSL_VALUE_QUIC_UDP_PAYLOAD_SIZE_MAX);
+}
+
+static int cannot_change_udp_payload_size_max(struct helper *h, struct helper_local *hl)
+{
+ return cannot_change_static_tp(h, hl, SSL_VALUE_QUIC_UDP_PAYLOAD_SIZE_MAX);
+}
+
+/* 89. Max udp payload size configuration */
+static const struct script_op script_89[] = {
+ OP_C_SET_ALPN("ossltest"),
+ OP_CHECK(modify_udp_payload_size_max, QUIC_DEFAULT_MAX_UDP_PAYLOAD_SIZE),
+ OP_C_CONNECT_WAIT(),
+
+ OP_C_SET_DEFAULT_STREAM_MODE(SSL_DEFAULT_STREAM_MODE_NONE),
+
+ OP_CHECK2(check_udp_payload_size_max,
+ SSL_VALUE_CLASS_FEATURE_PEER_REQUEST, QUIC_MIN_INITIAL_DGRAM_LEN),
+ OP_CHECK2(check_udp_payload_size_max,
+ SSL_VALUE_CLASS_FEATURE_REQUEST, QUIC_DEFAULT_MAX_UDP_PAYLOAD_SIZE),
+
+ OP_END
+};
+
+/* 90. Negotiated default max udp payload size if not configured */
+static const struct script_op script_90[] = {
+ OP_C_SET_ALPN("ossltest"),
+ OP_C_CONNECT_WAIT(),
+
+ OP_C_SET_DEFAULT_STREAM_MODE(SSL_DEFAULT_STREAM_MODE_NONE),
+
+ OP_CHECK2(check_udp_payload_size_max,
+ SSL_VALUE_CLASS_FEATURE_PEER_REQUEST, QUIC_MIN_INITIAL_DGRAM_LEN),
+ OP_CHECK2(check_udp_payload_size_max,
+ SSL_VALUE_CLASS_FEATURE_REQUEST, QUIC_MIN_INITIAL_DGRAM_LEN),
+
+ OP_END
+};
+
+/* 91. No late changes to max udp payload size */
+static const struct script_op script_91[] = {
+ OP_C_SET_ALPN("ossltest"),
+ OP_C_CONNECT_WAIT(),
+
+ OP_C_SET_DEFAULT_STREAM_MODE(SSL_DEFAULT_STREAM_MODE_NONE),
+
+ OP_CHECK2(cannot_change_udp_payload_size_max, QUIC_MIN_INITIAL_DGRAM_LEN,
+ QUIC_DEFAULT_MAX_UDP_PAYLOAD_SIZE),
+ OP_CHECK2(check_udp_payload_size_max,
+ SSL_VALUE_CLASS_FEATURE_PEER_REQUEST, QUIC_MIN_INITIAL_DGRAM_LEN),
+
+ OP_END
+};
+
+static int modify_window_con(struct helper *h, struct helper_local *hl)
+{
+ return modify_static_tp(h, hl, SSL_VALUE_QUIC_WINDOWCON);
+}
+
+static int check_window_con(struct helper *h, struct helper_local *hl)
+{
+ return check_static_tp(h, hl, SSL_VALUE_QUIC_WINDOWCON);
+}
+
+static int cannot_change_window_con(struct helper *h, struct helper_local *hl)
+{
+ return cannot_change_static_tp(h, hl, SSL_VALUE_QUIC_WINDOWCON);
+}
+
+/* 92. Connection window configuration */
+static const struct script_op script_92[] = {
+ OP_C_SET_ALPN("ossltest"),
+ OP_CHECK(modify_window_con, 800000),
+ OP_C_CONNECT_WAIT(),
+
+ OP_C_SET_DEFAULT_STREAM_MODE(SSL_DEFAULT_STREAM_MODE_NONE),
+
+ OP_CHECK2(check_window_con, SSL_VALUE_CLASS_FEATURE_PEER_REQUEST, 768 * 1024),
+ OP_CHECK2(check_window_con, SSL_VALUE_CLASS_FEATURE_REQUEST, 800000),
+
+ OP_END
+};
+
+/* 93. Negotiated default connection window if not configured */
+static const struct script_op script_93[] = {
+ OP_C_SET_ALPN("ossltest"),
+ OP_C_CONNECT_WAIT(),
+
+ OP_C_SET_DEFAULT_STREAM_MODE(SSL_DEFAULT_STREAM_MODE_NONE),
+
+ OP_CHECK2(check_window_con, SSL_VALUE_CLASS_FEATURE_PEER_REQUEST, 768 * 1024),
+ OP_CHECK2(check_window_con, SSL_VALUE_CLASS_FEATURE_REQUEST, 768 * 1024),
+
+ OP_END
+};
+
+/* 94. No late changes to connection window */
+static const struct script_op script_94[] = {
+ OP_C_SET_ALPN("ossltest"),
+ OP_C_CONNECT_WAIT(),
+
+ OP_C_SET_DEFAULT_STREAM_MODE(SSL_DEFAULT_STREAM_MODE_NONE),
+
+ OP_CHECK2(cannot_change_window_con, 768 * 1024, 800000),
+ OP_CHECK2(check_window_con, SSL_VALUE_CLASS_FEATURE_PEER_REQUEST, 768 * 1024),
+
+ OP_END
+};
+
+static int modify_window_bidi_stream(struct helper *h, struct helper_local *hl)
+{
+ return modify_static_tp(h, hl, SSL_VALUE_QUIC_WINDOWBSTR);
+}
+
+static int check_window_bidi_stream(struct helper *h, struct helper_local *hl)
+{
+ return check_static_tp(h, hl, SSL_VALUE_QUIC_WINDOWBSTR);
+}
+
+static int cannot_change_window_bidi_stream(struct helper *h, struct helper_local *hl)
+{
+ return cannot_change_static_tp(h, hl, SSL_VALUE_QUIC_WINDOWBSTR);
+}
+
+/* 95. Bidi stream window configuration */
+static const struct script_op script_95[] = {
+ OP_C_SET_ALPN("ossltest"),
+ OP_CHECK(modify_window_bidi_stream, 600000),
+ OP_C_CONNECT_WAIT(),
+
+ OP_C_SET_DEFAULT_STREAM_MODE(SSL_DEFAULT_STREAM_MODE_NONE),
+
+ OP_CHECK2(check_window_bidi_stream, SSL_VALUE_CLASS_FEATURE_PEER_REQUEST, 512 * 1024),
+ OP_CHECK2(check_window_bidi_stream, SSL_VALUE_CLASS_FEATURE_REQUEST, 600000),
+
+ OP_END
+};
+
+/* 96. Negotiated default bidi stream window if not configured */
+static const struct script_op script_96[] = {
+ OP_C_SET_ALPN("ossltest"),
+ OP_C_CONNECT_WAIT(),
+
+ OP_C_SET_DEFAULT_STREAM_MODE(SSL_DEFAULT_STREAM_MODE_NONE),
+
+ OP_CHECK2(check_window_bidi_stream, SSL_VALUE_CLASS_FEATURE_PEER_REQUEST, 512 * 1024),
+ OP_CHECK2(check_window_bidi_stream, SSL_VALUE_CLASS_FEATURE_REQUEST, 512 * 1024),
+
+ OP_END
+};
+
+/* 97. No late changes to bidi stream window */
+static const struct script_op script_97[] = {
+ OP_C_SET_ALPN("ossltest"),
+ OP_C_CONNECT_WAIT(),
+
+ OP_C_SET_DEFAULT_STREAM_MODE(SSL_DEFAULT_STREAM_MODE_NONE),
+
+ OP_CHECK2(cannot_change_window_bidi_stream, 512 * 1024, 600000),
+ OP_CHECK2(check_window_bidi_stream, SSL_VALUE_CLASS_FEATURE_PEER_REQUEST, 512 * 1024),
+
+ OP_END
+};
+
+static int modify_window_uni_stream(struct helper *h, struct helper_local *hl)
+{
+ return modify_static_tp(h, hl, SSL_VALUE_QUIC_WINDOWUSTR);
+}
+
+static int check_window_uni_stream(struct helper *h, struct helper_local *hl)
+{
+ return check_static_tp(h, hl, SSL_VALUE_QUIC_WINDOWUSTR);
+}
+
+static int cannot_change_window_uni_stream(struct helper *h, struct helper_local *hl)
+{
+ return cannot_change_static_tp(h, hl, SSL_VALUE_QUIC_WINDOWUSTR);
+}
+
+/* 98. Uni stream window configuration */
+static const struct script_op script_98[] = {
+ OP_C_SET_ALPN("ossltest"),
+ OP_CHECK(modify_window_uni_stream, 600000),
+ OP_C_CONNECT_WAIT(),
+
+ OP_C_SET_DEFAULT_STREAM_MODE(SSL_DEFAULT_STREAM_MODE_NONE),
+
+ OP_CHECK2(check_window_uni_stream, SSL_VALUE_CLASS_FEATURE_PEER_REQUEST, 512 * 1024),
+ OP_CHECK2(check_window_uni_stream, SSL_VALUE_CLASS_FEATURE_REQUEST, 600000),
+
+ OP_END
+};
+
+/* 99. Negotiated default uni stream window if not configured */
+static const struct script_op script_99[] = {
+ OP_C_SET_ALPN("ossltest"),
+ OP_C_CONNECT_WAIT(),
+
+ OP_C_SET_DEFAULT_STREAM_MODE(SSL_DEFAULT_STREAM_MODE_NONE),
+
+ OP_CHECK2(check_window_uni_stream, SSL_VALUE_CLASS_FEATURE_PEER_REQUEST, 512 * 1024),
+ OP_CHECK2(check_window_uni_stream, SSL_VALUE_CLASS_FEATURE_REQUEST, 512 * 1024),
+
+ OP_END
+};
+
+/* 100. No late changes to uni stream window */
+static const struct script_op script_100[] = {
+ OP_C_SET_ALPN("ossltest"),
+ OP_C_CONNECT_WAIT(),
+
+ OP_C_SET_DEFAULT_STREAM_MODE(SSL_DEFAULT_STREAM_MODE_NONE),
+
+ OP_CHECK2(cannot_change_window_uni_stream, 512 * 1024, 600000),
+ OP_CHECK2(check_window_uni_stream, SSL_VALUE_CLASS_FEATURE_PEER_REQUEST, 512 * 1024),
+
+ OP_END
+};
+
+static int
+modify_ack_delay_exponent(struct helper *h, struct helper_local *hl)
+{
+ if (!TEST_false(SSL_set_feature_request_uint(h->c_conn,
+ SSL_VALUE_QUIC_ACK_DELAY_EXPONENT,
+ QUIC_MAX_ACK_DELAY_EXP + 1)))
+ return 0;
+
+ return modify_static_tp(h, hl, SSL_VALUE_QUIC_ACK_DELAY_EXPONENT);
+}
+
+static int check_ack_delay_exponent(struct helper *h, struct helper_local *hl)
+{
+ return check_static_tp(h, hl, SSL_VALUE_QUIC_ACK_DELAY_EXPONENT);
+}
+
+static int cannot_change_ack_delay_exponent(struct helper *h, struct helper_local *hl)
+{
+ return cannot_change_static_tp(h, hl, SSL_VALUE_QUIC_ACK_DELAY_EXPONENT);
+}
+
+/* 101. Ack delay exponent configuration */
+static const struct script_op script_101[] = {
+ OP_C_SET_ALPN("ossltest"),
+ OP_CHECK(modify_ack_delay_exponent, QUIC_DEFAULT_ACK_DELAY_EXP + 1),
+ OP_C_CONNECT_WAIT(),
+
+ OP_C_SET_DEFAULT_STREAM_MODE(SSL_DEFAULT_STREAM_MODE_NONE),
+
+ OP_CHECK2(check_ack_delay_exponent,
+ SSL_VALUE_CLASS_FEATURE_PEER_REQUEST, QUIC_DEFAULT_ACK_DELAY_EXP),
+ OP_CHECK2(check_ack_delay_exponent,
+ SSL_VALUE_CLASS_FEATURE_REQUEST, QUIC_DEFAULT_ACK_DELAY_EXP + 1),
+
+ OP_END
+};
+
+/* 102. Negotiated default ack delay exponent if not configured */
+static const struct script_op script_102[] = {
+ OP_C_SET_ALPN("ossltest"),
+ OP_C_CONNECT_WAIT(),
+
+ OP_C_SET_DEFAULT_STREAM_MODE(SSL_DEFAULT_STREAM_MODE_NONE),
+
+ OP_CHECK2(check_ack_delay_exponent,
+ SSL_VALUE_CLASS_FEATURE_PEER_REQUEST, QUIC_DEFAULT_ACK_DELAY_EXP),
+ OP_CHECK2(check_ack_delay_exponent,
+ SSL_VALUE_CLASS_FEATURE_REQUEST, QUIC_DEFAULT_ACK_DELAY_EXP),
+
+ OP_END
+};
+
+/* 103. No late changes to ack delay exponent */
+static const struct script_op script_103[] = {
+ OP_C_SET_ALPN("ossltest"),
+ OP_C_CONNECT_WAIT(),
+
+ OP_C_SET_DEFAULT_STREAM_MODE(SSL_DEFAULT_STREAM_MODE_NONE),
+
+ OP_CHECK2(cannot_change_ack_delay_exponent, QUIC_DEFAULT_ACK_DELAY_EXP,
+ QUIC_DEFAULT_ACK_DELAY_EXP + 1),
+ OP_CHECK2(check_ack_delay_exponent,
+ SSL_VALUE_CLASS_FEATURE_PEER_REQUEST, QUIC_DEFAULT_ACK_DELAY_EXP),
+
+ OP_END
+};
+
+static int modify_ack_delay_max(struct helper *h, struct helper_local *hl)
+{
+ if (!TEST_false(SSL_set_feature_request_uint(h->c_conn,
+ SSL_VALUE_QUIC_ACK_DELAY_MAX,
+ QUIC_MAX_MAX_ACK_DELAY + 1)))
+ return 0;
+
+ return modify_static_tp(h, hl, SSL_VALUE_QUIC_ACK_DELAY_MAX);
+}
+
+static int check_ack_delay_max(struct helper *h, struct helper_local *hl)
+{
+ return check_static_tp(h, hl, SSL_VALUE_QUIC_ACK_DELAY_MAX);
+}
+
+static int cannot_change_ack_delay_max(struct helper *h, struct helper_local *hl)
+{
+ return cannot_change_static_tp(h, hl, SSL_VALUE_QUIC_ACK_DELAY_MAX);
+}
+
+/* 104. Max ack delay configuration */
+static const struct script_op script_104[] = {
+ OP_C_SET_ALPN("ossltest"),
+ OP_CHECK(modify_ack_delay_max, QUIC_DEFAULT_MAX_ACK_DELAY / 2),
+ OP_C_CONNECT_WAIT(),
+
+ OP_C_SET_DEFAULT_STREAM_MODE(SSL_DEFAULT_STREAM_MODE_NONE),
+
+ OP_CHECK2(check_ack_delay_max,
+ SSL_VALUE_CLASS_FEATURE_PEER_REQUEST, QUIC_DEFAULT_MAX_ACK_DELAY),
+ OP_CHECK2(check_ack_delay_max,
+ SSL_VALUE_CLASS_FEATURE_REQUEST, QUIC_DEFAULT_MAX_ACK_DELAY / 2),
+
+ OP_END
+};
+
+/* 105. Negotiated default max ack delay if not configured */
+static const struct script_op script_105[] = {
+ OP_C_SET_ALPN("ossltest"),
+ OP_C_CONNECT_WAIT(),
+
+ OP_C_SET_DEFAULT_STREAM_MODE(SSL_DEFAULT_STREAM_MODE_NONE),
+
+ OP_CHECK2(check_ack_delay_max,
+ SSL_VALUE_CLASS_FEATURE_PEER_REQUEST, QUIC_DEFAULT_MAX_ACK_DELAY),
+ OP_CHECK2(check_ack_delay_max,
+ SSL_VALUE_CLASS_FEATURE_REQUEST, QUIC_DEFAULT_MAX_ACK_DELAY),
+
+ OP_END
+};
+
+/* 106. No late changes to max ack delay */
+static const struct script_op script_106[] = {
+ OP_C_SET_ALPN("ossltest"),
+ OP_C_CONNECT_WAIT(),
+
+ OP_C_SET_DEFAULT_STREAM_MODE(SSL_DEFAULT_STREAM_MODE_NONE),
+
+ OP_CHECK2(cannot_change_ack_delay_max, QUIC_DEFAULT_MAX_ACK_DELAY,
+ QUIC_DEFAULT_MAX_ACK_DELAY / 2),
+ OP_CHECK2(check_ack_delay_max,
+ SSL_VALUE_CLASS_FEATURE_PEER_REQUEST, QUIC_DEFAULT_MAX_ACK_DELAY),
+
+ OP_END
+};
+
static const struct script_op *const scripts[] = {
script_1,
script_2,
@@ -6085,6 +6469,24 @@ static const struct script_op *const scripts[] = {
script_86,
script_87,
script_88,
+ script_89,
+ script_90,
+ script_91,
+ script_92,
+ script_93,
+ script_94,
+ script_95,
+ script_96,
+ script_97,
+ script_98,
+ script_99,
+ script_100,
+ script_101,
+ script_102,
+ script_103,
+ script_104,
+ script_105,
+ script_106,
};
static int test_script(int idx)
diff --git a/util/other.syms b/util/other.syms
index 66635d79b2..dc34638f09 100644
--- a/util/other.syms
+++ b/util/other.syms
@@ -797,6 +797,12 @@ SSL_VALUE_CLASS_FEATURE_REQUEST define
SSL_VALUE_CLASS_FEATURE_PEER_REQUEST define
SSL_VALUE_CLASS_FEATURE_NEGOTIATED define
SSL_VALUE_QUIC_IDLE_TIMEOUT define
+SSL_VALUE_QUIC_UDP_PAYLOAD_SIZE_MAX define
+SSL_VALUE_QUIC_WINDOWCON define
+SSL_VALUE_QUIC_WINDOWBSTR define
+SSL_VALUE_QUIC_WINDOWUSTR define
+SSL_VALUE_QUIC_ACK_DELAY_EXPONENT define
+SSL_VALUE_QUIC_ACK_DELAY_MAX define
SSL_VALUE_QUIC_STREAM_BIDI_LOCAL_AVAIL define
SSL_VALUE_QUIC_STREAM_BIDI_REMOTE_AVAIL define
SSL_VALUE_QUIC_STREAM_UNI_LOCAL_AVAIL define