tun_rs/platform/netbsd/
device.rs

1use crate::{
2    builder::{DeviceConfig, Layer},
3    platform::netbsd::sys::*,
4    platform::{
5        unix::{sockaddr_union, Fd, Tun},
6        ETHER_ADDR_LEN,
7    },
8    ToIpv4Address, ToIpv4Netmask, ToIpv6Address, ToIpv6Netmask,
9};
10
11use crate::platform::unix::device::{ctl, ctl_v6};
12use libc::{self, c_char, c_short, AF_LINK, IFF_RUNNING, IFF_UP, IFNAMSIZ, O_RDWR};
13use nix::sys::socket::{LinkAddr, SockaddrLike};
14use std::io::ErrorKind;
15use std::os::fd::{FromRawFd, IntoRawFd, RawFd};
16use std::os::unix::fs::MetadataExt;
17use std::{io, mem, net::IpAddr, os::unix::io::AsRawFd, ptr, sync::Mutex};
18
19/// A TUN device using the TUN/TAP Linux driver.
20pub struct DeviceImpl {
21    name: String,
22    pub(crate) tun: Tun,
23    pub(crate) op_lock: Mutex<bool>,
24}
25impl IntoRawFd for DeviceImpl {
26    fn into_raw_fd(mut self) -> RawFd {
27        let fd = self.tun.fd.inner;
28        self.tun.fd.inner = -1;
29        fd
30    }
31}
32impl Drop for DeviceImpl {
33    fn drop(&mut self) {
34        if self.tun.fd.inner < 0 {
35            return;
36        }
37        unsafe {
38            if let (Ok(ctl), Ok(req)) = (ctl(), self.request()) {
39                libc::close(self.tun.fd.inner);
40                self.tun.fd.inner = -1;
41                _ = siocifdestroy(ctl.as_raw_fd(), &req);
42            }
43        }
44    }
45}
46impl DeviceImpl {
47    /// Create a new `Device` for the given `Configuration`.
48    pub(crate) fn new(config: DeviceConfig) -> io::Result<Self> {
49        let layer = config.layer.unwrap_or(Layer::L3);
50        let associate_route = if layer == Layer::L3 {
51            config.associate_route.unwrap_or(true)
52        } else {
53            false
54        };
55        if let Some(dev_name) = config.dev_name.as_ref() {
56            Self::check_name(layer, dev_name)?;
57            if !config.reuse_dev.unwrap_or(true) {
58                let exists = Self::exists(dev_name)?;
59                if exists {
60                    return Err(io::Error::new(
61                        ErrorKind::AlreadyExists,
62                        format!("device {dev_name} already exists"),
63                    ));
64                }
65            }
66        }
67        let (dev_fd, name) = match layer {
68            Layer::L2 => Self::create_tap(config.dev_name)?,
69            Layer::L3 => Self::create_tun(config.dev_name)?,
70        };
71
72        let tun = Tun::new(dev_fd);
73        if matches!(layer, Layer::L3) {
74            Self::enable_tunsifhead_impl(&tun.fd)?;
75            tun.set_ignore_packet_info(!config.packet_information.unwrap_or(false));
76        }
77        Ok(DeviceImpl {
78            name,
79            tun,
80            op_lock: Mutex::new(associate_route),
81        })
82    }
83    fn check_name(layer: Layer, dev_name: &str) -> io::Result<()> {
84        if dev_name.len() > IFNAMSIZ {
85            return Err(io::Error::new(
86                ErrorKind::InvalidInput,
87                "device name too long",
88            ));
89        }
90        let device_prefix = match layer {
91            Layer::L2 => "tap",
92            Layer::L3 => "tun",
93        };
94        if !dev_name.starts_with(device_prefix) {
95            Err(io::Error::new(
96                ErrorKind::InvalidInput,
97                format!("device name must start with {device_prefix}"),
98            ))
99        } else {
100            Ok(())
101        }
102    }
103    fn create_tap(dev_name: Option<String>) -> io::Result<(Fd, String)> {
104        let device_prefix = "tap";
105        if let Some(dev_name) = dev_name {
106            if let Err(e) = DeviceImpl::create_dev(&dev_name) {
107                if e.kind() != ErrorKind::AlreadyExists {
108                    return Err(e);
109                }
110            }
111
112            let if_index = dev_name[3..]
113                .parse::<u32>()
114                .map_err(|e| io::Error::new(ErrorKind::InvalidInput, e))?;
115            let device_path = format!("/dev/{device_prefix}{if_index}\0");
116
117            let fd = Self::open_and_makedev_dev(&dev_name, &device_path)?;
118            Ok((fd, dev_name))
119        } else {
120            let device_path = format!("/dev/{device_prefix}\0");
121            let fd =
122                unsafe { libc::open(device_path.as_ptr() as *const _, O_RDWR | libc::O_CLOEXEC) };
123            let fd = Fd::new(fd)?;
124            unsafe {
125                let mut req: ifreq = mem::zeroed();
126                if let Err(err) = tapgifname(fd.as_raw_fd(), &mut req) {
127                    return Err(io::Error::from(err));
128                }
129                let cstr = std::ffi::CStr::from_ptr(req.ifr_name.as_ptr());
130                let dev_name = cstr.to_string_lossy().to_string();
131                Ok((fd, dev_name))
132            }
133        }
134    }
135    fn create_tun(dev_name: Option<String>) -> io::Result<(Fd, String)> {
136        let device_prefix = "tun";
137        if let Some(dev_name) = dev_name {
138            let if_index = dev_name[3..]
139                .parse::<u32>()
140                .map_err(|e| io::Error::new(ErrorKind::InvalidInput, e))?;
141            let device_path = format!("/dev/{device_prefix}{if_index}\0");
142            let fd = Self::open_and_makedev_dev(&dev_name, &device_path)?;
143            Ok((fd, dev_name))
144        } else {
145            for index in 0..256 {
146                let dev_name = format!("{device_prefix}{index}");
147                let device_path = format!("/dev/{device_prefix}{index}\0");
148
149                match Self::open_and_makedev_dev(&dev_name, &device_path) {
150                    Ok(dev) => {
151                        return Ok((dev, dev_name));
152                    }
153                    Err(e) => {
154                        if e.raw_os_error() != Some(libc::EBUSY) {
155                            return Err(e);
156                        }
157                    }
158                }
159            }
160            Err(io::Error::last_os_error())
161        }
162    }
163    fn open_and_makedev_dev(dev_name: &str, device_path: &str) -> io::Result<Fd> {
164        let fd = unsafe { libc::open(device_path.as_ptr() as *const _, O_RDWR | libc::O_CLOEXEC) };
165        match Fd::new(fd) {
166            Ok(fd) => Ok(fd),
167            Err(ref e) if e.kind() == ErrorKind::NotFound => {
168                DeviceImpl::makedev_dev(dev_name)?;
169                let fd = unsafe {
170                    libc::open(device_path.as_ptr() as *const _, O_RDWR | libc::O_CLOEXEC)
171                };
172                Ok(Fd::new(fd)?)
173            }
174            Err(e) => Err(e),
175        }
176    }
177    fn makedev_dev(name: &str) -> io::Result<()> {
178        let status = std::process::Command::new("sh")
179            .arg("MAKEDEV")
180            .arg(name)
181            .current_dir("/dev")
182            .status()?;
183
184        if status.success() {
185            Ok(())
186        } else {
187            Err(io::Error::other(format!(
188                "MAKEDEV {} failed with status {:?}",
189                name,
190                status.code()
191            )))
192        }
193    }
194    fn create_dev(name: &str) -> io::Result<()> {
195        unsafe {
196            let mut req: ifreq = mem::zeroed();
197            ptr::copy_nonoverlapping(
198                name.as_ptr() as *const c_char,
199                req.ifr_name.as_mut_ptr(),
200                name.len(),
201            );
202            if let Err(err) = siocifcreate(ctl()?.as_raw_fd(), &req) {
203                return Err(io::Error::from(err));
204            }
205        }
206        Ok(())
207    }
208    fn exists(dev_name: &str) -> io::Result<bool> {
209        unsafe {
210            let mut req: ifreq = mem::zeroed();
211            ptr::copy_nonoverlapping(
212                dev_name.as_ptr() as *const c_char,
213                req.ifr_name.as_mut_ptr(),
214                dev_name.len(),
215            );
216            let ctl = ctl()?;
217            if let Err(err) = siocgifflags(ctl.as_raw_fd(), &mut req) {
218                if err == nix::errno::Errno::ENXIO {
219                    return Ok(false);
220                }
221                Err(io::Error::from(err))
222            } else {
223                Ok(true)
224            }
225        }
226    }
227    pub(crate) fn from_tun(tun: Tun) -> io::Result<Self> {
228        let name = Self::name_of_fd(tun.as_raw_fd())?;
229        Ok(Self {
230            name,
231            tun,
232            op_lock: Mutex::new(true),
233        })
234    }
235
236    fn enable_tunsifhead_impl(device_fd: &Fd) -> std::io::Result<()> {
237        unsafe {
238            if let Err(err) = sioctunsifhead(device_fd.as_raw_fd(), &1 as *const _) {
239                return Err(io::Error::from(err));
240            }
241        }
242        Ok(())
243    }
244
245    fn calc_dest_addr(&self, addr: IpAddr, netmask: IpAddr) -> io::Result<IpAddr> {
246        let prefix_len = ipnet::ip_mask_to_prefix(netmask)
247            .map_err(|e| io::Error::new(ErrorKind::InvalidInput, e))?;
248        Ok(ipnet::IpNet::new(addr, prefix_len)
249            .map_err(|e| io::Error::new(ErrorKind::InvalidInput, e))?
250            .broadcast())
251    }
252
253    /// Set the IPv4 alias of the device.
254    fn add_address(
255        &self,
256        addr: IpAddr,
257        mask: IpAddr,
258        dest: Option<IpAddr>,
259        associate_route: bool,
260    ) -> io::Result<()> {
261        unsafe {
262            match (addr, mask) {
263                (IpAddr::V4(addr), IpAddr::V4(mask)) => {
264                    let ctl = ctl()?;
265                    let mut req: ifaliasreq = mem::zeroed();
266                    let tun_name = self.name_impl()?;
267                    ptr::copy_nonoverlapping(
268                        tun_name.as_ptr() as *const c_char,
269                        req.ifra_name.as_mut_ptr(),
270                        tun_name.len(),
271                    );
272
273                    req.ifra_addr = crate::platform::unix::sockaddr_union::from((addr, 0)).addr;
274                    if let Some(dest) = dest {
275                        req.ifra_dstaddr =
276                            crate::platform::unix::sockaddr_union::from((dest, 0)).addr;
277                    }
278                    req.ifra_mask = crate::platform::unix::sockaddr_union::from((mask, 0)).addr;
279
280                    if let Err(err) = siocaifaddr(ctl.as_raw_fd(), &req) {
281                        return Err(io::Error::from(err));
282                    }
283                    if let Err(e) = self.add_route(addr.into(), mask.into(), associate_route) {
284                        log::warn!("{e:?}");
285                    }
286                }
287                (IpAddr::V6(addr), IpAddr::V6(mask)) => {
288                    let tun_name = self.name_impl()?;
289                    let mut req: in6_aliasreq = mem::zeroed();
290                    ptr::copy_nonoverlapping(
291                        tun_name.as_ptr() as *const c_char,
292                        req.ifra_name.as_mut_ptr(),
293                        tun_name.len(),
294                    );
295                    req.ifra_addr = sockaddr_union::from((addr, 0)).addr6;
296                    req.ifra_prefixmask = sockaddr_union::from((mask, 0)).addr6;
297                    req.ifra_lifetime.ia6t_vltime = 0xffffffff_u32;
298                    req.ifra_lifetime.ia6t_pltime = 0xffffffff_u32;
299                    // req.ifra_flags = IN6_IFF_NODAD;
300                    if let Err(err) = siocaifaddr_in6(ctl_v6()?.as_raw_fd(), &req) {
301                        return Err(io::Error::from(err));
302                    }
303                }
304                _ => {
305                    unreachable!();
306                }
307            }
308            Ok(())
309        }
310    }
311
312    /// Prepare a new request.
313    unsafe fn request(&self) -> io::Result<ifreq> {
314        let mut req: ifreq = mem::zeroed();
315        let tun_name = self.name_impl()?;
316        ptr::copy_nonoverlapping(
317            tun_name.as_ptr() as *const c_char,
318            req.ifr_name.as_mut_ptr(),
319            tun_name.len(),
320        );
321
322        Ok(req)
323    }
324
325    /// # Safety
326    unsafe fn request_v6(&self) -> io::Result<in6_ifreq> {
327        let tun_name = self.name_impl()?;
328        let mut req: in6_ifreq = mem::zeroed();
329        ptr::copy_nonoverlapping(
330            tun_name.as_ptr() as *const c_char,
331            req.ifra_name.as_mut_ptr(),
332            tun_name.len(),
333        );
334        req.ifr_ifru.ifru_flags = IN6_IFF_NODAD as _;
335        Ok(req)
336    }
337
338    fn add_route(&self, addr: IpAddr, netmask: IpAddr, associate_route: bool) -> io::Result<()> {
339        if !associate_route {
340            return Ok(());
341        }
342        let if_index = self.if_index_impl()?;
343        let prefix_len = ipnet::ip_mask_to_prefix(netmask)
344            .map_err(|e| io::Error::new(ErrorKind::InvalidInput, e))?;
345        let mut manager = route_manager::RouteManager::new()?;
346        let route = route_manager::Route::new(addr, prefix_len).with_if_index(if_index);
347        manager.add(&route)?;
348        Ok(())
349    }
350
351    /// Retrieves the name of the network interface.
352    pub(crate) fn name_impl(&self) -> io::Result<String> {
353        Ok(self.name.clone())
354    }
355    fn name_of_fd(tun: RawFd) -> io::Result<String> {
356        unsafe {
357            let mut req: ifreq = mem::zeroed();
358            if tapgifname(tun, &mut req).is_ok() {
359                let cstr = std::ffi::CStr::from_ptr(req.ifr_name.as_ptr());
360                let dev_name = cstr.to_string_lossy().to_string();
361                // tap
362                return Ok(dev_name);
363            }
364        }
365        let file = unsafe { std::fs::File::from_raw_fd(tun) };
366        let metadata = file.metadata()?;
367        let rdev = metadata.rdev();
368        let index = rdev % 256;
369        std::mem::forget(file); // prevent fd being closed
370        Ok(format!("tun{index}"))
371    }
372
373    fn remove_all_address_v4(&self) -> io::Result<()> {
374        unsafe {
375            let req_v4 = self.request()?;
376            loop {
377                if let Err(err) = siocdifaddr(ctl()?.as_raw_fd(), &req_v4) {
378                    if err == nix::errno::Errno::EADDRNOTAVAIL {
379                        break;
380                    }
381                    return Err(io::Error::from(err));
382                }
383            }
384        }
385        Ok(())
386    }
387}
388
389// Public User Interface
390impl DeviceImpl {
391    /// Retrieves the name of the network interface.
392    pub fn name(&self) -> io::Result<String> {
393        let _guard = self.op_lock.lock().unwrap();
394        self.name_impl()
395    }
396    /// If false, the program will not modify or manage routes in any way, allowing the system to handle all routing natively.
397    /// If true (default), the program will automatically add or remove routes to provide consistent routing behavior across all platforms.
398    /// Set this to be false to obtain the platform's default routing behavior.
399    pub fn set_associate_route(&self, associate_route: bool) {
400        *self.op_lock.lock().unwrap() = associate_route;
401    }
402    /// Retrieve whether route is associated with the IP setting interface, see [`DeviceImpl::set_associate_route`]
403    pub fn associate_route(&self) -> bool {
404        *self.op_lock.lock().unwrap()
405    }
406    /// Enables or disables the network interface.
407    pub fn enabled(&self, value: bool) -> io::Result<()> {
408        let _guard = self.op_lock.lock().unwrap();
409        unsafe {
410            let mut req = self.request()?;
411            let ctl = ctl()?;
412
413            if let Err(err) = siocgifflags(ctl.as_raw_fd(), &mut req) {
414                return Err(io::Error::from(err));
415            }
416
417            if value {
418                req.ifr_ifru.ifru_flags |= (IFF_UP | IFF_RUNNING) as c_short;
419            } else {
420                req.ifr_ifru.ifru_flags &= !(IFF_UP as c_short);
421            }
422
423            if let Err(err) = siocsifflags(ctl.as_raw_fd(), &req) {
424                return Err(io::Error::from(err));
425            }
426
427            Ok(())
428        }
429    }
430    /// Retrieves the current MTU (Maximum Transmission Unit) for the interface.
431    pub fn mtu(&self) -> io::Result<u16> {
432        let _guard = self.op_lock.lock().unwrap();
433        unsafe {
434            let mut req: ifreq = mem::zeroed();
435            let tun_name = self.name_impl()?;
436            ptr::copy_nonoverlapping(
437                tun_name.as_ptr() as *const c_char,
438                req.ifr_name.as_mut_ptr(),
439                tun_name.len(),
440            );
441            if let Err(err) = siocgifmtu(ctl()?.as_raw_fd(), &mut req) {
442                return Err(io::Error::from(err));
443            }
444
445            let r: u16 = req.ifr_ifru.ifru_mtu.try_into().map_err(io::Error::other)?;
446            Ok(r)
447        }
448    }
449    /// Sets the MTU (Maximum Transmission Unit) for the interface.
450    /// # Note
451    /// The specified value must be less than or equal to `1500`; it's a limitation of NetBSD.
452    pub fn set_mtu(&self, value: u16) -> io::Result<()> {
453        let _guard = self.op_lock.lock().unwrap();
454        unsafe {
455            let mut req: ifreq = mem::zeroed();
456            let tun_name = self.name_impl()?;
457            ptr::copy_nonoverlapping(
458                tun_name.as_ptr() as *const c_char,
459                req.ifr_name.as_mut_ptr(),
460                tun_name.len(),
461            );
462            req.ifr_ifru.ifru_mtu = value as _;
463
464            if let Err(err) = siocsifmtu(ctl()?.as_raw_fd(), &req) {
465                return Err(io::Error::from(err));
466            }
467            Ok(())
468        }
469    }
470    /// Sets the IPv4 network address, netmask, and an optional destination address.
471    /// Remove all previous set IPv4 addresses and set the specified address.
472    pub fn set_network_address<IPv4: ToIpv4Address, Netmask: ToIpv4Netmask>(
473        &self,
474        address: IPv4,
475        netmask: Netmask,
476        destination: Option<IPv4>,
477    ) -> io::Result<()> {
478        let guard = self.op_lock.lock().unwrap();
479        let addr = address.ipv4()?.into();
480        let netmask = netmask.netmask()?.into();
481        let default_dest = self.calc_dest_addr(addr, netmask)?;
482        let dest = destination
483            .map(|d| d.ipv4())
484            .transpose()?
485            .map(|v| v.into())
486            .unwrap_or(default_dest);
487        self.remove_all_address_v4()?;
488        self.add_address(addr, netmask, Some(dest), *guard)?;
489        Ok(())
490    }
491    /// Add IPv4 network address, netmask
492    pub fn add_address_v4<IPv4: ToIpv4Address, Netmask: ToIpv4Netmask>(
493        &self,
494        address: IPv4,
495        netmask: Netmask,
496    ) -> io::Result<()> {
497        let guard = self.op_lock.lock().unwrap();
498        let addr = address.ipv4()?.into();
499        let netmask = netmask.netmask()?.into();
500        let dest = self.calc_dest_addr(addr, netmask)?;
501        self.add_address(addr, netmask, Some(dest), *guard)?;
502        Ok(())
503    }
504    /// Removes an IP address from the interface.
505    pub fn remove_address(&self, addr: IpAddr) -> io::Result<()> {
506        let _guard = self.op_lock.lock().unwrap();
507        unsafe {
508            match addr {
509                IpAddr::V4(addr) => {
510                    let mut req_v4 = self.request()?;
511                    req_v4.ifr_ifru.ifru_addr = sockaddr_union::from((addr, 0)).addr;
512                    if let Err(err) = siocdifaddr(ctl()?.as_raw_fd(), &req_v4) {
513                        return Err(io::Error::from(err));
514                    }
515                }
516                IpAddr::V6(addr) => {
517                    let mut req_v6 = self.request_v6()?;
518                    req_v6.ifr_ifru.ifru_addr = sockaddr_union::from((addr, 0)).addr6;
519                    if let Err(err) = siocdifaddr_in6(ctl_v6()?.as_raw_fd(), &req_v6) {
520                        return Err(io::Error::from(err));
521                    }
522                }
523            }
524            Ok(())
525        }
526    }
527    /// Adds an IPv6 address to the interface.
528    pub fn add_address_v6<IPv6: ToIpv6Address, Netmask: ToIpv6Netmask>(
529        &self,
530        addr: IPv6,
531        netmask: Netmask,
532    ) -> io::Result<()> {
533        let guard = self.op_lock.lock().unwrap();
534        let addr = addr.ipv6()?;
535        let netmask = netmask.netmask()?;
536        self.add_address(addr.into(), netmask.into(), None, *guard)
537    }
538    /// Sets the MAC (hardware) address for the interface.
539    ///
540    /// This function constructs an interface request and copies the provided MAC address
541    /// into the hardware address field. It then applies the change via a system call.
542    /// This operation is typically supported only for TAP devices.
543    pub fn set_mac_address(&self, eth_addr: [u8; ETHER_ADDR_LEN as usize]) -> io::Result<()> {
544        let _guard = self.op_lock.lock().unwrap();
545        unsafe {
546            let mut req: ifaliasreq = mem::zeroed();
547            let tun_name = self.name_impl()?;
548            ptr::copy_nonoverlapping(
549                tun_name.as_ptr() as *const c_char,
550                req.ifra_name.as_mut_ptr(),
551                tun_name.len(),
552            );
553            req.ifra_addr.sa_len = ETHER_ADDR_LEN;
554            req.ifra_addr.sa_family = AF_LINK as u8;
555            req.ifra_addr.sa_data[0..ETHER_ADDR_LEN as usize]
556                .copy_from_slice(eth_addr.map(|c| c as i8).as_slice());
557            if let Err(err) = siocsifphyaddr(ctl()?.as_raw_fd(), &req) {
558                return Err(io::Error::from(err));
559            }
560            Ok(())
561        }
562    }
563    /// Retrieves the MAC (hardware) address of the interface.
564    ///
565    /// This function queries the MAC address by the interface name using a helper function.
566    /// An error is returned if the MAC address cannot be found.
567    pub fn mac_address(&self) -> io::Result<[u8; ETHER_ADDR_LEN as usize]> {
568        let _guard = self.op_lock.lock().unwrap();
569        let name = self.name_impl()?;
570        let interfaces = nix::ifaddrs::getifaddrs()?;
571        let interfaces = interfaces.filter(|item| item.interface_name == name);
572        for addr in interfaces {
573            if let Some(address) = addr.address {
574                if address.family() == Some(nix::sys::socket::AddressFamily::Link) {
575                    // This is a workaround, but it's safe because `SockaddrStorage` is a union whose layout is represented by C.
576                    // So, casting from `SockaddrStorage` to its variant thereof is safe.
577                    // The `if` condition ensures that the dereferencing of the resulting pointer gets a valid value of `LinkAddr`.
578                    // However, it is preferred to use `as_link_addr` once `nix` fixes it.
579                    unsafe {
580                        let link_ptr = &address as *const _ as *const LinkAddr;
581                        if let Some(mac) = (*link_ptr).addr() {
582                            return Ok(mac);
583                        }
584                    }
585                }
586            }
587        }
588        Err(std::io::Error::other("Unable to get Mac address"))
589    }
590    /// In Layer3(i.e. TUN mode), we need to put the tun interface into "multi_af" mode, which will prepend the address
591    /// family to all packets (same as FreeBSD).
592    /// If this is not enabled, the kernel silently drops all IPv6 packets on output and gets confused on input.
593    pub fn enable_tunsifhead(&self) -> io::Result<()> {
594        let _guard = self.op_lock.lock().unwrap();
595        Self::enable_tunsifhead_impl(&self.tun.fd)
596    }
597
598    /// Returns whether the TUN device is set to ignore packet information (PI).
599    ///
600    /// When enabled, the device does not prepend the `struct tun_pi` header
601    /// to packets, which can simplify packet processing in some cases.
602    ///
603    /// # Returns
604    /// * `true` - The TUN device ignores packet information.
605    /// * `false` - The TUN device includes packet information.
606    pub fn ignore_packet_info(&self) -> bool {
607        let _guard = self.op_lock.lock().unwrap();
608        self.tun.ignore_packet_info()
609    }
610    /// Sets whether the TUN device should ignore packet information (PI).
611    ///
612    /// When `ignore_packet_info` is set to `true`, the TUN device does not
613    /// prepend the `struct tun_pi` header to packets. This can be useful
614    /// if the additional metadata is not needed.
615    ///
616    /// # Parameters
617    /// * `ign`
618    ///     - If `true`, the TUN device will ignore packet information.
619    ///     - If `false`, it will include packet information.
620    pub fn set_ignore_packet_info(&self, ign: bool) {
621        let _guard = self.op_lock.lock().unwrap();
622        if let Ok(name) = self.name_impl() {
623            if name.starts_with("tun") {
624                self.tun.set_ignore_packet_info(ign)
625            }
626        }
627    }
628}
629
630impl From<Layer> for c_short {
631    fn from(layer: Layer) -> Self {
632        match layer {
633            Layer::L2 => 2,
634            Layer::L3 => 3,
635        }
636    }
637}