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 = ð_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