Skip to main content

timestamped_socket/
socket.rs

1use std::{
2    marker::PhantomData,
3    net::{SocketAddr, SocketAddrV4, SocketAddrV6},
4    os::fd::AsRawFd,
5    sync::Arc,
6};
7
8use statime_base::{Timestamp, UniversalTimestamp, TAI, UTC};
9use tokio::io::{unix::AsyncFd, Interest};
10
11use crate::{
12    control_message::{ControlMessage, MessageQueue, EXPECTED_MAX_CMSG_SIZE},
13    interface::InterfaceName,
14    networkaddress::{sealed::PrivateToken, MulticastJoinable, NetworkAddress},
15    raw_socket::RawSocket,
16};
17
18#[cfg(not(any(target_os = "linux", target_os = "freebsd", target_os = "macos")))]
19mod fallback;
20#[cfg(target_os = "freebsd")]
21mod freebsd;
22#[cfg(target_os = "linux")]
23mod linux;
24#[cfg(target_os = "macos")]
25mod macos;
26
27#[cfg(not(any(target_os = "linux", target_os = "freebsd", target_os = "macos")))]
28use self::fallback::configure_timestamping;
29#[cfg(target_os = "freebsd")]
30use self::freebsd::configure_timestamping;
31#[cfg(target_os = "linux")]
32pub use self::linux::*;
33#[cfg(target_os = "macos")]
34use self::macos::configure_timestamping;
35
36/// Timestamps on a message returned by a socket.
37#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, Default)]
38pub struct TimestampData {
39    /// The timestamping mode requested by the user when creating the socket.
40    ///
41    /// If this was not available, this is set to `None`.
42    pub timestamp_mode: InterfaceTimestampMode,
43    /// The hardware based timestamp returned by the OS
44    pub hardware: Option<Timestamp<TAI>>,
45    /// The software based timestamp returned by the OS
46    pub software: Option<Timestamp<UTC>>,
47}
48
49impl TimestampData {
50    /// Creates a new `TimestampData` with the given requested timestamping mode, but no timestamps.
51    fn empty(timestamp_mode: InterfaceTimestampMode) -> Self {
52        Self::default().with_timestamp_mode(timestamp_mode)
53    }
54
55    /// Returns the timestamp type selected by the requested timestamping mode.
56    ///
57    /// If the requested timestamping mode is `None` or if the requested timestamp
58    /// is not available, this function will return `None`.
59    #[must_use]
60    pub fn selected_timestamp(self) -> Option<UniversalTimestamp> {
61        match self.timestamp_mode {
62            InterfaceTimestampMode::SoftwareAll | InterfaceTimestampMode::SoftwareRecv => {
63                self.software.map(UniversalTimestamp::from)
64            }
65            InterfaceTimestampMode::HardwareAll
66            | InterfaceTimestampMode::HardwareRecv
67            | InterfaceTimestampMode::HardwarePTPAll
68            | InterfaceTimestampMode::HardwarePTPRecv => {
69                self.hardware.map(UniversalTimestamp::from)
70            }
71            InterfaceTimestampMode::None => Option::None,
72        }
73    }
74
75    /// Create a new `TimestampData` with the given requested timestamping mode.
76    fn with_timestamp_mode(self, timestamp_mode: InterfaceTimestampMode) -> Self {
77        Self {
78            timestamp_mode,
79            ..self
80        }
81    }
82}
83
84/// A timestamp mode usable on all sockets.
85#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
86pub enum GeneralTimestampMode {
87    /// Get kernel timestamps for both sending and receiving.
88    SoftwareAll,
89    /// Get kernel timestamps only for receiving.
90    SoftwareRecv,
91    /// Don't request any timestamps.
92    #[default]
93    None,
94}
95
96/// A timestamp mode for a specific hardware interface.
97#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
98pub enum InterfaceTimestampMode {
99    /// Hardware timestamps for both sending and receiving of all messages.
100    HardwareAll,
101    /// Hardware timestamps for receiving of all messages.
102    HardwareRecv,
103    /// Hardware timestamps for both sending and receiving PTP messages.
104    HardwarePTPAll,
105    /// Hardware timestamps for receiving PTP Messages.
106    HardwarePTPRecv,
107    /// Kernel timestamps for sending and receiving.
108    SoftwareAll,
109    /// Kernel timestamps for receving.
110    SoftwareRecv,
111    /// Don't request any timestamps.
112    #[default]
113    None,
114}
115
116impl From<GeneralTimestampMode> for InterfaceTimestampMode {
117    fn from(value: GeneralTimestampMode) -> Self {
118        match value {
119            GeneralTimestampMode::SoftwareAll => InterfaceTimestampMode::SoftwareAll,
120            GeneralTimestampMode::SoftwareRecv => InterfaceTimestampMode::SoftwareRecv,
121            GeneralTimestampMode::None => InterfaceTimestampMode::None,
122        }
123    }
124}
125
126/// Results from a receive operation.
127#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
128pub struct RecvResult<A> {
129    /// Number of bytes written to the provided buffer.
130    pub bytes_read: usize,
131    /// Address of the sender of the message.
132    pub remote_addr: A,
133    /// Address at which the message was received.
134    pub local_addr: A,
135    /// Timestamps for the message.
136    pub timestamp_data: TimestampData,
137}
138
139/// A UDP network socket.
140#[derive(Debug)]
141pub struct Socket<A, S> {
142    timestamp_mode: InterfaceTimestampMode,
143    // FIXME: Remove the arc once tokio also allows polling asyncfds for the Error interest
144    raw: Arc<AsyncFd<RawSocket>>,
145    #[cfg(target_os = "linux")]
146    send_counter: std::sync::Mutex<u32>,
147    local_addr: A,
148    _state: PhantomData<S>,
149}
150
151/// Marker type for sockets not bound to a specific remote.
152#[non_exhaustive]
153pub struct Open;
154/// Marker type for sockets bound to a specific remote.
155#[non_exhaustive]
156pub struct Connected;
157
158/// Token used to recognize the corresponding send timestamp for a split send operation.
159#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
160pub struct SendTimestampToken(u32);
161
162impl<A: NetworkAddress, S> Socket<A, S> {
163    /// The local address at which the socket is listening.
164    pub fn local_addr(&self) -> A {
165        self.local_addr
166    }
167
168    fn inner_recv(&self, buf: &mut [u8], socket: &RawSocket) -> std::io::Result<RecvResult<A>> {
169        let mut control_buf = [0; EXPECTED_MAX_CMSG_SIZE];
170
171        // loops for when we receive an interrupt during the recv
172        let (bytes_read, control_messages, remote_address) =
173            socket.receive_message(buf, &mut control_buf, MessageQueue::Normal)?;
174
175        let mut timestamp_data = TimestampData::empty(self.timestamp_mode);
176        let mut local_addr = self.local_addr;
177
178        // Loops through the control messages, but we should only get a single message
179        // in practice
180        for msg in control_messages {
181            match msg {
182                ControlMessage::Timestamping { software, hardware } => {
183                    tracing::trace!("Timestamps: {:?} {:?}", software, hardware);
184
185                    // Keep the first timestamp of each kind
186                    timestamp_data.software = timestamp_data.software.or(software);
187                    timestamp_data.hardware = timestamp_data.hardware.or(hardware);
188                }
189
190                #[cfg(target_os = "linux")]
191                ControlMessage::ReceiveError(error) => {
192                    tracing::debug!(
193                        "unexpected error control message on receive: {}",
194                        error.ee_errno
195                    );
196                }
197
198                ControlMessage::DestinationIp(addr) => {
199                    if let Some(addr) = A::from_ip_and_port(addr, self.local_addr.port()) {
200                        local_addr = addr;
201                    }
202                }
203
204                ControlMessage::Other(msg) => {
205                    tracing::debug!(
206                        "unexpected control message on receive: {} {}",
207                        msg.cmsg_level,
208                        msg.cmsg_type,
209                    );
210                }
211            }
212        }
213
214        let remote_addr =
215            A::from_sockaddr(remote_address, PrivateToken).ok_or(std::io::ErrorKind::Other)?;
216
217        Ok(RecvResult {
218            bytes_read,
219            remote_addr,
220            local_addr,
221            timestamp_data,
222        })
223    }
224
225    /// Poll to receive a packet on the socket.
226    ///
227    /// Note that on multiple calls to [`poll_recv`](Socket::poll_recv), only
228    /// the [`Waker`](std::task::Waker) from the [`Context`](std::task::Context)
229    /// on the most recent call is scheduled to receive a wakeup.
230    pub fn poll_recv(
231        &self,
232        buf: &mut [u8],
233        cx: &mut std::task::Context<'_>,
234    ) -> std::task::Poll<std::io::Result<RecvResult<A>>> {
235        // Assumes the socket will not be endlessly marked ready when it is
236        // isn't. This is an assumption on tokio and the underlying OS behaving
237        // somewhat reasonable.
238        loop {
239            match self.raw.poll_read_ready(cx) {
240                std::task::Poll::Ready(Ok(mut guard)) => {
241                    if let Ok(result) = guard.try_io(|inner| self.inner_recv(buf, inner.get_ref()))
242                    {
243                        break std::task::Poll::Ready(result);
244                    }
245                }
246                std::task::Poll::Ready(Err(e)) => break std::task::Poll::Ready(Err(e)),
247                std::task::Poll::Pending => break std::task::Poll::Pending,
248            }
249        }
250    }
251
252    /// Receive a datagram.
253    ///
254    /// # Errors
255    /// May return errors for previous send operations, or if it is no longer
256    /// possible to receive on the given socket.
257    pub async fn recv(&self, buf: &mut [u8]) -> std::io::Result<RecvResult<A>> {
258        self.raw
259            .async_io(Interest::READABLE, |socket| self.inner_recv(buf, socket))
260            .await
261    }
262
263    fn send_inner(
264        &self,
265        send_call: impl FnOnce() -> std::io::Result<()>,
266    ) -> std::io::Result<Option<SendTimestampToken>> {
267        if matches!(
268            self.timestamp_mode,
269            InterfaceTimestampMode::HardwareAll
270                | InterfaceTimestampMode::HardwarePTPAll
271                | InterfaceTimestampMode::SoftwareAll
272        ) {
273            #[cfg(target_os = "linux")]
274            {
275                let mut counter = self.send_counter.lock().unwrap();
276                send_call()?;
277                let token = SendTimestampToken(*counter);
278                *counter = counter.wrapping_add(1);
279                return Ok(Some(token));
280            }
281
282            #[cfg(not(target_os = "linux"))]
283            {
284                unreachable!("Should not be able to create send timestamping sockets on platforms other than linux")
285            }
286        }
287        send_call()?;
288        Ok(None)
289    }
290
291    /// Wait for the next send timestamp to be returned.
292    ///
293    /// Note: There is no poll variant for this function, as that is currently
294    /// impossible to implement with the tools tokio provides. To compensate,
295    /// the future returned here is independent of the self reference and can
296    /// be stored to simulate the existence of a poll function.
297    ///
298    /// # Errors
299    /// May fail if the timestamp is not available, or if the network had
300    /// issues sending the message.
301    pub fn get_send_timestamp(
302        &self,
303    ) -> impl std::future::Future<Output = std::io::Result<(SendTimestampToken, TimestampData)>>
304           + Send
305           + Sync
306           + 'static {
307        let timestamp_mode = self.timestamp_mode;
308        let socket = self.raw.clone();
309        async move {
310            if matches!(
311                timestamp_mode,
312                InterfaceTimestampMode::HardwareAll
313                    | InterfaceTimestampMode::HardwarePTPAll
314                    | InterfaceTimestampMode::SoftwareAll
315            ) {
316                #[cfg(target_os = "linux")]
317                {
318                    let (counter, timestamp) = Self::fetch_send_timestamp(socket).await?;
319                    return Ok((
320                        SendTimestampToken(counter),
321                        timestamp.with_timestamp_mode(timestamp_mode),
322                    ));
323                }
324
325                #[cfg(not(target_os = "linux"))]
326                {
327                    let _ = socket;
328                    unreachable!("Should not be able to create send timestamping sockets on platforms other than linux")
329                }
330            }
331            Err(std::io::ErrorKind::Unsupported.into())
332        }
333    }
334
335    /// Retrieves the timestamp for a given [`SendTimestampToken`].
336    ///
337    /// If multiple tokens are still pending for a send timestamp, this function may
338    /// drop the timestamps for those tokens . Therefore, this should not be used in
339    /// contexts where the socket may also be polled or there may otherwise be multiple
340    /// tokens in use.
341    async fn send_timestamp_for_transmit(
342        &mut self,
343        token: SendTimestampToken,
344    ) -> std::io::Result<TimestampData> {
345        use std::time::Duration;
346
347        const TIMEOUT: Duration = Duration::from_millis(200);
348
349        tokio::time::timeout(TIMEOUT, async {
350            loop {
351                let (cur_token, timestamp_data) = self.get_send_timestamp().await?;
352                if cur_token == token {
353                    return Ok(timestamp_data.with_timestamp_mode(self.timestamp_mode));
354                }
355            }
356        })
357        .await
358        .unwrap_or(Ok(TimestampData::empty(self.timestamp_mode)))
359    }
360}
361
362impl<A: NetworkAddress> Socket<A, Open> {
363    /// Send a packet to a given receiver on the socket.
364    ///
365    /// Note that on multiple calls to `poll_send_*`, only
366    /// the [`Waker`](std::task::Waker) from the [`Context`](std::task::Context)
367    /// on the most recent call is scheduled to receive a wakeup.
368    pub fn poll_send_to(
369        &self,
370        buf: &[u8],
371        addr: A,
372        cx: &mut std::task::Context<'_>,
373    ) -> std::task::Poll<std::io::Result<Option<SendTimestampToken>>> {
374        let addr = addr.to_sockaddr(PrivateToken);
375
376        // Assumes the socket will not be endlessly marked ready when it is
377        // isn't. This is an assumption on tokio and the underlying OS behaving
378        // somewhat reasonable.
379        loop {
380            match self.raw.poll_write_ready(cx) {
381                std::task::Poll::Ready(Ok(mut guard)) => {
382                    if let Ok(result) =
383                        guard.try_io(|inner| self.send_inner(|| inner.get_ref().send_to(buf, addr)))
384                    {
385                        break std::task::Poll::Ready(result);
386                    }
387                }
388                std::task::Poll::Ready(Err(e)) => break std::task::Poll::Ready(Err(e)),
389                std::task::Poll::Pending => break std::task::Poll::Pending,
390            }
391        }
392    }
393
394    /// Send a packet to a given receiver on the socket.
395    ///
396    /// When used in combination with the polling send functions, if there are timestamps
397    /// pending for some `SendTimestampToken`, these may be skipped and become unavailable
398    /// when calling this function.
399    ///
400    /// # Errors
401    /// May fail if the remote cannot be reached.
402    pub async fn send_to(&mut self, buf: &[u8], addr: A) -> std::io::Result<TimestampData> {
403        let addr = addr.to_sockaddr(PrivateToken);
404
405        if let Some(token) = self
406            .raw
407            .async_io(Interest::WRITABLE, |socket| {
408                self.send_inner(|| socket.send_to(buf, addr))
409            })
410            .await?
411        {
412            self.send_timestamp_for_transmit(token).await
413        } else {
414            Ok(TimestampData::empty(self.timestamp_mode))
415        }
416    }
417
418    /// Send a packet to a given receiver on the socket, using the specified origin address.
419    ///
420    /// Note that on multiple calls to `poll_send_*`, only
421    /// the [`Waker`](std::task::Waker) from the [`Context`](std::task::Context)
422    /// on the most recent call is scheduled to receive a wakeup.
423    pub fn poll_send_from_to(
424        &self,
425        buf: &[u8],
426        from: A,
427        to: A,
428        cx: &mut std::task::Context<'_>,
429    ) -> std::task::Poll<std::io::Result<Option<SendTimestampToken>>> {
430        let from = from.to_sockaddr(PrivateToken);
431        let to = to.to_sockaddr(PrivateToken);
432
433        // Assumes the socket will not be endlessly marked ready when it is
434        // isn't. This is an assumption on tokio and the underlying OS behaving
435        // somewhat reasonable.
436        loop {
437            match self.raw.poll_write_ready(cx) {
438                std::task::Poll::Ready(Ok(mut guard)) => {
439                    if let Ok(result) = guard.try_io(|inner| {
440                        self.send_inner(|| inner.get_ref().send_from_to(buf, from, to))
441                    }) {
442                        break std::task::Poll::Ready(result);
443                    }
444                }
445                std::task::Poll::Ready(Err(e)) => break std::task::Poll::Ready(Err(e)),
446                std::task::Poll::Pending => break std::task::Poll::Pending,
447            }
448        }
449    }
450
451    /// Send a packet to a given receiver on the socket, using the specified origin address.
452    ///
453    /// When used in combination with the polling send functions, if there are timestamps
454    /// pending for some `SendTimestampToken`, these may be skipped and become unavailable
455    /// when calling this function.
456    ///
457    /// # Errors
458    /// May fail if the from or to address is not available or reachable.
459    pub async fn send_from_to(
460        &mut self,
461        buf: &[u8],
462        from: A,
463        to: A,
464    ) -> std::io::Result<TimestampData> {
465        let from = from.to_sockaddr(PrivateToken);
466        let to = to.to_sockaddr(PrivateToken);
467
468        if let Some(token) = self
469            .raw
470            .async_io(Interest::WRITABLE, |socket| {
471                self.send_inner(|| socket.send_from_to(buf, from, to))
472            })
473            .await?
474        {
475            self.send_timestamp_for_transmit(token).await
476        } else {
477            Ok(TimestampData::empty(self.timestamp_mode))
478        }
479    }
480
481    /// Connect the socket to a specific remote.
482    ///
483    /// # Errors
484    /// May fail if the remote is not reachable, or the network the
485    /// socket is attached to is no longer available.
486    pub fn connect(self, addr: A) -> std::io::Result<Socket<A, Connected>> {
487        let addr = addr.to_sockaddr(PrivateToken);
488        self.raw.get_ref().connect(addr)?;
489        Ok(Socket {
490            timestamp_mode: self.timestamp_mode,
491            raw: self.raw,
492            #[cfg(target_os = "linux")]
493            send_counter: self.send_counter,
494            local_addr: self.local_addr,
495            _state: PhantomData,
496        })
497    }
498}
499
500impl<A: NetworkAddress> Socket<A, Connected> {
501    /// Address of the remote to which this socket is connected.
502    ///
503    /// # Errors
504    /// May error if the operating system cannot provide the addres
505    /// of the peer.
506    pub fn peer_addr(&self) -> std::io::Result<A> {
507        let addr = self.raw.get_ref().getpeername()?;
508        A::from_sockaddr(addr, PrivateToken).ok_or_else(|| std::io::ErrorKind::Other.into())
509    }
510
511    /// Send a packet on the socket.
512    ///
513    /// Note that on multiple calls to `poll_send_*`, only
514    /// the [`Waker`](std::task::Waker) from the [`Context`](std::task::Context)
515    /// on the most recent call is scheduled to receive a wakeup.
516    pub fn poll_send(
517        &self,
518        buf: &[u8],
519        cx: &mut std::task::Context<'_>,
520    ) -> std::task::Poll<std::io::Result<Option<SendTimestampToken>>> {
521        // Assumes the socket will not be endlessly marked ready when it is
522        // isn't. This is an assumption on tokio and the underlying OS behaving
523        // somewhat reasonable.
524        loop {
525            match self.raw.poll_write_ready(cx) {
526                std::task::Poll::Ready(Ok(mut guard)) => {
527                    if let Ok(result) =
528                        guard.try_io(|inner| self.send_inner(|| inner.get_ref().send(buf)))
529                    {
530                        break std::task::Poll::Ready(result);
531                    }
532                }
533                std::task::Poll::Ready(Err(e)) => break std::task::Poll::Ready(Err(e)),
534                std::task::Poll::Pending => break std::task::Poll::Pending,
535            }
536        }
537    }
538
539    /// Send a packet on the socket.
540    ///
541    /// When used in combination with the polling send functions, if there are timestamps
542    /// pending for some `SendTimestampToken`, these may be skipped and become unavailable
543    /// when calling this function.
544    ///
545    /// # Errors
546    /// May fail if the remote or network this socket is attached to is no longer available,
547    /// or if the timestamp for the operation cannot be obtained.
548    pub async fn send(&mut self, buf: &[u8]) -> std::io::Result<TimestampData> {
549        if let Some(token) = self
550            .raw
551            .async_io(Interest::WRITABLE, |socket| {
552                self.send_inner(|| socket.send(buf))
553            })
554            .await?
555        {
556            self.send_timestamp_for_transmit(token).await
557        } else {
558            Ok(TimestampData::empty(self.timestamp_mode))
559        }
560    }
561
562    /// Send a packet on the socket.
563    ///
564    /// Note that on multiple calls to `poll_send_*`, only
565    /// the [`Waker`](std::task::Waker) from the [`Context`](std::task::Context)
566    /// on the most recent call is scheduled to receive a wakeup.
567    pub fn poll_send_from(
568        &self,
569        buf: &[u8],
570        from: A,
571        cx: &mut std::task::Context<'_>,
572    ) -> std::task::Poll<std::io::Result<Option<SendTimestampToken>>> {
573        let from = from.to_sockaddr(PrivateToken);
574
575        // Assumes the socket will not be endlessly marked ready when it is
576        // isn't. This is an assumption on tokio and the underlying OS behaving
577        // somewhat reasonable.
578        loop {
579            match self.raw.poll_write_ready(cx) {
580                std::task::Poll::Ready(Ok(mut guard)) => {
581                    if let Ok(result) = guard
582                        .try_io(|inner| self.send_inner(|| inner.get_ref().send_from(buf, from)))
583                    {
584                        break std::task::Poll::Ready(result);
585                    }
586                }
587                std::task::Poll::Ready(Err(e)) => break std::task::Poll::Ready(Err(e)),
588                std::task::Poll::Pending => break std::task::Poll::Pending,
589            }
590        }
591    }
592
593    /// Send a packet on the socket, with the given local address.
594    ///
595    /// When used in combination with the polling send functions, if there are timestamps
596    /// pending for some `SendTimestampToken`, these may be skipped and become unavailable
597    /// when calling this function.
598    ///
599    /// # Errors
600    /// May error if the remote cannot be reached, or if the network the socket is attached
601    /// to is no longer available.
602    pub async fn send_from(&mut self, buf: &[u8], from: A) -> std::io::Result<TimestampData> {
603        let from = from.to_sockaddr(PrivateToken);
604
605        if let Some(token) = self
606            .raw
607            .async_io(Interest::WRITABLE, |socket| {
608                self.send_inner(|| socket.send_from(buf, from))
609            })
610            .await?
611        {
612            self.send_timestamp_for_transmit(token).await
613        } else {
614            Ok(TimestampData::empty(self.timestamp_mode))
615        }
616    }
617}
618
619impl<A: MulticastJoinable, S> Socket<A, S> {
620    /// Indicate that you want to receive messages for a specific multicast address on the socket.
621    ///
622    /// # Errors
623    /// May error if it is not possible to unsubscribe from the given address.
624    pub fn join_multicast(&self, addr: A, interface: InterfaceName) -> std::io::Result<()> {
625        addr.join_multicast(self.raw.get_ref().as_raw_fd(), interface, PrivateToken)
626    }
627
628    /// Indicate that you no longer want to receive messages for a specific multicast address on the socket.
629    ///
630    /// # Errors
631    /// May error if it is not possible to unsubscribe from the given address.
632    pub fn leave_multicast(&self, addr: A, interface: InterfaceName) -> std::io::Result<()> {
633        addr.leave_multicast(self.raw.get_ref().as_raw_fd(), interface, PrivateToken)
634    }
635}
636
637/// Open a socket listening to a given ip address.
638///
639/// # Errors
640/// May error if the requested timestamp mode is not possible, or if the requested addres is unavailable.
641pub fn open_ip(
642    addr: SocketAddr,
643    timestamping: GeneralTimestampMode,
644    #[cfg_attr(not(target_os = "linux"), expect(unused))] reuse_addr: bool,
645) -> std::io::Result<Socket<SocketAddr, Open>> {
646    // Setup the socket
647    let socket = match addr {
648        SocketAddr::V4(_) => RawSocket::open(libc::PF_INET, libc::SOCK_DGRAM, libc::IPPROTO_UDP),
649        SocketAddr::V6(_) => RawSocket::open(libc::PF_INET6, libc::SOCK_DGRAM, libc::IPPROTO_UDP),
650    }?;
651    match addr {
652        SocketAddr::V4(_) => socket.enable_destination_ipv4()?,
653        SocketAddr::V6(_) => socket.enable_destination_ipv6()?,
654    }
655    #[cfg(target_os = "linux")]
656    if reuse_addr {
657        socket.reuse_addr()?;
658    }
659    socket.bind(addr.to_sockaddr(PrivateToken))?;
660    socket.set_nonblocking(true)?;
661    configure_timestamping(&socket, None, timestamping.into(), None)?;
662
663    let local_addr = SocketAddr::from_sockaddr(socket.getsockname()?, PrivateToken)
664        .ok_or::<std::io::Error>(std::io::ErrorKind::Other.into())?;
665
666    // Safety: We own the file descriptor and so can transfer it, and it is
667    // guaranteed to be open by the earlier error checking.
668    Ok(Socket {
669        timestamp_mode: timestamping.into(),
670        raw: Arc::new(unsafe { AsyncFd::register(socket) }?),
671        #[cfg(target_os = "linux")]
672        send_counter: std::sync::Mutex::new(0),
673        local_addr,
674        _state: PhantomData,
675    })
676}
677
678/// Open an ipv4 specific socket listening to a given ip address.
679///
680/// # Errors
681/// May error if the requested timestamp mode is not possible, or if the requested addres is unavailable.
682pub fn open_ipv4(
683    addr: SocketAddrV4,
684    timestamping: GeneralTimestampMode,
685    #[cfg_attr(not(target_os = "linux"), expect(unused))] reuse_addr: bool,
686) -> std::io::Result<Socket<SocketAddrV4, Open>> {
687    // Setup the socket
688    let socket = RawSocket::open(libc::PF_INET, libc::SOCK_DGRAM, libc::IPPROTO_UDP)?;
689    socket.enable_destination_ipv4()?;
690    #[cfg(target_os = "linux")]
691    if reuse_addr {
692        socket.reuse_addr()?;
693    }
694    socket.bind(addr.to_sockaddr(PrivateToken))?;
695    socket.set_nonblocking(true)?;
696    configure_timestamping(&socket, None, timestamping.into(), None)?;
697
698    let local_addr = SocketAddrV4::from_sockaddr(socket.getsockname()?, PrivateToken)
699        .ok_or::<std::io::Error>(std::io::ErrorKind::Other.into())?;
700
701    // Safety: We own the file descriptor and so can transfer it, and it is
702    // guaranteed to be open by the earlier error checking.
703    Ok(Socket {
704        timestamp_mode: timestamping.into(),
705        raw: Arc::new(unsafe { AsyncFd::register(socket) }?),
706        #[cfg(target_os = "linux")]
707        send_counter: std::sync::Mutex::new(0),
708        local_addr,
709        _state: PhantomData,
710    })
711}
712
713/// Open an ipv6 specific socket listening to a given ip address.
714///
715/// # Errors
716/// May error if the requested timestamp mode is not possible, or if the requested addres is unavailable.
717pub fn open_ipv6(
718    addr: SocketAddrV6,
719    timestamping: GeneralTimestampMode,
720    #[cfg_attr(not(target_os = "linux"), expect(unused))] reuse_addr: bool,
721) -> std::io::Result<Socket<SocketAddrV6, Open>> {
722    // Setup the socket
723    let socket = RawSocket::open(libc::PF_INET6, libc::SOCK_DGRAM, libc::IPPROTO_UDP)?;
724    socket.ipv6_only()?;
725    socket.enable_destination_ipv6()?;
726    #[cfg(target_os = "linux")]
727    if reuse_addr {
728        socket.reuse_addr()?;
729    }
730    socket.bind(addr.to_sockaddr(PrivateToken))?;
731    socket.set_nonblocking(true)?;
732    configure_timestamping(&socket, None, timestamping.into(), None)?;
733
734    let local_addr = SocketAddrV6::from_sockaddr(socket.getsockname()?, PrivateToken)
735        .ok_or::<std::io::Error>(std::io::ErrorKind::Other.into())?;
736
737    // Safety: We own the file descriptor and so can transfer it, and it is
738    // guaranteed to be open by the earlier error checking.
739    Ok(Socket {
740        timestamp_mode: timestamping.into(),
741        raw: Arc::new(unsafe { AsyncFd::register(socket) }?),
742        #[cfg(target_os = "linux")]
743        send_counter: std::sync::Mutex::new(0),
744        local_addr,
745        _state: PhantomData,
746    })
747}
748
749/// Open a socket connected to the given remote address.
750///
751/// # Errors
752/// Errors if it is not possible to setup the connection to the remote.
753pub fn connect_address(
754    addr: SocketAddr,
755    timestamping: GeneralTimestampMode,
756) -> std::io::Result<Socket<SocketAddr, Connected>> {
757    // Setup the socket
758    let socket = match addr {
759        SocketAddr::V4(_) => RawSocket::open(libc::PF_INET, libc::SOCK_DGRAM, libc::IPPROTO_UDP),
760        SocketAddr::V6(_) => RawSocket::open(libc::PF_INET6, libc::SOCK_DGRAM, libc::IPPROTO_UDP),
761    }?;
762    match addr {
763        SocketAddr::V4(_) => socket.enable_destination_ipv4()?,
764        SocketAddr::V6(_) => socket.enable_destination_ipv6()?,
765    }
766    socket.connect(addr.to_sockaddr(PrivateToken))?;
767    socket.set_nonblocking(true)?;
768    configure_timestamping(&socket, None, timestamping.into(), None)?;
769
770    let local_addr = SocketAddr::from_sockaddr(socket.getsockname()?, PrivateToken)
771        .ok_or::<std::io::Error>(std::io::ErrorKind::Other.into())?;
772
773    // Safety: We own the file descriptor and so can transfer it, and it is
774    // guaranteed to be open by the earlier error checking.
775    Ok(Socket {
776        timestamp_mode: timestamping.into(),
777        raw: Arc::new(unsafe { AsyncFd::register(socket) }?),
778        #[cfg(target_os = "linux")]
779        send_counter: std::sync::Mutex::new(0),
780        local_addr,
781        _state: PhantomData,
782    })
783}
784
785#[cfg(test)]
786mod tests {
787    use super::*;
788    use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
789
790    #[tokio::test]
791    async fn test_open_ip() {
792        let mut a = open_ip(
793            SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 5125),
794            GeneralTimestampMode::None,
795            false,
796        )
797        .unwrap();
798        let mut b = connect_address(
799            SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 5125),
800            GeneralTimestampMode::None,
801        )
802        .unwrap();
803        assert!(b.send(&[1, 2, 3]).await.is_ok());
804        let mut buf = [0; 4];
805        let recv_result = a.recv(&mut buf).await.unwrap();
806        assert_eq!(recv_result.bytes_read, 3);
807        assert_eq!(&buf[0..3], &[1, 2, 3]);
808        assert!(a.send_to(&[4, 5, 6], recv_result.remote_addr).await.is_ok());
809        let recv_result = b.recv(&mut buf).await.unwrap();
810        assert_eq!(recv_result.bytes_read, 3);
811        assert_eq!(&buf[0..3], &[4, 5, 6]);
812    }
813
814    #[tokio::test]
815    async fn test_open_ip_dest_addr() {
816        let a = open_ip(
817            SocketAddr::new(IpAddr::V4(Ipv4Addr::UNSPECIFIED), 5127),
818            GeneralTimestampMode::None,
819            false,
820        )
821        .unwrap();
822        let mut b = connect_address(
823            SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 5127),
824            GeneralTimestampMode::None,
825        )
826        .unwrap();
827        assert!(b.send(&[1, 2, 3]).await.is_ok());
828        let mut buf = [0; 4];
829        let recv_result = a.recv(&mut buf).await.unwrap();
830        assert_eq!(recv_result.bytes_read, 3);
831        assert_eq!(&buf[0..3], &[1, 2, 3]);
832        assert_eq!(
833            recv_result.local_addr,
834            SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 5127)
835        );
836        assert_ne!(a.local_addr().ip(), IpAddr::V4(Ipv4Addr::LOCALHOST));
837
838        let a = open_ip(
839            SocketAddr::new(IpAddr::V6(Ipv6Addr::UNSPECIFIED), 5129),
840            GeneralTimestampMode::None,
841            false,
842        )
843        .unwrap();
844        let mut b = connect_address(
845            SocketAddr::new(IpAddr::V6(Ipv6Addr::LOCALHOST), 5129),
846            GeneralTimestampMode::None,
847        )
848        .unwrap();
849        assert!(b.send(&[1, 2, 3]).await.is_ok());
850        let mut buf = [0; 4];
851        let recv_result = a.recv(&mut buf).await.unwrap();
852        assert_eq!(recv_result.bytes_read, 3);
853        assert_eq!(&buf[0..3], &[1, 2, 3]);
854        assert_eq!(
855            recv_result.local_addr,
856            SocketAddr::new(IpAddr::V6(Ipv6Addr::LOCALHOST), 5129)
857        );
858        assert_ne!(a.local_addr().ip(), IpAddr::V6(Ipv6Addr::LOCALHOST));
859    }
860
861    #[tokio::test]
862    async fn test_send_from() {
863        let mut a = open_ip(
864            SocketAddr::new(IpAddr::V4(Ipv4Addr::UNSPECIFIED), 5130),
865            GeneralTimestampMode::None,
866            false,
867        )
868        .unwrap();
869        let mut b = connect_address(
870            SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 5130),
871            GeneralTimestampMode::None,
872        )
873        .unwrap();
874        b.send_from(
875            &[1, 2, 3],
876            SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 0),
877        )
878        .await
879        .unwrap();
880        let mut buf = [0; 4];
881        let recv_result = a.recv(&mut buf).await.unwrap();
882        assert_eq!(recv_result.bytes_read, 3);
883        assert_eq!(&buf[0..3], &[1, 2, 3]);
884        assert_eq!(
885            recv_result.remote_addr.ip(),
886            IpAddr::V4(Ipv4Addr::LOCALHOST)
887        );
888
889        a.send_from_to(
890            &[1, 2, 3],
891            SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 0),
892            dbg!(b.local_addr()),
893        )
894        .await
895        .unwrap();
896        let mut buf = [0; 4];
897        let recv_result = b.recv(&mut buf).await.unwrap();
898        assert_eq!(recv_result.bytes_read, 3);
899        assert_eq!(&buf[0..3], &[1, 2, 3]);
900        assert_eq!(
901            recv_result.remote_addr.ip(),
902            IpAddr::V4(Ipv4Addr::LOCALHOST)
903        );
904    }
905
906    #[tokio::test]
907    async fn test_send_from_v6() {
908        let mut a = open_ip(
909            SocketAddr::new(IpAddr::V6(Ipv6Addr::UNSPECIFIED), 5131),
910            GeneralTimestampMode::None,
911            false,
912        )
913        .unwrap();
914        let mut b = connect_address(
915            SocketAddr::new(IpAddr::V6(Ipv6Addr::LOCALHOST), 5131),
916            GeneralTimestampMode::None,
917        )
918        .unwrap();
919        b.send_from(
920            &[1, 2, 3],
921            SocketAddr::new(IpAddr::V6(Ipv6Addr::LOCALHOST), 0),
922        )
923        .await
924        .unwrap();
925        let mut buf = [0; 4];
926        let recv_result = a.recv(&mut buf).await.unwrap();
927        assert_eq!(recv_result.bytes_read, 3);
928        assert_eq!(&buf[0..3], &[1, 2, 3]);
929        assert_eq!(
930            recv_result.remote_addr.ip(),
931            IpAddr::V6(Ipv6Addr::LOCALHOST)
932        );
933
934        a.send_from_to(
935            &[1, 2, 3],
936            SocketAddr::new(IpAddr::V6(Ipv6Addr::LOCALHOST), 0),
937            dbg!(b.local_addr()),
938        )
939        .await
940        .unwrap();
941        let mut buf = [0; 4];
942        let recv_result = b.recv(&mut buf).await.unwrap();
943        assert_eq!(recv_result.bytes_read, 3);
944        assert_eq!(&buf[0..3], &[1, 2, 3]);
945        assert_eq!(
946            recv_result.remote_addr.ip(),
947            IpAddr::V6(Ipv6Addr::LOCALHOST)
948        );
949    }
950}