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