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