nex_socket/icmp/
config.rs

1use socket2::Type as SockType;
2use std::{net::SocketAddr, time::Duration};
3
4use crate::SocketFamily;
5
6/// ICMP protocol version.
7#[derive(Debug, Clone, Copy, PartialEq, Eq)]
8pub enum IcmpKind {
9    V4,
10    V6,
11}
12
13/// ICMP socket type, either DGRAM or RAW.
14#[derive(Debug, Clone, Copy, PartialEq, Eq)]
15pub enum IcmpSocketType {
16    Dgram,
17    Raw,
18}
19
20impl IcmpSocketType {
21    /// Returns true if the socket type is DGRAM.
22    pub fn is_dgram(&self) -> bool {
23        matches!(self, IcmpSocketType::Dgram)
24    }
25
26    /// Returns true if the socket type is RAW.
27    pub fn is_raw(&self) -> bool {
28        matches!(self, IcmpSocketType::Raw)
29    }
30
31    /// Converts the ICMP socket type from a `socket2::Type`.
32    pub(crate) fn from_sock_type(sock_type: SockType) -> Self {
33        match sock_type {
34            SockType::DGRAM => IcmpSocketType::Dgram,
35            SockType::RAW => IcmpSocketType::Raw,
36            _ => panic!("Invalid ICMP socket type"),
37        }
38    }
39
40    /// Converts the ICMP socket type to a `socket2::Type`.
41    pub(crate) fn to_sock_type(&self) -> SockType {
42        match self {
43            IcmpSocketType::Dgram => SockType::DGRAM,
44            IcmpSocketType::Raw => SockType::RAW,
45        }
46    }
47}
48
49/// Configuration for an ICMP socket.
50#[derive(Debug, Clone)]
51pub struct IcmpConfig {
52    /// The socket family.
53    pub socket_family: SocketFamily,
54    /// Optional bind address for the socket.
55    pub bind: Option<SocketAddr>,
56    /// Time-to-live for IPv4 packets.
57    pub ttl: Option<u32>,
58    /// Hop limit for IPv6 packets.
59    pub hoplimit: Option<u32>,
60    /// Read timeout for the socket.
61    pub read_timeout: Option<Duration>,
62    /// Write timeout for the socket.
63    pub write_timeout: Option<Duration>,
64    /// Network interface to use for the socket.
65    pub interface: Option<String>,
66    /// Socket type hint, DGRAM preferred on Linux, RAW fallback on macOS/Windows.
67    pub sock_type_hint: IcmpSocketType,
68    /// FreeBSD only: optional FIB (Forwarding Information Base) support.
69    pub fib: Option<u32>,
70}
71
72impl IcmpConfig {
73    /// Creates a new ICMP configuration with the specified kind.
74    pub fn new(kind: IcmpKind) -> Self {
75        Self {
76            socket_family: match kind {
77                IcmpKind::V4 => SocketFamily::IPV4,
78                IcmpKind::V6 => SocketFamily::IPV6,
79            },
80            bind: None,
81            ttl: None,
82            hoplimit: None,
83            read_timeout: None,
84            write_timeout: None,
85            interface: None,
86            sock_type_hint: IcmpSocketType::Dgram,
87            fib: None,
88        }
89    }
90
91    /// Set bind address for the socket.
92    pub fn with_bind(mut self, addr: SocketAddr) -> Self {
93        self.bind = Some(addr);
94        self
95    }
96
97    /// Set the time-to-live for IPv4 packets.
98    pub fn with_ttl(mut self, ttl: u32) -> Self {
99        self.ttl = Some(ttl);
100        self
101    }
102
103    /// Set the hop limit for IPv6 packets.
104    pub fn with_hoplimit(mut self, hops: u32) -> Self {
105        self.hoplimit = Some(hops);
106        self
107    }
108
109    /// Set the read timeout for the socket.
110    pub fn with_read_timeout(mut self, timeout: Duration) -> Self {
111        self.read_timeout = Some(timeout);
112        self
113    }
114
115    /// Set the write timeout for the socket.
116    pub fn with_write_timeout(mut self, timeout: Duration) -> Self {
117        self.write_timeout = Some(timeout);
118        self
119    }
120
121    /// Set the network interface to use for the socket.
122    pub fn with_interface(mut self, iface: impl Into<String>) -> Self {
123        self.interface = Some(iface.into());
124        self
125    }
126
127    /// Set the socket type hint. (DGRAM or RAW)
128    pub fn with_sock_type(mut self, ty: IcmpSocketType) -> Self {
129        self.sock_type_hint = ty;
130        self
131    }
132
133    /// Set the FIB (Forwarding Information Base) for FreeBSD.
134    pub fn with_fib(mut self, fib: u32) -> Self {
135        self.fib = Some(fib);
136        self
137    }
138}
139
140#[cfg(test)]
141mod tests {
142    use super::*;
143    #[test]
144    fn icmp_config_builders() {
145        let addr: SocketAddr = "127.0.0.1:0".parse().unwrap();
146        let cfg = IcmpConfig::new(IcmpKind::V4)
147            .with_bind(addr)
148            .with_ttl(4)
149            .with_interface("eth0")
150            .with_sock_type(IcmpSocketType::Raw);
151        assert_eq!(cfg.socket_family, SocketFamily::IPV4);
152        assert_eq!(cfg.bind, Some(addr));
153        assert_eq!(cfg.ttl, Some(4));
154        assert_eq!(cfg.interface.as_deref(), Some("eth0"));
155        assert_eq!(cfg.sock_type_hint, IcmpSocketType::Raw);
156    }
157}