1use std::io;
8use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4, UdpSocket};
9use std::os::fd::{AsRawFd, RawFd};
10
11use crate::MAX_WIRE_DATAGRAM;
12
13pub const BATCH_SIZE: usize = 32;
15
16pub const BATCH_SIZE_64: usize = 64;
18
19pub const BATCH_SIZE_128: usize = 128;
21
22#[derive(Debug, Clone, Copy, PartialEq, Eq)]
24pub struct ReceivedDatagram {
25 pub len: usize,
27 pub src: SocketAddr,
29}
30
31impl ReceivedDatagram {
32 #[must_use]
34 pub const fn empty() -> Self {
35 Self {
36 len: 0,
37 src: SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::UNSPECIFIED, 0)),
38 }
39 }
40}
41
42impl Default for ReceivedDatagram {
43 fn default() -> Self {
44 Self::empty()
45 }
46}
47
48#[derive(Debug)]
51pub struct BatchUdpSocket {
52 fd: RawFd,
53}
54
55impl BatchUdpSocket {
56 #[must_use]
58 pub fn new(sock: &UdpSocket) -> Self {
59 Self {
60 fd: sock.as_raw_fd(),
61 }
62 }
63
64 #[must_use]
66 pub const fn from_raw_fd(fd: RawFd) -> Self {
67 Self { fd }
68 }
69
70 #[must_use]
72 pub const fn raw_fd(&self) -> RawFd {
73 self.fd
74 }
75
76 pub fn recvmmsg_batch<const N: usize>(
89 &mut self,
90 buffers: &mut [[u8; MAX_WIRE_DATAGRAM]; N],
91 out: &mut [ReceivedDatagram; N],
92 ) -> io::Result<usize> {
93 if N == 0 {
94 return Ok(0);
95 }
96
97 let mut storages: [libc::sockaddr_storage; N] = unsafe {
98 std::mem::zeroed()
102 };
103 let addrlens = [libc::socklen_t::try_from(std::mem::size_of::<libc::sockaddr_storage>())
104 .expect("sockaddr_storage size fits socklen_t"); N];
105 let mut iovecs = [libc::iovec {
106 iov_base: std::ptr::null_mut(),
107 iov_len: 0,
108 }; N];
109 let mut msgs = [crate::empty_mmsghdr(); N];
110
111 for i in 0..N {
112 iovecs[i].iov_base = buffers[i].as_mut_ptr().cast::<libc::c_void>();
113 iovecs[i].iov_len = MAX_WIRE_DATAGRAM;
114 msgs[i].msg_hdr.msg_iov = &raw mut iovecs[i];
115 msgs[i].msg_hdr.msg_iovlen = 1;
116 msgs[i].msg_hdr.msg_name = std::ptr::from_mut(&mut storages[i]).cast::<libc::c_void>();
117 msgs[i].msg_hdr.msg_namelen = addrlens[i];
118 }
119
120 loop {
121 let ret = unsafe {
128 libc::recvmmsg(
129 self.fd,
130 msgs.as_mut_ptr(),
131 u32::try_from(N).expect("N fits u32"),
132 libc::MSG_DONTWAIT as crate::MsgFlags,
133 std::ptr::null_mut(),
134 )
135 };
136
137 if ret < 0 {
138 let err = io::Error::last_os_error();
139 let raw = err.raw_os_error().unwrap_or(0);
140 if raw == libc::EINTR {
141 continue;
142 }
143 if raw == libc::EWOULDBLOCK || raw == libc::EAGAIN {
144 return Ok(0);
145 }
146 return Err(err);
147 }
148
149 let received = usize::try_from(ret).expect("recvmmsg return fits usize");
150 assert!(received <= N, "recvmmsg returned count exceeding N");
151
152 for i in 0..received {
153 let len = usize::try_from(msgs[i].msg_len).expect("msg_len fits usize");
154 let src = crate::addr::sockaddr_to_std(&storages[i], msgs[i].msg_hdr.msg_namelen)
155 .unwrap_or_else(|_| {
156 SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::UNSPECIFIED, 0))
157 });
158 out[i] = ReceivedDatagram { len, src };
159 }
160
161 return Ok(received);
162 }
163 }
164
165 pub fn sendmmsg_batch(&mut self, packets: &[(&[u8], SocketAddr)]) -> io::Result<usize> {
174 self.sendmmsg_burst::<BATCH_SIZE>(packets)
175 }
176
177 pub fn sendmmsg_burst<const N: usize>(
186 &mut self,
187 packets: &[(&[u8], SocketAddr)],
188 ) -> io::Result<usize> {
189 let count = packets.len().min(N);
190 if count == 0 {
191 return Ok(0);
192 }
193
194 let mut storages: [libc::sockaddr_storage; N] = unsafe {
195 std::mem::zeroed()
198 };
199 let mut addrlens = [0 as libc::socklen_t; N];
200 let mut iovecs = [libc::iovec {
201 iov_base: std::ptr::null_mut(),
202 iov_len: 0,
203 }; N];
204 let mut msgs = [crate::empty_mmsghdr(); N];
205
206 for (i, &(payload, dst)) in packets[..count].iter().enumerate() {
207 let (storage, addr_len) = crate::addr::std_to_sockaddr(dst);
208 storages[i] = storage;
209 addrlens[i] = addr_len;
210 iovecs[i].iov_base = payload.as_ptr().cast_mut().cast::<libc::c_void>();
211 iovecs[i].iov_len = payload.len();
212 msgs[i].msg_hdr.msg_iov = &raw mut iovecs[i];
213 msgs[i].msg_hdr.msg_iovlen = 1;
214 msgs[i].msg_hdr.msg_name = std::ptr::from_mut(&mut storages[i]).cast::<libc::c_void>();
215 msgs[i].msg_hdr.msg_namelen = addrlens[i];
216 }
217
218 loop {
219 let ret = unsafe {
224 libc::sendmmsg(
225 self.fd,
226 msgs.as_mut_ptr(),
227 u32::try_from(count).expect("count fits u32"),
228 libc::MSG_NOSIGNAL as crate::MsgFlags,
229 )
230 };
231
232 if ret < 0 {
233 let err = io::Error::last_os_error();
234 let raw = err.raw_os_error().unwrap_or(0);
235 if raw == libc::EINTR {
236 continue;
237 }
238 if raw == libc::EWOULDBLOCK || raw == libc::EAGAIN || raw == libc::ENOBUFS {
239 return Ok(0);
240 }
241 return Err(err);
242 }
243
244 return Ok(usize::try_from(ret).expect("sendmmsg return fits usize"));
245 }
246 }
247}
248
249impl AsRawFd for BatchUdpSocket {
250 fn as_raw_fd(&self) -> RawFd {
251 self.fd
252 }
253}
254
255pub fn set_udp_gso_segment(sock: &UdpSocket, segment_size: u16) -> io::Result<bool> {
258 let val: libc::c_int = libc::c_int::from(segment_size);
259 let ret = unsafe {
261 libc::setsockopt(
262 sock.as_raw_fd(),
263 libc::SOL_UDP,
264 libc::UDP_SEGMENT,
265 std::ptr::from_ref(&val).cast::<libc::c_void>(),
266 std::mem::size_of::<libc::c_int>() as libc::socklen_t,
267 )
268 };
269 if ret == 0 {
270 Ok(true)
271 } else {
272 let err = io::Error::last_os_error();
273 let raw = err.raw_os_error().unwrap_or(0);
274 if raw == libc::EINVAL || raw == libc::ENOPROTOOPT || raw == libc::EOPNOTSUPP {
275 Ok(false)
276 } else {
277 Err(err)
278 }
279 }
280}
281
282pub fn send_gso_superpacket(
285 sock: &UdpSocket,
286 payload: &[u8],
287 segment_size: u16,
288 dst: SocketAddr,
289) -> io::Result<usize> {
290 let (mut storage, addr_len) = crate::addr::std_to_sockaddr(dst);
291 let mut iov = libc::iovec {
292 iov_base: payload.as_ptr().cast_mut().cast::<libc::c_void>(),
293 iov_len: payload.len(),
294 };
295 let mut control = [0u8; 64];
296 let mut msg: libc::msghdr = unsafe { std::mem::zeroed() };
298 msg.msg_name = std::ptr::from_mut(&mut storage).cast::<libc::c_void>();
299 msg.msg_namelen = addr_len;
300 msg.msg_iov = &raw mut iov;
301 msg.msg_iovlen = 1;
302 msg.msg_control = control.as_mut_ptr().cast::<libc::c_void>();
303 let cmsg_space =
305 usize::try_from(unsafe { libc::CMSG_SPACE(std::mem::size_of::<u16>() as u32) })
306 .unwrap_or(control.len());
307 msg.msg_controllen = cmsg_space as crate::CmsgLen;
308
309 unsafe {
311 let cmsg = libc::CMSG_FIRSTHDR(&raw const msg);
312 if !cmsg.is_null() {
313 (*cmsg).cmsg_level = libc::SOL_UDP;
314 (*cmsg).cmsg_type = libc::UDP_SEGMENT;
315 (*cmsg).cmsg_len = usize::try_from(libc::CMSG_LEN(std::mem::size_of::<u16>() as u32))
316 .unwrap_or(0) as crate::CmsgLen;
317 let seg_ptr = libc::CMSG_DATA(cmsg).cast::<u16>();
318 *seg_ptr = segment_size;
319 }
320 }
321
322 loop {
323 let ret = unsafe { libc::sendmsg(sock.as_raw_fd(), &raw const msg, libc::MSG_NOSIGNAL) };
325 if ret < 0 {
326 let err = io::Error::last_os_error();
327 let raw = err.raw_os_error().unwrap_or(0);
328 if raw == libc::EINTR {
329 continue;
330 }
331 if raw == libc::EWOULDBLOCK || raw == libc::EAGAIN || raw == libc::ENOBUFS {
332 return Ok(0);
333 }
334 return Err(err);
335 }
336 return Ok(usize::try_from(ret).unwrap_or(0));
337 }
338}