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