xref: /cloud-hypervisor/net_util/src/tap.rs (revision eb0b14f70ed5ed44b76579145fd2a741c0100ae4)
1 // Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2 // SPDX-License-Identifier: Apache-2.0
3 //
4 // Portions Copyright 2017 The Chromium OS Authors. All rights reserved.
5 // Use of this source code is governed by a BSD-style license that can be
6 // found in the THIRD-PARTY file.
7 
8 use std::fs::File;
9 use std::io::{Error as IoError, Read, Result as IoResult, Write};
10 use std::net::{IpAddr, Ipv6Addr};
11 use std::os::raw::*;
12 use std::os::unix::io::{AsRawFd, FromRawFd, RawFd};
13 
14 use thiserror::Error;
15 use vmm_sys_util::ioctl::{ioctl_with_mut_ref, ioctl_with_ref, ioctl_with_val};
16 
17 use super::{
18     create_inet_socket, create_sockaddr, create_unix_socket, vnet_hdr_len, Error as NetUtilError,
19     MacAddr,
20 };
21 use crate::mac::MAC_ADDR_LEN;
22 
23 #[derive(Error, Debug)]
24 pub enum Error {
25     #[error("Couldn't open /dev/net/tun: {0}")]
26     OpenTun(#[source] IoError),
27     #[error("Unable to configure tap interface: {0}")]
28     ConfigureTap(#[source] IoError),
29     #[error("Unable to retrieve features: {0}")]
30     GetFeatures(#[source] IoError),
31     #[error("Missing multiqueue support in the kernel")]
32     MultiQueueKernelSupport,
33     #[error("ioctl ({0}) failed: {1}")]
34     IoctlError(c_ulong, #[source] IoError),
35     #[error("Failed to create a socket: {0}")]
36     NetUtil(#[source] NetUtilError),
37     #[error("Invalid interface name")]
38     InvalidIfname,
39     #[error("Error parsing MAC data: {0}")]
40     MacParsing(#[source] IoError),
41     #[error("Invalid netmask")]
42     InvalidNetmask,
43 }
44 
45 pub type Result<T> = ::std::result::Result<T, Error>;
46 
47 /// Handle for a network tap interface.
48 ///
49 /// For now, this simply wraps the file descriptor for the tap device so methods
50 /// can run ioctls on the interface. The tap interface fd will be closed when
51 /// Tap goes out of scope, and the kernel will clean up the interface
52 /// automatically.
53 #[derive(Debug)]
54 pub struct Tap {
55     tap_file: File,
56     if_name: Vec<u8>,
57 }
58 
59 impl PartialEq for Tap {
60     fn eq(&self, other: &Tap) -> bool {
61         self.if_name == other.if_name
62     }
63 }
64 
65 impl std::clone::Clone for Tap {
66     fn clone(&self) -> Self {
67         Tap {
68             tap_file: self.tap_file.try_clone().unwrap(),
69             if_name: self.if_name.clone(),
70         }
71     }
72 }
73 
74 // Returns a byte vector representing the contents of a null terminated C string which
75 // contains if_name.
76 fn build_terminated_if_name(if_name: &str) -> Result<Vec<u8>> {
77     // Convert the string slice to bytes, and shadow the variable,
78     // since we no longer need the &str version.
79     let if_name = if_name.as_bytes();
80 
81     // TODO: the 16usize limit of the if_name member from struct Tap is pretty arbitrary.
82     // We leave it as is for now, but this should be refactored at some point.
83     if if_name.len() > 15 {
84         return Err(Error::InvalidIfname);
85     }
86 
87     let mut terminated_if_name = vec![b'\0'; if_name.len() + 1];
88     terminated_if_name[..if_name.len()].copy_from_slice(if_name);
89 
90     Ok(terminated_if_name)
91 }
92 
93 fn ipv6_mask_to_prefix(mask: Ipv6Addr) -> Result<u8> {
94     let mask = mask.segments();
95     let mut iter = mask.iter();
96 
97     let mut prefix = 0;
98     for &segment in &mut iter {
99         if segment == 0xffff {
100             prefix += 16;
101         } else if segment == 0 {
102             break;
103         } else {
104             let prefix_bits = segment.leading_ones() as u8;
105             if segment << prefix_bits != 0 {
106                 return Err(Error::InvalidNetmask);
107             }
108 
109             prefix += prefix_bits;
110             break;
111         }
112     }
113 
114     // Check that remaining bits are all unset
115     for &segment in iter {
116         if segment != 0 {
117             return Err(Error::InvalidNetmask);
118         }
119     }
120 
121     Ok(prefix)
122 }
123 
124 impl Tap {
125     unsafe fn ioctl_with_mut_ref<F: AsRawFd, T>(fd: &F, req: c_ulong, arg: &mut T) -> Result<()> {
126         let ret = ioctl_with_mut_ref(fd, req, arg);
127         if ret < 0 {
128             return Err(Error::IoctlError(req, IoError::last_os_error()));
129         }
130 
131         Ok(())
132     }
133 
134     unsafe fn ioctl_with_ref<F: AsRawFd, T>(fd: &F, req: c_ulong, arg: &T) -> Result<()> {
135         let ret = ioctl_with_ref(fd, req, arg);
136         if ret < 0 {
137             return Err(Error::IoctlError(req, IoError::last_os_error()));
138         }
139 
140         Ok(())
141     }
142 
143     unsafe fn ioctl_with_val<F: AsRawFd>(fd: &F, req: c_ulong, arg: c_ulong) -> Result<()> {
144         let ret = ioctl_with_val(fd, req, arg);
145         if ret < 0 {
146             return Err(Error::IoctlError(req, IoError::last_os_error()));
147         }
148 
149         Ok(())
150     }
151 
152     pub fn open_named(if_name: &str, num_queue_pairs: usize, flags: Option<i32>) -> Result<Tap> {
153         let terminated_if_name = build_terminated_if_name(if_name)?;
154 
155         // SAFETY: FFI call
156         let fd = unsafe {
157             // Open calls are safe because we give a constant null-terminated
158             // string and verify the result.
159             libc::open(
160                 c"/dev/net/tun".as_ptr() as *const c_char,
161                 flags.unwrap_or(libc::O_RDWR | libc::O_NONBLOCK | libc::O_CLOEXEC),
162             )
163         };
164         if fd < 0 {
165             return Err(Error::OpenTun(IoError::last_os_error()));
166         }
167 
168         // SAFETY: We just checked that the fd is valid.
169         let tuntap = unsafe { File::from_raw_fd(fd) };
170 
171         // Let's validate some features before going any further.
172         // ioctl is safe since we call it with a valid tap fd and check the return
173         // value.
174         let mut features = 0;
175         // SAFETY: IOCTL with correct arguments
176         let ret = unsafe { ioctl_with_mut_ref(&tuntap, net_gen::TUNGETFEATURES(), &mut features) };
177         if ret < 0 {
178             return Err(Error::GetFeatures(IoError::last_os_error()));
179         }
180 
181         // Check if the user parameters match the kernel support for MQ
182         if (features & net_gen::IFF_MULTI_QUEUE == 0) && num_queue_pairs > 1 {
183             return Err(Error::MultiQueueKernelSupport);
184         }
185 
186         // This is pretty messy because of the unions used by ifreq. Since we
187         // don't call as_mut on the same union field more than once, this block
188         // is safe.
189         let mut ifreq: net_gen::ifreq = Default::default();
190         // SAFETY: see the comment above.
191         unsafe {
192             let ifrn_name = ifreq.ifr_ifrn.ifrn_name.as_mut();
193             let name_slice = &mut ifrn_name[..terminated_if_name.len()];
194             name_slice.copy_from_slice(terminated_if_name.as_slice());
195             ifreq.ifr_ifru.ifru_flags =
196                 (net_gen::IFF_TAP | net_gen::IFF_NO_PI | net_gen::IFF_VNET_HDR) as c_short;
197             if num_queue_pairs > 1 {
198                 ifreq.ifr_ifru.ifru_flags |= net_gen::IFF_MULTI_QUEUE as c_short;
199             }
200         }
201 
202         // SAFETY: ioctl is safe since we call it with a valid tap fd and check the return
203         // value.
204         let ret = unsafe { ioctl_with_mut_ref(&tuntap, net_gen::TUNSETIFF(), &mut ifreq) };
205         if ret < 0 {
206             return Err(Error::ConfigureTap(IoError::last_os_error()));
207         }
208 
209         // SAFETY: only the name is accessed, and it's cloned out.
210         let mut if_name = unsafe { ifreq.ifr_ifrn.ifrn_name }.to_vec();
211         if_name.truncate(terminated_if_name.len() - 1);
212         Ok(Tap {
213             tap_file: tuntap,
214             if_name,
215         })
216     }
217 
218     /// Create a new tap interface.
219     pub fn new(num_queue_pairs: usize) -> Result<Tap> {
220         Self::open_named("vmtap%d", num_queue_pairs, None)
221     }
222 
223     pub fn from_tap_fd(fd: RawFd, num_queue_pairs: usize) -> Result<Tap> {
224         // Ensure that the file is opened non-blocking, this is particularly
225         // needed when opened via the shell for macvtap.
226         // SAFETY: FFI call
227         let ret = unsafe {
228             let mut flags = libc::fcntl(fd, libc::F_GETFL);
229             flags |= libc::O_NONBLOCK;
230             libc::fcntl(fd, libc::F_SETFL, flags)
231         };
232         if ret < 0 {
233             return Err(Error::ConfigureTap(IoError::last_os_error()));
234         }
235 
236         // SAFETY: fd is a tap fd
237         let tap_file = unsafe { File::from_raw_fd(fd) };
238         let mut ifreq: net_gen::ifreq = Default::default();
239 
240         // Get current config including name
241         // SAFETY: IOCTL with correct arguments
242         unsafe { Self::ioctl_with_mut_ref(&tap_file, net_gen::TUNGETIFF(), &mut ifreq)? };
243 
244         // SAFETY: We only access one field of the ifru union
245         let if_name = unsafe { ifreq.ifr_ifrn.ifrn_name }.to_vec();
246 
247         // Try and update flags. Depending on how the tap was created (macvtap
248         // or via open_named()) this might return -EEXIST so we just ignore that.
249         // SAFETY: access union fields
250         unsafe {
251             ifreq.ifr_ifru.ifru_flags =
252                 (net_gen::IFF_TAP | net_gen::IFF_NO_PI | net_gen::IFF_VNET_HDR) as c_short;
253             if num_queue_pairs > 1 {
254                 ifreq.ifr_ifru.ifru_flags |= net_gen::IFF_MULTI_QUEUE as c_short;
255             }
256         }
257         // SAFETY: IOCTL with correct arguments
258         let ret = unsafe { ioctl_with_mut_ref(&tap_file, net_gen::TUNSETIFF(), &mut ifreq) };
259         if ret < 0 && IoError::last_os_error().raw_os_error().unwrap() != libc::EEXIST {
260             return Err(Error::ConfigureTap(IoError::last_os_error()));
261         }
262 
263         let tap = Tap { tap_file, if_name };
264         let vnet_hdr_size = vnet_hdr_len() as i32;
265         tap.set_vnet_hdr_size(vnet_hdr_size)?;
266 
267         Ok(tap)
268     }
269 
270     /// Set the host-side IP address for the tap interface.
271     pub fn set_ip_addr(&self, ip_addr: IpAddr, netmask: Option<IpAddr>) -> Result<()> {
272         let sock = create_inet_socket(ip_addr).map_err(Error::NetUtil)?;
273 
274         let mut ifreq = self.get_ifreq();
275 
276         match ip_addr {
277             IpAddr::V4(addr) => {
278                 let addr = create_sockaddr(addr);
279 
280                 ifreq.ifr_ifru.ifru_addr = addr;
281 
282                 // SAFETY: ioctl is safe. Called with a valid sock fd, and we check the return.
283                 unsafe {
284                     Self::ioctl_with_ref(&sock, net_gen::sockios::SIOCSIFADDR as c_ulong, &ifreq)?;
285                 }
286 
287                 if let Some(IpAddr::V4(mask)) = netmask {
288                     ifreq.ifr_ifru.ifru_netmask = create_sockaddr(mask);
289 
290                     // SAFETY: ioctl is safe. Called with a valid sock fd, and we check the return.
291                     unsafe {
292                         Self::ioctl_with_ref(
293                             &sock,
294                             net_gen::sockios::SIOCSIFNETMASK as c_ulong,
295                             &ifreq,
296                         )?;
297                     }
298                 };
299 
300                 Ok(())
301             }
302             IpAddr::V6(addr) => {
303                 let ifindex = {
304                     // SAFETY: ioctl is safe. Called with a valid sock fd, and we check the return.
305                     unsafe {
306                         Self::ioctl_with_ref(
307                             &sock,
308                             net_gen::sockios::SIOCGIFINDEX as c_ulong,
309                             &ifreq,
310                         )?;
311                     }
312 
313                     // SAFETY: ifru_ivalue contains the ifindex and is set by the previous ioctl
314                     unsafe {
315                         match ifreq.ifr_ifru.ifru_ivalue {
316                             0 => return Err(Error::InvalidIfname),
317                             i => i,
318                         }
319                     }
320                 };
321 
322                 let prefixlen = match netmask {
323                     Some(IpAddr::V6(netmask)) => ipv6_mask_to_prefix(netmask)?,
324                     Some(IpAddr::V4(_)) => return Err(Error::InvalidNetmask),
325                     None => 0,
326                 };
327 
328                 let ifreq = net_gen::in6_ifreq {
329                     // SAFETY: addr can be safely transmuted to in6_addr
330                     ifr6_addr: unsafe {
331                         std::mem::transmute::<[u8; 16], net_gen::ipv6::in6_addr>(addr.octets())
332                     },
333                     ifr6_prefixlen: prefixlen as u32,
334                     ifr6_ifindex: ifindex,
335                 };
336 
337                 // SAFETY: ioctl is safe. Called with a valid sock fd, and we check the return.
338                 unsafe {
339                     Self::ioctl_with_ref(&sock, net_gen::sockios::SIOCSIFADDR as c_ulong, &ifreq)
340                 }
341             }
342         }
343     }
344 
345     /// Set mac addr for tap interface.
346     pub fn set_mac_addr(&self, addr: MacAddr) -> Result<()> {
347         // Checking if the mac address already matches the desired one
348         // is useful to avoid making the "set ioctl" in the case where
349         // the VMM is running without the privilege to do that.
350         // In practice this comes from a reboot after the configuration
351         // has been update with the kernel generated address.
352         if self.get_mac_addr()? == addr {
353             return Ok(());
354         }
355 
356         let sock = create_unix_socket().map_err(Error::NetUtil)?;
357 
358         let mut ifreq = self.get_ifreq();
359 
360         // SAFETY: ioctl is safe. Called with a valid sock fd, and we check the return.
361         unsafe { Self::ioctl_with_ref(&sock, net_gen::sockios::SIOCGIFHWADDR as c_ulong, &ifreq)? };
362 
363         // SAFETY: We only access one field of the ifru union
364         unsafe {
365             let ifru_hwaddr = &mut ifreq.ifr_ifru.ifru_hwaddr;
366             for (i, v) in addr.get_bytes().iter().enumerate() {
367                 ifru_hwaddr.sa_data[i] = *v as c_uchar;
368             }
369         }
370 
371         // SAFETY: ioctl is safe. Called with a valid sock fd, and we check the return.
372         unsafe { Self::ioctl_with_ref(&sock, net_gen::sockios::SIOCSIFHWADDR as c_ulong, &ifreq) }
373     }
374 
375     /// Get mac addr for tap interface.
376     pub fn get_mac_addr(&self) -> Result<MacAddr> {
377         let sock = create_unix_socket().map_err(Error::NetUtil)?;
378 
379         let ifreq = self.get_ifreq();
380 
381         // SAFETY: ioctl is safe. Called with a valid sock fd, and we check the return.
382         unsafe { Self::ioctl_with_ref(&sock, net_gen::sockios::SIOCGIFHWADDR as c_ulong, &ifreq)? };
383 
384         // SAFETY: We only access one field of the ifru union
385         let addr = unsafe {
386             MacAddr::from_bytes(&ifreq.ifr_ifru.ifru_hwaddr.sa_data[0..MAC_ADDR_LEN])
387                 .map_err(Error::MacParsing)?
388         };
389         Ok(addr)
390     }
391 
392     #[cfg(not(fuzzing))]
393     pub fn mtu(&self) -> Result<i32> {
394         let sock = create_unix_socket().map_err(Error::NetUtil)?;
395 
396         let ifreq = self.get_ifreq();
397 
398         // SAFETY: ioctl is safe. Called with a valid sock fd, and we check the return.
399         unsafe { Self::ioctl_with_ref(&sock, net_gen::sockios::SIOCGIFMTU as c_ulong, &ifreq)? };
400 
401         // SAFETY: access a union field
402         let mtu = unsafe { ifreq.ifr_ifru.ifru_mtu };
403 
404         Ok(mtu)
405     }
406 
407     #[cfg(fuzzing)]
408     pub fn mtu(&self) -> Result<i32> {
409         // Consistent with the `virtio_devices::net::MIN_MTU`
410         Ok(1280)
411     }
412 
413     pub fn set_mtu(&self, mtu: i32) -> Result<()> {
414         let sock = create_unix_socket().map_err(Error::NetUtil)?;
415 
416         let mut ifreq = self.get_ifreq();
417         ifreq.ifr_ifru.ifru_mtu = mtu;
418 
419         // SAFETY: ioctl is safe. Called with a valid sock fd, and we check the return.
420         unsafe { Self::ioctl_with_ref(&sock, net_gen::sockios::SIOCSIFMTU as c_ulong, &ifreq) }
421     }
422 
423     /// Set the offload flags for the tap interface.
424     pub fn set_offload(&self, flags: c_uint) -> Result<()> {
425         // SAFETY: ioctl is safe. Called with a valid tap fd, and we check the return.
426         unsafe { Self::ioctl_with_val(&self.tap_file, net_gen::TUNSETOFFLOAD(), flags as c_ulong) }
427     }
428 
429     /// Enable the tap interface.
430     pub fn enable(&self) -> Result<()> {
431         let sock = create_unix_socket().map_err(Error::NetUtil)?;
432 
433         let mut ifreq = self.get_ifreq();
434 
435         // SAFETY: IOCTL with correct arguments
436         unsafe { Self::ioctl_with_ref(&sock, net_gen::sockios::SIOCGIFFLAGS as c_ulong, &ifreq)? };
437 
438         // If TAP device is already up don't try and enable it
439         // SAFETY: access a union field
440         let ifru_flags = unsafe { ifreq.ifr_ifru.ifru_flags };
441         if ifru_flags & net_gen::net_device_flags_IFF_UP as i16
442             == net_gen::net_device_flags_IFF_UP as i16
443         {
444             return Ok(());
445         }
446 
447         ifreq.ifr_ifru.ifru_flags = net_gen::net_device_flags_IFF_UP as i16;
448 
449         // SAFETY: ioctl is safe. Called with a valid sock fd, and we check the return.
450         unsafe { Self::ioctl_with_ref(&sock, net_gen::sockios::SIOCSIFFLAGS as c_ulong, &ifreq) }
451     }
452 
453     /// Set the size of the vnet hdr.
454     pub fn set_vnet_hdr_size(&self, size: c_int) -> Result<()> {
455         // SAFETY: ioctl is safe. Called with a valid tap fd, and we check the return.
456         unsafe { Self::ioctl_with_ref(&self.tap_file, net_gen::TUNSETVNETHDRSZ(), &size) }
457     }
458 
459     fn get_ifreq(&self) -> net_gen::ifreq {
460         let mut ifreq: net_gen::ifreq = Default::default();
461 
462         // This sets the name of the interface, which is the only entry
463         // in a single-field union.
464         // SAFETY: access union fields and we're sure the copy is okay.
465         unsafe {
466             let ifrn_name = ifreq.ifr_ifrn.ifrn_name.as_mut();
467             let name_slice = &mut ifrn_name[..self.if_name.len()];
468             name_slice.copy_from_slice(&self.if_name);
469         }
470 
471         ifreq
472     }
473 
474     pub fn get_if_name(&self) -> Vec<u8> {
475         self.if_name.clone()
476     }
477 
478     #[cfg(fuzzing)]
479     pub fn new_for_fuzzing(tap_file: File, if_name: Vec<u8>) -> Self {
480         Tap { tap_file, if_name }
481     }
482 }
483 
484 impl Read for Tap {
485     fn read(&mut self, buf: &mut [u8]) -> IoResult<usize> {
486         self.tap_file.read(buf)
487     }
488 }
489 
490 impl Write for Tap {
491     fn write(&mut self, buf: &[u8]) -> IoResult<usize> {
492         self.tap_file.write(buf)
493     }
494 
495     fn flush(&mut self) -> IoResult<()> {
496         Ok(())
497     }
498 }
499 
500 impl AsRawFd for Tap {
501     fn as_raw_fd(&self) -> RawFd {
502         self.tap_file.as_raw_fd()
503     }
504 }
505 
506 #[cfg(test)]
507 mod tests {
508     use std::net::Ipv4Addr;
509     use std::sync::{mpsc, Mutex};
510     use std::time::Duration;
511     use std::{str, thread};
512 
513     use once_cell::sync::Lazy;
514     use pnet::packet::ethernet::{EtherTypes, EthernetPacket, MutableEthernetPacket};
515     use pnet::packet::ip::IpNextHeaderProtocols;
516     use pnet::packet::ipv4::{Ipv4Packet, MutableIpv4Packet};
517     use pnet::packet::udp::{MutableUdpPacket, UdpPacket};
518     use pnet::packet::{MutablePacket, Packet};
519     use pnet::util::MacAddr;
520     use pnet_datalink::Channel::Ethernet;
521     use pnet_datalink::{DataLinkReceiver, DataLinkSender, NetworkInterface};
522 
523     use super::*;
524 
525     static DATA_STRING: &str = "test for tap";
526     static SUBNET_MASK: &str = "255.255.255.0";
527 
528     // We needed to have a mutex as a global variable, so we used once_cell for testing. The main
529     // potential problem, caused by tests being run in parallel by cargo, is creating different
530     // TAPs and trying to associate the same address, so we hide the IP address &str behind this
531     // mutex, more as a convention to remember to lock it at the very beginning of each function
532     // susceptible to this issue. Another variant is to use a different IP address per function,
533     // but we must remember to pick an unique one each time.
534     static TAP_IP_LOCK: Lazy<Mutex<&'static str>> = Lazy::new(|| Mutex::new("192.168.241.1"));
535 
536     // Describes the outcomes we are currently interested in when parsing a packet (we use
537     // an UDP packet for testing).
538     struct ParsedPkt<'a> {
539         eth: EthernetPacket<'a>,
540         ipv4: Option<Ipv4Packet<'a>>,
541         udp: Option<UdpPacket<'a>>,
542     }
543 
544     impl<'a> ParsedPkt<'a> {
545         fn new(buf: &'a [u8]) -> Self {
546             let eth = EthernetPacket::new(buf).unwrap();
547             let mut ipv4 = None;
548             let mut udp = None;
549 
550             if eth.get_ethertype() == EtherTypes::Ipv4 {
551                 let ipv4_start = 14;
552                 ipv4 = Some(Ipv4Packet::new(&buf[ipv4_start..]).unwrap());
553 
554                 // Hiding the old ipv4 variable for the rest of this block.
555                 let ipv4 = Ipv4Packet::new(eth.payload()).unwrap();
556 
557                 if ipv4.get_next_level_protocol() == IpNextHeaderProtocols::Udp {
558                     // The value in header_length indicates the number of 32 bit words
559                     // that make up the header, not the actual length in bytes.
560                     let udp_start = ipv4_start + ipv4.get_header_length() as usize * 4;
561                     udp = Some(UdpPacket::new(&buf[udp_start..]).unwrap());
562                 }
563             }
564 
565             ParsedPkt { eth, ipv4, udp }
566         }
567 
568         fn print(&self) {
569             print!(
570                 "{} {} {} ",
571                 self.eth.get_source(),
572                 self.eth.get_destination(),
573                 self.eth.get_ethertype()
574             );
575             if let Some(ref ipv4) = self.ipv4 {
576                 print!(
577                     "{} {} {} ",
578                     ipv4.get_source(),
579                     ipv4.get_destination(),
580                     ipv4.get_next_level_protocol()
581                 );
582             }
583             if let Some(ref udp) = self.udp {
584                 print!(
585                     "{} {} {}",
586                     udp.get_source(),
587                     udp.get_destination(),
588                     str::from_utf8(udp.payload()).unwrap()
589                 );
590             }
591             println!();
592         }
593     }
594 
595     fn tap_name_to_string(tap: &Tap) -> String {
596         let null_pos = tap.if_name.iter().position(|x| *x == 0).unwrap();
597         str::from_utf8(&tap.if_name[..null_pos])
598             .unwrap()
599             .to_string()
600     }
601 
602     // Given a buffer of appropriate size, this fills in the relevant fields based on the
603     // provided information. Payload refers to the UDP payload.
604     fn pnet_build_packet(buf: &mut [u8], dst_mac: MacAddr, payload: &[u8]) {
605         let mut eth = MutableEthernetPacket::new(buf).unwrap();
606         eth.set_source(MacAddr::new(0x06, 0, 0, 0, 0, 0));
607         eth.set_destination(dst_mac);
608         eth.set_ethertype(EtherTypes::Ipv4);
609 
610         let mut ipv4 = MutableIpv4Packet::new(eth.payload_mut()).unwrap();
611         ipv4.set_version(4);
612         ipv4.set_header_length(5);
613         ipv4.set_total_length(20 + 8 + payload.len() as u16);
614         ipv4.set_ttl(200);
615         ipv4.set_next_level_protocol(IpNextHeaderProtocols::Udp);
616         ipv4.set_source(Ipv4Addr::new(192, 168, 241, 1));
617         ipv4.set_destination(Ipv4Addr::new(192, 168, 241, 2));
618 
619         let mut udp = MutableUdpPacket::new(ipv4.payload_mut()).unwrap();
620         udp.set_source(1000);
621         udp.set_destination(1001);
622         udp.set_length(8 + payload.len() as u16);
623         udp.set_payload(payload);
624     }
625 
626     // Sends a test packet on the interface named "ifname".
627     fn pnet_send_packet(ifname: String) {
628         let payload = DATA_STRING.as_bytes();
629 
630         // eth hdr + ip hdr + udp hdr + payload len
631         let buf_size = 14 + 20 + 8 + payload.len();
632 
633         let (mac, mut tx, _) = pnet_get_mac_tx_rx(ifname);
634 
635         let res = tx.build_and_send(1, buf_size, &mut |buf| {
636             pnet_build_packet(buf, mac, payload);
637         });
638         // Make sure build_and_send() -> Option<io::Result<()>> succeeds.
639         res.unwrap().unwrap();
640     }
641 
642     // For a given interface name, this returns a tuple that contains the MAC address of the
643     // interface, an object that can be used to send Ethernet frames, and a receiver of
644     // Ethernet frames arriving at the specified interface.
645     fn pnet_get_mac_tx_rx(
646         ifname: String,
647     ) -> (MacAddr, Box<dyn DataLinkSender>, Box<dyn DataLinkReceiver>) {
648         let interface_name_matches = |iface: &NetworkInterface| iface.name == ifname;
649 
650         // Find the network interface with the provided name.
651         let interfaces = pnet_datalink::interfaces();
652         let interface = interfaces.into_iter().find(interface_name_matches).unwrap();
653 
654         if let Ok(Ethernet(tx, rx)) = pnet_datalink::channel(&interface, Default::default()) {
655             (interface.mac.unwrap(), tx, rx)
656         } else {
657             panic!("datalink channel error or unhandled channel type");
658         }
659     }
660 
661     #[test]
662     fn test_tap_create() {
663         let _tap_ip_guard = TAP_IP_LOCK.lock().unwrap();
664 
665         let t = Tap::new(1).unwrap();
666         println!("created tap: {t:?}");
667     }
668 
669     #[test]
670     fn test_tap_from_fd() {
671         let _tap_ip_guard = TAP_IP_LOCK.lock().unwrap();
672 
673         let orig_tap = Tap::new(1).unwrap();
674         let fd = orig_tap.as_raw_fd();
675         let _new_tap = Tap::from_tap_fd(fd, 1).unwrap();
676     }
677 
678     #[test]
679     fn test_tap_configure() {
680         // This should be the first thing to be called inside the function, so everything else
681         // is torn down by the time the mutex is automatically released. Also, we should
682         // explicitly bind the MutexGuard to a variable via let, the make sure it lives until
683         // the end of the function.
684         let tap_ip_guard = TAP_IP_LOCK.lock().unwrap();
685 
686         let tap = Tap::new(1).unwrap();
687         let ip_addr = IpAddr::V4((*tap_ip_guard).parse().unwrap());
688         let netmask = IpAddr::V4(SUBNET_MASK.parse().unwrap());
689 
690         tap.set_ip_addr(ip_addr, Some(netmask)).unwrap();
691     }
692 
693     #[test]
694     fn test_tap_configure_ipv6() {
695         let tap_ip6_lock: Mutex<&'static str> = Mutex::new("2001:db8:85a3::8a2e:370:7334");
696         let tap_ip6_guard = tap_ip6_lock.lock().unwrap();
697 
698         let tap = Tap::new(1).unwrap();
699         let ip_addr = IpAddr::V6((*tap_ip6_guard).parse().unwrap());
700         let netmask = IpAddr::V6("ffff:ffff::".parse().unwrap());
701 
702         tap.set_ip_addr(ip_addr, Some(netmask)).unwrap();
703     }
704 
705     #[test]
706     fn test_set_options() {
707         let _tap_ip_guard = TAP_IP_LOCK.lock().unwrap();
708 
709         // This line will fail to provide an initialized FD if the test is not run as root.
710         let tap = Tap::new(1).unwrap();
711         tap.set_vnet_hdr_size(16).unwrap();
712         tap.set_offload(0).unwrap();
713     }
714 
715     #[test]
716     fn test_tap_enable() {
717         let _tap_ip_guard = TAP_IP_LOCK.lock().unwrap();
718 
719         let tap = Tap::new(1).unwrap();
720         tap.enable().unwrap();
721     }
722 
723     #[test]
724     fn test_raw_fd() {
725         let _tap_ip_guard = TAP_IP_LOCK.lock().unwrap();
726 
727         let tap = Tap::new(1).unwrap();
728         assert_eq!(tap.as_raw_fd(), tap.tap_file.as_raw_fd());
729     }
730 
731     #[test]
732     fn test_read() {
733         let tap_ip_guard = TAP_IP_LOCK.lock().unwrap();
734 
735         let mut tap = Tap::new(1).unwrap();
736         let ip_addr = IpAddr::V4((*tap_ip_guard).parse().unwrap());
737         let netmask = IpAddr::V4(SUBNET_MASK.parse().unwrap());
738         tap.set_ip_addr(ip_addr, Some(netmask)).unwrap();
739         tap.enable().unwrap();
740 
741         // Send a packet to the interface. We expect to be able to receive it on the associated fd.
742         pnet_send_packet(tap_name_to_string(&tap));
743 
744         let mut buf = [0u8; 4096];
745 
746         let mut found_packet_sz = None;
747 
748         // In theory, this could actually loop forever if something keeps sending data through the
749         // tap interface, but it's highly unlikely.
750         while found_packet_sz.is_none() {
751             let size = tap.read(&mut buf).unwrap();
752 
753             // We skip the first 10 bytes because the IFF_VNET_HDR flag is set when the interface
754             // is created, and the legacy header is 10 bytes long without a certain flag which
755             // is not set in Tap::new().
756             let eth_bytes = &buf[10..size];
757 
758             let packet = EthernetPacket::new(eth_bytes).unwrap();
759             if packet.get_ethertype() != EtherTypes::Ipv4 {
760                 // not an IPv4 packet
761                 continue;
762             }
763 
764             let ipv4_bytes = &eth_bytes[14..];
765             let packet = Ipv4Packet::new(ipv4_bytes).unwrap();
766 
767             // Our packet should carry an UDP payload, and not contain IP options.
768             if packet.get_next_level_protocol() != IpNextHeaderProtocols::Udp
769                 && packet.get_header_length() != 5
770             {
771                 continue;
772             }
773 
774             let udp_bytes = &ipv4_bytes[20..];
775 
776             let udp_len = UdpPacket::new(udp_bytes).unwrap().get_length() as usize;
777 
778             // Skip the header bytes.
779             let inner_string = str::from_utf8(&udp_bytes[8..udp_len]).unwrap();
780 
781             if inner_string.eq(DATA_STRING) {
782                 found_packet_sz = Some(size);
783                 break;
784             }
785         }
786 
787         assert!(found_packet_sz.is_some());
788     }
789 
790     #[test]
791     fn test_write() {
792         let tap_ip_guard = TAP_IP_LOCK.lock().unwrap();
793 
794         let mut tap = Tap::new(1).unwrap();
795         let ip_addr = IpAddr::V4((*tap_ip_guard).parse().unwrap());
796         let netmask = IpAddr::V4(SUBNET_MASK.parse().unwrap());
797         tap.set_ip_addr(ip_addr, Some(netmask)).unwrap();
798         tap.enable().unwrap();
799 
800         let (mac, _, mut rx) = pnet_get_mac_tx_rx(tap_name_to_string(&tap));
801 
802         let payload = DATA_STRING.as_bytes();
803 
804         // vnet hdr + eth hdr + ip hdr + udp hdr + payload len
805         let buf_size = 10 + 14 + 20 + 8 + payload.len();
806 
807         let mut buf = vec![0u8; buf_size];
808         // leave the vnet hdr as is
809         pnet_build_packet(&mut buf[10..], mac, payload);
810 
811         tap.write_all(&buf).unwrap();
812         tap.flush().unwrap();
813 
814         let (channel_tx, channel_rx) = mpsc::channel();
815 
816         // We use a separate thread to wait for the test packet because the API exposed by pnet is
817         // blocking. This thread will be killed when the main thread exits.
818         let _handle = thread::spawn(move || loop {
819             let buf = rx.next().unwrap();
820             let p = ParsedPkt::new(buf);
821             p.print();
822 
823             if let Some(ref udp) = p.udp {
824                 if payload == udp.payload() {
825                     channel_tx.send(true).unwrap();
826                     break;
827                 }
828             }
829         });
830 
831         // We wait for at most SLEEP_MILLIS * SLEEP_ITERS milliseconds for the reception of the
832         // test packet to be detected.
833         static SLEEP_MILLIS: u64 = 500;
834         static SLEEP_ITERS: u32 = 6;
835 
836         let mut found_test_packet = false;
837 
838         for _ in 0..SLEEP_ITERS {
839             thread::sleep(Duration::from_millis(SLEEP_MILLIS));
840             if let Ok(true) = channel_rx.try_recv() {
841                 found_test_packet = true;
842                 break;
843             }
844         }
845 
846         assert!(found_test_packet);
847     }
848 }
849