libp2p_quic/config.rs
1// Copyright 2017-2020 Parity Technologies (UK) Ltd.
2//
3// Permission is hereby granted, free of charge, to any person obtaining a
4// copy of this software and associated documentation files (the "Software"),
5// to deal in the Software without restriction, including without limitation
6// the rights to use, copy, modify, merge, publish, distribute, sublicense,
7// and/or sell copies of the Software, and to permit persons to whom the
8// Software is furnished to do so, subject to the following conditions:
9//
10// The above copyright notice and this permission notice shall be included in
11// all copies or substantial portions of the Software.
12//
13// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
14// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
18// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
19// DEALINGS IN THE SOFTWARE.
20
21use std::{sync::Arc, time::Duration};
22
23use quinn::{
24 crypto::rustls::{QuicClientConfig, QuicServerConfig},
25 MtuDiscoveryConfig, VarInt,
26};
27
28/// Config for the transport.
29#[derive(Clone)]
30pub struct Config {
31 /// Timeout for the initial handshake when establishing a connection.
32 /// The actual timeout is the minimum of this and the [`Config::max_idle_timeout`].
33 pub handshake_timeout: Duration,
34 /// Maximum duration of inactivity in ms to accept before timing out the connection.
35 pub max_idle_timeout: u32,
36 /// Period of inactivity before sending a keep-alive packet.
37 /// Must be set lower than the idle_timeout of both
38 /// peers to be effective.
39 ///
40 /// See [`quinn::TransportConfig::keep_alive_interval`] for more
41 /// info.
42 pub keep_alive_interval: Duration,
43 /// Maximum number of incoming bidirectional streams that may be open
44 /// concurrently by the remote peer.
45 pub max_concurrent_stream_limit: u32,
46
47 /// Max unacknowledged data in bytes that may be sent on a single stream.
48 pub max_stream_data: u32,
49
50 /// Max unacknowledged data in bytes that may be sent in total on all streams
51 /// of a connection.
52 pub max_connection_data: u32,
53
54 /// Support QUIC version draft-29 for dialing and listening.
55 ///
56 /// Per default only QUIC Version 1 / [`libp2p_core::multiaddr::Protocol::QuicV1`]
57 /// is supported.
58 ///
59 /// If support for draft-29 is enabled servers support draft-29 and version 1 on all
60 /// QUIC listening addresses.
61 /// As client the version is chosen based on the remote's address.
62 pub support_draft_29: bool,
63
64 /// TLS client config for the inner [`quinn::ClientConfig`].
65 client_tls_config: Arc<QuicClientConfig>,
66 /// TLS server config for the inner [`quinn::ServerConfig`].
67 server_tls_config: Arc<QuicServerConfig>,
68 /// Libp2p identity of the node.
69 keypair: libp2p_identity::Keypair,
70
71 /// Parameters governing MTU discovery. See [`MtuDiscoveryConfig`] for details.
72 mtu_discovery_config: Option<MtuDiscoveryConfig>,
73}
74
75impl Config {
76 /// Creates a new configuration object with default values.
77 pub fn new(keypair: &libp2p_identity::Keypair) -> Self {
78 let client_tls_config = Arc::new(
79 QuicClientConfig::try_from(libp2p_tls::make_client_config(keypair, None).unwrap())
80 .unwrap(),
81 );
82 let server_tls_config = Arc::new(
83 QuicServerConfig::try_from(libp2p_tls::make_server_config(keypair).unwrap()).unwrap(),
84 );
85 Self {
86 client_tls_config,
87 server_tls_config,
88 support_draft_29: false,
89 handshake_timeout: Duration::from_secs(5),
90 max_idle_timeout: 10 * 1000,
91 max_concurrent_stream_limit: 256,
92 keep_alive_interval: Duration::from_secs(5),
93 max_connection_data: 15_000_000,
94
95 // Ensure that one stream is not consuming the whole connection.
96 max_stream_data: 10_000_000,
97 keypair: keypair.clone(),
98 mtu_discovery_config: Some(Default::default()),
99 }
100 }
101
102 /// Set the upper bound to the max UDP payload size that MTU discovery will search for.
103 pub fn mtu_upper_bound(mut self, value: u16) -> Self {
104 self.mtu_discovery_config
105 .get_or_insert_with(Default::default)
106 .upper_bound(value);
107 self
108 }
109
110 /// Disable MTU path discovery (it is enabled by default).
111 pub fn disable_path_mtu_discovery(mut self) -> Self {
112 self.mtu_discovery_config = None;
113 self
114 }
115}
116
117/// Represents the inner configuration for [`quinn`].
118#[derive(Debug, Clone)]
119pub(crate) struct QuinnConfig {
120 pub(crate) client_config: quinn::ClientConfig,
121 pub(crate) server_config: quinn::ServerConfig,
122 pub(crate) endpoint_config: quinn::EndpointConfig,
123}
124
125impl From<Config> for QuinnConfig {
126 fn from(config: Config) -> QuinnConfig {
127 let Config {
128 client_tls_config,
129 server_tls_config,
130 max_idle_timeout,
131 max_concurrent_stream_limit,
132 keep_alive_interval,
133 max_connection_data,
134 max_stream_data,
135 support_draft_29,
136 handshake_timeout: _,
137 keypair,
138 mtu_discovery_config,
139 } = config;
140 let mut transport = quinn::TransportConfig::default();
141 // Disable uni-directional streams.
142 transport.max_concurrent_uni_streams(0u32.into());
143 transport.max_concurrent_bidi_streams(max_concurrent_stream_limit.into());
144 // Disable datagrams.
145 transport.datagram_receive_buffer_size(None);
146 transport.keep_alive_interval(Some(keep_alive_interval));
147 transport.max_idle_timeout(Some(VarInt::from_u32(max_idle_timeout).into()));
148 transport.allow_spin(false);
149 transport.stream_receive_window(max_stream_data.into());
150 transport.receive_window(max_connection_data.into());
151 transport.mtu_discovery_config(mtu_discovery_config);
152 let transport = Arc::new(transport);
153
154 let mut server_config = quinn::ServerConfig::with_crypto(server_tls_config);
155 server_config.transport = Arc::clone(&transport);
156 // Disables connection migration.
157 // Long-term this should be enabled, however we then need to handle address change
158 // on connections in the `Connection`.
159 server_config.migration(false);
160
161 let mut client_config = quinn::ClientConfig::new(client_tls_config);
162 client_config.transport_config(transport);
163
164 let mut endpoint_config = keypair
165 .derive_secret(b"libp2p quic stateless reset key")
166 .map(|secret| {
167 let reset_key = Arc::new(ring::hmac::Key::new(ring::hmac::HMAC_SHA256, &secret));
168 quinn::EndpointConfig::new(reset_key)
169 })
170 .unwrap_or_default();
171
172 if !support_draft_29 {
173 endpoint_config.supported_versions(vec![1]);
174 }
175
176 QuinnConfig {
177 client_config,
178 server_config,
179 endpoint_config,
180 }
181 }
182}