1use crate::net::{decode_addr, encode_addr, SockAddrBuf, Socket, UdpOptions};
7use crate::os::sys;
8#[cfg(not(windows))]
9use core::ffi::c_void;
10use std::io;
11#[cfg(any(target_os = "linux", windows))]
12use std::net::Ipv6Addr;
13use std::net::{IpAddr, Ipv4Addr, SocketAddr};
14
15#[derive(Clone, Copy, Debug, PartialEq, Eq)]
17pub struct RecvMeta {
18 pub peer: SocketAddr,
20 pub len: usize,
22 pub segment_size: usize,
25 pub ecn: u8,
27 pub local_ip: Option<IpAddr>,
29 pub truncated: bool,
31}
32
33impl RecvMeta {
34 fn empty() -> RecvMeta {
35 RecvMeta {
36 peer: SocketAddr::new(IpAddr::V4(Ipv4Addr::UNSPECIFIED), 0),
37 len: 0,
38 segment_size: 0,
39 ecn: 0,
40 local_ip: None,
41 truncated: false,
42 }
43 }
44}
45
46impl Default for RecvMeta {
47 fn default() -> Self {
48 RecvMeta::empty()
49 }
50}
51
52#[derive(Clone, Copy, Debug)]
54pub struct Transmit<'a> {
55 pub peer: SocketAddr,
57 pub data: &'a [u8],
60 pub segment_size: usize,
62 pub ecn: u8,
64 pub src_ip: Option<IpAddr>,
66}
67
68pub const BATCH: usize = 32;
70
71#[cfg(any(target_os = "linux", windows))]
72const CONTROL: usize = 128;
73
74#[cfg(any(target_os = "linux", windows))]
75#[repr(C, align(8))]
76#[derive(Clone, Copy)]
77struct Control([u8; CONTROL]);
78
79pub(crate) fn configure(s: &Socket, v6: bool, opts: &UdpOptions) -> io::Result<()> {
81 #[cfg(target_os = "linux")]
82 {
83 if opts.gro {
84 let _ = s.setsockopt_int(sys::IPPROTO_UDP, sys::UDP_GRO, 1);
86 }
87 if opts.pktinfo {
88 s.setsockopt_int(sys::IPPROTO_IP, sys::IP_PKTINFO, 1).ok();
89 if v6 {
90 s.setsockopt_int(sys::IPPROTO_IPV6, sys::IPV6_RECVPKTINFO, 1)?;
91 }
92 }
93 if opts.ecn {
94 s.setsockopt_int(sys::IPPROTO_IP, sys::IP_RECVTOS, 1).ok();
95 if v6 {
96 s.setsockopt_int(sys::IPPROTO_IPV6, sys::IPV6_RECVTCLASS, 1)?;
97 }
98 }
99 if opts.dont_fragment {
100 s.setsockopt_int(sys::IPPROTO_IP, sys::IP_MTU_DISCOVER, sys::IP_PMTUDISC_PROBE).ok();
101 if v6 {
102 s.setsockopt_int(sys::IPPROTO_IPV6, sys::IPV6_MTU_DISCOVER, sys::IP_PMTUDISC_PROBE)?;
103 }
104 }
105 }
106 #[cfg(windows)]
107 {
108 let mut off: u32 = 0;
111 let mut ret = 0u32;
112 unsafe {
114 sys::WSAIoctl(s.raw(), sys::SIO_UDP_CONNRESET, (&mut off as *mut u32).cast(), 4, core::ptr::null_mut(), 0, &mut ret, core::ptr::null_mut(), core::ptr::null_mut());
115 }
116 if opts.gro {
117 let _ = s.setsockopt_int(sys::IPPROTO_UDP, sys::UDP_RECV_MAX_COALESCED_SIZE, 65527);
118 }
119 if opts.pktinfo {
120 s.setsockopt_int(sys::IPPROTO_IP, sys::IP_PKTINFO, 1).ok();
121 if v6 {
122 s.setsockopt_int(sys::IPPROTO_IPV6, sys::IPV6_PKTINFO, 1)?;
123 }
124 }
125 if opts.ecn {
126 s.setsockopt_int(sys::IPPROTO_IP, sys::IP_ECN, 1).ok();
127 if v6 {
128 s.setsockopt_int(sys::IPPROTO_IPV6, sys::IPV6_ECN, 1).ok();
129 }
130 }
131 if opts.dont_fragment {
132 s.setsockopt_int(sys::IPPROTO_IP, sys::IP_DONTFRAGMENT, 1).ok();
133 if v6 {
134 s.setsockopt_int(sys::IPPROTO_IPV6, sys::IPV6_DONTFRAG, 1).ok();
135 }
136 }
137 }
138 #[cfg(not(any(target_os = "linux", windows)))]
139 {
140 let _ = (s, v6, opts);
141 }
142 Ok(())
143}
144
145#[cfg(any(target_os = "linux", windows))]
150mod cmsg {
151 pub const HDR: usize = 16;
152 pub const fn align(n: usize) -> usize {
153 (n + 7) & !7
154 }
155 pub const fn space(n: usize) -> usize {
156 HDR + align(n)
157 }
158
159 pub fn parse(buf: &[u8], mut f: impl FnMut(i32, i32, &[u8])) {
161 let mut off = 0;
162 while off + HDR <= buf.len() {
163 let len = usize::from_ne_bytes(buf[off..off + 8].try_into().unwrap());
164 let level = i32::from_ne_bytes(buf[off + 8..off + 12].try_into().unwrap());
165 let ty = i32::from_ne_bytes(buf[off + 12..off + 16].try_into().unwrap());
166 if len < HDR || off + len > buf.len() {
167 break;
168 }
169 f(level, ty, &buf[off + HDR..off + len]);
170 off += align(len);
171 }
172 }
173
174 pub fn push(buf: &mut [u8], used: usize, level: i32, ty: i32, data: &[u8]) -> usize {
176 let total = space(data.len());
177 if used + total > buf.len() {
178 return used;
179 }
180 let b = &mut buf[used..used + total];
181 b.fill(0);
182 b[0..8].copy_from_slice(&(HDR + data.len()).to_ne_bytes());
183 b[8..12].copy_from_slice(&level.to_ne_bytes());
184 b[12..16].copy_from_slice(&ty.to_ne_bytes());
185 b[HDR..HDR + data.len()].copy_from_slice(data);
186 used + total
187 }
188}
189
190#[cfg(target_os = "linux")]
191fn apply_cmsg(meta: &mut RecvMeta, level: i32, ty: i32, data: &[u8]) {
192 let int = |d: &[u8]| if d.len() >= 4 { i32::from_ne_bytes(d[..4].try_into().unwrap()) } else { d.first().copied().unwrap_or(0) as i32 };
193 match (level, ty) {
194 (sys::IPPROTO_UDP, sys::UDP_GRO) => meta.segment_size = int(data) as usize,
195 (sys::IPPROTO_IP, sys::IP_TOS) => meta.ecn = (data.first().copied().unwrap_or(0)) & 3,
196 (sys::IPPROTO_IPV6, sys::IPV6_TCLASS) => meta.ecn = (int(data) & 3) as u8,
197 (sys::IPPROTO_IP, sys::IP_PKTINFO) if data.len() >= 12 => {
198 meta.local_ip = Some(IpAddr::V4(Ipv4Addr::new(data[8], data[9], data[10], data[11])));
199 }
200 (sys::IPPROTO_IPV6, sys::IPV6_PKTINFO) if data.len() >= 16 => {
201 let mut a = [0u8; 16];
202 a.copy_from_slice(&data[..16]);
203 meta.local_ip = Some(IpAddr::V6(Ipv6Addr::from(a)));
204 }
205 _ => {}
206 }
207}
208
209#[cfg(windows)]
210fn apply_cmsg(meta: &mut RecvMeta, level: i32, ty: i32, data: &[u8]) {
211 let int = |d: &[u8]| if d.len() >= 4 { i32::from_ne_bytes(d[..4].try_into().unwrap()) } else { 0 };
212 match (level, ty) {
213 (sys::IPPROTO_UDP, sys::UDP_COALESCED_INFO) => meta.segment_size = int(data) as usize,
214 (sys::IPPROTO_IP, sys::IP_ECN) | (sys::IPPROTO_IPV6, sys::IPV6_ECN) => meta.ecn = (int(data) & 3) as u8,
215 (sys::IPPROTO_IP, sys::IP_PKTINFO) if data.len() >= 4 => {
216 meta.local_ip = Some(IpAddr::V4(Ipv4Addr::new(data[0], data[1], data[2], data[3])));
217 }
218 (sys::IPPROTO_IPV6, sys::IPV6_PKTINFO) if data.len() >= 16 => {
219 let mut a = [0u8; 16];
220 a.copy_from_slice(&data[..16]);
221 meta.local_ip = Some(IpAddr::V6(Ipv6Addr::from(a)));
222 }
223 _ => {}
224 }
225}
226
227#[cfg(any(target_os = "linux", windows))]
229fn build_cmsg(t: &Transmit<'_>, ctl: &mut [u8]) -> usize {
230 let mut used = 0;
231 if t.segment_size > 0 && t.segment_size < t.data.len() {
232 #[cfg(target_os = "linux")]
233 {
234 used = cmsg::push(ctl, used, sys::IPPROTO_UDP, sys::UDP_SEGMENT, &(t.segment_size as u16).to_ne_bytes());
235 }
236 #[cfg(windows)]
237 {
238 used = cmsg::push(ctl, used, sys::IPPROTO_UDP, sys::UDP_SEND_MSG_SIZE, &(t.segment_size as u32).to_ne_bytes());
239 }
240 }
241 if t.ecn != 0 {
242 let v = (t.ecn & 3) as i32;
243 #[cfg(target_os = "linux")]
244 let (l4, t4, l6, t6) = (sys::IPPROTO_IP, sys::IP_TOS, sys::IPPROTO_IPV6, sys::IPV6_TCLASS);
245 #[cfg(windows)]
246 let (l4, t4, l6, t6) = (sys::IPPROTO_IP, sys::IP_ECN, sys::IPPROTO_IPV6, sys::IPV6_ECN);
247 used = if t.peer.is_ipv4() { cmsg::push(ctl, used, l4, t4, &v.to_ne_bytes()) } else { cmsg::push(ctl, used, l6, t6, &v.to_ne_bytes()) };
248 }
249 match t.src_ip {
250 #[cfg(target_os = "linux")]
251 Some(IpAddr::V4(a)) => {
252 let mut d = [0u8; 12];
253 d[4..8].copy_from_slice(&a.octets());
254 used = cmsg::push(ctl, used, sys::IPPROTO_IP, sys::IP_PKTINFO, &d);
255 }
256 #[cfg(windows)]
257 Some(IpAddr::V4(a)) => {
258 let mut d = [0u8; 8];
259 d[..4].copy_from_slice(&a.octets());
260 used = cmsg::push(ctl, used, sys::IPPROTO_IP, sys::IP_PKTINFO, &d);
261 }
262 Some(IpAddr::V6(a)) => {
263 let mut d = [0u8; 20];
264 d[..16].copy_from_slice(&a.octets());
265 used = cmsg::push(ctl, used, sys::IPPROTO_IPV6, sys::IPV6_PKTINFO, &d);
266 }
267 None => {}
268 }
269 used
270}
271
272#[cfg(windows)]
277pub(crate) fn wsa_recvmsg(s: &Socket) -> io::Result<sys::LPFN_WSARECVMSG> {
278 use std::sync::OnceLock;
279 static FN: OnceLock<usize> = OnceLock::new();
280 if let Some(&f) = FN.get() {
281 return Ok(unsafe { core::mem::transmute::<usize, sys::LPFN_WSARECVMSG>(f) });
283 }
284 let mut f: usize = 0;
285 let mut ret = 0u32;
286 let guid = sys::WSAID_WSARECVMSG;
287 let r = unsafe {
289 sys::WSAIoctl(
290 s.raw(),
291 sys::SIO_GET_EXTENSION_FUNCTION_POINTER,
292 (&guid as *const sys::GUID).cast(),
293 core::mem::size_of::<sys::GUID>() as u32,
294 (&mut f as *mut usize).cast(),
295 core::mem::size_of::<usize>() as u32,
296 &mut ret,
297 core::ptr::null_mut(),
298 core::ptr::null_mut(),
299 )
300 };
301 if r != 0 || f == 0 {
302 return Err(crate::net::last_error());
303 }
304 let _ = FN.set(f);
305 Ok(unsafe { core::mem::transmute::<usize, sys::LPFN_WSARECVMSG>(f) })
307}
308
309#[cfg(windows)]
311pub(crate) fn finish_meta_windows(name: &SockAddrBuf, namelen: i32, control: &[u8], flags: u32, len: usize) -> RecvMeta {
312 let mut meta = RecvMeta::empty();
313 if let Some(p) = decode_addr(name, namelen.max(0) as usize) {
314 meta.peer = p;
315 }
316 meta.len = len;
317 cmsg::parse(control, |l, t, d| apply_cmsg(&mut meta, l, t, d));
318 if meta.segment_size == 0 {
319 meta.segment_size = len;
320 }
321 meta.truncated = flags & sys::MSG_TRUNC != 0;
322 meta
323}
324
325impl Socket {
330 pub fn recv_msgs(&self, bufs: &mut [Vec<u8>], meta: &mut [RecvMeta]) -> io::Result<usize> {
334 let n = bufs.len().min(meta.len()).min(BATCH);
335 if n == 0 {
336 return Ok(0);
337 }
338 #[cfg(target_os = "linux")]
339 {
340 let mut names = [SockAddrBuf::new(); BATCH];
341 let mut ctls = [Control([0; CONTROL]); BATCH];
342 let mut iovs = [sys::iovec { iov_base: core::ptr::null_mut(), iov_len: 0 }; BATCH];
343 let mut hdrs: [sys::mmsghdr; BATCH] = unsafe { core::mem::zeroed() };
345 for i in 0..n {
346 bufs[i].clear();
347 iovs[i] = sys::iovec { iov_base: bufs[i].as_mut_ptr().cast(), iov_len: bufs[i].capacity() };
348 let h = &mut hdrs[i].msg_hdr;
349 h.msg_name = names[i].as_mut_ptr();
350 h.msg_namelen = 128;
351 h.msg_iov = &mut iovs[i];
352 h.msg_iovlen = 1;
353 h.msg_control = ctls[i].0.as_mut_ptr().cast();
354 h.msg_controllen = CONTROL;
355 }
356 let got = unsafe { sys::recvmmsg(self.raw(), hdrs.as_mut_ptr(), n as u32, sys::MSG_DONTWAIT, core::ptr::null_mut()) };
359 if got < 0 {
360 return Err(crate::net::last_error());
361 }
362 let got = got as usize;
363 for i in 0..got {
364 let h = &hdrs[i].msg_hdr;
365 let len = (hdrs[i].msg_len as usize).min(bufs[i].capacity());
366 unsafe { bufs[i].set_len(len) };
368 let mut m = RecvMeta::empty();
369 if let Some(p) = decode_addr(&names[i], h.msg_namelen as usize) {
370 m.peer = p;
371 }
372 m.len = len;
373 cmsg::parse(&ctls[i].0[..h.msg_controllen.min(CONTROL)], |l, t, d| apply_cmsg(&mut m, l, t, d));
374 if m.segment_size == 0 {
375 m.segment_size = len;
376 }
377 m.truncated = h.msg_flags & sys::MSG_TRUNC != 0;
378 meta[i] = m;
379 }
380 Ok(got)
381 }
382 #[cfg(windows)]
383 {
384 let f = wsa_recvmsg(self)?;
385 let mut got = 0;
386 while got < n {
387 let mut name = SockAddrBuf::new();
388 let mut ctl = Control([0; CONTROL]);
389 bufs[got].clear();
390 let mut wb = sys::WSABUF { len: bufs[got].capacity().min(u32::MAX as usize) as u32, buf: bufs[got].as_mut_ptr() };
391 let mut msg = sys::WSAMSG {
392 name: name.as_mut_ptr(),
393 namelen: 128,
394 lpBuffers: &mut wb,
395 dwBufferCount: 1,
396 Control: sys::WSABUF { len: CONTROL as u32, buf: ctl.0.as_mut_ptr() },
397 dwFlags: 0,
398 };
399 let mut len = 0u32;
400 let r = unsafe { f(self.raw(), &mut msg, &mut len, core::ptr::null_mut(), core::ptr::null_mut()) };
403 if r != 0 {
404 let e = crate::net::last_error();
405 if e.raw_os_error() == Some(sys::WSAEMSGSIZE) {
406 msg.dwFlags |= sys::MSG_TRUNC;
407 len = wb.len;
408 } else if got > 0 && e.kind() == io::ErrorKind::WouldBlock {
409 break;
410 } else {
411 return Err(e);
412 }
413 }
414 unsafe { bufs[got].set_len((len as usize).min(bufs[got].capacity())) };
416 meta[got] = finish_meta_windows(&name, msg.namelen, &ctl.0[..(msg.Control.len as usize).min(CONTROL)], msg.dwFlags, len as usize);
417 got += 1;
418 }
419 Ok(got)
420 }
421 #[cfg(not(any(target_os = "linux", windows)))]
422 {
423 let mut got = 0;
424 while got < n {
425 let mut name = SockAddrBuf::new();
426 bufs[got].clear();
427 let mut iov = sys::iovec { iov_base: bufs[got].as_mut_ptr().cast(), iov_len: bufs[got].capacity() };
428 let mut h: sys::msghdr = unsafe { core::mem::zeroed() };
430 h.msg_name = name.as_mut_ptr();
431 h.msg_namelen = 128;
432 h.msg_iov = &mut iov;
433 h.msg_iovlen = 1;
434 let r = unsafe { sys::recvmsg(self.raw(), &mut h, 0) };
436 if r < 0 {
437 let e = crate::net::last_error();
438 if got > 0 && e.kind() == io::ErrorKind::WouldBlock {
439 break;
440 }
441 return Err(e);
442 }
443 let len = (r as usize).min(bufs[got].capacity());
444 unsafe { bufs[got].set_len(len) };
446 let mut m = RecvMeta::empty();
447 if let Some(p) = decode_addr(&name, h.msg_namelen as usize) {
448 m.peer = p;
449 }
450 m.len = len;
451 m.segment_size = len;
452 meta[got] = m;
453 got += 1;
454 }
455 Ok(got)
456 }
457 }
458
459 pub fn send_msgs(&self, msgs: &[Transmit<'_>]) -> io::Result<usize> {
462 let n = msgs.len().min(BATCH);
463 if n == 0 {
464 return Ok(0);
465 }
466 #[cfg(target_os = "linux")]
467 {
468 let mut names = [SockAddrBuf::new(); BATCH];
469 let mut ctls = [Control([0; CONTROL]); BATCH];
470 let mut iovs = [sys::iovec { iov_base: core::ptr::null_mut(), iov_len: 0 }; BATCH];
471 let mut hdrs: [sys::mmsghdr; BATCH] = unsafe { core::mem::zeroed() };
473 for (i, t) in msgs[..n].iter().enumerate() {
474 let nl = encode_addr(&t.peer, &mut names[i]);
475 let cl = build_cmsg(t, &mut ctls[i].0);
476 iovs[i] = sys::iovec { iov_base: t.data.as_ptr() as *mut c_void, iov_len: t.data.len() };
477 let h = &mut hdrs[i].msg_hdr;
478 h.msg_name = names[i].as_mut_ptr();
479 h.msg_namelen = nl;
480 h.msg_iov = &mut iovs[i];
481 h.msg_iovlen = 1;
482 if cl > 0 {
483 h.msg_control = ctls[i].0.as_mut_ptr().cast();
484 h.msg_controllen = cl;
485 }
486 }
487 let r = unsafe { sys::sendmmsg(self.raw(), hdrs.as_mut_ptr(), n as u32, sys::MSG_DONTWAIT | sys::MSG_NOSIGNAL) };
489 if r < 0 {
490 Err(crate::net::last_error())
491 } else {
492 Ok(r as usize)
493 }
494 }
495 #[cfg(windows)]
496 {
497 for (i, t) in msgs[..n].iter().enumerate() {
498 let mut name = SockAddrBuf::new();
499 let nl = encode_addr(&t.peer, &mut name);
500 let mut ctl = Control([0; CONTROL]);
501 let cl = build_cmsg(t, &mut ctl.0);
502 let mut wb = sys::WSABUF { len: t.data.len() as u32, buf: t.data.as_ptr() as *mut u8 };
503 let msg = sys::WSAMSG {
504 name: name.as_mut_ptr(),
505 namelen: nl as i32,
506 lpBuffers: &mut wb,
507 dwBufferCount: 1,
508 Control: sys::WSABUF { len: cl as u32, buf: if cl > 0 { ctl.0.as_mut_ptr() } else { core::ptr::null_mut() } },
509 dwFlags: 0,
510 };
511 let mut sent = 0u32;
512 let r = unsafe { sys::WSASendMsg(self.raw(), &msg, 0, &mut sent, core::ptr::null_mut(), core::ptr::null_mut()) };
515 if r != 0 {
516 let e = crate::net::last_error();
517 if i > 0 && e.kind() == io::ErrorKind::WouldBlock {
518 return Ok(i);
519 }
520 return Err(e);
521 }
522 }
523 Ok(n)
524 }
525 #[cfg(not(any(target_os = "linux", windows)))]
526 {
527 for (i, t) in msgs[..n].iter().enumerate() {
528 let seg = if t.segment_size > 0 { t.segment_size } else { t.data.len().max(1) };
529 for chunk in t.data.chunks(seg) {
530 let mut name = SockAddrBuf::new();
531 let nl = encode_addr(&t.peer, &mut name);
532 let mut iov = sys::iovec { iov_base: chunk.as_ptr() as *mut c_void, iov_len: chunk.len() };
533 let mut h: sys::msghdr = unsafe { core::mem::zeroed() };
535 h.msg_name = name.as_mut_ptr();
536 h.msg_namelen = nl;
537 h.msg_iov = &mut iov;
538 h.msg_iovlen = 1;
539 let r = unsafe { sys::sendmsg(self.raw(), &h, sys::MSG_NOSIGNAL) };
541 if r < 0 {
542 let e = crate::net::last_error();
543 if i > 0 && e.kind() == io::ErrorKind::WouldBlock {
544 return Ok(i);
545 }
546 return Err(e);
547 }
548 }
549 }
550 Ok(n)
551 }
552 }
553}
554
555#[cfg(test)]
556mod tests {
557 use super::*;
558
559 fn recv_wait(s: &Socket, bufs: &mut [Vec<u8>], meta: &mut [RecvMeta], want: usize) -> usize {
560 let mut total = 0;
561 let start = std::time::Instant::now();
562 while total < want && start.elapsed() < std::time::Duration::from_secs(5) {
563 match s.recv_msgs(&mut bufs[total..], &mut meta[total..]) {
564 Ok(n) => total += n,
565 Err(e) if e.kind() == io::ErrorKind::WouldBlock => std::thread::sleep(std::time::Duration::from_millis(1)),
566 Err(e) => panic!("{e}"),
567 }
568 }
569 total
570 }
571
572 #[test]
573 fn batch_send_and_receive_with_metadata() {
574 let opts = UdpOptions { pktinfo: true, ecn: true, ..Default::default() };
575 let a = Socket::udp_bind("127.0.0.1:0".parse().unwrap(), &opts).unwrap();
576 let b = Socket::udp_bind("127.0.0.1:0".parse().unwrap(), &opts).unwrap();
577 let to = a.local_addr().unwrap();
578 let payloads: Vec<Vec<u8>> = (0..5u8).map(|i| vec![i; 100 + i as usize]).collect();
579 let tx: Vec<Transmit<'_>> = payloads.iter().map(|p| Transmit { peer: to, data: p, segment_size: 0, ecn: 2, src_ip: None }).collect();
580 assert_eq!(b.send_msgs(&tx).unwrap(), 5);
581 let mut bufs: Vec<Vec<u8>> = (0..8).map(|_| Vec::with_capacity(2048)).collect();
582 let mut meta = [RecvMeta::default(); 8];
583 assert_eq!(recv_wait(&a, &mut bufs, &mut meta, 5), 5);
584 for i in 0..5 {
585 assert_eq!(bufs[i], payloads[i]);
586 assert_eq!(meta[i].peer, b.local_addr().unwrap());
587 assert_eq!(meta[i].len, 100 + i);
588 assert!(!meta[i].truncated);
589 #[cfg(target_os = "linux")]
590 {
591 assert_eq!(meta[i].ecn, 2, "ECN bits reported");
592 assert_eq!(meta[i].local_ip, Some("127.0.0.1".parse().unwrap()));
593 }
594 #[cfg(windows)]
595 assert_eq!(meta[i].local_ip, Some("127.0.0.1".parse().unwrap()));
596 }
597 assert!(matches!(a.recv_msgs(&mut bufs, &mut meta), Err(e) if e.kind() == io::ErrorKind::WouldBlock));
598 }
599
600 #[test]
601 fn segmented_send_arrives_as_separate_datagrams() {
602 let a = Socket::udp_bind("127.0.0.1:0".parse().unwrap(), &UdpOptions::default()).unwrap();
605 let b = Socket::udp_bind("127.0.0.1:0".parse().unwrap(), &UdpOptions::default()).unwrap();
606 let data: Vec<u8> = (0..1000u32).map(|i| i as u8).collect();
607 let t = Transmit { peer: a.local_addr().unwrap(), data: &data, segment_size: 400, ecn: 0, src_ip: None };
608 match b.send_msgs(&[t]) {
609 Ok(1) => {}
610 Err(e) if cfg!(any(target_os = "linux", windows)) => {
612 eprintln!("segmentation offload unavailable: {e}");
613 return;
614 }
615 other => panic!("{other:?}"),
616 }
617 let mut bufs: Vec<Vec<u8>> = (0..4).map(|_| Vec::with_capacity(2048)).collect();
618 let mut meta = [RecvMeta::default(); 4];
619 assert_eq!(recv_wait(&a, &mut bufs, &mut meta, 3), 3);
620 assert_eq!(bufs[0], data[..400]);
621 assert_eq!(bufs[1], data[400..800]);
622 assert_eq!(bufs[2], data[800..]);
623 }
624
625 #[cfg(target_os = "linux")]
626 #[test]
627 fn gro_coalesces_or_reports_single_segments() {
628 let a = Socket::udp_bind("127.0.0.1:0".parse().unwrap(), &UdpOptions { gro: true, ..Default::default() }).unwrap();
629 let b = Socket::udp_bind("127.0.0.1:0".parse().unwrap(), &UdpOptions::default()).unwrap();
630 let data = vec![7u8; 1200];
631 let t = Transmit { peer: a.local_addr().unwrap(), data: &data, segment_size: 300, ecn: 0, src_ip: None };
632 assert_eq!(b.send_msgs(&[t]).unwrap(), 1);
633 let mut bufs: Vec<Vec<u8>> = (0..4).map(|_| Vec::with_capacity(65536)).collect();
634 let mut meta = [RecvMeta::default(); 4];
635 let mut total = 0;
636 let start = std::time::Instant::now();
637 let mut got = 0;
638 while total < 1200 && start.elapsed() < std::time::Duration::from_secs(5) {
639 match a.recv_msgs(&mut bufs[got..], &mut meta[got..]) {
640 Ok(n) => {
641 for m in &meta[got..got + n] {
642 assert_eq!(m.segment_size, 300);
643 total += m.len;
644 }
645 got += n;
646 }
647 Err(e) if e.kind() == io::ErrorKind::WouldBlock => std::thread::sleep(std::time::Duration::from_millis(1)),
648 Err(e) => panic!("{e}"),
649 }
650 }
651 assert_eq!(total, 1200);
652 }
653
654 #[test]
655 fn ipv6_datagrams() {
656 let Ok(a) = Socket::udp_bind("[::1]:0".parse().unwrap(), &UdpOptions { pktinfo: true, ..Default::default() }) else { return };
657 let b = Socket::udp_bind("[::1]:0".parse().unwrap(), &UdpOptions::default()).unwrap();
658 let t = Transmit { peer: a.local_addr().unwrap(), data: b"six", segment_size: 0, ecn: 0, src_ip: None };
659 assert_eq!(b.send_msgs(&[t]).unwrap(), 1);
660 let mut bufs = vec![Vec::with_capacity(64)];
661 let mut meta = [RecvMeta::default()];
662 assert_eq!(recv_wait(&a, &mut bufs, &mut meta, 1), 1);
663 assert_eq!(bufs[0], b"six");
664 assert_eq!(meta[0].peer, b.local_addr().unwrap());
665 #[cfg(any(target_os = "linux", windows))]
666 assert_eq!(meta[0].local_ip, Some("::1".parse().unwrap()));
667 }
668}