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}