1#[cfg(unix)]
7mod unix_socket {
8 use std::{
9 io,
10 net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV6},
11 os::fd::{AsRawFd, FromRawFd, IntoRawFd, OwnedFd},
12 };
13
14 #[cfg(target_os = "linux")]
16 use linux_inner as plat;
17 use tokio::io::{Interest, unix::AsyncFd};
18 #[cfg(all(unix, not(target_os = "linux")))]
19 use unix_inner as plat;
20
21 use crate::{
22 capability::Capabilities,
23 ecn::Ecn,
24 item::{ReceiveItem, SendItem},
25 };
26
27 pub struct FastUdpSocket {
29 fd: AsyncFd<OwnedFd>,
30 caps: Capabilities,
31 }
32
33 impl FastUdpSocket {
34 pub fn build(addr: SocketAddr) -> FastUdpSocketBuilder {
35 FastUdpSocketBuilder {
36 addr,
37 disable_gso: false,
38 disable_gro: false,
39 disable_sendmmsg: false,
40 disable_ecn: false,
41 max_batch: 64,
42 enable_reuse_port: false,
43 }
44 }
45 }
46
47 impl FastUdpSocket {
48 pub async fn send(&self, item: SendItem<'_>) -> io::Result<()> {
49 let items = [item];
50 self.send_many(&items).await?;
51 Ok(())
52 }
53
54 pub async fn send_many(&self, items: &[SendItem<'_>]) -> io::Result<usize> {
55 if items.is_empty() {
56 return Ok(0);
57 }
58 let caps = &self.caps;
59 let limit = items.len().min(caps.max_batch);
60 let items = &items[..limit];
61 let mut state = plat::SendState::default();
62 self.fd
63 .async_io(Interest::WRITABLE, |inner| {
64 plat::send_batch(inner.as_raw_fd(), items, caps, &mut state)
65 })
66 .await
67 }
68
69 pub async fn receive(&self, item: &mut ReceiveItem<'_>) -> io::Result<()> {
70 self.receive_many(std::slice::from_mut(item)).await?;
71 Ok(())
72 }
73
74 pub async fn receive_many(&self, items: &mut [ReceiveItem<'_>]) -> io::Result<usize> {
75 if items.is_empty() {
76 return Ok(0);
77 }
78 let caps = &self.caps;
79 let limit = items.len().min(caps.max_batch);
80 let items = &mut items[..limit];
81 self.fd
82 .async_io(Interest::READABLE, |inner| plat::recv_batch(inner.as_raw_fd(), items, caps))
83 .await
84 }
85
86 pub fn capabilities(&self) -> &Capabilities {
87 &self.caps
88 }
89
90 pub fn local_addr(&self) -> io::Result<SocketAddr> {
91 socket_local_addr(self.fd.get_ref())
92 }
93 }
94
95 pub struct FastUdpSocketBuilder {
97 addr: SocketAddr,
98 disable_gso: bool,
99 disable_gro: bool,
100 disable_sendmmsg: bool,
101 disable_ecn: bool,
102 max_batch: usize,
103 enable_reuse_port: bool,
104 }
105
106 impl FastUdpSocketBuilder {
107 pub fn gso(mut self, enable: bool) -> Self {
108 self.disable_gso = !enable;
109 self
110 }
111
112 pub fn gro(mut self, enable: bool) -> Self {
113 self.disable_gro = !enable;
114 self
115 }
116
117 pub fn sendmmsg(mut self, enable: bool) -> Self {
118 self.disable_sendmmsg = !enable;
119 self
120 }
121
122 pub fn ecn(mut self, enable: bool) -> Self {
123 self.disable_ecn = !enable;
124 self
125 }
126
127 pub fn reuse_port(mut self, enable: bool) -> Self {
128 self.enable_reuse_port = enable;
129 self
130 }
131
132 pub fn max_batch_size(mut self, n: usize) -> Self {
133 self.max_batch = n;
134 self
135 }
136
137 pub fn bind(self) -> io::Result<FastUdpSocket> {
138 let is_ipv6 = self.addr.is_ipv6();
139
140 let fd: OwnedFd = if self.enable_reuse_port {
141 create_socket_with_reuse_port(self.addr)?
142 } else {
143 let std_socket = std::net::UdpSocket::bind(self.addr)?;
144 std_socket.set_nonblocking(true)?;
145
146 let raw_fd = std_socket.into_raw_fd();
147 unsafe { OwnedFd::from_raw_fd(raw_fd) }
148 };
149
150 let config = BuildConfig {
151 disable_gso: self.disable_gso,
152 disable_gro: self.disable_gro,
153 disable_sendmmsg: self.disable_sendmmsg,
154 disable_ecn: self.disable_ecn,
155 max_batch: self.max_batch,
156 };
157
158 let caps = plat::setup_and_probe(&fd, &config, is_ipv6)?;
159 let async_fd = unsafe { AsyncFd::register(fd) }?;
163
164 Ok(FastUdpSocket {
165 fd: async_fd,
166 caps,
167 })
168 }
169 }
170
171 const CMSG_BUF_SIZE: usize = 128;
178
179 #[repr(align(8))]
181 struct CmsgBuf {
182 buf: [u8; CMSG_BUF_SIZE],
183 len: usize,
184 }
185
186 impl CmsgBuf {
187 fn new() -> Self {
188 Self {
189 buf: [0u8; CMSG_BUF_SIZE],
190 len: 0,
191 }
192 }
193
194 #[cfg(any(target_os = "linux", target_vendor = "apple"))]
196 fn push(&mut self, level: libc::c_int, ty: libc::c_int, data: *const u8, data_len: usize) -> bool {
197 let space = unsafe { libc::CMSG_SPACE(data_len as u32) as usize };
198 if self.len + space > self.buf.len() {
199 return false;
200 }
201
202 let header = unsafe { self.buf.as_mut_ptr().add(self.len) as *mut libc::cmsghdr };
206 unsafe {
207 (*header).cmsg_len = libc::CMSG_LEN(data_len as u32) as _;
208 (*header).cmsg_level = level;
209 (*header).cmsg_type = ty;
210 std::ptr::copy_nonoverlapping(data, libc::CMSG_DATA(header), data_len);
211 }
212 self.len += space;
213 true
214 }
215
216 fn as_ptr(&self) -> *const libc::c_void {
225 if self.len == 0 {
226 std::ptr::null()
227 } else {
228 self.buf.as_ptr() as *const libc::c_void
229 }
230 }
231
232 fn len(&self) -> usize {
233 self.len
234 }
235 }
236
237 #[repr(align(8))]
239 struct RecvCmsgBuf([u8; CMSG_BUF_SIZE]);
240
241 impl RecvCmsgBuf {
242 fn new() -> Self {
243 RecvCmsgBuf([0u8; CMSG_BUF_SIZE])
244 }
245 }
246
247 fn reject_zero_segment_size(items: &[SendItem<'_>]) -> io::Result<()> {
250 if items.iter().any(|item| item.segment_size == Some(0)) {
251 return Err(io::Error::new(
252 io::ErrorKind::InvalidInput,
253 "segment_size must be greater than zero",
254 ));
255 }
256 Ok(())
257 }
258
259 fn set_bool_opt(fd: std::os::fd::RawFd, level: libc::c_int, name: libc::c_int) -> io::Result<()> {
261 let enable: libc::c_int = 1;
262 let ret = unsafe {
263 libc::setsockopt(
264 fd,
265 level,
266 name,
267 &enable as *const libc::c_int as *const libc::c_void,
268 std::mem::size_of::<libc::c_int>() as libc::socklen_t,
269 )
270 };
271 if ret < 0 {
272 return Err(io::Error::last_os_error());
273 }
274 Ok(())
275 }
276
277 #[cfg(target_os = "linux")]
279 fn get_int_opt(fd: std::os::fd::RawFd, level: libc::c_int, name: libc::c_int) -> io::Result<libc::c_int> {
280 let mut value: libc::c_int = 0;
281 let mut len = std::mem::size_of::<libc::c_int>() as libc::socklen_t;
282 let ret =
283 unsafe { libc::getsockopt(fd, level, name, &mut value as *mut libc::c_int as *mut libc::c_void, &mut len) };
284 if ret < 0 {
285 return Err(io::Error::last_os_error());
286 }
287 Ok(value)
288 }
289
290 fn create_socket_with_reuse_port(addr: SocketAddr) -> io::Result<OwnedFd> {
291 let domain = if addr.is_ipv6() { libc::AF_INET6 } else { libc::AF_INET };
292
293 let raw_fd = unsafe { libc::socket(domain, libc::SOCK_DGRAM, 0) };
297 if raw_fd < 0 {
298 return Err(io::Error::last_os_error());
299 }
300 let fd = unsafe { OwnedFd::from_raw_fd(raw_fd) };
301
302 set_cloexec(&fd)?;
303 set_nonblocking(&fd)?;
304 set_bool_opt(fd.as_raw_fd(), libc::SOL_SOCKET, libc::SO_REUSEPORT)?;
305
306 let mut sock_addr: libc::sockaddr_storage = unsafe { std::mem::zeroed() };
307 let addr_len = fill_sockaddr(&addr, &mut sock_addr);
308 let ret = unsafe { libc::bind(fd.as_raw_fd(), &sock_addr as *const _ as *const libc::sockaddr, addr_len) };
309 if ret < 0 {
310 return Err(io::Error::last_os_error());
311 }
312
313 Ok(fd)
314 }
315
316 fn set_cloexec(fd: &OwnedFd) -> io::Result<()> {
318 let ret = unsafe { libc::fcntl(fd.as_raw_fd(), libc::F_SETFD, libc::FD_CLOEXEC) };
319 if ret < 0 {
320 return Err(io::Error::last_os_error());
321 }
322 Ok(())
323 }
324
325 fn set_nonblocking(fd: &OwnedFd) -> io::Result<()> {
327 let flags = unsafe { libc::fcntl(fd.as_raw_fd(), libc::F_GETFL) };
328 if flags < 0 {
329 return Err(io::Error::last_os_error());
330 }
331 let ret = unsafe { libc::fcntl(fd.as_raw_fd(), libc::F_SETFL, flags | libc::O_NONBLOCK) };
332 if ret < 0 {
333 return Err(io::Error::last_os_error());
334 }
335 Ok(())
336 }
337
338 #[cfg(any(target_os = "linux", target_vendor = "apple"))]
339 fn ecn_to_tos(ecn: Ecn) -> u8 {
340 ecn.to_tos_bits()
341 }
342
343 fn fill_sockaddr(addr: &SocketAddr, ss: &mut libc::sockaddr_storage) -> libc::socklen_t {
345 unsafe {
346 std::ptr::write_bytes(ss as *mut _ as *mut u8, 0, std::mem::size_of::<libc::sockaddr_storage>());
347 }
348 match addr {
349 SocketAddr::V4(v4) => {
350 let sin = ss as *mut _ as *mut libc::sockaddr_in;
351 unsafe {
352 (*sin).sin_family = libc::AF_INET as libc::sa_family_t;
353 (*sin).sin_port = v4.port().to_be();
354 (*sin).sin_addr.s_addr = u32::from_be_bytes(v4.ip().octets()).to_be();
355 }
356 #[cfg(any(
358 target_vendor = "apple",
359 target_os = "freebsd",
360 target_os = "dragonfly",
361 target_os = "netbsd",
362 target_os = "openbsd"
363 ))]
364 unsafe {
365 (*sin).sin_len = std::mem::size_of::<libc::sockaddr_in>() as u8;
366 }
367 std::mem::size_of::<libc::sockaddr_in>() as libc::socklen_t
368 }
369 SocketAddr::V6(v6) => {
370 let sin6 = ss as *mut _ as *mut libc::sockaddr_in6;
371 unsafe {
372 (*sin6).sin6_family = libc::AF_INET6 as libc::sa_family_t;
373 (*sin6).sin6_port = v6.port().to_be();
374 (*sin6).sin6_addr.s6_addr = v6.ip().octets();
375 (*sin6).sin6_flowinfo = v6.flowinfo().to_be();
376 (*sin6).sin6_scope_id = v6.scope_id();
377 }
378 #[cfg(any(
379 target_vendor = "apple",
380 target_os = "freebsd",
381 target_os = "dragonfly",
382 target_os = "netbsd",
383 target_os = "openbsd"
384 ))]
385 unsafe {
386 (*sin6).sin6_len = std::mem::size_of::<libc::sockaddr_in6>() as u8;
387 }
388 std::mem::size_of::<libc::sockaddr_in6>() as libc::socklen_t
389 }
390 }
391 }
392
393 fn storage_to_addr(ss: &libc::sockaddr_storage) -> SocketAddr {
397 match ss.ss_family as libc::c_int {
398 libc::AF_INET => {
399 let sin = unsafe { &*(ss as *const _ as *const libc::sockaddr_in) };
400 let ip = Ipv4Addr::from(sin.sin_addr.s_addr.to_ne_bytes());
401 SocketAddr::new(IpAddr::V4(ip), u16::from_be(sin.sin_port))
402 }
403 libc::AF_INET6 => {
404 let sin6 = unsafe { &*(ss as *const _ as *const libc::sockaddr_in6) };
405 let ip = Ipv6Addr::from(sin6.sin6_addr.s6_addr);
406 SocketAddr::V6(SocketAddrV6::new(
407 ip,
408 u16::from_be(sin6.sin6_port),
409 u32::from_be(sin6.sin6_flowinfo),
410 sin6.sin6_scope_id,
411 ))
412 }
413 _ => SocketAddr::new(IpAddr::V4(Ipv4Addr::UNSPECIFIED), 0),
414 }
415 }
416
417 fn sendmsg_raw(fd: std::os::fd::RawFd, data: &[u8], destination: &SocketAddr, cmsgs: &CmsgBuf) -> io::Result<()> {
419 let mut iov = libc::iovec {
422 iov_base: data.as_ptr() as *mut libc::c_void,
423 iov_len: data.len(),
424 };
425
426 let mut addr: libc::sockaddr_storage = unsafe { std::mem::zeroed() };
427 let addr_len = fill_sockaddr(destination, &mut addr);
428
429 let mut mhdr: libc::msghdr = unsafe { std::mem::zeroed() };
430 mhdr.msg_name = &mut addr as *mut _ as *mut libc::c_void;
431 mhdr.msg_namelen = addr_len;
432 mhdr.msg_iov = &mut iov as *mut libc::iovec;
433 mhdr.msg_iovlen = 1;
434 mhdr.msg_control = cmsgs.as_ptr() as *mut libc::c_void;
435 mhdr.msg_controllen = cmsgs.len() as _;
436
437 let ret = unsafe { libc::sendmsg(fd, &mhdr, 0) };
438 if ret < 0 {
439 return Err(io::Error::last_os_error());
440 }
441 Ok(())
442 }
443
444 fn recvmsg_raw(
449 fd: std::os::fd::RawFd,
450 buf: &mut [u8],
451 cmsg_buf: &mut RecvCmsgBuf,
452 ecn_enabled: bool,
453 ) -> io::Result<(usize, SocketAddr, Option<u32>, Option<Ecn>)> {
454 let mut iov = libc::iovec {
455 iov_base: buf.as_mut_ptr() as *mut libc::c_void,
456 iov_len: buf.len(),
457 };
458 let mut addr: libc::sockaddr_storage = unsafe { std::mem::zeroed() };
459
460 let mut mhdr: libc::msghdr = unsafe { std::mem::zeroed() };
461 mhdr.msg_name = &mut addr as *mut _ as *mut libc::c_void;
462 mhdr.msg_namelen = std::mem::size_of::<libc::sockaddr_storage>() as _;
463 mhdr.msg_iov = &mut iov as *mut libc::iovec;
464 mhdr.msg_iovlen = 1;
465 mhdr.msg_control = cmsg_buf.0.as_mut_ptr() as *mut libc::c_void;
466 mhdr.msg_controllen = cmsg_buf.0.len() as _;
467
468 let ret = unsafe { libc::recvmsg(fd, &mut mhdr, 0) };
469 if ret < 0 {
470 return Err(io::Error::last_os_error());
471 }
472
473 let (gro_seg, ecn) = decode_cmsgs(&mhdr, ecn_enabled);
474 Ok((ret as usize, storage_to_addr(&addr), gro_seg, ecn))
475 }
476
477 fn decode_cmsgs(mhdr: &libc::msghdr, ecn_enabled: bool) -> (Option<u32>, Option<Ecn>) {
483 #[cfg_attr(not(target_os = "linux"), allow(unused_mut))]
484 let mut gro_seg = None;
485 let mut ecn = None;
486
487 let mut cmsg = unsafe { libc::CMSG_FIRSTHDR(mhdr) };
488 while !cmsg.is_null() {
489 let level = unsafe { (*cmsg).cmsg_level };
490 let ty = unsafe { (*cmsg).cmsg_type };
491 let data = unsafe { libc::CMSG_DATA(cmsg) };
492 let header_len = unsafe { libc::CMSG_LEN(0) } as usize;
493 let payload_len = (unsafe { (*cmsg).cmsg_len } as usize).saturating_sub(header_len);
494
495 #[cfg(target_os = "linux")]
497 if level == libc::SOL_UDP && ty == libc::UDP_GRO && gro_seg.is_none() && payload_len >= 4 {
498 gro_seg = Some(unsafe { std::ptr::read_unaligned(data as *const libc::c_int) } as u32);
499 }
500
501 if ecn_enabled && ecn.is_none() {
502 if level == libc::IPPROTO_IP && (ty == libc::IP_TOS || ty == libc::IP_RECVTOS) && payload_len >= 1 {
505 ecn = Some(Ecn::from_tos_bits(unsafe { std::ptr::read_unaligned(data as *const u8) }));
506 }
507 if level == libc::IPPROTO_IPV6 && ty == libc::IPV6_TCLASS {
510 if payload_len >= std::mem::size_of::<libc::c_int>() {
511 let tc = unsafe { std::ptr::read_unaligned(data as *const libc::c_int) };
512 ecn = Some(Ecn::from_tos_bits(tc as u8));
513 } else if payload_len >= 1 {
514 ecn = Some(Ecn::from_tos_bits(unsafe { std::ptr::read_unaligned(data as *const u8) }));
515 }
516 }
517 }
518
519 cmsg = unsafe { libc::CMSG_NXTHDR(mhdr, cmsg) };
520 }
521
522 (gro_seg, ecn)
523 }
524
525 #[cfg(target_os = "linux")]
527 fn push_gso_cmsg(cmsgs: &mut CmsgBuf, segment_size: u16) -> bool {
528 cmsgs.push(
529 libc::SOL_UDP,
530 libc::UDP_SEGMENT,
531 &segment_size as *const u16 as *const u8,
532 std::mem::size_of::<u16>(),
533 )
534 }
535
536 #[cfg(any(target_os = "linux", target_vendor = "apple"))]
538 fn push_ecn_cmsg(cmsgs: &mut CmsgBuf, ecn: Ecn, is_v6: bool) -> bool {
539 if is_v6 {
540 let value = ecn_to_tos(ecn) as libc::c_int;
541 cmsgs.push(
542 libc::IPPROTO_IPV6,
543 libc::IPV6_TCLASS,
544 &value as *const libc::c_int as *const u8,
545 std::mem::size_of::<libc::c_int>(),
546 )
547 } else if cfg!(target_vendor = "apple") {
548 let value = ecn_to_tos(ecn) as libc::c_int;
550 cmsgs.push(
551 libc::IPPROTO_IP,
552 libc::IP_TOS,
553 &value as *const libc::c_int as *const u8,
554 std::mem::size_of::<libc::c_int>(),
555 )
556 } else {
557 let value = ecn_to_tos(ecn);
559 cmsgs.push(libc::IPPROTO_IP, libc::IP_TOS, &value as *const u8, std::mem::size_of::<u8>())
560 }
561 }
562
563 fn socket_local_addr(fd: &OwnedFd) -> io::Result<SocketAddr> {
564 let mut addr: libc::sockaddr_storage = unsafe { std::mem::zeroed() };
565 let mut len = std::mem::size_of::<libc::sockaddr_storage>() as libc::socklen_t;
566 let ret = unsafe { libc::getsockname(fd.as_raw_fd(), &mut addr as *mut _ as *mut libc::sockaddr, &mut len) };
567 if ret < 0 {
568 return Err(io::Error::last_os_error());
569 }
570 Ok(storage_to_addr(&addr))
571 }
572
573 #[cfg_attr(not(target_os = "linux"), allow(dead_code))]
576 pub(crate) struct BuildConfig {
577 pub disable_gso: bool,
578 pub disable_gro: bool,
579 pub disable_sendmmsg: bool,
580 pub disable_ecn: bool,
581 pub max_batch: usize,
582 }
583
584 #[cfg(target_os = "linux")]
586 mod linux_inner {
587 use std::{
588 io,
589 os::fd::{AsRawFd, OwnedFd},
590 };
591
592 use super::{
593 BuildConfig, CmsgBuf, RecvCmsgBuf, fill_sockaddr, get_int_opt, push_ecn_cmsg, push_gso_cmsg, recvmsg_raw,
594 reject_zero_segment_size, sendmsg_raw, set_bool_opt,
595 };
596 use crate::{
597 capability::Capabilities,
598 item::{ReceiveItem, SendItem},
599 };
600
601 #[derive(Default)]
605 pub struct SendState {
606 pub gso_einval: bool,
607 pub chunk_offset: usize,
608 }
609
610 fn send_one(fd: std::os::fd::RawFd, item: &SendItem, ecn_enabled: bool, gso_enabled: bool) -> io::Result<()> {
611 let mut cmsgs = CmsgBuf::new();
612
613 if gso_enabled
614 && let Some(segment_size) = item.segment_size
615 && !push_gso_cmsg(&mut cmsgs, segment_size)
616 {
617 return Err(io::Error::other("control message buffer overflow"));
618 }
619
620 if ecn_enabled
621 && let Some(ecn) = item.ecn
622 && !push_ecn_cmsg(&mut cmsgs, ecn, item.destination.is_ipv6())
623 {
624 return Err(io::Error::other("control message buffer overflow"));
625 }
626
627 sendmsg_raw(fd, item.data, &item.destination, &cmsgs)
628 }
629
630 const SENDMMSG_MAX: usize = 64;
631
632 fn sendmmsg_batch(fd: std::os::fd::RawFd, items: &[SendItem], caps: &Capabilities) -> io::Result<usize> {
633 let mut total_sent = 0;
634
635 while total_sent < items.len() {
636 let chunk_len = (items.len() - total_sent).min(SENDMMSG_MAX);
637 let chunk = &items[total_sent..total_sent + chunk_len];
638
639 let mut addrs: [libc::sockaddr_storage; SENDMMSG_MAX] = unsafe { std::mem::zeroed() };
640 let mut iovs: [libc::iovec; SENDMMSG_MAX] = unsafe { std::mem::zeroed() };
641 let mut cmsg_bufs: [CmsgBuf; SENDMMSG_MAX] = std::array::from_fn(|_| CmsgBuf::new());
642 let mut msgs: [libc::mmsghdr; SENDMMSG_MAX] = unsafe { std::mem::zeroed() };
643
644 for i in 0..chunk_len {
645 let item = &chunk[i];
646 let addr_len = fill_sockaddr(&item.destination, &mut addrs[i]);
647
648 if caps.ecn
649 && let Some(ecn) = item.ecn
650 && !push_ecn_cmsg(&mut cmsg_bufs[i], ecn, item.destination.is_ipv6())
651 {
652 return Err(io::Error::other("control message buffer overflow"));
653 }
654
655 iovs[i].iov_base = item.data.as_ptr() as *mut _;
656 iovs[i].iov_len = item.data.len();
657
658 let mhdr = &mut msgs[i].msg_hdr;
659 mhdr.msg_name = &mut addrs[i] as *mut _ as *mut _;
660 mhdr.msg_namelen = addr_len;
661 mhdr.msg_iov = &mut iovs[i] as *mut _ as *mut _;
662 mhdr.msg_iovlen = 1;
663 mhdr.msg_control = cmsg_bufs[i].as_ptr() as *mut _;
664 mhdr.msg_controllen = cmsg_bufs[i].len() as _;
665 mhdr.msg_flags = 0;
666 }
667
668 let ret = unsafe { libc::sendmmsg(fd, msgs.as_mut_ptr(), chunk_len as u32, 0) };
669 if ret < 0 {
670 let e = io::Error::last_os_error();
671 if total_sent > 0 && e.kind() == io::ErrorKind::WouldBlock {
672 return Ok(total_sent);
673 }
674 return Err(e);
675 }
676 let sent = ret as usize;
677 total_sent += sent;
678 if sent < chunk_len {
679 break;
680 }
681 }
682
683 Ok(total_sent)
684 }
685
686 pub fn send_batch(
687 fd: std::os::fd::RawFd,
688 items: &[SendItem<'_>],
689 caps: &Capabilities,
690 state: &mut SendState,
691 ) -> io::Result<usize> {
692 if items.is_empty() {
693 return Ok(0);
694 }
695
696 reject_zero_segment_size(items)?;
697
698 if items.len() == 1 && items[0].segment_size.is_some() && caps.gso && !state.gso_einval {
700 match send_one(fd, &items[0], caps.ecn, true) {
701 Ok(()) => {
702 state.chunk_offset = 0;
703 return Ok(1);
704 }
705 Err(e) if e.raw_os_error() == Some(libc::EINVAL) => {
706 state.gso_einval = true;
707 }
710 Err(e) if e.kind() == io::ErrorKind::WouldBlock => return Err(e),
711 Err(e) => return Err(e),
712 }
713 }
714
715 if caps.sendmmsg && items.len() > 1 && !items.iter().any(|i| i.segment_size.is_some()) {
717 return sendmmsg_batch(fd, items, caps);
718 }
719
720 let mut count = 0usize;
721 for item in items {
722 if let Some(seg) = item.segment_size {
723 let seg = seg as usize;
725 let data_slice = &item.data[state.chunk_offset..];
726 for chunk in data_slice.chunks(seg) {
727 let mut chunk_item = SendItem::new(item.destination, chunk);
728 if let Some(ecn) = item.ecn {
729 chunk_item = chunk_item.ecn(ecn);
730 }
731 match send_one(fd, &chunk_item, caps.ecn, false) {
732 Ok(()) => state.chunk_offset += chunk.len(),
733 Err(e) if e.kind() == io::ErrorKind::WouldBlock => {
734 if count == 0 && state.chunk_offset == 0 {
735 return Err(e);
736 }
737 return Ok(count);
738 }
739 Err(e) => return Err(e),
740 }
741 }
742 state.chunk_offset = 0;
743 count += 1;
744 } else {
745 match send_one(fd, item, caps.ecn, false) {
746 Ok(()) => count += 1,
747 Err(e) if e.kind() == io::ErrorKind::WouldBlock => {
748 if count == 0 {
749 return Err(e);
750 }
751 return Ok(count);
752 }
753 Err(e) => return Err(e),
754 }
755 }
756 }
757 Ok(count)
758 }
759
760 fn recv_one(fd: std::os::fd::RawFd, item: &mut ReceiveItem<'_>, ecn_enabled: bool) -> io::Result<()> {
761 let mut cmsg_buf = RecvCmsgBuf::new();
762 let (len, source, _gro_seg, ecn) = recvmsg_raw(fd, item.buf, &mut cmsg_buf, ecn_enabled)?;
763
764 item.len = len;
765 item.source = source;
766 item.ecn = ecn;
767
768 Ok(())
769 }
770
771 fn recv_gro(fd: std::os::fd::RawFd, items: &mut [ReceiveItem<'_>], ecn_enabled: bool) -> io::Result<usize> {
772 let mut gro_buf = [0u8; 65536];
773 let mut cmsg_buf = RecvCmsgBuf::new();
774
775 let (total, source, seg_size, ecn_val) = recvmsg_raw(fd, &mut gro_buf, &mut cmsg_buf, ecn_enabled)?;
776
777 if seg_size.is_none() {
778 if items.is_empty() {
779 return Ok(0);
780 }
781 let copy_len = total.min(items[0].buf.len());
782 items[0].buf[..copy_len].copy_from_slice(&gro_buf[..copy_len]);
783 items[0].len = copy_len;
784 items[0].source = source;
785 items[0].ecn = ecn_val;
786 return Ok(1);
787 }
788
789 let seg = seg_size.unwrap() as usize;
790 let mut offset = 0;
791 let mut count = 0;
792 for item in items.iter_mut() {
793 if offset >= total {
794 break;
795 }
796 let seg_end = (offset + seg).min(total);
797 let copy_len = (seg_end - offset).min(item.buf.len());
798 item.buf[..copy_len].copy_from_slice(&gro_buf[offset..offset + copy_len]);
799 item.len = copy_len;
800 item.source = source;
801 item.ecn = ecn_val;
802 count += 1;
803 offset = seg_end;
804 }
805 Ok(count)
806 }
807
808 pub fn recv_batch(
809 fd: std::os::fd::RawFd,
810 items: &mut [ReceiveItem<'_>],
811 caps: &Capabilities,
812 ) -> io::Result<usize> {
813 if items.is_empty() {
814 return Ok(0);
815 }
816
817 if caps.gro {
818 return recv_gro(fd, items, caps.ecn);
819 }
820
821 let mut count = 0;
822 for item in items.iter_mut() {
823 match recv_one(fd, item, caps.ecn) {
824 Ok(()) => count += 1,
825 Err(e) if e.kind() == io::ErrorKind::WouldBlock => {
826 if count == 0 {
827 return Err(e);
828 }
829 return Ok(count);
830 }
831 Err(e) => return Err(e),
832 }
833 }
834 Ok(count)
835 }
836
837 fn probe_gso(fd: &OwnedFd) -> bool {
838 get_int_opt(fd.as_raw_fd(), libc::SOL_UDP, libc::UDP_SEGMENT).is_ok()
839 }
840
841 fn probe_gro(fd: &OwnedFd) -> bool {
842 set_bool_opt(fd.as_raw_fd(), libc::IPPROTO_UDP, libc::UDP_GRO).is_ok()
843 }
844
845 fn enable_ecn_recv(fd: &OwnedFd, is_ipv6: bool) -> bool {
846 let (level, name) = if is_ipv6 {
847 (libc::IPPROTO_IPV6, libc::IPV6_RECVTCLASS)
848 } else {
849 (libc::IPPROTO_IP, libc::IP_RECVTOS)
850 };
851 set_bool_opt(fd.as_raw_fd(), level, name).is_ok()
852 }
853
854 pub fn setup_and_probe(fd: &OwnedFd, config: &BuildConfig, is_ipv6: bool) -> io::Result<Capabilities> {
855 set_bool_opt(fd.as_raw_fd(), libc::SOL_SOCKET, libc::SO_REUSEADDR)?;
856
857 let gso = if config.disable_gso { false } else { probe_gso(fd) };
858 let gro = if config.disable_gro { false } else { probe_gro(fd) };
859 let sendmmsg = !config.disable_sendmmsg;
860 let ecn = if config.disable_ecn {
861 false
862 } else {
863 enable_ecn_recv(fd, is_ipv6)
864 };
865
866 Ok(Capabilities {
867 gso,
868 gro,
869 sendmmsg,
870 ecn,
871 max_batch: config.max_batch,
872 })
873 }
874 }
875
876 #[cfg(all(unix, not(target_os = "linux")))]
878 mod unix_inner {
879 use std::{
880 io,
881 os::fd::{AsRawFd, OwnedFd},
882 };
883
884 #[cfg(target_vendor = "apple")]
885 use super::push_ecn_cmsg;
886 use super::{
887 BuildConfig, CmsgBuf, RecvCmsgBuf, recvmsg_raw, reject_zero_segment_size, sendmsg_raw, set_bool_opt,
888 };
889 use crate::{
890 capability::Capabilities,
891 item::{ReceiveItem, SendItem},
892 };
893
894 #[derive(Default)]
898 pub struct SendState {
899 pub chunk_offset: usize,
900 }
901
902 fn send_one(fd: std::os::fd::RawFd, item: &SendItem, ecn_enabled: bool) -> io::Result<()> {
903 #[cfg_attr(not(target_vendor = "apple"), allow(unused_mut))]
904 let mut cmsgs = CmsgBuf::new();
905
906 #[cfg(target_vendor = "apple")]
908 if ecn_enabled
909 && let Some(ecn) = item.ecn
910 && !push_ecn_cmsg(&mut cmsgs, ecn, item.destination.is_ipv6())
911 {
912 return Err(io::Error::other("control message buffer overflow"));
913 }
914 #[cfg(not(target_vendor = "apple"))]
915 let _ = ecn_enabled;
916
917 sendmsg_raw(fd, item.data, &item.destination, &cmsgs)
918 }
919
920 pub fn send_batch(
921 fd: std::os::fd::RawFd,
922 items: &[SendItem<'_>],
923 caps: &Capabilities,
924 state: &mut SendState,
925 ) -> io::Result<usize> {
926 if items.is_empty() {
927 return Ok(0);
928 }
929
930 reject_zero_segment_size(items)?;
931
932 let mut count = 0;
933 for item in items {
934 if let Some(seg) = item.segment_size {
935 let seg = seg as usize;
938 let data_slice = &item.data[state.chunk_offset..];
939 for chunk in data_slice.chunks(seg) {
940 let mut chunk_item = SendItem::new(item.destination, chunk);
941 if let Some(ecn) = item.ecn {
942 chunk_item = chunk_item.ecn(ecn);
943 }
944 match send_one(fd, &chunk_item, caps.ecn) {
945 Ok(()) => state.chunk_offset += chunk.len(),
946 Err(e) if e.kind() == io::ErrorKind::WouldBlock => {
947 if count == 0 && state.chunk_offset == 0 {
948 return Err(e);
949 }
950 return Ok(count);
951 }
952 Err(e) => return Err(e),
953 }
954 }
955 state.chunk_offset = 0;
956 count += 1;
957 } else {
958 match send_one(fd, item, caps.ecn) {
959 Ok(()) => count += 1,
960 Err(e) if e.kind() == io::ErrorKind::WouldBlock => {
961 if count == 0 {
962 return Err(e);
963 }
964 return Ok(count);
965 }
966 Err(e) => return Err(e),
967 }
968 }
969 }
970 Ok(count)
971 }
972
973 fn recv_one(fd: std::os::fd::RawFd, item: &mut ReceiveItem<'_>, ecn_enabled: bool) -> io::Result<()> {
974 let mut cmsg_buf = RecvCmsgBuf::new();
975 let (len, source, _gro_seg, ecn) = recvmsg_raw(fd, item.buf, &mut cmsg_buf, ecn_enabled)?;
976
977 item.len = len;
978 item.source = source;
979 item.ecn = ecn;
980
981 Ok(())
982 }
983
984 pub fn recv_batch(
985 fd: std::os::fd::RawFd,
986 items: &mut [ReceiveItem<'_>],
987 caps: &Capabilities,
988 ) -> io::Result<usize> {
989 if items.is_empty() {
990 return Ok(0);
991 }
992
993 let mut count = 0;
994 for item in items.iter_mut() {
995 match recv_one(fd, item, caps.ecn) {
996 Ok(()) => count += 1,
997 Err(e) if e.kind() == io::ErrorKind::WouldBlock => {
998 if count == 0 {
999 return Err(e);
1000 }
1001 return Ok(count);
1002 }
1003 Err(e) => return Err(e),
1004 }
1005 }
1006 Ok(count)
1007 }
1008
1009 #[cfg(target_vendor = "apple")]
1016 fn enable_ecn_recv(fd: &OwnedFd, is_ipv6: bool) -> bool {
1017 let (level, name) = if is_ipv6 {
1018 (libc::IPPROTO_IPV6, libc::IPV6_RECVTCLASS)
1019 } else {
1020 (libc::IPPROTO_IP, libc::IP_RECVTOS)
1021 };
1022 set_bool_opt(fd.as_raw_fd(), level, name).is_ok()
1023 }
1024
1025 #[cfg(not(target_vendor = "apple"))]
1027 fn enable_ecn_recv(_fd: &OwnedFd, _is_ipv6: bool) -> bool {
1028 false
1029 }
1030
1031 pub fn setup_and_probe(fd: &OwnedFd, config: &BuildConfig, is_ipv6: bool) -> io::Result<Capabilities> {
1032 set_bool_opt(fd.as_raw_fd(), libc::SOL_SOCKET, libc::SO_REUSEADDR)?;
1033
1034 let ecn = !config.disable_ecn && enable_ecn_recv(fd, is_ipv6);
1035
1036 Ok(Capabilities {
1037 gso: false,
1038 gro: false,
1039 sendmmsg: false,
1040 ecn,
1041 max_batch: config.max_batch,
1042 })
1043 }
1044 }
1045}
1046
1047#[cfg(unix)]
1049pub use unix_socket::{FastUdpSocket, FastUdpSocketBuilder};
1050
1051#[cfg(not(unix))]
1053compile_error!("tokio_fast_udp only supports Unix targets");