nex_datalink/
lib.rs

1//! Provides functionality for interacting with the data link layer, support for sending and receiving packets.
2
3#![deny(warnings)]
4
5use std::io;
6use std::option::Option;
7use std::time::Duration;
8
9mod bindings;
10
11pub mod async_io;
12
13#[cfg(windows)]
14#[path = "wpcap.rs"]
15mod backend;
16
17#[cfg(windows)]
18pub mod wpcap;
19
20#[cfg(all(any(target_os = "linux", target_os = "android")))]
21#[path = "linux.rs"]
22mod backend;
23
24#[cfg(any(target_os = "linux", target_os = "android"))]
25pub mod linux;
26
27#[cfg(all(any(
28    target_os = "freebsd",
29    target_os = "openbsd",
30    target_os = "netbsd",
31    target_os = "illumos",
32    target_os = "solaris",
33    target_os = "macos",
34    target_os = "ios"
35)))]
36#[path = "bpf.rs"]
37mod backend;
38
39#[cfg(any(
40    target_os = "freebsd",
41    target_os = "netbsd",
42    target_os = "illumos",
43    target_os = "solaris",
44    target_os = "macos",
45    target_os = "ios"
46))]
47pub mod bpf;
48
49#[cfg(feature = "pcap")]
50pub mod pcap;
51
52/// Type alias for an `EtherType`.
53pub type EtherType = u16;
54
55/// Type of data link channel to present (Linux only).
56#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
57pub enum ChannelType {
58    /// Send and receive layer 2 packets directly, including headers.
59    Layer2,
60    /// Send and receive IP packets - send and receive network layer packets.
61    Layer3(EtherType),
62}
63
64/// A channel for sending and receiving at the data link layer.
65#[non_exhaustive]
66pub enum Channel {
67    /// A datalink channel which sends and receives Ethernet packets.
68    Ethernet(Box<dyn RawSender>, Box<dyn RawReceiver>),
69}
70
71/// Socket fanout type (Linux only).
72#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
73pub enum FanoutType {
74    HASH,
75    LB,
76    CPU,
77    ROLLOVER,
78    RND,
79    QM,
80    CBPF,
81    EBPF,
82}
83
84/// Fanout settings (Linux only).
85#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
86pub struct FanoutOption {
87    pub group_id: u16,
88    pub fanout_type: FanoutType,
89    pub defrag: bool,
90    pub rollover: bool,
91}
92
93/// A generic configuration type, encapsulating all options supported by each backend.
94///
95/// Each option should be treated as a hint - each backend is free to ignore any and all
96/// options which don't apply to it.
97#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
98pub struct Config {
99    /// The size of buffer to use when writing packets. Defaults to 4096.
100    pub write_buffer_size: usize,
101
102    /// The size of buffer to use when reading packets. Defaults to 4096.
103    pub read_buffer_size: usize,
104
105    /// Linux/BPF/Netmap only: The read timeout. Defaults to None.
106    pub read_timeout: Option<Duration>,
107
108    /// Linux/BPF/Netmap only: The write timeout. Defaults to None.
109    pub write_timeout: Option<Duration>,
110
111    /// Linux only: Specifies whether to read packets at the datalink layer or network layer.
112    /// Defaults to Layer2
113    pub channel_type: ChannelType,
114
115    /// BPF/macOS only: The number of /dev/bpf* file descriptors to attempt before failing. Defaults
116    /// to: 1000.
117    pub bpf_fd_attempts: usize,
118
119    pub linux_fanout: Option<FanoutOption>,
120
121    pub promiscuous: bool,
122}
123
124impl Default for Config {
125    fn default() -> Config {
126        Config {
127            write_buffer_size: 4096,
128            read_buffer_size: 4096,
129            read_timeout: None,
130            write_timeout: None,
131            channel_type: ChannelType::Layer2,
132            bpf_fd_attempts: 1000,
133            linux_fanout: None,
134            promiscuous: true,
135        }
136    }
137}
138
139/// Creates a new datalink channel for sending and receiving raw packets.
140///
141/// This function sets up a channel to send and receive raw packets directly from a data link layer
142/// such as Ethernet. It uses the provided network interface and configuration settings to
143/// establish the channel. Note that the actual usage of the configuration may vary based on the
144/// underlying backend; some settings may be ignored or treated differently depending on the system
145/// and library capabilities.
146///
147/// The function returns a `Channel` object encapsulating the transmission and reception capabilities.
148#[inline]
149pub fn channel(
150    network_interface: &nex_core::interface::Interface,
151    configuration: Config,
152) -> io::Result<Channel> {
153    backend::channel(network_interface, (&configuration).into())
154}
155
156/// Trait to enable sending `$packet` packets.
157pub trait RawSender: Send {
158    /// Create and send a number of packets.
159    ///
160    /// This will call `func` `num_packets` times. The function will be provided with a
161    /// mutable packet to manipulate, which will then be sent. This allows packets to be
162    /// built in-place, avoiding the copy required for `send`. If there is not sufficient
163    /// capacity in the buffer, None will be returned.
164    fn build_and_send(
165        &mut self,
166        num_packets: usize,
167        packet_size: usize,
168        func: &mut dyn FnMut(&mut [u8]),
169    ) -> Option<io::Result<()>>;
170
171    /// Send a packet.
172    ///
173    /// This may require an additional copy compared to `build_and_send`, depending on the
174    /// operating system being used.
175    fn send(&mut self, packet: &[u8]) -> Option<io::Result<()>>;
176}
177
178/// Structure for receiving packets at the data link layer. Should be constructed using
179/// `channel()`.
180pub trait RawReceiver: Send {
181    /// Get the next ethernet frame in the channel.
182    fn next(&mut self) -> io::Result<&[u8]>;
183}
184
185#[cfg(test)]
186mod tests {
187    use super::*;
188
189    #[test]
190    fn config_default_values() {
191        let cfg = Config::default();
192        assert_eq!(cfg.write_buffer_size, 4096);
193        assert_eq!(cfg.read_buffer_size, 4096);
194        assert_eq!(cfg.read_timeout, None);
195        assert_eq!(cfg.write_timeout, None);
196        assert_eq!(cfg.channel_type, ChannelType::Layer2);
197        assert_eq!(cfg.bpf_fd_attempts, 1000);
198        assert!(cfg.linux_fanout.is_none());
199        assert!(cfg.promiscuous);
200    }
201}