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