1use std::{
2 ffi::{c_int, c_uchar},
3 mem::MaybeUninit,
4};
5
6use super::{CMsgHdr, Encoder, MsgHdr};
7#[cfg(not(target_os = "netbsd"))]
9use crate::imp::IpTosTy;
10
11#[derive(Copy, Clone)]
15#[repr(C)]
16#[allow(dead_code)] pub(crate) union Payload {
18 #[cfg(not(target_os = "netbsd"))]
19 ecn_v4: IpTosTy,
20 ecn_v6: c_int,
21 ecn_byte: u8,
23 segment_size: u16,
24 #[cfg(not(target_os = "redox"))]
25 pktinfo_v6: libc::in6_pktinfo,
26 #[cfg(any(target_os = "linux", target_os = "android"))]
27 pktinfo_v4: libc::in_pktinfo,
28 #[cfg(any(bsd, apple, solarish))]
29 dst_addr_v4: libc::in_addr,
30 #[cfg(any(target_os = "linux", target_os = "android"))]
31 timestamp: libc::timespec,
32}
33
34pub(crate) const MSG_CTRUNC: c_int = libc::MSG_CTRUNC;
36
37const fn cmsg_space(payload_len: usize) -> usize {
41 unsafe { libc::CMSG_SPACE(payload_len as _) as usize }
42}
43
44const fn common_align(a: usize, b: usize) -> usize {
46 1 << (a | b).trailing_zeros()
48}
49
50pub(crate) const PAYLOAD_ALIGN: usize = common_align(
55 common_align(unsafe { libc::CMSG_LEN(0) } as usize, cmsg_space(1)),
56 align_of::<ControlBuf<0>>(),
57);
58
59const MESSAGE_LEN: usize = cmsg_space(size_of::<Payload>());
61
62pub(crate) const SEND_LEN: usize = 3 * MESSAGE_LEN;
66
67pub(crate) const RECV_LEN: usize = 4 * MESSAGE_LEN;
72
73#[derive(Copy, Clone)]
75#[repr(C)]
76pub(crate) struct ControlBuf<const N: usize> {
77 _align: [usize; 0],
81 bytes: [MaybeUninit<u8>; N],
82}
83
84pub(crate) type SendBuf = ControlBuf<SEND_LEN>;
86
87pub(crate) type RecvBuf = ControlBuf<RECV_LEN>;
89
90impl<const N: usize> ControlBuf<N> {
91 pub(crate) const fn zeroed() -> Self {
93 Self {
94 _align: [],
95 bytes: [MaybeUninit::new(0); N],
96 }
97 }
98
99 pub(crate) const fn uninit() -> Self {
101 Self {
102 _align: [],
103 bytes: [MaybeUninit::uninit(); N],
104 }
105 }
106
107 pub(crate) fn as_mut_ptr(&mut self) -> *mut u8 {
108 self.bytes.as_mut_ptr().cast()
109 }
110
111 pub(crate) const fn len(&self) -> usize {
113 N
114 }
115}
116
117impl<M: MsgHdr<ControlMessage = libc::cmsghdr>> Encoder<'_, M> {
122 #[cfg(not(target_os = "netbsd"))]
124 pub(crate) fn push_ecn_v4(&mut self, ecn: IpTosTy) {
125 self.push(libc::IPPROTO_IP, libc::IP_TOS, ecn);
126 }
127
128 #[cfg(not(target_os = "redox"))]
130 pub(crate) fn push_ecn_v6(&mut self, ecn: c_int) {
131 self.push(libc::IPPROTO_IPV6, libc::IPV6_TCLASS, ecn);
132 }
133
134 #[cfg(any(target_os = "linux", target_os = "android"))]
136 pub(crate) fn push_segment_size(&mut self, segment_size: u16) {
137 self.push(libc::SOL_UDP, libc::UDP_SEGMENT, segment_size);
138 }
139
140 #[cfg(any(target_os = "linux", target_os = "android"))]
142 pub(crate) fn push_pktinfo_v4(&mut self, pktinfo: libc::in_pktinfo) {
143 self.push(libc::IPPROTO_IP, libc::IP_PKTINFO, pktinfo);
144 }
145
146 #[cfg(any(bsd, apple, solarish))]
150 pub(crate) fn push_src_addr_v4(&mut self, addr: libc::in_addr) {
151 self.push(libc::IPPROTO_IP, libc::IP_RECVDSTADDR, addr);
152 }
153
154 #[cfg(not(target_os = "redox"))]
156 pub(crate) fn push_pktinfo_v6(&mut self, pktinfo: libc::in6_pktinfo) {
157 self.push(libc::IPPROTO_IPV6, libc::IPV6_PKTINFO, pktinfo);
158 }
159}
160
161impl MsgHdr for libc::msghdr {
163 type ControlMessage = libc::cmsghdr;
164
165 fn cmsg_first_hdr(&self) -> *mut Self::ControlMessage {
166 unsafe { libc::CMSG_FIRSTHDR(self) }
167 }
168
169 fn cmsg_nxt_hdr(&self, cmsg: &Self::ControlMessage) -> *mut Self::ControlMessage {
170 unsafe { libc::CMSG_NXTHDR(self, cmsg) }
171 }
172
173 fn set_control_len(&mut self, len: usize) {
174 self.msg_controllen = len as _;
175 if len == 0 {
176 self.msg_control = std::ptr::null_mut();
179 }
180 }
181
182 fn control_len(&self) -> usize {
183 self.msg_controllen as _
184 }
185
186 fn recv_flags(&self) -> c_int {
187 self.msg_flags
188 }
189}
190
191impl CMsgHdr for libc::cmsghdr {
193 fn cmsg_len(length: usize) -> usize {
194 unsafe { libc::CMSG_LEN(length as _) as usize }
195 }
196
197 fn cmsg_space(length: usize) -> usize {
198 unsafe { libc::CMSG_SPACE(length as _) as usize }
199 }
200
201 fn cmsg_data(&self) -> *mut c_uchar {
202 unsafe { libc::CMSG_DATA(self) }
203 }
204
205 fn set(&mut self, level: c_int, ty: c_int, len: usize) {
206 self.cmsg_level = level as _;
207 self.cmsg_type = ty as _;
208 self.cmsg_len = len as _;
209 }
210
211 fn len(&self) -> usize {
212 self.cmsg_len as _
213 }
214}
215
216#[cfg(test)]
217mod tests {
218 use std::mem;
219
220 use super::*;
221
222 fn sent_payload_lens() -> Vec<usize> {
226 vec![
227 size_of::<c_int>(), size_of::<u16>(), size_of::<Payload>(),
231 ]
232 }
233
234 fn received_payload_lens() -> Vec<usize> {
236 vec![
237 size_of::<c_int>(), size_of::<Payload>(), size_of::<c_int>(), #[cfg(any(target_os = "linux", target_os = "android"))]
241 size_of::<libc::timespec>(), ]
243 }
244
245 fn libc_cmsg_space(payload_lens: &[usize]) -> usize {
246 payload_lens
247 .iter()
248 .map(|len| unsafe { libc::CMSG_SPACE(*len as _) as usize })
249 .sum()
250 }
251
252 #[test]
257 fn control_len_covers_libc() {
258 let sent = libc_cmsg_space(&sent_payload_lens());
259 assert!(SEND_LEN >= sent, "SEND_LEN is {SEND_LEN}, need {sent}");
260
261 let received = libc_cmsg_space(&received_payload_lens());
262 assert!(
263 RECV_LEN >= received,
264 "RECV_LEN is {RECV_LEN}, need {received}"
265 );
266 }
267
268 #[test]
276 fn payloads_are_aligned() {
277 let mut buf = RecvBuf::zeroed();
278 let mut hdr: libc::msghdr = unsafe { mem::zeroed() };
279 hdr.msg_control = buf.as_mut_ptr().cast();
280 hdr.msg_controllen = buf.len() as _;
281
282 let mut encoder = unsafe { Encoder::new(&mut hdr) };
285 for _ in 0..received_payload_lens().len() {
286 encoder.push(libc::SOL_SOCKET, 0, Payload { ecn_v6: 0 });
287 }
288 encoder.finish();
289
290 let mut count = 0;
291 for cmsg in unsafe { super::super::Iter::new(&hdr) } {
292 assert_eq!(
293 cmsg.cmsg_data() as usize % PAYLOAD_ALIGN,
294 0,
295 "payload {count} is not aligned to {PAYLOAD_ALIGN}",
296 );
297 count += 1;
298 }
299 assert_eq!(count, received_payload_lens().len());
300 }
301}