1#![expect(unsafe_code)]
8
9use futures::AsyncRead;
10use linux_net_bindings::gen_if;
11use linux_net_bindings::gen_if_tun;
12use linux_net_bindings::tun_set_iff;
13use pal_async::driver::Driver;
14use pal_async::pipe::PolledPipe;
15use std::ffi::CString;
16use std::fs::File;
17use std::io;
18use std::io::Write;
19use std::os::raw::c_short;
20use std::os::unix::prelude::AsRawFd;
21use std::pin::Pin;
22use std::task::Context;
23use std::task::Poll;
24use thiserror::Error;
25
26#[derive(Error, Debug)]
27pub enum Error {
28 #[error("TAP interface name is too long: {0:#}")]
29 TapNameTooLong(usize),
30 #[error("failed to open /dev/net/tun")]
31 OpenTunFailed(#[source] io::Error),
32 #[error("TUNSETIFF ioctl failed")]
33 SetTapAttributes(#[source] io::Error),
34 #[error("TAP name conversion to C string failed")]
35 TapNameConversion(#[source] std::ffi::NulError),
36}
37
38#[derive(Debug)]
40pub struct Tap {
41 tap: File,
42}
43
44impl Tap {
45 pub fn new(name: &str) -> Result<Self, Error> {
46 let tap = Self::open_tap_interface(name)?;
47 Ok(Self { tap })
48 }
49
50 fn open_tap_interface(tap_name: &str) -> Result<File, Error> {
51 let tap_file = std::fs::OpenOptions::new()
64 .read(true)
65 .write(true)
66 .open("/dev/net/tun")
67 .map_err(Error::OpenTunFailed)?;
68
69 let mut ifreq: gen_if::ifreq = Default::default();
71
72 let tap_name_cstr = CString::new(tap_name.as_bytes()).map_err(Error::TapNameConversion)?;
73 let tap_name_bytes = tap_name_cstr.into_bytes_with_nul();
74 let tap_name_length = tap_name_bytes.len();
75
76 let name_slice = unsafe { ifreq.ifr_ifrn.ifrn_name.as_mut() };
79
80 if name_slice.len() < tap_name_length {
81 Err(Error::TapNameTooLong(tap_name_length))
82 } else {
83 for i in 0..tap_name_length {
84 name_slice[i] = tap_name_bytes[i] as libc::c_char;
85 }
86 ifreq.ifr_ifru.ifru_flags = (gen_if_tun::IFF_TAP | gen_if_tun::IFF_NO_PI) as c_short;
87
88 unsafe {
90 tun_set_iff(tap_file.as_raw_fd(), &ifreq)
91 .map_err(|_e| Error::SetTapAttributes(io::Error::last_os_error()))?;
92 };
93 Ok(tap_file)
94 }
95 }
96
97 pub fn polled(self, driver: &(impl Driver + ?Sized)) -> io::Result<PolledTap> {
98 Ok(PolledTap {
99 tap: PolledPipe::new(driver, self.tap)?,
100 })
101 }
102}
103
104pub struct PolledTap {
106 tap: PolledPipe,
107}
108
109impl PolledTap {
110 pub fn into_inner(self) -> Tap {
111 Tap {
112 tap: self.tap.into_inner(),
113 }
114 }
115}
116
117impl AsyncRead for PolledTap {
118 fn poll_read(
119 mut self: Pin<&mut Self>,
120 cx: &mut Context<'_>,
121 buf: &mut [u8],
122 ) -> Poll<io::Result<usize>> {
123 Pin::new(&mut self.tap).poll_read(cx, buf)
124 }
125}
126
127impl Write for PolledTap {
128 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
129 self.tap.get().write(buf)
132 }
133
134 fn flush(&mut self) -> io::Result<()> {
135 Ok(())
136 }
137}