Skip to main content

tun_rs/platform/linux/
device.rs

1use crate::platform::linux::offload::{
2    gso_none_checksum, gso_split, handle_gro, VirtioNetHdr, VIRTIO_NET_HDR_F_NEEDS_CSUM,
3    VIRTIO_NET_HDR_GSO_NONE, VIRTIO_NET_HDR_GSO_TCPV4, VIRTIO_NET_HDR_GSO_TCPV6,
4    VIRTIO_NET_HDR_GSO_UDP_L4, VIRTIO_NET_HDR_LEN,
5};
6use crate::platform::unix::device::{ctl, ctl_v6};
7use crate::platform::{ExpandBuffer, GROTable};
8use crate::{
9    builder::{DeviceConfig, Layer},
10    platform::linux::sys::*,
11    platform::{
12        unix::{ipaddr_to_sockaddr, sockaddr_union, Fd, Tun},
13        ETHER_ADDR_LEN,
14    },
15    ToIpv4Address, ToIpv4Netmask, ToIpv6Address, ToIpv6Netmask,
16};
17use ipnet::IpNet;
18use libc::{
19    self, c_char, c_short, ifreq, in6_ifreq, ARPHRD_ETHER, IFF_MULTI_QUEUE, IFF_NO_PI, IFF_RUNNING,
20    IFF_TAP, IFF_TUN, IFF_UP, IFNAMSIZ, O_RDWR,
21};
22use std::net::Ipv6Addr;
23use std::sync::{Arc, RwLock};
24use std::{
25    ffi::CString,
26    io, mem,
27    net::{IpAddr, Ipv4Addr},
28    os::unix::io::{AsRawFd, RawFd},
29    ptr,
30};
31
32const OVERWRITE_SIZE: usize = mem::size_of::<libc::__c_anonymous_ifr_ifru>();
33
34/// A TUN device using the TUN/TAP Linux driver.
35pub struct DeviceImpl {
36    pub(crate) tun: Tun,
37    pub(crate) vnet_hdr: bool,
38    pub(crate) udp_gso: bool,
39    flags: c_short,
40    pub(crate) op_lock: Arc<RwLock<()>>,
41}
42
43impl DeviceImpl {
44    /// Create a new `Device` for the given `Configuration`.
45    pub(crate) fn new(config: DeviceConfig) -> std::io::Result<Self> {
46        let dev_name = match config.dev_name.as_ref() {
47            Some(tun_name) => {
48                let tun_name = CString::new(tun_name.clone())?;
49
50                if tun_name.as_bytes_with_nul().len() > IFNAMSIZ {
51                    return Err(std::io::Error::new(
52                        std::io::ErrorKind::InvalidInput,
53                        "device name too long",
54                    ));
55                }
56
57                Some(tun_name)
58            }
59
60            None => None,
61        };
62
63        // Create the device node if it is missing.
64        // Silently ignore errors, let opening the device report an error.
65        // This way, we don't fail if someone races us to create the device node.
66        if let Ok(false) = std::fs::exists("/dev/net/tun") {
67            std::fs::create_dir_all("/dev/net").ok();
68            unsafe {
69                libc::mknod(
70                    c"/dev/net/tun".as_ptr(),
71                    0o666 | libc::S_IFCHR,
72                    libc::makedev(10, 200),
73                );
74            }
75        }
76
77        unsafe {
78            let mut req: ifreq = mem::zeroed();
79
80            if let Some(dev_name) = dev_name.as_ref() {
81                ptr::copy_nonoverlapping(
82                    dev_name.as_ptr() as *const c_char,
83                    req.ifr_name.as_mut_ptr(),
84                    dev_name.as_bytes_with_nul().len(),
85                );
86            }
87            let multi_queue = config.multi_queue.unwrap_or(false);
88            let device_type: c_short = config.layer.unwrap_or(Layer::L3).into();
89            let iff_no_pi = IFF_NO_PI as c_short;
90            let iff_vnet_hdr = libc::IFF_VNET_HDR as c_short;
91            let iff_multi_queue = IFF_MULTI_QUEUE as c_short;
92            let packet_information = config.packet_information.unwrap_or(false);
93            let offload = config.offload.unwrap_or(false);
94            req.ifr_ifru.ifru_flags = device_type
95                | if packet_information { 0 } else { iff_no_pi }
96                | if multi_queue { iff_multi_queue } else { 0 }
97                | if offload { iff_vnet_hdr } else { 0 };
98
99            let fd = libc::open(
100                c"/dev/net/tun".as_ptr() as *const _,
101                O_RDWR | libc::O_CLOEXEC,
102                0,
103            );
104            let tun_fd = Fd::new(fd)?;
105            if let Err(err) = tunsetiff(tun_fd.inner, &mut req as *mut _ as *mut _) {
106                return Err(io::Error::from(err));
107            }
108            let (vnet_hdr, udp_gso) = if offload && libc::IFF_VNET_HDR != 0 {
109                // tunTCPOffloads were added in Linux v2.6. We require their support if IFF_VNET_HDR is set.
110                let tun_tcp_offloads = libc::TUN_F_CSUM | libc::TUN_F_TSO4 | libc::TUN_F_TSO6;
111                let tun_udp_offloads = libc::TUN_F_USO4 | libc::TUN_F_USO6;
112                if let Err(err) = tunsetoffload(tun_fd.inner, tun_tcp_offloads as _) {
113                    log::warn!("unsupported offload: {err:?}");
114                    (false, false)
115                } else {
116                    // tunUDPOffloads were added in Linux v6.2. We do not return an
117                    // error if they are unsupported at runtime.
118                    let rs =
119                        tunsetoffload(tun_fd.inner, (tun_tcp_offloads | tun_udp_offloads) as _);
120                    (true, rs.is_ok())
121                }
122            } else {
123                // The TUN_F_* offload mask is device-wide state, not
124                // per-fd. When attaching to a persistent TUN that a
125                // previous process configured with offload=true, the
126                // mask remains set across detaches: the kernel will
127                // still deliver GSO aggregates to this new fd even
128                // though IFF_VNET_HDR is not set, and the offload-
129                // unaware caller will read them as oversized single
130                // packets (ping survives, TCP bulk transfer fails).
131                // Explicitly reset the mask. No-op on a freshly
132                // created device (mask is already zero). Failure is
133                // logged but not propagated, mirroring the
134                // best-effort treatment of UDP offload above.
135                if let Err(err) = tunsetoffload(tun_fd.inner, 0 as _) {
136                    log::warn!("failed to clear TUN offload mask: {err:?}");
137                }
138                (false, false)
139            };
140
141            let device = DeviceImpl {
142                tun: Tun::new(tun_fd),
143                vnet_hdr,
144                udp_gso,
145                flags: req.ifr_ifru.ifru_flags,
146                op_lock: Arc::new(RwLock::new(())),
147            };
148            Ok(device)
149        }
150    }
151    unsafe fn set_tcp_offloads(&self) -> io::Result<()> {
152        let tun_tcp_offloads = libc::TUN_F_CSUM | libc::TUN_F_TSO4 | libc::TUN_F_TSO6;
153        tunsetoffload(self.as_raw_fd(), tun_tcp_offloads as _)
154            .map(|_| ())
155            .map_err(|e| e.into())
156    }
157    unsafe fn set_tcp_udp_offloads(&self) -> io::Result<()> {
158        let tun_tcp_offloads = libc::TUN_F_CSUM | libc::TUN_F_TSO4 | libc::TUN_F_TSO6;
159        let tun_udp_offloads = libc::TUN_F_USO4 | libc::TUN_F_USO6;
160        tunsetoffload(self.as_raw_fd(), (tun_tcp_offloads | tun_udp_offloads) as _)
161            .map(|_| ())
162            .map_err(|e| e.into())
163    }
164    pub(crate) fn from_tun(tun: Tun) -> io::Result<Self> {
165        Ok(Self {
166            tun,
167            vnet_hdr: false,
168            udp_gso: false,
169            flags: 0,
170            op_lock: Arc::new(RwLock::new(())),
171        })
172    }
173
174    /// # Prerequisites
175    /// - The `IFF_MULTI_QUEUE` flag must be enabled.
176    /// - The system must support network interface multi-queue functionality.
177    ///
178    /// # Description
179    /// When multi-queue is enabled, create a new queue by duplicating an existing one.
180    pub(crate) fn try_clone(&self) -> io::Result<DeviceImpl> {
181        let flags = self.flags;
182        if flags & (IFF_MULTI_QUEUE as c_short) != IFF_MULTI_QUEUE as c_short {
183            return Err(io::Error::new(
184                io::ErrorKind::Unsupported,
185                "iff_multi_queue not enabled",
186            ));
187        }
188        unsafe {
189            let mut req = self.request()?;
190            req.ifr_ifru.ifru_flags = flags;
191            let fd = libc::open(
192                c"/dev/net/tun".as_ptr() as *const _,
193                O_RDWR | libc::O_CLOEXEC,
194            );
195            let tun_fd = Fd::new(fd)?;
196            if let Err(err) = tunsetiff(tun_fd.inner, &mut req as *mut _ as *mut _) {
197                return Err(io::Error::from(err));
198            }
199            let dev = DeviceImpl {
200                tun: Tun::new(tun_fd),
201                vnet_hdr: self.vnet_hdr,
202                udp_gso: self.udp_gso,
203                flags,
204                op_lock: self.op_lock.clone(),
205            };
206            if dev.vnet_hdr {
207                if dev.udp_gso {
208                    dev.set_tcp_udp_offloads()?
209                } else {
210                    dev.set_tcp_offloads()?;
211                }
212            }
213
214            Ok(dev)
215        }
216    }
217    /// Returns whether UDP Generic Segmentation Offload (GSO) is enabled.
218    ///
219    /// This is determined by the `udp_gso` flag in the device.
220    pub fn udp_gso(&self) -> bool {
221        self.udp_gso
222    }
223    /// Returns whether TCP Generic Segmentation Offload (GSO) is enabled.
224    ///
225    /// In this implementation, this is represented by the `vnet_hdr` flag.
226    pub fn tcp_gso(&self) -> bool {
227        self.vnet_hdr
228    }
229    /// Sets the transmit queue length for the network interface.
230    ///
231    /// This method constructs an interface request (`ifreq`) structure,
232    /// assigns the desired transmit queue length to the `ifru_metric` field,
233    /// and calls the `change_tx_queue_len` function using the control file descriptor.
234    /// If the underlying operation fails, an I/O error is returned.
235    pub fn set_tx_queue_len(&self, tx_queue_len: u32) -> io::Result<()> {
236        let _guard = self.op_lock.write().unwrap();
237        unsafe {
238            let mut ifreq = self.request()?;
239            ifreq.ifr_ifru.ifru_metric = tx_queue_len as _;
240            if let Err(err) = change_tx_queue_len(ctl()?.as_raw_fd(), &ifreq) {
241                return Err(io::Error::from(err));
242            }
243        }
244        Ok(())
245    }
246    /// Retrieves the current transmit queue length for the network interface.
247    ///
248    /// This function constructs an interface request structure and calls `tx_queue_len`
249    /// to populate it with the current transmit queue length. The value is then returned.
250    pub fn tx_queue_len(&self) -> io::Result<u32> {
251        let _guard = self.op_lock.read().unwrap();
252        unsafe {
253            let mut ifreq = self.request()?;
254            if let Err(err) = tx_queue_len(ctl()?.as_raw_fd(), &mut ifreq) {
255                return Err(io::Error::from(err));
256            }
257            Ok(ifreq.ifr_ifru.ifru_metric as _)
258        }
259    }
260    /// Make the device persistent.
261    ///
262    /// By default, TUN/TAP devices are destroyed when the process exits.
263    /// Calling this method makes the device persist after the program terminates,
264    /// allowing it to be reused by other processes.
265    ///
266    /// # Example
267    ///
268    /// ```no_run
269    /// # #[cfg(all(target_os = "linux", not(target_env = "ohos")))]
270    /// # {
271    /// use tun_rs::DeviceBuilder;
272    ///
273    /// let dev = DeviceBuilder::new()
274    ///     .name("persistent-tun")
275    ///     .ipv4("10.0.0.1", 24, None)
276    ///     .build_sync()?;
277    ///
278    /// // Make the device persistent so it survives after program exit
279    /// dev.persist()?;
280    /// println!("Device will persist after program exits");
281    /// # }
282    /// # Ok::<(), std::io::Error>(())
283    /// ```
284    pub fn persist(&self) -> io::Result<()> {
285        let _guard = self.op_lock.write().unwrap();
286        unsafe {
287            if let Err(err) = tunsetpersist(self.as_raw_fd(), &1) {
288                Err(io::Error::from(err))
289            } else {
290                Ok(())
291            }
292        }
293    }
294
295    /// Set the owner (UID) of the device.
296    ///
297    /// This allows non-root users to access the TUN/TAP device.
298    ///
299    /// # Example
300    ///
301    /// ```no_run
302    /// # #[cfg(all(target_os = "linux", not(target_env = "ohos")))]
303    /// # {
304    /// use tun_rs::DeviceBuilder;
305    ///
306    /// let dev = DeviceBuilder::new()
307    ///     .ipv4("10.0.0.1", 24, None)
308    ///     .build_sync()?;
309    ///
310    /// // Set ownership to UID 1000 (typical first user on Linux)
311    /// dev.user(1000)?;
312    /// println!("Device ownership set to UID 1000");
313    /// # }
314    /// # Ok::<(), std::io::Error>(())
315    /// ```
316    pub fn user(&self, value: i32) -> io::Result<()> {
317        let _guard = self.op_lock.write().unwrap();
318        unsafe {
319            if let Err(err) = tunsetowner(self.as_raw_fd(), &value) {
320                Err(io::Error::from(err))
321            } else {
322                Ok(())
323            }
324        }
325    }
326
327    /// Set the group (GID) of the device.
328    ///
329    /// This allows members of a specific group to access the TUN/TAP device.
330    ///
331    /// # Example
332    ///
333    /// ```no_run
334    /// # #[cfg(all(target_os = "linux", not(target_env = "ohos")))]
335    /// # {
336    /// use tun_rs::DeviceBuilder;
337    ///
338    /// let dev = DeviceBuilder::new()
339    ///     .ipv4("10.0.0.1", 24, None)
340    ///     .build_sync()?;
341    ///
342    /// // Set group ownership to GID 1000
343    /// dev.group(1000)?;
344    /// println!("Device group ownership set to GID 1000");
345    /// # }
346    /// # Ok::<(), std::io::Error>(())
347    /// ```
348    pub fn group(&self, value: i32) -> io::Result<()> {
349        let _guard = self.op_lock.write().unwrap();
350        unsafe {
351            if let Err(err) = tunsetgroup(self.as_raw_fd(), &value) {
352                Err(io::Error::from(err))
353            } else {
354                Ok(())
355            }
356        }
357    }
358    /// Sends multiple packets in a batch with GRO (Generic Receive Offload) coalescing.
359    ///
360    /// This method allows efficient transmission of multiple packets by batching them together
361    /// and applying GRO optimizations. When offload is enabled, packets may be coalesced
362    /// to reduce system call overhead and improve throughput.
363    ///
364    /// # Arguments
365    ///
366    /// * `gro_table` - A mutable reference to a [`GROTable`] that manages packet coalescing state.
367    ///   This table can be reused across multiple calls to amortize allocation overhead.
368    /// * `bufs` - A mutable slice of buffers containing the packets to send. Each buffer must
369    ///   implement the [`ExpandBuffer`] trait.
370    /// * `offset` - The byte offset within each buffer where the packet data begins.
371    ///   Must be >= `VIRTIO_NET_HDR_LEN` to accommodate the virtio network header when offload is enabled.
372    ///
373    /// # Returns
374    ///
375    /// Returns the total number of bytes successfully sent, or an I/O error.
376    ///
377    /// # Example
378    ///
379    /// ```no_run
380    /// # #[cfg(all(target_os = "linux", not(target_env = "ohos")))]
381    /// # {
382    /// use tun_rs::{DeviceBuilder, GROTable, VIRTIO_NET_HDR_LEN};
383    ///
384    /// let dev = DeviceBuilder::new()
385    ///     .ipv4("10.0.0.1", 24, None)
386    ///     .with(|builder| {
387    ///         builder.offload(true) // Enable offload for GRO
388    ///     })
389    ///     .build_sync()?;
390    ///
391    /// let mut gro_table = GROTable::default();
392    /// let offset = VIRTIO_NET_HDR_LEN;
393    ///
394    /// // Prepare packets to send
395    /// let mut packet1 = vec![0u8; offset + 100]; // VIRTIO_NET_HDR + packet data
396    /// let mut packet2 = vec![0u8; offset + 200];
397    /// // Fill in packet data at offset...
398    ///
399    /// let mut bufs = vec![packet1, packet2];
400    ///
401    /// // Send all packets in one batch
402    /// let bytes_sent = dev.send_multiple(&mut gro_table, &mut bufs, offset)?;
403    /// println!("Sent {} bytes across {} packets", bytes_sent, bufs.len());
404    /// # }
405    /// # Ok::<(), std::io::Error>(())
406    /// ```
407    ///
408    /// # Platform
409    ///
410    /// This method is only available on Linux.
411    ///
412    /// # Performance Notes
413    ///
414    /// - Use `IDEAL_BATCH_SIZE` for optimal batch size (typically 128 packets)
415    /// - Reuse the same `GROTable` instance across calls to avoid allocations
416    /// - Enable offload via `.offload(true)` in `DeviceBuilder` for best performance
417    pub fn send_multiple<B: ExpandBuffer>(
418        &self,
419        gro_table: &mut GROTable,
420        bufs: &mut [B],
421        offset: usize,
422    ) -> io::Result<usize> {
423        self.send_multiple0(gro_table, bufs, offset, |tun, buf| tun.send(buf))
424    }
425    pub(crate) fn send_multiple0<B: ExpandBuffer, W: FnMut(&Tun, &[u8]) -> io::Result<usize>>(
426        &self,
427        gro_table: &mut GROTable,
428        bufs: &mut [B],
429        mut offset: usize,
430        mut write_f: W,
431    ) -> io::Result<usize> {
432        gro_table.reset();
433        if bufs.is_empty() {
434            return Ok(0);
435        }
436        if bufs.len() > u16::MAX as usize {
437            return Err(io::Error::new(
438                io::ErrorKind::InvalidInput,
439                "too many packet buffers",
440            ));
441        }
442        if self.vnet_hdr {
443            handle_gro(
444                bufs,
445                offset,
446                &mut gro_table.tcp_gro_table,
447                &mut gro_table.udp_gro_table,
448                self.udp_gso,
449                &mut gro_table.to_write,
450            )?;
451            offset -= VIRTIO_NET_HDR_LEN;
452        } else {
453            for i in 0..bufs.len() {
454                gro_table.to_write.push(i);
455            }
456        }
457
458        let mut total = 0;
459        let mut err = Ok(());
460        for buf_idx in &gro_table.to_write {
461            let Some(buf) = bufs[*buf_idx].as_ref().get(offset..) else {
462                return Err(io::Error::new(
463                    io::ErrorKind::InvalidInput,
464                    "invalid offset",
465                ));
466            };
467            match write_f(&self.tun, buf) {
468                Ok(n) => {
469                    total += n;
470                }
471                Err(e) => {
472                    if let Some(code) = e.raw_os_error() {
473                        if libc::EBADFD == code {
474                            return Err(e);
475                        }
476                    }
477                    err = Err(e)
478                }
479            }
480        }
481        err?;
482        Ok(total)
483    }
484    /// Receives multiple packets in a batch with GSO (Generic Segmentation Offload) splitting.
485    ///
486    /// When offload is enabled, this method can receive large GSO packets from the TUN device
487    /// and automatically split them into MTU-sized segments, significantly improving receive
488    /// performance for high-bandwidth traffic.
489    ///
490    /// # Arguments
491    ///
492    /// * `original_buffer` - A mutable buffer to store the raw received data, including the
493    ///   virtio network header and the potentially large GSO packet. Recommended size is
494    ///   `VIRTIO_NET_HDR_LEN + 65535` bytes.
495    /// * `bufs` - A mutable slice of buffers to store the segmented packets. Each buffer will
496    ///   receive one MTU-sized packet after GSO splitting.
497    /// * `sizes` - A mutable slice to store the actual size of each packet in `bufs`.
498    ///   Must have the same length as `bufs`.
499    /// * `offset` - The byte offset within each output buffer where packet data should be written.
500    ///   This allows for pre-allocated header space.
501    ///
502    /// # Returns
503    ///
504    /// Returns the number of packets successfully received and split, or an I/O error.
505    ///
506    /// # Example
507    ///
508    /// ```no_run
509    /// # #[cfg(all(target_os = "linux", not(target_env = "ohos")))]
510    /// # {
511    /// use tun_rs::{DeviceBuilder, IDEAL_BATCH_SIZE, VIRTIO_NET_HDR_LEN};
512    ///
513    /// let dev = DeviceBuilder::new()
514    ///     .ipv4("10.0.0.1", 24, None)
515    ///     .with(|builder| {
516    ///         builder.offload(true) // Enable offload for GSO
517    ///     })
518    ///     .build_sync()?;
519    ///
520    /// // Buffer for the raw received packet (with virtio header)
521    /// let mut original_buffer = vec![0u8; VIRTIO_NET_HDR_LEN + 65535];
522    ///
523    /// // Output buffers for segmented packets
524    /// let mut bufs = vec![vec![0u8; 1500]; IDEAL_BATCH_SIZE];
525    /// let mut sizes = vec![0usize; IDEAL_BATCH_SIZE];
526    /// let offset = 0;
527    ///
528    /// loop {
529    ///     // Receive and segment packets
530    ///     let num_packets = dev.recv_multiple(&mut original_buffer, &mut bufs, &mut sizes, offset)?;
531    ///
532    ///     // Process each segmented packet
533    ///     for i in 0..num_packets {
534    ///         let packet = &bufs[i][offset..offset + sizes[i]];
535    ///         println!("Received packet {}: {} bytes", i, sizes[i]);
536    ///         // Process packet...
537    ///     }
538    /// }
539    /// # }
540    /// # Ok::<(), std::io::Error>(())
541    /// ```
542    ///
543    /// # Platform
544    ///
545    /// This method is only available on Linux.
546    ///
547    /// # Performance Notes
548    ///
549    /// - Use `IDEAL_BATCH_SIZE` (128) for the number of output buffers
550    /// - A single `recv_multiple` call may return multiple MTU-sized packets from one large GSO packet
551    /// - The performance benefit is most noticeable with TCP traffic using large send/receive windows
552    pub fn recv_multiple<B: AsRef<[u8]> + AsMut<[u8]>>(
553        &self,
554        original_buffer: &mut [u8],
555        bufs: &mut [B],
556        sizes: &mut [usize],
557        offset: usize,
558    ) -> io::Result<usize> {
559        self.recv_multiple0(original_buffer, bufs, sizes, offset, |tun, buf| {
560            tun.recv(buf)
561        })
562    }
563    pub(crate) fn recv_multiple0<
564        B: AsRef<[u8]> + AsMut<[u8]>,
565        R: Fn(&Tun, &mut [u8]) -> io::Result<usize>,
566    >(
567        &self,
568        original_buffer: &mut [u8],
569        bufs: &mut [B],
570        sizes: &mut [usize],
571        offset: usize,
572        read_f: R,
573    ) -> io::Result<usize> {
574        if bufs.is_empty() || bufs.len() != sizes.len() {
575            return Err(io::Error::other("bufs error"));
576        }
577        if self.vnet_hdr {
578            let len = read_f(&self.tun, original_buffer)?;
579            if len <= VIRTIO_NET_HDR_LEN {
580                Err(io::Error::other(format!(
581                    "length of packet ({len}) <= VIRTIO_NET_HDR_LEN ({VIRTIO_NET_HDR_LEN})",
582                )))?
583            }
584            let hdr = VirtioNetHdr::decode(&original_buffer[..VIRTIO_NET_HDR_LEN])?;
585            self.handle_virtio_read(
586                hdr,
587                &mut original_buffer[VIRTIO_NET_HDR_LEN..len],
588                bufs,
589                sizes,
590                offset,
591            )
592        } else {
593            let Some(buf) = bufs[0].as_mut().get_mut(offset..) else {
594                return Err(io::Error::new(
595                    io::ErrorKind::InvalidInput,
596                    "invalid offset",
597                ));
598            };
599            let len = read_f(&self.tun, buf)?;
600            sizes[0] = len;
601            Ok(1)
602        }
603    }
604    /// https://github.com/WireGuard/wireguard-go/blob/12269c2761734b15625017d8565745096325392f/tun/tun_linux.go#L375
605    /// handleVirtioRead splits in into bufs, leaving offset bytes at the front of
606    /// each buffer. It mutates sizes to reflect the size of each element of bufs,
607    /// and returns the number of packets read.
608    pub(crate) fn handle_virtio_read<B: AsRef<[u8]> + AsMut<[u8]>>(
609        &self,
610        mut hdr: VirtioNetHdr,
611        input: &mut [u8],
612        bufs: &mut [B],
613        sizes: &mut [usize],
614        offset: usize,
615    ) -> io::Result<usize> {
616        if sizes.len() < bufs.len() {
617            return Err(io::Error::other("sizes must be at least as long as bufs"));
618        }
619        if bufs.len() > u16::MAX as usize {
620            return Err(io::Error::new(
621                io::ErrorKind::InvalidInput,
622                "too many packet buffers",
623            ));
624        }
625        for buf in bufs.iter() {
626            if offset > buf.as_ref().len() {
627                return Err(io::Error::new(
628                    io::ErrorKind::InvalidInput,
629                    "invalid offset",
630                ));
631            }
632        }
633        let len = input.len();
634        if hdr.gso_type == VIRTIO_NET_HDR_GSO_NONE {
635            if hdr.flags & VIRTIO_NET_HDR_F_NEEDS_CSUM != 0 {
636                // This means CHECKSUM_PARTIAL in skb context. We are responsible
637                // for computing the checksum starting at hdr.csumStart and placing
638                // at hdr.csumOffset.
639                gso_none_checksum(input, hdr.csum_start, hdr.csum_offset);
640            }
641            if bufs[0].as_ref()[offset..].len() < len {
642                Err(io::Error::other(format!(
643                    "read len {len} overflows bufs element len {}",
644                    bufs[0].as_ref().len()
645                )))?
646            }
647            sizes[0] = len;
648            bufs[0].as_mut()[offset..offset + len].copy_from_slice(input);
649            return Ok(1);
650        }
651        if hdr.gso_size == 0 {
652            return Err(io::Error::new(
653                io::ErrorKind::InvalidInput,
654                "virtioNetHdr.gsoSize must be non-zero",
655            ));
656        }
657        if hdr.gso_type != VIRTIO_NET_HDR_GSO_TCPV4
658            && hdr.gso_type != VIRTIO_NET_HDR_GSO_TCPV6
659            && hdr.gso_type != VIRTIO_NET_HDR_GSO_UDP_L4
660        {
661            Err(io::Error::other(format!(
662                "unsupported virtio GSO type: {}",
663                hdr.gso_type
664            )))?
665        }
666        let ip_version = input[0] >> 4;
667        match ip_version {
668            4 => {
669                if hdr.gso_type != VIRTIO_NET_HDR_GSO_TCPV4
670                    && hdr.gso_type != VIRTIO_NET_HDR_GSO_UDP_L4
671                {
672                    Err(io::Error::other(format!(
673                        "ip header version: 4, GSO type: {}",
674                        hdr.gso_type
675                    )))?
676                }
677            }
678            6 => {
679                if hdr.gso_type != VIRTIO_NET_HDR_GSO_TCPV6
680                    && hdr.gso_type != VIRTIO_NET_HDR_GSO_UDP_L4
681                {
682                    Err(io::Error::other(format!(
683                        "ip header version: 6, GSO type: {}",
684                        hdr.gso_type
685                    )))?
686                }
687            }
688            ip_version => Err(io::Error::other(format!(
689                "invalid ip header version: {ip_version}"
690            )))?,
691        }
692        // Don't trust hdr.hdrLen from the kernel as it can be equal to the length
693        // of the entire first packet when the kernel is handling it as part of a
694        // FORWARD path. Instead, parse the transport header length and add it onto
695        // csumStart, which is synonymous for IP header length.
696        if hdr.gso_type == VIRTIO_NET_HDR_GSO_UDP_L4 {
697            hdr.hdr_len = hdr.csum_start + 8
698        } else {
699            if len <= hdr.csum_start as usize + 12 {
700                Err(io::Error::other("packet is too short"))?
701            }
702
703            let tcp_h_len = ((input[hdr.csum_start as usize + 12] as u16) >> 4) * 4;
704            if !(20..=60).contains(&tcp_h_len) {
705                // A TCP header must be between 20 and 60 bytes in length.
706                Err(io::Error::other(format!(
707                    "tcp header len is invalid: {tcp_h_len}"
708                )))?
709            }
710            hdr.hdr_len = hdr.csum_start + tcp_h_len
711        }
712        if len < hdr.hdr_len as usize {
713            Err(io::Error::other(format!(
714                "length of packet ({len}) < virtioNetHdr.hdr_len ({})",
715                hdr.hdr_len
716            )))?
717        }
718        if hdr.hdr_len < hdr.csum_start {
719            Err(io::Error::other(format!(
720                "virtioNetHdr.hdrLen ({}) < virtioNetHdr.csumStart ({})",
721                hdr.hdr_len, hdr.csum_start
722            )))?
723        }
724        let c_sum_at = (hdr.csum_start + hdr.csum_offset) as usize;
725        if c_sum_at + 1 >= len {
726            Err(io::Error::other(format!(
727                "end of checksum offset ({}) exceeds packet length ({len})",
728                c_sum_at + 1,
729            )))?
730        }
731        gso_split(input, hdr, bufs, sizes, offset, ip_version == 6)
732    }
733    pub fn remove_address_v6_impl(&self, addr: Ipv6Addr, prefix: u8) -> io::Result<()> {
734        unsafe {
735            let if_index = self.if_index_impl()?;
736            let ctl = ctl_v6()?;
737            let mut ifrv6: in6_ifreq = mem::zeroed();
738            ifrv6.ifr6_ifindex = if_index as i32;
739            ifrv6.ifr6_prefixlen = prefix as _;
740            ifrv6.ifr6_addr = sockaddr_union::from(std::net::SocketAddr::new(addr.into(), 0))
741                .addr6
742                .sin6_addr;
743            if let Err(err) = siocdifaddr_in6(ctl.as_raw_fd(), &ifrv6) {
744                return Err(io::Error::from(err));
745            }
746        }
747        Ok(())
748    }
749}
750
751impl DeviceImpl {
752    /// Prepare a new request.
753    unsafe fn request(&self) -> io::Result<ifreq> {
754        request(&self.name_impl()?)
755    }
756    fn set_address_v4(&self, addr: Ipv4Addr) -> io::Result<()> {
757        unsafe {
758            let mut req = self.request()?;
759            ipaddr_to_sockaddr(addr, 0, &mut req.ifr_ifru.ifru_addr, OVERWRITE_SIZE);
760            if let Err(err) = siocsifaddr(ctl()?.as_raw_fd(), &req) {
761                return Err(io::Error::from(err));
762            }
763        }
764        Ok(())
765    }
766    fn set_netmask(&self, value: Ipv4Addr) -> io::Result<()> {
767        unsafe {
768            let mut req = self.request()?;
769            ipaddr_to_sockaddr(value, 0, &mut req.ifr_ifru.ifru_netmask, OVERWRITE_SIZE);
770            if let Err(err) = siocsifnetmask(ctl()?.as_raw_fd(), &req) {
771                return Err(io::Error::from(err));
772            }
773            Ok(())
774        }
775    }
776
777    fn set_destination(&self, value: Ipv4Addr) -> io::Result<()> {
778        unsafe {
779            let mut req = self.request()?;
780            ipaddr_to_sockaddr(value, 0, &mut req.ifr_ifru.ifru_dstaddr, OVERWRITE_SIZE);
781            if let Err(err) = siocsifdstaddr(ctl()?.as_raw_fd(), &req) {
782                return Err(io::Error::from(err));
783            }
784            Ok(())
785        }
786    }
787
788    /// Retrieves the name of the network interface.
789    pub(crate) fn name_impl(&self) -> io::Result<String> {
790        unsafe { name(self.as_raw_fd()) }
791    }
792
793    fn ifru_flags(&self) -> io::Result<i16> {
794        unsafe {
795            let ctl = ctl()?;
796            let mut req = self.request()?;
797
798            if let Err(err) = siocgifflags(ctl.as_raw_fd(), &mut req) {
799                return Err(io::Error::from(err));
800            }
801            Ok(req.ifr_ifru.ifru_flags)
802        }
803    }
804
805    fn remove_all_address_v4(&self) -> io::Result<()> {
806        let interface = netconfig_rs::Interface::try_from_index(self.if_index_impl()?)
807            .map_err(io::Error::from)?;
808        let list = interface.addresses().map_err(io::Error::from)?;
809        for x in list {
810            if x.addr().is_ipv4() {
811                interface.remove_address(x).map_err(io::Error::from)?;
812            }
813        }
814        Ok(())
815    }
816}
817
818//Public User Interface
819impl DeviceImpl {
820    /// Retrieves the name of the network interface.
821    pub fn name(&self) -> io::Result<String> {
822        let _guard = self.op_lock.read().unwrap();
823        self.name_impl()
824    }
825    pub fn remove_address_v6(&self, addr: Ipv6Addr, prefix: u8) -> io::Result<()> {
826        let _guard = self.op_lock.write().unwrap();
827        self.remove_address_v6_impl(addr, prefix)
828    }
829    /// Sets a new name for the network interface.
830    ///
831    /// This function converts the provided name into a C-compatible string,
832    /// checks that its length does not exceed the maximum allowed (IFNAMSIZ),
833    /// and then copies it into an interface request structure. It then uses a system call
834    /// (via `siocsifname`) to apply the new name.
835    ///
836    /// # Example
837    ///
838    /// ```no_run
839    /// # #[cfg(all(target_os = "linux", not(target_env = "ohos")))]
840    /// # {
841    /// use tun_rs::DeviceBuilder;
842    ///
843    /// let dev = DeviceBuilder::new()
844    ///     .name("tun0")
845    ///     .ipv4("10.0.0.1", 24, None)
846    ///     .build_sync()?;
847    ///
848    /// // Rename the device
849    /// dev.set_name("vpn-tun")?;
850    /// println!("Device renamed to vpn-tun");
851    /// # }
852    /// # Ok::<(), std::io::Error>(())
853    /// ```
854    pub fn set_name(&self, value: &str) -> io::Result<()> {
855        let _guard = self.op_lock.write().unwrap();
856        unsafe {
857            let tun_name = CString::new(value)?;
858
859            if tun_name.as_bytes_with_nul().len() > IFNAMSIZ {
860                return Err(io::Error::new(io::ErrorKind::InvalidInput, "name too long"));
861            }
862
863            let mut req = self.request()?;
864            ptr::copy_nonoverlapping(
865                tun_name.as_ptr() as *const c_char,
866                req.ifr_ifru.ifru_newname.as_mut_ptr(),
867                value.len(),
868            );
869
870            if let Err(err) = siocsifname(ctl()?.as_raw_fd(), &req) {
871                return Err(io::Error::from(err));
872            }
873
874            Ok(())
875        }
876    }
877    /// Checks whether the network interface is currently running.
878    ///
879    /// The interface is considered running if both the IFF_UP and IFF_RUNNING flags are set.
880    pub fn is_running(&self) -> io::Result<bool> {
881        let _guard = self.op_lock.read().unwrap();
882        let flags = self.ifru_flags()?;
883        Ok(flags & (IFF_UP | IFF_RUNNING) as c_short == (IFF_UP | IFF_RUNNING) as c_short)
884    }
885    /// Enables or disables the network interface.
886    ///
887    /// If `value` is true, the interface is enabled by setting the IFF_UP and IFF_RUNNING flags.
888    /// If false, the IFF_UP flag is cleared. The change is applied using a system call.
889    pub fn enabled(&self, value: bool) -> io::Result<()> {
890        let _guard = self.op_lock.write().unwrap();
891        unsafe {
892            let ctl = ctl()?;
893            let mut req = self.request()?;
894
895            if let Err(err) = siocgifflags(ctl.as_raw_fd(), &mut req) {
896                return Err(io::Error::from(err));
897            }
898
899            if value {
900                req.ifr_ifru.ifru_flags |= (IFF_UP | IFF_RUNNING) as c_short;
901            } else {
902                req.ifr_ifru.ifru_flags &= !(IFF_UP as c_short);
903            }
904
905            if let Err(err) = siocsifflags(ctl.as_raw_fd(), &req) {
906                return Err(io::Error::from(err));
907            }
908
909            Ok(())
910        }
911    }
912    /// Retrieves the broadcast address of the network interface.
913    ///
914    /// This function populates an interface request with the broadcast address via a system call,
915    /// converts it into a sockaddr structure, and then extracts the IP address.
916    ///
917    /// # Example
918    ///
919    /// ```no_run
920    /// # #[cfg(all(target_os = "linux", not(target_env = "ohos")))]
921    /// # {
922    /// use tun_rs::DeviceBuilder;
923    ///
924    /// let dev = DeviceBuilder::new()
925    ///     .ipv4("10.0.0.1", 24, None)
926    ///     .build_sync()?;
927    ///
928    /// // Get the broadcast address
929    /// let broadcast = dev.broadcast()?;
930    /// println!("Broadcast address: {}", broadcast);
931    /// # }
932    /// # Ok::<(), std::io::Error>(())
933    /// ```
934    pub fn broadcast(&self) -> io::Result<IpAddr> {
935        let _guard = self.op_lock.read().unwrap();
936        unsafe {
937            let mut req = self.request()?;
938            if let Err(err) = siocgifbrdaddr(ctl()?.as_raw_fd(), &mut req) {
939                return Err(io::Error::from(err));
940            }
941            let sa = sockaddr_union::from(req.ifr_ifru.ifru_broadaddr);
942            Ok(std::net::SocketAddr::try_from(sa)?.ip())
943        }
944    }
945    /// Sets the broadcast address of the network interface.
946    ///
947    /// This function converts the given IP address into a sockaddr structure (with a specified overwrite size)
948    /// and then applies it to the interface via a system call.
949    pub fn set_broadcast(&self, value: IpAddr) -> io::Result<()> {
950        let _guard = self.op_lock.write().unwrap();
951        unsafe {
952            let mut req = self.request()?;
953            ipaddr_to_sockaddr(value, 0, &mut req.ifr_ifru.ifru_broadaddr, OVERWRITE_SIZE);
954            if let Err(err) = siocsifbrdaddr(ctl()?.as_raw_fd(), &req) {
955                return Err(io::Error::from(err));
956            }
957            Ok(())
958        }
959    }
960    /// Sets the IPv4 network address, netmask, and an optional destination address.
961    /// Remove all previous set IPv4 addresses and set the specified address.
962    ///
963    /// # Example
964    ///
965    /// ```no_run
966    /// # #[cfg(all(target_os = "linux", not(target_env = "ohos")))]
967    /// # {
968    /// use tun_rs::DeviceBuilder;
969    ///
970    /// let dev = DeviceBuilder::new()
971    ///     .ipv4("10.0.0.1", 24, None)
972    ///     .build_sync()?;
973    ///
974    /// // Change the primary IPv4 address
975    /// dev.set_network_address("10.1.0.1", 24, None)?;
976    /// println!("Updated device address to 10.1.0.1/24");
977    /// # }
978    /// # Ok::<(), std::io::Error>(())
979    /// ```
980    pub fn set_network_address<IPv4: ToIpv4Address, Netmask: ToIpv4Netmask>(
981        &self,
982        address: IPv4,
983        netmask: Netmask,
984        destination: Option<IPv4>,
985    ) -> io::Result<()> {
986        let _guard = self.op_lock.write().unwrap();
987        self.remove_all_address_v4()?;
988        self.set_address_v4(address.ipv4()?)?;
989        self.set_netmask(netmask.netmask()?)?;
990        if let Some(destination) = destination {
991            self.set_destination(destination.ipv4()?)?;
992        }
993        Ok(())
994    }
995    /// Add IPv4 network address and netmask to the interface.
996    ///
997    /// This allows multiple IPv4 addresses on a single TUN/TAP device.
998    ///
999    /// # Example
1000    ///
1001    /// ```no_run
1002    /// # #[cfg(all(target_os = "linux", not(target_env = "ohos")))]
1003    /// # {
1004    /// use tun_rs::DeviceBuilder;
1005    ///
1006    /// let dev = DeviceBuilder::new()
1007    ///     .ipv4("10.0.0.1", 24, None)
1008    ///     .build_sync()?;
1009    ///
1010    /// // Add additional IPv4 addresses
1011    /// dev.add_address_v4("10.0.1.1", 24)?;
1012    /// dev.add_address_v4("10.0.2.1", 24)?;
1013    /// println!("Added multiple IPv4 addresses");
1014    /// # }
1015    /// # Ok::<(), std::io::Error>(())
1016    /// ```
1017    pub fn add_address_v4<IPv4: ToIpv4Address, Netmask: ToIpv4Netmask>(
1018        &self,
1019        address: IPv4,
1020        netmask: Netmask,
1021    ) -> io::Result<()> {
1022        let _guard = self.op_lock.write().unwrap();
1023        let interface = netconfig_rs::Interface::try_from_index(self.if_index_impl()?)
1024            .map_err(io::Error::from)?;
1025        interface
1026            .add_address(IpNet::new_assert(address.ipv4()?.into(), netmask.prefix()?))
1027            .map_err(io::Error::from)
1028    }
1029    /// Removes an IP address from the interface.
1030    ///
1031    /// For IPv4 addresses, it iterates over the current addresses and if a match is found,
1032    /// resets the address to `0.0.0.0` (unspecified).
1033    /// For IPv6 addresses, it retrieves the interface addresses by name and removes the matching address,
1034    /// taking into account its prefix length.
1035    ///
1036    /// # Example
1037    ///
1038    /// ```no_run
1039    /// # #[cfg(all(target_os = "linux", not(target_env = "ohos")))]
1040    /// # {
1041    /// use std::net::IpAddr;
1042    /// use tun_rs::DeviceBuilder;
1043    ///
1044    /// let dev = DeviceBuilder::new()
1045    ///     .ipv4("10.0.0.1", 24, None)
1046    ///     .build_sync()?;
1047    ///
1048    /// // Add an additional address
1049    /// dev.add_address_v4("10.0.1.1", 24)?;
1050    ///
1051    /// // Later, remove it
1052    /// dev.remove_address("10.0.1.1".parse::<IpAddr>().unwrap())?;
1053    /// println!("Removed address 10.0.1.1");
1054    /// # }
1055    /// # Ok::<(), std::io::Error>(())
1056    /// ```
1057    pub fn remove_address(&self, addr: IpAddr) -> io::Result<()> {
1058        let _guard = self.op_lock.write().unwrap();
1059        match addr {
1060            IpAddr::V4(_) => {
1061                let interface = netconfig_rs::Interface::try_from_index(self.if_index_impl()?)
1062                    .map_err(io::Error::from)?;
1063                let list = interface.addresses().map_err(io::Error::from)?;
1064                for x in list {
1065                    if x.addr() == addr {
1066                        interface.remove_address(x).map_err(io::Error::from)?;
1067                    }
1068                }
1069            }
1070            IpAddr::V6(addr_v6) => {
1071                let addrs = crate::platform::get_if_addrs_by_name(self.name_impl()?)?;
1072                for x in addrs {
1073                    if let Some(ip_addr) = x.address.ip_addr() {
1074                        if ip_addr == addr {
1075                            if let Some(netmask) = x.address.netmask() {
1076                                let prefix = ipnet::ip_mask_to_prefix(netmask).unwrap_or(0);
1077                                self.remove_address_v6_impl(addr_v6, prefix)?
1078                            }
1079                        }
1080                    }
1081                }
1082            }
1083        }
1084        Ok(())
1085    }
1086    /// Adds an IPv6 address to the interface.
1087    ///
1088    /// This function creates an `in6_ifreq` structure, fills in the interface index,
1089    /// prefix length, and IPv6 address (converted into a sockaddr structure),
1090    /// and then applies it using a system call.
1091    ///
1092    /// # Example
1093    ///
1094    /// ```no_run
1095    /// # #[cfg(all(target_os = "linux", not(target_env = "ohos")))]
1096    /// # {
1097    /// use tun_rs::DeviceBuilder;
1098    ///
1099    /// let dev = DeviceBuilder::new()
1100    ///     .ipv4("10.0.0.1", 24, None)
1101    ///     .build_sync()?;
1102    ///
1103    /// // Add IPv6 addresses
1104    /// dev.add_address_v6("fd00::1", 64)?;
1105    /// dev.add_address_v6("fd00::2", 64)?;
1106    /// println!("Added IPv6 addresses");
1107    /// # }
1108    /// # Ok::<(), std::io::Error>(())
1109    /// ```
1110    pub fn add_address_v6<IPv6: ToIpv6Address, Netmask: ToIpv6Netmask>(
1111        &self,
1112        addr: IPv6,
1113        netmask: Netmask,
1114    ) -> io::Result<()> {
1115        let _guard = self.op_lock.write().unwrap();
1116        unsafe {
1117            let if_index = self.if_index_impl()?;
1118            let ctl = ctl_v6()?;
1119            let mut ifrv6: in6_ifreq = mem::zeroed();
1120            ifrv6.ifr6_ifindex = if_index as i32;
1121            ifrv6.ifr6_prefixlen = netmask.prefix()? as u32;
1122            ifrv6.ifr6_addr =
1123                sockaddr_union::from(std::net::SocketAddr::new(addr.ipv6()?.into(), 0))
1124                    .addr6
1125                    .sin6_addr;
1126            if let Err(err) = siocsifaddr_in6(ctl.as_raw_fd(), &ifrv6) {
1127                return Err(io::Error::from(err));
1128            }
1129        }
1130        Ok(())
1131    }
1132    /// Retrieves the current MTU (Maximum Transmission Unit) for the interface.
1133    ///
1134    /// This function constructs an interface request and uses a system call (via `siocgifmtu`)
1135    /// to obtain the MTU. The result is then converted to a u16.
1136    pub fn mtu(&self) -> io::Result<u16> {
1137        let _guard = self.op_lock.read().unwrap();
1138        unsafe {
1139            let mut req = self.request()?;
1140
1141            if let Err(err) = siocgifmtu(ctl()?.as_raw_fd(), &mut req) {
1142                return Err(io::Error::from(err));
1143            }
1144
1145            req.ifr_ifru
1146                .ifru_mtu
1147                .try_into()
1148                .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, format!("{e:?}")))
1149        }
1150    }
1151    /// Sets the MTU (Maximum Transmission Unit) for the interface.
1152    ///
1153    /// This function creates an interface request, sets the `ifru_mtu` field to the new value,
1154    /// and then applies it via a system call.
1155    ///
1156    /// # Example
1157    ///
1158    /// ```no_run
1159    /// # #[cfg(all(target_os = "linux", not(target_env = "ohos")))]
1160    /// # {
1161    /// use tun_rs::DeviceBuilder;
1162    ///
1163    /// let dev = DeviceBuilder::new()
1164    ///     .ipv4("10.0.0.1", 24, None)
1165    ///     .mtu(1400)
1166    ///     .build_sync()?;
1167    ///
1168    /// // Change MTU to accommodate larger packets
1169    /// dev.set_mtu(9000)?; // Jumbo frames
1170    /// println!("MTU set to 9000 bytes");
1171    /// # }
1172    /// # Ok::<(), std::io::Error>(())
1173    /// ```
1174    pub fn set_mtu(&self, value: u16) -> io::Result<()> {
1175        let _guard = self.op_lock.write().unwrap();
1176        unsafe {
1177            let mut req = self.request()?;
1178            req.ifr_ifru.ifru_mtu = value as i32;
1179
1180            if let Err(err) = siocsifmtu(ctl()?.as_raw_fd(), &req) {
1181                return Err(io::Error::from(err));
1182            }
1183            Ok(())
1184        }
1185    }
1186    /// Sets the MAC (hardware) address for the interface.
1187    ///
1188    /// This function constructs an interface request and copies the provided MAC address
1189    /// into the hardware address field. It then applies the change via a system call.
1190    /// This operation is typically supported only for TAP devices.
1191    pub fn set_mac_address(&self, eth_addr: [u8; ETHER_ADDR_LEN as usize]) -> io::Result<()> {
1192        let _guard = self.op_lock.write().unwrap();
1193        unsafe {
1194            let mut req = self.request()?;
1195            req.ifr_ifru.ifru_hwaddr.sa_family = ARPHRD_ETHER;
1196            req.ifr_ifru.ifru_hwaddr.sa_data[0..ETHER_ADDR_LEN as usize]
1197                .copy_from_slice(eth_addr.map(|c| c as _).as_slice());
1198            if let Err(err) = siocsifhwaddr(ctl()?.as_raw_fd(), &req) {
1199                return Err(io::Error::from(err));
1200            }
1201            Ok(())
1202        }
1203    }
1204    /// Retrieves the MAC (hardware) address of the interface.
1205    ///
1206    /// This function queries the MAC address by the interface name using a helper function.
1207    /// An error is returned if the MAC address cannot be found.
1208    pub fn mac_address(&self) -> io::Result<[u8; ETHER_ADDR_LEN as usize]> {
1209        let _guard = self.op_lock.read().unwrap();
1210        unsafe {
1211            let mut req = self.request()?;
1212
1213            siocgifhwaddr(ctl()?.as_raw_fd(), &mut req).map_err(io::Error::from)?;
1214
1215            let hw = &req.ifr_ifru.ifru_hwaddr.sa_data;
1216
1217            let mut mac = [0u8; ETHER_ADDR_LEN as usize];
1218            for (i, b) in hw.iter().take(6).enumerate() {
1219                mac[i] = *b as u8;
1220            }
1221
1222            Ok(mac)
1223        }
1224    }
1225}
1226
1227unsafe fn name(fd: RawFd) -> io::Result<String> {
1228    let mut req: ifreq = mem::zeroed();
1229    if let Err(err) = tungetiff(fd, &mut req as *mut _ as *mut _) {
1230        return Err(io::Error::from(err));
1231    }
1232    let c_str = std::ffi::CStr::from_ptr(req.ifr_name.as_ptr() as *const c_char);
1233    let tun_name = c_str.to_string_lossy().into_owned();
1234    Ok(tun_name)
1235}
1236
1237unsafe fn request(name: &str) -> io::Result<ifreq> {
1238    let mut req: ifreq = mem::zeroed();
1239    ptr::copy_nonoverlapping(
1240        name.as_ptr() as *const c_char,
1241        req.ifr_name.as_mut_ptr(),
1242        name.len(),
1243    );
1244    Ok(req)
1245}
1246
1247impl From<Layer> for c_short {
1248    fn from(layer: Layer) -> Self {
1249        match layer {
1250            Layer::L2 => IFF_TAP as c_short,
1251            Layer::L3 => IFF_TUN as c_short,
1252        }
1253    }
1254}