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