1#[cfg(feature = "qlog")]
2use std::path::PathBuf;
3use std::{io, net::SocketAddr, num::ParseIntError, str::FromStr, sync::Arc, time::Duration};
4
5use anyhow::{Context, Result};
6use clap::{Parser, ValueEnum};
7use noq::{
8 AckFrequencyConfig, TransportConfig, VarInt,
9 congestion::{self, ControllerFactory},
10 udp::UdpSocketState,
11};
12use rustls::crypto::ring::cipher_suite;
13use socket2::{Domain, Protocol, Socket, Type};
14use tracing::warn;
15
16#[cfg_attr(not(feature = "json-output"), allow(dead_code))]
17pub mod stats;
18
19pub mod noprotection;
20
21pub mod client;
22pub mod server;
23
24#[derive(Parser)]
26pub struct CommonOpt {
27 #[clap(long, default_value = "2M", value_parser = parse_byte_size)]
32 pub send_buffer_size: u64,
33 #[clap(long, default_value = "2M", value_parser = parse_byte_size)]
38 pub recv_buffer_size: u64,
39 #[clap(long)]
41 pub conn_stats: bool,
42 #[clap(long = "keylog")]
44 pub keylog: bool,
45 #[clap(long, default_value = "1200")]
47 pub initial_mtu: u16,
48 #[clap(long = "no-protection")]
50 pub no_protection: bool,
51 #[clap(long, group = "common")]
53 pub initial_rtt: Option<u64>,
54 #[clap(long = "ack-frequency")]
56 pub ack_frequency: bool,
57 #[clap(long = "congestion")]
59 pub cong_alg: Option<CongestionAlgorithm>,
60 #[clap(long, value_parser = parse_byte_size)]
66 pub stream_receive_window: Option<u64>,
67 #[clap(long, value_parser = parse_byte_size)]
73 pub receive_window: Option<u64>,
74 #[clap(long, value_parser = parse_byte_size)]
79 pub send_window: Option<u64>,
80 #[clap(long, default_value = "1472")]
82 pub max_udp_payload_size: u16,
83 #[cfg(feature = "qlog")]
87 #[clap(long = "qlog")]
88 pub qlog_dir: Option<PathBuf>,
89}
90
91impl CommonOpt {
92 pub fn build_transport_config(
93 &self,
94 #[cfg(feature = "qlog")] name: &str,
95 ) -> io::Result<TransportConfig> {
96 let mut transport = TransportConfig::default();
97 transport.initial_mtu(self.initial_mtu);
98
99 if let Some(initial_rtt) = self.initial_rtt {
100 transport.initial_rtt(Duration::from_millis(initial_rtt));
101 }
102
103 if self.ack_frequency {
104 transport.ack_frequency_config(Some(AckFrequencyConfig::default()));
105 }
106
107 if let Some(cong_alg) = self.cong_alg {
108 transport.congestion_controller_factory(cong_alg.build());
109 }
110
111 if let Some(stream_receive_window) = self.stream_receive_window {
112 transport.stream_receive_window(
113 VarInt::from_u64(stream_receive_window).unwrap_or(VarInt::MAX),
114 );
115 }
116
117 if let Some(receive_window) = self.receive_window {
118 transport.receive_window(VarInt::from_u64(receive_window).unwrap_or(VarInt::MAX));
119 }
120
121 if let Some(send_window) = self.send_window {
122 transport.send_window(send_window);
123 }
124
125 #[cfg(feature = "qlog")]
126 if let Some(qlog_dir) = &self.qlog_dir {
127 transport.qlog_from_path(qlog_dir, name);
128 } else {
129 transport.qlog_from_env(name);
130 }
131
132 Ok(transport)
133 }
134
135 pub fn bind_socket(&self, addr: SocketAddr) -> Result<std::net::UdpSocket> {
136 let socket = Socket::new(Domain::for_address(addr), Type::DGRAM, Some(Protocol::UDP))
137 .context("create socket")?;
138
139 if addr.is_ipv6() {
140 socket.set_only_v6(false).context("set_only_v6")?;
141 }
142
143 socket
144 .bind(&socket2::SockAddr::from(addr))
145 .context("binding endpoint")?;
146
147 let socket_state = UdpSocketState::new((&socket).into())?;
148 socket_state
149 .set_send_buffer_size((&socket).into(), self.send_buffer_size as usize)
150 .context("send buffer size")?;
151 socket_state
152 .set_recv_buffer_size((&socket).into(), self.recv_buffer_size as usize)
153 .context("recv buffer size")?;
154
155 let buf_size = socket_state
156 .send_buffer_size((&socket).into())
157 .context("send buffer size")?;
158 if buf_size < self.send_buffer_size as usize {
159 warn!(
160 "Unable to set desired send buffer size. Desired: {}, Actual: {}",
161 self.send_buffer_size, buf_size
162 );
163 }
164
165 let buf_size = socket_state
166 .recv_buffer_size((&socket).into())
167 .context("recv buffer size")?;
168 if buf_size < self.recv_buffer_size as usize {
169 warn!(
170 "Unable to set desired recv buffer size. Desired: {}, Actual: {}",
171 self.recv_buffer_size, buf_size
172 );
173 }
174
175 Ok(socket.into())
176 }
177}
178
179pub fn parse_byte_size(s: &str) -> Result<u64, ParseIntError> {
180 let s = s.trim();
181
182 let multiplier = match s.chars().last() {
183 Some('T') => 1024 * 1024 * 1024 * 1024,
184 Some('G') => 1024 * 1024 * 1024,
185 Some('M') => 1024 * 1024,
186 Some('k') => 1024,
187 _ => 1,
188 };
189
190 let s = match multiplier {
191 1 => s,
192 _ => &s[..s.len() - 1],
193 };
194
195 Ok(u64::from_str(s)? * multiplier)
196}
197
198#[derive(Clone, Copy, ValueEnum)]
199pub enum CongestionAlgorithm {
200 Cubic,
201 Bbr3,
202 NewReno,
203}
204
205impl CongestionAlgorithm {
206 pub fn build(self) -> Arc<dyn ControllerFactory + Send + Sync + 'static> {
207 match self {
208 Self::Cubic => Arc::new(congestion::CubicConfig::default()),
209 Self::Bbr3 => Arc::new(congestion::Bbr3Config::default()),
210 Self::NewReno => Arc::new(congestion::NewRenoConfig::default()),
211 }
212 }
213}
214
215pub static PERF_CIPHER_SUITES: &[rustls::SupportedCipherSuite] = &[
216 cipher_suite::TLS13_AES_128_GCM_SHA256,
217 cipher_suite::TLS13_AES_256_GCM_SHA384,
218 cipher_suite::TLS13_CHACHA20_POLY1305_SHA256,
219];