1#![expect(unsafe_code)]
8
9use crate::VirtioNetHdr;
10use futures::AsyncRead;
11use linux_net_bindings::gen_if;
12use linux_net_bindings::gen_if_tun;
13use linux_net_bindings::tun_get_iff;
14use linux_net_bindings::tun_get_vnet_hdr_sz;
15use linux_net_bindings::tun_set_iff;
16use linux_net_bindings::tun_set_offload;
17use linux_net_bindings::tun_set_vnet_hdr_sz;
18use pal_async::driver::Driver;
19use pal_async::pipe::PolledPipe;
20use std::ffi::CString;
21use std::fs::File;
22use std::io;
23use std::io::Write;
24use std::os::fd::OwnedFd;
25use std::os::raw::c_short;
26use std::os::unix::prelude::AsRawFd;
27use std::pin::Pin;
28use std::task::Context;
29use std::task::Poll;
30use thiserror::Error;
31
32#[derive(Error, Debug)]
33pub enum Error {
34 #[error("TAP interface name is too long: {0:#}")]
35 TapNameTooLong(usize),
36 #[error("failed to open /dev/net/tun")]
37 OpenTunFailed(#[source] io::Error),
38 #[error("TUNSETIFF ioctl failed")]
39 SetTapAttributes(#[source] io::Error),
40 #[error("TUNGETIFF ioctl failed")]
41 GetTapAttributes(#[source] io::Error),
42 #[error("TUNGETVNETHDRSZ ioctl failed")]
43 GetVnetHdrSize(#[source] io::Error),
44 #[error("TUNSETVNETHDRSZ ioctl failed")]
45 SetVnetHdrSize(#[source] io::Error),
46 #[error("TUNSETOFFLOAD ioctl failed")]
47 SetOffload(#[source] io::Error),
48 #[error("TAP name conversion to C string failed")]
49 TapNameConversion(#[source] std::ffi::NulError),
50 #[error("TAP interface does not have IFF_VNET_HDR set")]
51 NoVnetHdr,
52 #[error("TAP interface has unexpected vnet header size {actual}, expected {expected}")]
53 WrongVnetHdrSize { expected: usize, actual: usize },
54}
55
56pub fn open_tap(name: &str) -> Result<OwnedFd, Error> {
60 let tap_file = std::fs::OpenOptions::new()
61 .read(true)
62 .write(true)
63 .open("/dev/net/tun")
64 .map_err(Error::OpenTunFailed)?;
65
66 let mut ifreq: gen_if::ifreq = Default::default();
67
68 let tap_name_cstr = CString::new(name.as_bytes()).map_err(Error::TapNameConversion)?;
69 let tap_name_bytes = tap_name_cstr.into_bytes_with_nul();
70 let tap_name_length = tap_name_bytes.len();
71
72 let name_slice = unsafe { ifreq.ifr_ifrn.ifrn_name.as_mut() };
75
76 if name_slice.len() < tap_name_length {
77 return Err(Error::TapNameTooLong(tap_name_length));
78 }
79
80 for i in 0..tap_name_length {
81 name_slice[i] = tap_name_bytes[i] as libc::c_char;
82 }
83 ifreq.ifr_ifru.ifru_flags =
84 (gen_if_tun::IFF_TAP | gen_if_tun::IFF_NO_PI | gen_if_tun::IFF_VNET_HDR) as c_short;
85
86 unsafe {
88 tun_set_iff(tap_file.as_raw_fd(), &ifreq)
89 .map_err(|_e| Error::SetTapAttributes(io::Error::last_os_error()))?;
90 };
91
92 let fd = OwnedFd::from(tap_file);
93 Ok(fd)
94}
95
96#[derive(Debug)]
102pub struct Tap {
103 tap: File,
104}
105
106impl Tap {
107 pub fn new(fd: OwnedFd) -> Result<Self, Error> {
114 let tap: File = fd.into();
115
116 let mut ifreq: gen_if::ifreq = Default::default();
118 unsafe {
120 tun_get_iff(tap.as_raw_fd(), &mut ifreq)
121 .map_err(|_e| Error::GetTapAttributes(io::Error::last_os_error()))?;
122 };
123 if unsafe { ifreq.ifr_ifru.ifru_flags } as u32 & gen_if_tun::IFF_VNET_HDR == 0 {
126 return Err(Error::NoVnetHdr);
127 }
128
129 let expected_sz = size_of::<VirtioNetHdr>() as std::os::raw::c_int;
131 unsafe {
133 tun_set_vnet_hdr_sz(tap.as_raw_fd(), &expected_sz)
134 .map_err(|_e| Error::SetVnetHdrSize(io::Error::last_os_error()))?;
135 };
136
137 let mut actual_sz: std::os::raw::c_int = 0;
139 unsafe {
141 tun_get_vnet_hdr_sz(tap.as_raw_fd(), &mut actual_sz)
142 .map_err(|_e| Error::GetVnetHdrSize(io::Error::last_os_error()))?;
143 };
144 if actual_sz != expected_sz {
145 return Err(Error::WrongVnetHdrSize {
146 expected: expected_sz as usize,
147 actual: actual_sz as usize,
148 });
149 }
150
151 Ok(Self { tap })
152 }
153
154 pub fn set_offloads(&self, flags: u32) -> Result<(), Error> {
158 unsafe {
160 tun_set_offload(self.tap.as_raw_fd(), flags as std::os::raw::c_int)
161 .map_err(|_e| Error::SetOffload(io::Error::last_os_error()))?;
162 };
163 Ok(())
164 }
165
166 pub fn polled(self, driver: &(impl Driver + ?Sized)) -> io::Result<PolledTap> {
167 Ok(PolledTap {
168 tap: PolledPipe::new(driver, self.tap)?,
169 })
170 }
171}
172
173pub struct PolledTap {
175 tap: PolledPipe,
176}
177
178impl PolledTap {
179 pub fn into_inner(self) -> Tap {
180 Tap {
181 tap: self.tap.into_inner(),
182 }
183 }
184}
185
186impl AsyncRead for PolledTap {
187 fn poll_read(
188 mut self: Pin<&mut Self>,
189 cx: &mut Context<'_>,
190 buf: &mut [u8],
191 ) -> Poll<io::Result<usize>> {
192 Pin::new(&mut self.tap).poll_read(cx, buf)
193 }
194}
195
196impl Write for PolledTap {
197 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
198 self.tap.get().write(buf)
201 }
202
203 fn write_vectored(&mut self, bufs: &[io::IoSlice<'_>]) -> io::Result<usize> {
204 self.tap.get().write_vectored(bufs)
205 }
206
207 fn flush(&mut self) -> io::Result<()> {
208 Ok(())
209 }
210}