1use std::alloc::{Layout, alloc_zeroed, dealloc, handle_alloc_error};
34use std::cell::{Cell, RefCell};
35use std::collections::VecDeque;
36use std::io;
37use std::net::{IpAddr, SocketAddr, SocketAddrV6, UdpSocket};
38use std::os::fd::AsRawFd;
39use std::ptr::NonNull;
40use std::rc::Rc;
41use std::sync::atomic::{AtomicU16, Ordering};
42use std::task::Poll;
43
44use io_uring::{cqueue, opcode, types};
45
46use crate::Error;
47use crate::metrics::Counters;
48use crate::shared::{Cqe, Op, Shared};
49use crate::worker::Owner;
50
51const CONTROL_LEN: usize = 64;
54const NAME_LEN: usize = std::mem::size_of::<libc::sockaddr_storage>();
56const RECV_OVERHEAD: usize = 16 + NAME_LEN + CONTROL_LEN;
58const MAX_RECV: usize = 64 * 1024;
60const MAX_GSO_SEGMENTS: usize = 64;
62const MAX_RX_BUFFERS: u16 = 1 << 15;
65const INITIAL_RX_BUFFERS: u16 = 16;
68const INITIAL_TX_BUFFERS: u16 = 64;
70
71fn grown(len: usize, max: u16) -> Option<u16> {
73 let max = usize::from(max);
74 match len < max {
75 true => Some(len.saturating_mul(2).clamp(1, max) as u16),
76 false => None,
77 }
78}
79
80#[derive(Clone, Debug)]
83#[non_exhaustive]
84pub struct Config {
85 pub gro: bool,
87 pub gso: bool,
90 pub multishot: bool,
93 pub rx_buffers_max: u16,
101 pub rx_buffer_len: usize,
103 pub tx_buffers_max: u16,
108 pub tx_buffer_len: usize,
110}
111
112impl Default for Config {
113 fn default() -> Self {
114 Self {
115 gro: true,
116 gso: true,
117 multishot: true,
118 rx_buffers_max: 256,
121 rx_buffer_len: MAX_RECV + RECV_OVERHEAD,
122 tx_buffers_max: 1024,
123 tx_buffer_len: 64 * 1024,
124 }
125 }
126}
127
128struct RxBuf {
130 data: Box<[u8]>,
132 outstanding: usize,
134 kernel_done: bool,
137 claimed: bool,
139}
140
141struct Queued {
143 bid: u16,
144 start: usize,
145 len: usize,
146 from: SocketAddr,
147 stride: usize,
148 ecn: Option<Ecn>,
149}
150
151#[repr(u8)]
153#[derive(Debug, Clone, Copy, PartialEq, Eq)]
154pub enum Ecn {
155 Ect0 = 0b10,
157 Ect1 = 0b01,
159 Ce = 0b11,
161}
162
163impl Ecn {
164 fn from_bits(bits: u8) -> Option<Self> {
166 match bits & 0b11 {
167 0b10 => Some(Self::Ect0),
168 0b01 => Some(Self::Ect1),
169 0b11 => Some(Self::Ce),
170 _ => None,
171 }
172 }
173}
174
175#[derive(Debug, Clone, Copy)]
177pub struct Transmit {
178 pub to: SocketAddr,
180 pub len: usize,
182 pub segment: usize,
185 pub ecn: Option<Ecn>,
187}
188
189fn recycle_if_idle(rx: &mut Rx, bid: u16) -> bool {
192 let buf = &mut rx.bufs[bid as usize];
193 if buf.outstanding > 0 {
194 return false;
195 }
196 if buf.claimed {
197 buf.claimed = false;
199 return true;
200 }
201 if !buf.kernel_done {
202 return false;
204 }
205 buf.kernel_done = false;
207 let addr = buf.data.as_mut_ptr();
208 let len = buf.data.len();
209 if let Some(ring) = &mut rx.ring {
210 ring.add(bid, addr, len);
211 ring.publish();
212 }
213 true
214}
215
216fn should_grow(rx: &Rx, multishot: bool) -> bool {
222 if rx.starved {
223 return true;
224 }
225 match multishot {
226 true => !rx.bufs.iter().any(|buf| !buf.kernel_done),
228 false => !rx.bufs.iter().any(|buf| !buf.claimed && buf.outstanding == 0),
230 }
231}
232
233fn grow_rx(rx: &mut Rx, config: &Config) -> bool {
239 let Some(target) = grown(rx.bufs.len(), config.rx_buffers_max) else {
240 return false;
241 };
242 while rx.bufs.len() < usize::from(target) {
243 let bid = rx.bufs.len() as u16;
244 rx.bufs.push(RxBuf {
245 data: vec![0u8; config.rx_buffer_len].into_boxed_slice(),
246 outstanding: 0,
247 kernel_done: false,
248 claimed: false,
249 });
250 if let Some(ring) = &mut rx.ring {
251 let buf = &mut rx.bufs[bid as usize];
252 let addr = buf.data.as_mut_ptr();
253 let len = buf.data.len();
254 ring.add(bid, addr, len);
255 }
256 }
257 if let Some(ring) = &mut rx.ring {
258 ring.publish();
259 }
260 true
261}
262
263fn grow_tx(tx: &mut Tx, config: &Config) -> bool {
269 let Some(target) = grown(tx.bufs.len(), config.tx_buffers_max) else {
270 return false;
271 };
272 while tx.bufs.len() < usize::from(target) {
273 tx.free.push(tx.bufs.len() as u16);
274 tx.bufs.push(TxSlot::new(config.tx_buffer_len));
275 }
276 true
277}
278
279struct BufRing {
281 ptr: NonNull<types::BufRingEntry>,
282 layout: Layout,
283 mask: u16,
284 tail: u16,
285}
286
287impl BufRing {
288 fn new(entries: u16) -> Self {
289 let layout = Layout::from_size_align(entries as usize * std::mem::size_of::<types::BufRingEntry>(), 4096)
290 .expect("buffer ring layout");
291 let ptr = unsafe { alloc_zeroed(layout) };
293 let ptr = NonNull::new(ptr.cast::<types::BufRingEntry>()).unwrap_or_else(|| handle_alloc_error(layout));
294 Self {
295 ptr,
296 layout,
297 mask: entries - 1,
298 tail: 0,
299 }
300 }
301
302 fn add(&mut self, bid: u16, addr: *mut u8, len: usize) {
304 let index = (self.tail & self.mask) as usize;
305 let entry = unsafe { &mut *self.ptr.as_ptr().add(index) };
307 entry.set_addr(addr as u64);
308 entry.set_len(len as u32);
309 entry.set_bid(bid);
310 self.tail = self.tail.wrapping_add(1);
311 }
312
313 fn publish(&self) {
315 let tail = unsafe { types::BufRingEntry::tail(self.ptr.as_ptr()) }.cast_mut();
317 unsafe { AtomicU16::from_ptr(tail) }.store(self.tail, Ordering::Release);
319 }
320}
321
322impl Drop for BufRing {
323 fn drop(&mut self) {
324 unsafe { dealloc(self.ptr.as_ptr().cast(), self.layout) };
327 }
328}
329
330struct Rx {
332 bufs: Vec<RxBuf>,
333 ring: Option<BufRing>,
334 hdr: Box<libc::msghdr>,
336 queue: VecDeque<Queued>,
337 waiters: kio::WaiterList,
338 armed: Option<u64>,
340 starved: bool,
343 error: Option<i32>,
345}
346
347struct Tx {
349 bufs: Vec<TxSlot>,
350 free: Vec<u16>,
351 waiters: kio::WaiterList,
352 stalled: bool,
354 error: Option<i32>,
356}
357
358struct TxSlot {
361 data: Box<[u8]>,
362 headers: Vec<SendHdr>,
363 in_flight: usize,
366}
367
368impl TxSlot {
369 fn new(len: usize) -> Self {
370 Self {
371 data: vec![0u8; len].into_boxed_slice(),
372 headers: Vec::new(),
373 in_flight: 0,
374 }
375 }
376}
377
378pub enum Bound {
384 Lone(UdpSocket),
386 Member(moq_sock::shard::Socket),
391}
392
393impl From<UdpSocket> for Bound {
394 fn from(socket: UdpSocket) -> Self {
395 Self::Lone(socket)
396 }
397}
398
399impl From<moq_sock::shard::Socket> for Bound {
400 fn from(member: moq_sock::shard::Socket) -> Self {
401 Self::Member(member)
402 }
403}
404
405pub(crate) struct SockShared {
407 io: UdpSocket,
408 owner: Owner,
410 shard: Option<moq_sock::shard::Shard>,
412 metrics: std::sync::Arc<Counters>,
415 config: Config,
416 bgid: u16,
417 closed: Cell<bool>,
418 rx: RefCell<Rx>,
419 tx: RefCell<Tx>,
420}
421
422impl SockShared {
423 fn worker_gone(&self) -> bool {
426 self.owner.handle().is_none()
427 }
428
429 fn release_rx(self: &Rc<Self>, bid: u16) {
431 let mut rx = self.rx.borrow_mut();
432 rx.bufs[bid as usize].outstanding -= 1;
433 if !recycle_if_idle(&mut rx, bid) {
434 return;
435 }
436 if rx.armed.is_none() && rx.error.is_none() {
438 drop(rx);
439 if let Some(shared) = self.owner.upgrade() {
440 arm_recv(&shared, self);
441 }
442 }
443 }
444
445 fn release_tx(&self, id: u16) {
446 let mut tx = self.tx.borrow_mut();
447 debug_assert_eq!(tx.bufs[id as usize].in_flight, 0);
448 tx.free.push(id);
449 tx.stalled = false;
450 tx.waiters.wake();
451 }
452
453 fn stage_tx(&self, id: u16) {
454 self.tx.borrow_mut().bufs[id as usize].in_flight += 1;
455 }
456
457 fn complete_tx(&self, id: u16) {
458 let mut tx = self.tx.borrow_mut();
459 let slot = &mut tx.bufs[id as usize];
460 debug_assert!(slot.in_flight > 0);
461 slot.in_flight -= 1;
462 if slot.in_flight == 0 {
463 tx.free.push(id);
464 tx.stalled = false;
465 tx.waiters.wake();
466 }
467 }
468
469 fn fail_rx(&self, code: i32) {
470 let mut rx = self.rx.borrow_mut();
471 rx.error.get_or_insert(code);
472 rx.waiters.wake();
473 }
474
475 fn fail_tx(&self, code: i32) {
476 let mut tx = self.tx.borrow_mut();
477 tx.error.get_or_insert(code);
478 tx.waiters.wake();
479 }
480}
481
482impl Drop for SockShared {
483 fn drop(&mut self) {
484 if self.rx.borrow().ring.is_some()
487 && let Some(shared) = self.owner.upgrade()
488 {
489 let ring = shared.ring.borrow_mut();
490 let _ = ring.submitter().unregister_buf_ring(self.bgid);
491 }
492 }
493}
494
495pub struct Socket {
500 shared: Rc<SockShared>,
501}
502
503impl Socket {
504 #[cfg(test)]
506 pub(crate) fn downgrade(&self) -> std::rc::Weak<SockShared> {
507 Rc::downgrade(&self.shared)
508 }
509
510 pub(crate) fn bind(shared: &Rc<Shared>, bound: Bound, config: Config) -> Result<Self, Error> {
511 let (io, shard) = match bound {
512 Bound::Lone(io) => (io, None),
513 Bound::Member(member) => {
514 let shard = member.shard();
515 (member.into_inner(), Some(shard))
516 }
517 };
518 let floor = if config.gro { MAX_RECV + RECV_OVERHEAD } else { 2048 };
519 if config.rx_buffer_len < floor || config.rx_buffers_max == 0 || config.tx_buffers_max == 0 {
520 return Err(io::Error::new(
521 io::ErrorKind::InvalidInput,
522 format!(
523 "receive buffers must hold one worst-case receive ({floor} bytes) and both pools need at least one buffer"
524 ),
525 )
526 .into());
527 }
528 if config.rx_buffers_max > MAX_RX_BUFFERS {
530 return Err(io::Error::new(
531 io::ErrorKind::InvalidInput,
532 format!(
533 "receive pool holds at most {MAX_RX_BUFFERS} buffers, got {}",
534 config.rx_buffers_max
535 ),
536 )
537 .into());
538 }
539
540 if config.gro {
541 set_option(&io, libc::SOL_UDP, libc::UDP_GRO)?;
542 }
543 if io.local_addr()?.is_ipv6() {
546 set_option(&io, libc::IPPROTO_IPV6, libc::IPV6_RECVTCLASS)?;
547 }
548 set_option(&io, libc::IPPROTO_IP, libc::IP_RECVTOS)?;
549
550 let rx_cap = config.rx_buffers_max.next_power_of_two();
553 let rx_count = INITIAL_RX_BUFFERS.min(config.rx_buffers_max);
554 let mut bufs = Vec::with_capacity(rx_count as usize);
555 for _ in 0..rx_count {
556 bufs.push(RxBuf {
557 data: vec![0u8; config.rx_buffer_len].into_boxed_slice(),
558 outstanding: 0,
559 kernel_done: false,
560 claimed: false,
561 });
562 }
563
564 let bgid = shared.next_bgid.get();
565 shared
566 .next_bgid
567 .set(bgid.checked_add(1).expect("buffer group ids exhausted"));
568
569 let ring = if config.multishot {
570 let mut ring = BufRing::new(rx_cap);
571 {
572 let io_ring = shared.ring.borrow_mut();
573 unsafe {
577 io_ring
578 .submitter()
579 .register_buf_ring_with_flags(ring.ptr.as_ptr() as u64, rx_cap, bgid, 0)
580 .map_err(Error::ring)?;
581 }
582 }
583 for (bid, buf) in bufs.iter_mut().enumerate() {
584 let addr = buf.data.as_mut_ptr();
585 let len = buf.data.len();
586 ring.add(bid as u16, addr, len);
587 }
588 ring.publish();
589 Some(ring)
590 } else {
591 None
592 };
593
594 let mut hdr: Box<libc::msghdr> = Box::new(unsafe { std::mem::zeroed() });
595 hdr.msg_namelen = NAME_LEN as libc::socklen_t;
596 hdr.msg_controllen = CONTROL_LEN;
597
598 let tx_count = INITIAL_TX_BUFFERS.min(config.tx_buffers_max);
599 let tx = Tx {
600 bufs: (0..tx_count).map(|_| TxSlot::new(config.tx_buffer_len)).collect(),
601 free: (0..tx_count).collect(),
602 waiters: kio::WaiterList::new(),
603 stalled: false,
604 error: None,
605 };
606
607 let sock = Rc::new(SockShared {
608 io,
609 owner: Owner::new(shared),
610 shard,
611 metrics: shared.metrics.clone(),
612 config,
613 bgid,
614 closed: Cell::new(false),
615 rx: RefCell::new(Rx {
616 bufs,
617 ring,
618 hdr,
619 queue: VecDeque::new(),
620 waiters: kio::WaiterList::new(),
621 armed: None,
622 starved: false,
623 error: None,
624 }),
625 tx: RefCell::new(tx),
626 });
627
628 arm_recv(shared, &sock);
629 if let Some(code) = sock.rx.borrow().error {
630 return Err(io::Error::from_raw_os_error(code).into());
631 }
632 Ok(Self { shared: sock })
633 }
634
635 pub fn local_addr(&self) -> io::Result<SocketAddr> {
637 self.shared.io.local_addr()
638 }
639
640 pub(crate) fn owner(&self) -> Owner {
642 self.shared.owner.clone()
643 }
644
645 pub(crate) fn shard(&self) -> Option<moq_sock::shard::Shard> {
647 self.shared.shard
648 }
649
650 pub fn poll_recv(&self, waiter: &kio::Waiter) -> Poll<io::Result<Packet>> {
654 let mut rx = self.shared.rx.borrow_mut();
655 if let Some(queued) = rx.queue.pop_front() {
656 let buf = &rx.bufs[queued.bid as usize];
657 let ptr = unsafe { NonNull::new_unchecked(buf.data.as_ptr().cast_mut().add(queued.start)) };
660 return Poll::Ready(Ok(Packet {
661 sock: self.shared.clone(),
662 bid: queued.bid,
663 ptr,
664 len: queued.len,
665 stride: queued.stride,
666 from: queued.from,
667 ecn: queued.ecn,
668 }));
669 }
670 if let Some(code) = rx.error {
671 return Poll::Ready(Err(io::Error::from_raw_os_error(code)));
672 }
673 if self.shared.worker_gone() {
674 return Poll::Ready(Err(Shared::gone_error()));
675 }
676 waiter.register(&mut rx.waiters);
677 Poll::Pending
678 }
679
680 pub async fn recv(&self) -> io::Result<Packet> {
682 kio::wait(|waiter| self.poll_recv(waiter)).await
683 }
684
685 pub fn poll_acquire(&self, waiter: &kio::Waiter) -> Poll<io::Result<TxBuf>> {
688 let mut tx = self.shared.tx.borrow_mut();
689 if let Some(code) = tx.error {
690 return Poll::Ready(Err(io::Error::from_raw_os_error(code)));
691 }
692 if self.shared.worker_gone() {
693 return Poll::Ready(Err(Shared::gone_error()));
694 }
695 if tx.free.is_empty() {
696 grow_tx(&mut tx, &self.shared.config);
699 }
700 if let Some(id) = tx.free.pop() {
701 let slot = &mut tx.bufs[id as usize];
702 let ptr = unsafe { NonNull::new_unchecked(slot.data.as_mut_ptr()) };
705 let cap = slot.data.len();
706 return Poll::Ready(Ok(TxBuf {
707 sock: self.shared.clone(),
708 id,
709 ptr,
710 cap,
711 armed: false,
712 }));
713 }
714 if !tx.stalled {
715 tx.stalled = true;
716 self.shared.metrics.tx_stalls.add(1);
717 }
718 waiter.register(&mut tx.waiters);
719 Poll::Pending
720 }
721
722 pub async fn acquire(&self) -> io::Result<TxBuf> {
724 kio::wait(|waiter| self.poll_acquire(waiter)).await
725 }
726}
727
728impl Drop for Socket {
729 fn drop(&mut self) {
730 self.shared.closed.set(true);
731 let rx = self.shared.rx.borrow();
732 if let (Some(key), Some(shared)) = (rx.armed, self.shared.owner.upgrade()) {
733 drop(rx);
734 let _ = shared.cancel(key);
737 }
738 }
739}
740
741impl std::fmt::Debug for Socket {
742 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
743 f.debug_struct("Socket")
744 .field("addr", &self.shared.io.local_addr())
745 .finish()
746 }
747}
748
749pub struct Packet {
754 sock: Rc<SockShared>,
755 bid: u16,
756 ptr: NonNull<u8>,
757 len: usize,
758 stride: usize,
759 from: SocketAddr,
760 ecn: Option<Ecn>,
761}
762
763impl Packet {
764 pub fn from(&self) -> SocketAddr {
766 self.from
767 }
768
769 pub fn ecn(&self) -> Option<Ecn> {
772 self.ecn
773 }
774
775 pub fn stride(&self) -> usize {
777 self.stride
778 }
779
780 pub fn payload(&self) -> &[u8] {
782 unsafe { std::slice::from_raw_parts(self.ptr.as_ptr(), self.len) }
785 }
786
787 pub fn payload_mut(&mut self) -> &mut [u8] {
789 unsafe { std::slice::from_raw_parts_mut(self.ptr.as_ptr(), self.len) }
791 }
792
793 pub fn segments(&mut self) -> impl Iterator<Item = &mut [u8]> {
795 let stride = self.stride;
796 self.payload_mut().chunks_mut(stride)
797 }
798}
799
800impl Drop for Packet {
801 fn drop(&mut self) {
802 self.sock.release_rx(self.bid);
803 }
804}
805
806impl std::fmt::Debug for Packet {
807 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
808 f.debug_struct("Packet")
809 .field("from", &self.from)
810 .field("len", &self.len)
811 .field("stride", &self.stride)
812 .field("ecn", &self.ecn)
813 .finish()
814 }
815}
816
817pub struct TxBuf {
822 sock: Rc<SockShared>,
823 id: u16,
824 ptr: NonNull<u8>,
825 cap: usize,
826 armed: bool,
827}
828
829impl TxBuf {
830 pub fn send(mut self, transmit: Transmit) -> io::Result<()> {
840 let Transmit { to, len, segment, ecn } = transmit;
841 if len == 0 || len > self.cap || segment == 0 || segment > usize::from(u16::MAX) {
844 return Err(io::Error::new(
845 io::ErrorKind::InvalidInput,
846 format!(
847 "invalid send: {len} bytes in {segment} byte segments from a {} byte buffer",
848 self.cap
849 ),
850 ));
851 }
852 let shared = match self.sock.owner.upgrade() {
853 Some(shared) if !shared.stopped.get() => shared,
854 _ => return Err(Shared::gone_error()),
855 };
856
857 let segments = len.div_ceil(segment);
863 let limit = match self.sock.config.gso {
864 true => MAX_GSO_SEGMENTS,
865 false => shared.ring.borrow().params().sq_entries() as usize,
866 };
867 if segments > limit {
868 return Err(io::Error::new(
869 io::ErrorKind::InvalidInput,
870 format!("send of {segments} datagrams exceeds the {limit} one call may stage"),
871 ));
872 }
873
874 self.armed = true;
875 let sock = self.sock.clone();
876 let base = self.ptr.as_ptr();
877 let headers = {
878 let mut tx = sock.tx.borrow_mut();
879 let headers = &mut tx.bufs[self.id as usize].headers;
880 if headers.len() < segments {
881 headers.resize_with(segments, SendHdr::zeroed);
882 }
883 unsafe { NonNull::new_unchecked(headers.as_mut_ptr()) }
887 };
888 let staging = Staging {
889 sock: sock.clone(),
890 id: self.id,
891 headers,
892 };
893
894 let one = SendOne {
895 to,
896 ecn,
897 segment: sock.config.gso.then_some(segment as u16),
898 };
899 if sock.config.gso {
900 send_one(&shared, &staging, 0, base, len, &one)?;
901 } else {
902 for index in 0..segments {
903 let offset = index * segment;
904 let chunk = segment.min(len - offset);
905 send_one(&shared, &staging, index, unsafe { base.add(offset) }, chunk, &one)?;
907 }
908 }
909 sock.metrics.tx_datagrams.add(segments as u64);
910 Ok(())
911 }
912}
913
914impl std::ops::Deref for TxBuf {
915 type Target = [u8];
916
917 fn deref(&self) -> &[u8] {
918 unsafe { std::slice::from_raw_parts(self.ptr.as_ptr(), self.cap) }
920 }
921}
922
923impl std::ops::DerefMut for TxBuf {
924 fn deref_mut(&mut self) -> &mut [u8] {
925 unsafe { std::slice::from_raw_parts_mut(self.ptr.as_ptr(), self.cap) }
927 }
928}
929
930impl Drop for TxBuf {
931 fn drop(&mut self) {
932 if !self.armed {
933 self.sock.release_tx(self.id);
934 }
935 }
936}
937
938impl std::fmt::Debug for TxBuf {
939 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
940 f.debug_struct("TxBuf").field("cap", &self.cap).finish()
941 }
942}
943
944#[repr(C, align(8))]
946struct Control([u8; CONTROL_LEN]);
947
948struct SendHdr {
950 hdr: libc::msghdr,
951 iov: libc::iovec,
952 name: libc::sockaddr_storage,
953 control: Control,
954}
955
956impl SendHdr {
957 fn zeroed() -> Self {
958 unsafe { std::mem::zeroed() }
960 }
961}
962
963struct Staging {
965 sock: Rc<SockShared>,
966 id: u16,
967 headers: NonNull<SendHdr>,
969}
970
971pub(crate) struct SendOp {
974 sock: Rc<SockShared>,
975 id: u16,
976 expect: usize,
977}
978
979impl Drop for SendOp {
980 fn drop(&mut self) {
981 self.sock.complete_tx(self.id);
982 }
983}
984
985struct SendOne {
987 to: SocketAddr,
988 ecn: Option<Ecn>,
989 segment: Option<u16>,
991}
992
993fn send_one(
994 shared: &Rc<Shared>,
995 staging: &Staging,
996 index: usize,
997 base: *mut u8,
998 len: usize,
999 one: &SendOne,
1000) -> io::Result<()> {
1001 let SendOne { to, ecn, segment } = *one;
1002 let hdr = unsafe { &mut *staging.headers.as_ptr().add(index) };
1005 *hdr = SendHdr::zeroed();
1006 hdr.iov = libc::iovec {
1007 iov_base: base.cast(),
1008 iov_len: len,
1009 };
1010 let name_len = encode_addr(to, &mut hdr.name);
1011 hdr.hdr.msg_name = (&raw mut hdr.name).cast();
1012 hdr.hdr.msg_namelen = name_len;
1013 hdr.hdr.msg_iov = &raw mut hdr.iov;
1014 hdr.hdr.msg_iovlen = 1;
1015
1016 unsafe {
1020 let mut space = 0;
1021 if segment.is_some() {
1022 space += libc::CMSG_SPACE(std::mem::size_of::<u16>() as _) as usize;
1023 }
1024 if ecn.is_some() {
1025 space += libc::CMSG_SPACE(std::mem::size_of::<libc::c_int>() as _) as usize;
1026 }
1027 if space > 0 {
1028 hdr.hdr.msg_control = hdr.control.0.as_mut_ptr().cast();
1029 hdr.hdr.msg_controllen = space;
1030 }
1031 let mut cmsg = libc::CMSG_FIRSTHDR(&hdr.hdr);
1032 if let Some(segment) = segment {
1033 (*cmsg).cmsg_level = libc::SOL_UDP;
1034 (*cmsg).cmsg_type = libc::UDP_SEGMENT;
1035 (*cmsg).cmsg_len = libc::CMSG_LEN(std::mem::size_of::<u16>() as _) as usize;
1036 std::ptr::write_unaligned(libc::CMSG_DATA(cmsg).cast::<u16>(), segment);
1037 cmsg = libc::CMSG_NXTHDR(&hdr.hdr, cmsg);
1038 }
1039 if let Some(ecn) = ecn {
1040 let is_ipv4 = match to.ip() {
1043 IpAddr::V4(_) => true,
1044 IpAddr::V6(v6) => v6.to_ipv4_mapped().is_some(),
1045 };
1046 let (level, kind) = match is_ipv4 {
1047 true => (libc::IPPROTO_IP, libc::IP_TOS),
1048 false => (libc::IPPROTO_IPV6, libc::IPV6_TCLASS),
1049 };
1050 (*cmsg).cmsg_level = level;
1051 (*cmsg).cmsg_type = kind;
1052 (*cmsg).cmsg_len = libc::CMSG_LEN(std::mem::size_of::<libc::c_int>() as _) as usize;
1053 std::ptr::write_unaligned(libc::CMSG_DATA(cmsg).cast::<libc::c_int>(), ecn as libc::c_int);
1054 }
1055 }
1056 let hdr_ptr = &raw const hdr.hdr;
1057
1058 staging.sock.stage_tx(staging.id);
1062 let key = shared.insert(Op::Send(SendOp {
1063 sock: staging.sock.clone(),
1064 id: staging.id,
1065 expect: len,
1066 }));
1067 let entry = opcode::SendMsg::new(types::Fd(staging.sock.io.as_raw_fd()), hdr_ptr)
1068 .build()
1069 .user_data(key);
1070 if let Err(err) = shared.push(&entry) {
1071 shared.ops.borrow_mut().remove(key as usize);
1072 return Err(err);
1073 }
1074 staging.sock.metrics.tx_sends.add(1);
1075 Ok(())
1076}
1077
1078pub(crate) fn arm_recv(shared: &Rc<Shared>, sock: &Rc<SockShared>) {
1080 if sock.closed.get() || shared.stopped.get() {
1081 return;
1082 }
1083 let mut rx = sock.rx.borrow_mut();
1084 if rx.armed.is_some() || rx.error.is_some() {
1085 return;
1086 }
1087
1088 if should_grow(&rx, sock.config.multishot) {
1092 rx.starved = false;
1093 grow_rx(&mut rx, &sock.config);
1094 }
1095
1096 let entry = if sock.config.multishot {
1097 if !rx.bufs.iter().any(|buf| !buf.kernel_done) {
1101 sock.metrics.rx_exhausted.add(1);
1102 return;
1103 }
1104 let key = shared.insert(Op::Recv {
1105 sock: sock.clone(),
1106 one: None,
1107 });
1108 rx.armed = Some(key);
1109 opcode::RecvMsgMulti::new(types::Fd(sock.io.as_raw_fd()), &*rx.hdr, sock.bgid)
1110 .build()
1111 .user_data(key)
1112 } else {
1113 let Some(bid) = rx
1115 .bufs
1116 .iter()
1117 .position(|buf| !buf.claimed && buf.outstanding == 0)
1118 .map(|bid| bid as u16)
1119 else {
1120 sock.metrics.rx_exhausted.add(1);
1123 return;
1124 };
1125 rx.bufs[bid as usize].claimed = true;
1126
1127 let mut one: Box<OneshotRecv> = Box::new(unsafe { std::mem::zeroed() });
1129 one.bid = bid;
1130 one.iov = libc::iovec {
1131 iov_base: rx.bufs[bid as usize].data.as_mut_ptr().cast(),
1132 iov_len: rx.bufs[bid as usize].data.len(),
1133 };
1134 one.hdr.msg_name = (&raw mut one.name).cast();
1135 one.hdr.msg_namelen = NAME_LEN as libc::socklen_t;
1136 one.hdr.msg_iov = &raw mut one.iov;
1137 one.hdr.msg_iovlen = 1;
1138 one.hdr.msg_control = one.control.0.as_mut_ptr().cast();
1139 one.hdr.msg_controllen = CONTROL_LEN;
1140
1141 let hdr_ptr = &raw mut one.hdr;
1142 let key = shared.insert(Op::Recv {
1143 sock: sock.clone(),
1144 one: Some(one),
1145 });
1146 rx.armed = Some(key);
1147 opcode::RecvMsg::new(types::Fd(sock.io.as_raw_fd()), hdr_ptr)
1148 .build()
1149 .user_data(key)
1150 };
1151
1152 drop(rx);
1153 if let Err(err) = shared.push(&entry) {
1154 let key = sock.rx.borrow_mut().armed.take().expect("just armed");
1155 shared.ops.borrow_mut().remove(key as usize);
1156 sock.fail_rx(err.raw_os_error().unwrap_or(libc::EIO));
1157 }
1158}
1159
1160pub(crate) struct OneshotRecv {
1162 hdr: libc::msghdr,
1163 iov: libc::iovec,
1164 name: libc::sockaddr_storage,
1165 control: Control,
1166 bid: u16,
1167}
1168
1169pub(crate) fn on_recv(
1172 shared: &Rc<Shared>,
1173 sock: &Rc<SockShared>,
1174 one: Option<Box<OneshotRecv>>,
1175 cqe: Cqe,
1176 terminal: bool,
1177) {
1178 if terminal {
1179 sock.rx.borrow_mut().armed = None;
1180 }
1181
1182 if cqe.result < 0 {
1183 let code = -cqe.result;
1184 if let Some(one) = &one {
1185 let mut rx = sock.rx.borrow_mut();
1186 rx.bufs[one.bid as usize].claimed = false;
1187 }
1188 match code {
1189 libc::ENOBUFS => {
1192 sock.metrics.rx_enobufs.add(1);
1193 sock.rx.borrow_mut().starved = true;
1194 }
1195 libc::ECANCELED => return,
1197 _ => {
1198 sock.fail_rx(code);
1199 return;
1200 }
1201 }
1202 arm_recv(shared, sock);
1203 return;
1204 }
1205
1206 let received = match one {
1207 None => on_recv_multi(sock, cqe),
1208 Some(one) => on_recv_oneshot(*one, cqe),
1209 };
1210 match received {
1211 Ok((_, Some(queued))) => {
1212 sock.metrics.rx_receives.add(1);
1213 sock.metrics
1216 .rx_datagrams
1217 .add(queued.len.div_ceil(queued.stride.max(1)) as u64);
1218 let mut rx = sock.rx.borrow_mut();
1219 rx.bufs[queued.bid as usize].outstanding += 1;
1220 rx.queue.push_back(queued);
1221 rx.waiters.wake();
1222 }
1223 Ok((bid, None)) => {
1226 recycle_if_idle(&mut sock.rx.borrow_mut(), bid);
1227 }
1228 Err(code) => {
1229 sock.fail_rx(code);
1230 return;
1231 }
1232 }
1233 if terminal {
1234 arm_recv(shared, sock);
1235 }
1236}
1237
1238fn on_recv_multi(sock: &Rc<SockShared>, cqe: Cqe) -> Result<(u16, Option<Queued>), i32> {
1241 let mut rx = sock.rx.borrow_mut();
1242 let rx = &mut *rx;
1243 let Some(bid) = cqueue::buffer_select(cqe.flags) else {
1244 return Err(libc::EPROTO);
1245 };
1246 let len = cqe.result as usize;
1247 let buf = &mut rx.bufs[bid as usize];
1248 if len > buf.data.len() {
1249 return Err(libc::EPROTO);
1250 }
1251 buf.kernel_done = true;
1253
1254 let slice = &buf.data[..len];
1255 let Ok(out) = types::RecvMsgOut::parse(slice, &rx.hdr) else {
1256 tracing::warn!("dropping malformed multishot recvmsg completion");
1257 return Ok((bid, None));
1258 };
1259 if out.is_payload_truncated() || out.is_control_data_truncated() {
1260 tracing::warn!("dropping truncated receive (buffer tail too small for a full coalesce)");
1261 return Ok((bid, None));
1262 }
1263 let Some(from) = decode_addr(out.name_data()) else {
1264 tracing::warn!("dropping receive with an unparseable source address");
1265 return Ok((bid, None));
1266 };
1267 let payload = out.payload_data();
1268 if payload.is_empty() {
1269 return Ok((bid, None));
1270 }
1271 let meta = RecvMeta::parse(out.control_data());
1272 let payload_start = payload.as_ptr() as usize - buf.data.as_ptr() as usize;
1273 Ok((
1274 bid,
1275 Some(Queued {
1276 bid,
1277 start: payload_start,
1278 len: payload.len(),
1279 from,
1280 stride: meta.stride.unwrap_or(payload.len()),
1281 ecn: meta.ecn,
1282 }),
1283 ))
1284}
1285
1286fn on_recv_oneshot(one: OneshotRecv, cqe: Cqe) -> Result<(u16, Option<Queued>), i32> {
1290 let bid = one.bid;
1291 let len = cqe.result as usize;
1292
1293 if one.hdr.msg_flags & (libc::MSG_TRUNC | libc::MSG_CTRUNC) != 0 {
1294 tracing::warn!("dropping truncated oneshot receive");
1295 return Ok((bid, None));
1296 }
1297 let name = {
1298 let ptr = (&raw const one.name).cast::<u8>();
1300 unsafe { std::slice::from_raw_parts(ptr, (one.hdr.msg_namelen as usize).min(NAME_LEN)) }
1301 };
1302 let Some(from) = decode_addr(name) else {
1303 tracing::warn!("dropping receive with an unparseable source address");
1304 return Ok((bid, None));
1305 };
1306 if len == 0 {
1307 return Ok((bid, None));
1308 }
1309 let control = &one.control.0[..one.hdr.msg_controllen.min(CONTROL_LEN)];
1310 let meta = RecvMeta::parse(control);
1311 Ok((
1313 bid,
1314 Some(Queued {
1315 bid,
1316 start: 0,
1317 len,
1318 from,
1319 stride: meta.stride.unwrap_or(len),
1320 ecn: meta.ecn,
1321 }),
1322 ))
1323}
1324
1325pub(crate) fn on_send(op: SendOp, cqe: Cqe) {
1328 if cqe.result < 0 {
1329 let code = -cqe.result;
1330 if code == libc::ECONNREFUSED {
1331 tracing::debug!("send completed with ECONNREFUSED");
1333 return;
1334 }
1335 if code != libc::ECANCELED {
1336 op.sock.fail_tx(code);
1337 }
1338 } else if cqe.result as usize != op.expect {
1339 tracing::warn!(sent = cqe.result, expected = op.expect, "short UDP send");
1340 op.sock.fail_tx(libc::EIO);
1341 }
1342}
1343
1344#[derive(Default)]
1346struct RecvMeta {
1347 stride: Option<usize>,
1349 ecn: Option<Ecn>,
1351}
1352
1353impl RecvMeta {
1354 fn parse(control: &[u8]) -> Self {
1357 let mut meta = Self::default();
1358 let header_len = unsafe { libc::CMSG_LEN(0) as usize };
1359 let mut offset = 0;
1360
1361 while offset + header_len <= control.len() {
1362 let header = unsafe { control.as_ptr().add(offset).cast::<libc::cmsghdr>().read_unaligned() };
1364 let message_len = header.cmsg_len;
1365 if message_len < header_len || offset + message_len > control.len() {
1366 return meta;
1367 }
1368 let data = &control[offset + header_len..offset + message_len];
1369 match (header.cmsg_level, header.cmsg_type) {
1370 (libc::SOL_UDP, libc::UDP_GRO) => {
1371 meta.stride = read_int(data).and_then(|value| usize::try_from(value).ok());
1372 }
1373 (libc::IPPROTO_IP, libc::IP_TOS) => {
1375 meta.ecn = data.first().and_then(|bits| Ecn::from_bits(*bits));
1376 }
1377 (libc::IPPROTO_IPV6, libc::IPV6_TCLASS) => {
1378 meta.ecn = read_int(data).and_then(|value| Ecn::from_bits(value as u8));
1379 }
1380 _ => {}
1381 }
1382 let aligned = unsafe { libc::CMSG_SPACE((message_len - header_len) as _) as usize };
1384 offset = offset.saturating_add(aligned.max(header_len));
1385 }
1386
1387 meta
1388 }
1389}
1390
1391fn read_int(data: &[u8]) -> Option<libc::c_int> {
1393 let bytes = data.get(..std::mem::size_of::<libc::c_int>())?;
1394 Some(libc::c_int::from_ne_bytes(bytes.try_into().ok()?))
1395}
1396
1397fn set_option(io: &UdpSocket, level: libc::c_int, name: libc::c_int) -> io::Result<()> {
1399 let on: libc::c_int = 1;
1400 let ret = unsafe {
1402 libc::setsockopt(
1403 io.as_raw_fd(),
1404 level,
1405 name,
1406 (&raw const on).cast(),
1407 std::mem::size_of::<libc::c_int>() as libc::socklen_t,
1408 )
1409 };
1410 match ret {
1411 0 => Ok(()),
1412 _ => Err(io::Error::last_os_error()),
1413 }
1414}
1415
1416fn encode_addr(addr: SocketAddr, out: &mut libc::sockaddr_storage) -> libc::socklen_t {
1418 match addr {
1419 SocketAddr::V4(v4) => {
1420 let sin = libc::sockaddr_in {
1421 sin_family: libc::AF_INET as libc::sa_family_t,
1422 sin_port: v4.port().to_be(),
1423 sin_addr: libc::in_addr {
1424 s_addr: u32::from_ne_bytes(v4.ip().octets()),
1425 },
1426 sin_zero: [0; 8],
1427 };
1428 unsafe { (&raw mut *out).cast::<libc::sockaddr_in>().write(sin) };
1430 std::mem::size_of::<libc::sockaddr_in>() as libc::socklen_t
1431 }
1432 SocketAddr::V6(v6) => {
1433 let sin6 = libc::sockaddr_in6 {
1434 sin6_family: libc::AF_INET6 as libc::sa_family_t,
1435 sin6_port: v6.port().to_be(),
1436 sin6_flowinfo: v6.flowinfo(),
1437 sin6_addr: libc::in6_addr {
1438 s6_addr: v6.ip().octets(),
1439 },
1440 sin6_scope_id: v6.scope_id(),
1441 };
1442 unsafe { (&raw mut *out).cast::<libc::sockaddr_in6>().write(sin6) };
1444 std::mem::size_of::<libc::sockaddr_in6>() as libc::socklen_t
1445 }
1446 }
1447}
1448
1449fn decode_addr(name: &[u8]) -> Option<SocketAddr> {
1451 if name.len() < std::mem::size_of::<libc::sa_family_t>() {
1452 return None;
1453 }
1454 const FAMILY_LEN: usize = std::mem::size_of::<libc::sa_family_t>();
1455 let mut family = [0u8; FAMILY_LEN];
1456 family.copy_from_slice(&name[..FAMILY_LEN]);
1457 match libc::sa_family_t::from_ne_bytes(family) as libc::c_int {
1458 libc::AF_INET if name.len() >= std::mem::size_of::<libc::sockaddr_in>() => {
1459 let sin = unsafe { name.as_ptr().cast::<libc::sockaddr_in>().read_unaligned() };
1461 Some(SocketAddr::from((
1462 sin.sin_addr.s_addr.to_ne_bytes(),
1463 u16::from_be(sin.sin_port),
1464 )))
1465 }
1466 libc::AF_INET6 if name.len() >= std::mem::size_of::<libc::sockaddr_in6>() => {
1467 let sin6 = unsafe { name.as_ptr().cast::<libc::sockaddr_in6>().read_unaligned() };
1469 Some(SocketAddr::V6(SocketAddrV6::new(
1471 sin6.sin6_addr.s6_addr.into(),
1472 u16::from_be(sin6.sin6_port),
1473 sin6.sin6_flowinfo,
1474 sin6.sin6_scope_id,
1475 )))
1476 }
1477 _ => None,
1478 }
1479}
1480
1481#[cfg(test)]
1482mod tests {
1483 use super::*;
1484 use std::net::{Ipv4Addr, Ipv6Addr, SocketAddrV4};
1485
1486 fn roundtrip(addr: SocketAddr) -> Option<SocketAddr> {
1488 let mut storage: libc::sockaddr_storage = unsafe { std::mem::zeroed() };
1490 let len = encode_addr(addr, &mut storage) as usize;
1491 let name = unsafe { std::slice::from_raw_parts((&raw const storage).cast::<u8>(), len) };
1493 decode_addr(name)
1494 }
1495
1496 #[test]
1497 fn addr_roundtrip_v4() {
1498 let addr = SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(192, 0, 2, 7), 4443));
1499 assert_eq!(roundtrip(addr), Some(addr));
1500 }
1501
1502 #[test]
1503 fn addr_roundtrip_v6_keeps_scope_and_flow() {
1504 let ip = Ipv6Addr::new(0xfe80, 0, 0, 0, 0, 0, 0, 1);
1505 let addr = SocketAddr::V6(SocketAddrV6::new(ip, 4443, 0x12345, 3));
1506 assert_eq!(roundtrip(addr), Some(addr));
1507 }
1508
1509 fn empty_rx() -> Rx {
1511 Rx {
1512 bufs: Vec::new(),
1513 ring: Some(BufRing::new(64)),
1515 hdr: Box::new(unsafe { std::mem::zeroed() }),
1517 queue: VecDeque::new(),
1518 waiters: kio::WaiterList::new(),
1519 armed: None,
1520 starved: false,
1521 error: None,
1522 }
1523 }
1524
1525 #[test]
1530 fn a_recycled_buffer_does_not_mask_a_recorded_starvation() {
1531 let config = Config::default();
1532 let mut rx = empty_rx();
1533 grow_rx(&mut rx, &config);
1534 assert!(!should_grow(&rx, true), "a buffer is in the ring");
1535
1536 rx.starved = true;
1537 assert!(should_grow(&rx, true), "the kernel ran dry, recycle or not");
1538 assert!(should_grow(&rx, false), "and the oneshot path reads it too");
1539 }
1540
1541 #[test]
1544 fn the_receive_pool_doubles_to_its_ceiling() {
1545 let config = Config {
1546 rx_buffers_max: 40,
1547 ..Default::default()
1548 };
1549 let mut rx = empty_rx();
1550
1551 for expected in [1u16, 2, 4, 8, 16, 32, 40] {
1552 assert!(grow_rx(&mut rx, &config), "growth stopped short of {expected}");
1553 assert_eq!(rx.bufs.len(), usize::from(expected));
1554 assert_eq!(rx.ring.as_ref().expect("ring").tail, expected);
1556 }
1557 assert!(!grow_rx(&mut rx, &config), "grew past the ceiling");
1558 }
1559}