1use std::{io, os::fd::AsRawFd};
2
3use crate::{cmsg, imp::set_socket_option};
4
5pub(super) mod gso {
6 use super::*;
7 use std::{ffi::CStr, mem, str::FromStr, sync::OnceLock};
8
9 const SUPPORTED_SINCE: KernelVersion = KernelVersion {
12 version: 4,
13 major_revision: 18,
14 };
15
16 pub(crate) fn max_gso_segments(socket: &impl AsRawFd) -> usize {
20 const GSO_SIZE: libc::c_int = 1500;
21
22 if !SUPPORTED_BY_CURRENT_KERNEL.get_or_init(supported_by_current_kernel) {
23 return 1;
24 }
25
26 match set_socket_option(socket, libc::SOL_UDP, libc::UDP_SEGMENT, GSO_SIZE) {
29 Ok(()) => {
30 let _ = set_socket_option(socket, libc::SOL_UDP, libc::UDP_SEGMENT, 0);
35
36 64
37 }
38 Err(_e) => {
39 crate::log::debug!(
40 "failed to set `UDP_SEGMENT` socket option ({_e}); setting `max_gso_segments = 1`"
41 );
42
43 1
44 }
45 }
46 }
47
48 pub(crate) fn set_segment_size(
49 encoder: &mut cmsg::Encoder<'_, libc::msghdr>,
50 segment_size: u16,
51 ) {
52 encoder.push_segment_size(segment_size);
53 }
54
55 static SUPPORTED_BY_CURRENT_KERNEL: OnceLock<bool> = OnceLock::new();
57
58 fn supported_by_current_kernel() -> bool {
59 let kernel_version_string = match kernel_version_string() {
60 Ok(kernel_version_string) => kernel_version_string,
61 Err(_e) => {
62 crate::log::warn!("GSO disabled: uname returned {_e}");
63 return false;
64 }
65 };
66
67 let Some(kernel_version) = KernelVersion::from_str(&kernel_version_string) else {
68 crate::log::warn!(
69 "GSO disabled: failed to parse kernel version ({kernel_version_string})"
70 );
71 return false;
72 };
73
74 if kernel_version < SUPPORTED_SINCE {
75 crate::log::info!("GSO disabled: kernel too old ({kernel_version_string}); need 4.18+",);
76 return false;
77 }
78
79 true
80 }
81
82 fn kernel_version_string() -> io::Result<String> {
83 let mut n = unsafe { mem::zeroed() };
84 let r = unsafe { libc::uname(&mut n) };
85 if r != 0 {
86 return Err(io::Error::last_os_error());
87 }
88 Ok(unsafe {
89 CStr::from_ptr(n.release[..].as_ptr())
90 .to_string_lossy()
91 .into_owned()
92 })
93 }
94
95 #[derive(Eq, PartialEq, Ord, PartialOrd, Debug)]
97 struct KernelVersion {
98 version: u8,
99 major_revision: u8,
100 }
101
102 impl KernelVersion {
103 fn from_str(release: &str) -> Option<Self> {
104 let mut split = release
105 .split_once('-')
106 .map(|pair| pair.0)
107 .unwrap_or(release)
108 .split('.');
109
110 let version = u8::from_str(split.next()?).ok()?;
111 let major_revision = u8::from_str(split.next()?).ok()?;
112
113 Some(Self {
114 version,
115 major_revision,
116 })
117 }
118 }
119
120 #[cfg(test)]
121 mod test {
122 use super::*;
123
124 #[test]
125 fn parse_current_kernel_version_release_string() {
126 let release = kernel_version_string().unwrap();
127 KernelVersion::from_str(&release).unwrap();
128 }
129
130 #[test]
131 fn parse_kernel_version_release_string() {
132 assert_eq!(
134 KernelVersion::from_str("4.14"),
135 Some(KernelVersion {
136 version: 4,
137 major_revision: 14
138 })
139 );
140 assert_eq!(
141 KernelVersion::from_str("4.18"),
142 Some(KernelVersion {
143 version: 4,
144 major_revision: 18
145 })
146 );
147 assert_eq!(
149 KernelVersion::from_str("4.14.186-27095505"),
150 Some(KernelVersion {
151 version: 4,
152 major_revision: 14
153 })
154 );
155 assert_eq!(
156 KernelVersion::from_str("6.8.0-59-generic"),
157 Some(KernelVersion {
158 version: 6,
159 major_revision: 8
160 })
161 );
162 }
163 }
164}