tokio_quiche/settings/
config.rs1use foundations::telemetry::log;
28use std::borrow::Cow;
29use std::fs::File;
30use std::sync::Arc;
31use std::time::Duration;
32
33use qlog::writer::QlogCompression;
34
35use crate::quic::ConnectionHook;
36use crate::result::QuicResult;
37use crate::settings::CertificateKind;
38use crate::settings::ConnectionParams;
39use crate::settings::TlsCertificatePaths;
40use crate::socket::SocketCapabilities;
41
42const KEYLOGFILE_ENABLED: bool =
45 cfg!(capture_keylogs) || cfg!(feature = "capture_keylogs");
46
47pub(crate) struct Config {
49 pub quiche_config: quiche::Config,
50 pub disable_client_ip_validation: bool,
51 pub qlog_dir: Option<String>,
52 pub qlog_compression: QlogCompression,
53 pub has_gso: bool,
54 pub pacing_offload: bool,
55 pub enable_expensive_packet_count_metrics: bool,
56 pub keylog_file: Option<File>,
57 pub listen_backlog: usize,
58 pub handshake_timeout: Option<Duration>,
59 pub has_ippktinfo: bool,
60 pub has_ipv6pktinfo: bool,
61 pub pool_send_buffer: bool,
62 pub connection_hook: Option<Arc<dyn ConnectionHook + Send + Sync + 'static>>,
63}
64
65impl AsMut<quiche::Config> for Config {
66 fn as_mut(&mut self) -> &mut quiche::Config {
67 &mut self.quiche_config
68 }
69}
70
71impl Config {
72 pub(crate) fn new(
73 params: &ConnectionParams, socket_capabilities: SocketCapabilities,
74 ) -> QuicResult<Self> {
75 let quic_settings = ¶ms.settings;
76 let keylog_path = match &quic_settings.keylog_file {
77 Some(f) => Some(Cow::Borrowed(f.as_ref())),
78 None => std::env::var_os("SSLKEYLOGFILE").map(Cow::from),
79 };
80 let keylog_file = keylog_path.and_then(|path| if KEYLOGFILE_ENABLED {
81 File::options().create(true).append(true).open(path)
82 .inspect_err(|e| {
83 log::warn!("failed to open SSLKEYLOGFILE"; "error" => e);
84 })
85 .ok()
86 } else {
87 log::warn!("SSLKEYLOGFILE is set, but `--cfg capture_keylogs` was not enabled. No keys will be logged.");
88 None
89 });
90
91 let SocketCapabilities {
92 has_gso,
93 has_txtime: pacing_offload,
94 has_ippktinfo,
95 has_ipv6pktinfo,
96 ..
97 } = socket_capabilities;
98
99 #[cfg(feature = "gcongestion")]
100 let pacing_offload = quic_settings.enable_pacing && pacing_offload;
101
102 Ok(Config {
103 quiche_config: make_quiche_config(params, keylog_file.is_some())?,
104 disable_client_ip_validation: quic_settings
105 .disable_client_ip_validation,
106 qlog_dir: quic_settings.qlog_dir.clone(),
107 qlog_compression: quic_settings.qlog_compression,
108 has_gso,
109 pacing_offload,
110 enable_expensive_packet_count_metrics: quic_settings
111 .enable_expensive_packet_count_metrics,
112 keylog_file,
113 listen_backlog: quic_settings.listen_backlog,
114 handshake_timeout: quic_settings.handshake_timeout,
115 has_ippktinfo,
116 has_ipv6pktinfo,
117 pool_send_buffer: quic_settings.pool_send_buffer,
118 connection_hook: params.hooks.connection_hook.clone(),
119 })
120 }
121}
122
123fn make_quiche_config(
124 params: &ConnectionParams, should_log_keys: bool,
125) -> QuicResult<quiche::Config> {
126 let ssl_ctx_builder = params
127 .hooks
128 .connection_hook
129 .as_ref()
130 .zip(params.tls_cert)
131 .and_then(|(hook, tls)| hook.create_custom_ssl_context_builder(tls));
132
133 let mut config = if let Some(builder) = ssl_ctx_builder {
134 quiche::Config::with_boring_ssl_ctx_builder(
135 quiche::PROTOCOL_VERSION,
136 builder,
137 )?
138 } else {
139 quiche_config_with_tls(params.tls_cert)?
140 };
141
142 let quic_settings = ¶ms.settings;
143
144 let alpns: Vec<&[u8]> =
145 quic_settings.alpn.iter().map(Vec::as_slice).collect();
146 config.set_application_protos(&alpns).unwrap();
147
148 if let Some(timeout) = quic_settings.max_idle_timeout {
149 let ms = timeout
150 .as_millis()
151 .try_into()
152 .map_err(|_| "QuicSettings::max_idle_timeout exceeds u64")?;
153 config.set_max_idle_timeout(ms);
154 }
155
156 config.enable_dgram(
157 quic_settings.enable_dgram,
158 quic_settings.dgram_recv_max_queue_len,
159 quic_settings.dgram_send_max_queue_len,
160 );
161
162 config.set_max_recv_udp_payload_size(quic_settings.max_recv_udp_payload_size);
163 config.set_max_send_udp_payload_size(quic_settings.max_send_udp_payload_size);
164 config.set_initial_max_data(quic_settings.initial_max_data);
165 config.set_initial_max_stream_data_bidi_local(
166 quic_settings.initial_max_stream_data_bidi_local,
167 );
168 config.set_initial_max_stream_data_bidi_remote(
169 quic_settings.initial_max_stream_data_bidi_remote,
170 );
171 config.set_initial_max_stream_data_uni(
172 quic_settings.initial_max_stream_data_uni,
173 );
174 config.set_initial_max_streams_bidi(quic_settings.initial_max_streams_bidi);
175 config.set_initial_max_streams_uni(quic_settings.initial_max_streams_uni);
176 config.set_disable_active_migration(quic_settings.disable_active_migration);
177 config
178 .set_active_connection_id_limit(quic_settings.active_connection_id_limit);
179 config.set_cc_algorithm_name(quic_settings.cc_algorithm.as_str())?;
180 config.set_initial_congestion_window_packets(
181 quic_settings.initial_congestion_window_packets,
182 );
183 config.set_enable_relaxed_loss_threshold(
184 quic_settings.enable_relaxed_loss_threshold,
185 );
186 config.discover_pmtu(quic_settings.discover_path_mtu);
187 config.set_pmtud_max_probes(quic_settings.pmtud_max_probes);
188 config.enable_hystart(quic_settings.enable_hystart);
189
190 config.enable_pacing(quic_settings.enable_pacing);
191 if let Some(max_pacing_rate) = quic_settings.max_pacing_rate {
192 config.set_max_pacing_rate(max_pacing_rate);
193 }
194
195 if quic_settings.verify_peer {
196 config.verify_peer(quic_settings.verify_peer);
197 }
198
199 config.set_max_connection_window(quic_settings.max_connection_window);
200 config.set_max_stream_window(quic_settings.max_stream_window);
201 config.set_enable_send_streams_blocked(
202 quic_settings.enable_send_streams_blocked,
203 );
204 config.grease(quic_settings.grease);
205 config.set_max_amplification_factor(quic_settings.max_amplification_factor);
206 config.set_send_capacity_factor(quic_settings.send_capacity_factor);
207 config.set_ack_delay_exponent(quic_settings.ack_delay_exponent);
208 config.set_max_ack_delay(quic_settings.max_ack_delay);
209 config.set_path_challenge_recv_max_queue_len(
210 quic_settings.max_path_challenge_recv_queue_len,
211 );
212 config.set_stateless_reset_token(quic_settings.stateless_reset_token);
213 config.set_disable_dcid_reuse(quic_settings.disable_dcid_reuse);
214
215 if let Some(track_unknown_transport_params) =
216 quic_settings.track_unknown_transport_parameters
217 {
218 config.enable_track_unknown_transport_parameters(
219 track_unknown_transport_params,
220 );
221 }
222 if params.settings.enable_early_data {
223 config.enable_early_data();
224 }
225
226 if should_log_keys {
227 config.log_keys();
228 }
229
230 Ok(config)
231}
232
233fn quiche_config_with_tls(
234 tls_cert: Option<TlsCertificatePaths>,
235) -> QuicResult<quiche::Config> {
236 let Some(tls) = tls_cert else {
237 return Ok(quiche::Config::new(quiche::PROTOCOL_VERSION).unwrap());
238 };
239
240 match tls.kind {
241 #[cfg(not(feature = "rpk"))]
242 CertificateKind::RawPublicKey => {
243 panic!("Can't use RPK when compiled without rpk feature");
245 },
246 #[cfg(all(feature = "rpk", not(boring_v5)))]
247 CertificateKind::RawPublicKey => {
248 let mut ssl_ctx_builder = boring::ssl::SslContextBuilder::new_rpk()?;
252 let raw_public_key = read_file(tls.cert)?;
253 ssl_ctx_builder.set_rpk_certificate(&raw_public_key)?;
254
255 let raw_private_key = read_file(tls.private_key)?;
256 let pkey =
257 boring::pkey::PKey::private_key_from_pem(&raw_private_key)?;
258 ssl_ctx_builder.set_null_chain_private_key(&pkey)?;
259
260 Ok(quiche::Config::with_boring_ssl_ctx_builder(
261 quiche::PROTOCOL_VERSION,
262 ssl_ctx_builder,
263 )?)
264 },
265 #[cfg(all(feature = "rpk", boring_v5))]
266 CertificateKind::RawPublicKey => {
267 let raw_public_key = read_file(tls.cert)?;
272 let raw_private_key = read_file(tls.private_key)?;
273 let pkey =
274 boring::pkey::PKey::private_key_from_pem(&raw_private_key)?;
275
276 let mut credential_builder =
277 boring::ssl::SslCredential::new_raw_public_key()?;
278 credential_builder.set_spki_bytes(Some(&raw_public_key))?;
279 credential_builder.set_private_key(&pkey)?;
280 let credential = credential_builder.build();
281
282 let mut ssl_ctx_builder = boring::ssl::SslContextBuilder::new(
283 boring::ssl::SslMethod::tls(),
284 )?;
285 ssl_ctx_builder.add_credential(&credential)?;
286
287 Ok(quiche::Config::with_boring_ssl_ctx_builder(
288 quiche::PROTOCOL_VERSION,
289 ssl_ctx_builder,
290 )?)
291 },
292 CertificateKind::X509 => {
293 let mut config =
294 quiche::Config::new(quiche::PROTOCOL_VERSION).unwrap();
295 config.load_cert_chain_from_pem_file(tls.cert)?;
296 config.load_priv_key_from_pem_file(tls.private_key)?;
297 Ok(config)
298 },
299 }
300}
301
302#[cfg(feature = "rpk")]
303fn read_file(path: &str) -> QuicResult<Vec<u8>> {
304 use anyhow::Context as _;
305 std::fs::read(path)
306 .with_context(|| format!("read {path}"))
307 .map_err(Into::into)
308}