noq_udp/cmsg/
mod.rs

1use std::{
2    ffi::{c_int, c_uchar},
3    ptr,
4    sync::atomic::{AtomicBool, Ordering},
5};
6
7#[cfg(unix)]
8#[path = "unix.rs"]
9mod imp;
10
11#[cfg(windows)]
12#[path = "windows.rs"]
13mod imp;
14
15pub(crate) use imp::{PAYLOAD_ALIGN, RecvBuf, SendBuf};
16
17/// Helper to encode a series of control messages (native "cmsgs") to a buffer for use in `sendmsg`
18//  like API.
19///
20/// The operation must be "finished" for the native msghdr to be usable, either by calling `finish`
21/// explicitly or by dropping the `Encoder`.
22pub(crate) struct Encoder<'a, M: MsgHdr> {
23    hdr: &'a mut M,
24    cmsg: Option<&'a mut M::ControlMessage>,
25    len: usize,
26}
27
28impl<'a, M: MsgHdr> Encoder<'a, M> {
29    /// # Safety
30    /// - `hdr` must contain a suitably aligned pointer to a big enough buffer to hold control messages
31    ///   bytes. All bytes of this buffer can be safely written.
32    /// - The `Encoder` must be dropped before `hdr` is passed to a system call, and must not be leaked.
33    pub(crate) unsafe fn new(hdr: &'a mut M) -> Self {
34        Self {
35            cmsg: unsafe { hdr.cmsg_first_hdr().as_mut() },
36            hdr,
37            len: 0,
38        }
39    }
40
41    /// Append a control message to the buffer.
42    ///
43    /// Private: each message we send has its own method, next to the size covering it.
44    ///
45    /// # Panics
46    /// - If insufficient buffer space remains.
47    fn push<T: Copy>(&mut self, level: c_int, ty: c_int, value: T) {
48        const {
49            assert!(
50                align_of::<T>() <= PAYLOAD_ALIGN,
51                "control message payload is more aligned than a control message buffer can be",
52            );
53        }
54        let space = M::ControlMessage::cmsg_space(size_of_val(&value));
55        assert!(
56            self.hdr.control_len() >= self.len + space,
57            "control message buffer too small. Required: {}, Available: {}",
58            self.len + space,
59            self.hdr.control_len()
60        );
61        let cmsg = self.cmsg.take().expect("no control buffer space remaining");
62        cmsg.set(level, ty, M::ControlMessage::cmsg_len(size_of_val(&value)));
63        unsafe {
64            ptr::write(cmsg.cmsg_data() as *const T as *mut T, value);
65        }
66        self.len += space;
67        self.cmsg = unsafe { self.hdr.cmsg_nxt_hdr(cmsg).as_mut() };
68    }
69
70    /// Finishes appending control messages to the buffer
71    pub(crate) fn finish(self) {
72        // Delegates to the `Drop` impl
73    }
74}
75
76// Statically guarantees that the encoding operation is "finished" before the control buffer is read
77// by `sendmsg` like API.
78impl<M: MsgHdr> Drop for Encoder<'_, M> {
79    fn drop(&mut self) {
80        self.hdr.set_control_len(self.len as _);
81    }
82}
83
84/// Warns once if the kernel had more to say about a datagram than the buffer could hold.
85///
86/// The dropped messages cost us metadata, at worst the GRO segment size. `RECV_LEN` covers
87/// every option we enable, so one is unaccounted for, or the caller enabled their own on
88/// the socket they gave us.
89pub(crate) fn warn_if_control_truncated(hdr: &impl MsgHdr) {
90    static WARNED: AtomicBool = AtomicBool::new(false);
91
92    if hdr.recv_flags() & imp::MSG_CTRUNC != 0 && !WARNED.swap(true, Ordering::Relaxed) {
93        crate::log::warn!(
94            "control messages truncated on receive, some datagram metadata was dropped"
95        );
96    }
97}
98
99/// # Safety
100///
101/// `cmsg` must refer to a native cmsg containing a payload of type `T`
102pub(crate) unsafe fn decode<T: Copy, C: CMsgHdr>(cmsg: &impl CMsgHdr) -> T {
103    const {
104        assert!(
105            align_of::<T>() <= PAYLOAD_ALIGN,
106            "control message payload is more aligned than a control message buffer can be",
107        );
108    }
109    debug_assert_eq!(cmsg.len(), C::cmsg_len(size_of::<T>()));
110    unsafe { ptr::read(cmsg.cmsg_data() as *const T) }
111}
112
113pub(crate) struct Iter<'a, M: MsgHdr> {
114    hdr: &'a M,
115    cmsg: Option<&'a M::ControlMessage>,
116}
117
118impl<'a, M: MsgHdr> Iter<'a, M> {
119    /// # Safety
120    ///
121    /// `hdr` must hold a pointer to memory outliving `'a` which can be soundly read for the
122    /// lifetime of the constructed `Iter` and contains a buffer of native cmsgs, i.e. is aligned
123    //  for native `cmsghdr`, is fully initialized, and has correct internal links.
124    pub(crate) unsafe fn new(hdr: &'a M) -> Self {
125        Self {
126            hdr,
127            cmsg: unsafe { hdr.cmsg_first_hdr().as_ref() },
128        }
129    }
130}
131
132impl<'a, M: MsgHdr> Iterator for Iter<'a, M> {
133    type Item = &'a M::ControlMessage;
134
135    fn next(&mut self) -> Option<Self::Item> {
136        let current = self.cmsg.take()?;
137        self.cmsg = unsafe { self.hdr.cmsg_nxt_hdr(current).as_ref() };
138
139        #[cfg(apple_fast)]
140        {
141            // On MacOS < 14 CMSG_NXTHDR might continuously return a zeroed cmsg. In
142            // such case, return `None` instead, thus indicating the end of
143            // the cmsghdr chain.
144            if current.len() < size_of::<M::ControlMessage>() {
145                return None;
146            }
147        }
148
149        Some(current)
150    }
151}
152
153// Helper traits for native types for control messages
154pub(crate) trait MsgHdr {
155    type ControlMessage: CMsgHdr;
156
157    fn cmsg_first_hdr(&self) -> *mut Self::ControlMessage;
158
159    fn cmsg_nxt_hdr(&self, cmsg: &Self::ControlMessage) -> *mut Self::ControlMessage;
160
161    /// Sets the number of control messages added to this `struct msghdr`.
162    ///
163    /// Note that this is a destructive operation and should only be done as a finalisation
164    /// step.
165    fn set_control_len(&mut self, len: usize);
166
167    fn control_len(&self) -> usize;
168
169    /// The flags the kernel set on a received message, i.e. `msg_flags`.
170    fn recv_flags(&self) -> c_int;
171}
172
173pub(crate) trait CMsgHdr {
174    fn cmsg_len(length: usize) -> usize;
175
176    fn cmsg_space(length: usize) -> usize;
177
178    fn cmsg_data(&self) -> *mut c_uchar;
179
180    fn set(&mut self, level: c_int, ty: c_int, len: usize);
181
182    fn len(&self) -> usize;
183}