1use std::sync::Mutex;
2
3use serde::Deserialize;
4use socket2::Socket;
5
6mod platform;
7
8#[derive(Debug, Clone, Default, PartialEq, Eq, Deserialize)]
9#[non_exhaustive]
10pub struct TcpProfile {
11 pub ttl: u32,
12 pub mss: u32,
13 pub window_size: u32,
14 pub df: bool,
15 pub window_scale: u32,
16 pub no_delay: bool,
17 pub options: Vec<u8>,
18}
19
20impl TcpProfile {
21 pub(crate) fn apply(&self, socket: &Socket, is_v6: bool) {
22 if self.ttl > 0 {
23 let result = if is_v6 {
24 socket.set_unicast_hops_v6(self.ttl)
25 } else {
26 socket.set_ttl_v4(self.ttl)
27 };
28 if let Err(e) = result {
29 log_once(if is_v6 { "IPV6_UNICAST_HOPS" } else { "IP_TTL" }, &e);
30 }
31 }
32
33 if self.no_delay
34 && let Err(e) = socket.set_tcp_nodelay(true)
35 {
36 log_once("TCP_NODELAY", &e);
37 }
38
39 platform::apply_platform_options(socket, self, is_v6);
40 }
41}
42
43static LOGGED: Mutex<Vec<&'static str>> = Mutex::new(Vec::new());
44
45fn log_once(option: &'static str, err: &std::io::Error) {
46 let mut logged = crate::util::lock(&LOGGED);
47 if !logged.contains(&option) {
48 logged.push(option);
49 tracing::warn!(option, error = %err, "setsockopt failed (non-fatal)");
50 }
51}
52
53#[cfg(test)]
54mod tests;