// Copyright (c) 2020 Huawei Technologies Co.,Ltd. All rights reserved. // // StratoVirt is licensed under Mulan PSL v2. // You can use this software according to the terms and conditions of the Mulan // PSL v2. // You may obtain a copy of Mulan PSL v2 at: // http://license.coscl.org.cn/MulanPSL2 // THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY // KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. use error_chain::bail; use std::fs::{File, OpenOptions}; use std::io::{Read, Result as IoResult, Write}; use std::os::unix::fs::OpenOptionsExt; use std::os::unix::io::{AsRawFd, FromRawFd, RawFd}; use vmm_sys_util::ioctl::{ioctl_with_mut_ref, ioctl_with_ref, ioctl_with_val}; use vmm_sys_util::{ioctl_expr, ioctl_ioc_nr, ioctl_ior_nr, ioctl_iow_nr}; use super::errors::{Result, ResultExt}; pub const TUN_F_CSUM: u32 = 1; pub const TUN_F_TSO4: u32 = 2; pub const TUN_F_TSO6: u32 = 4; pub const TUN_F_UFO: u32 = 16; pub const TUN_F_VIRTIO: u32 = TUN_F_CSUM | TUN_F_TSO4 | TUN_F_TSO6 | TUN_F_UFO; const IFF_TAP: u16 = 0x02; pub const IFF_MULTI_QUEUE: u16 = 0x100; const IFF_NO_PI: u16 = 0x1000; const IFF_VNET_HDR: u16 = 0x4000; const TUNTAP_PATH: &str = "/dev/net/tun"; ioctl_iow_nr!(TUNSETIFF, 84, 202, ::std::os::raw::c_int); ioctl_ior_nr!(TUNGETFEATURES, 84, 207, ::std::os::raw::c_uint); ioctl_iow_nr!(TUNSETOFFLOAD, 84, 208, ::std::os::raw::c_int); ioctl_iow_nr!(TUNSETVNETHDRSZ, 84, 216, ::std::os::raw::c_int); #[repr(C)] pub struct IfReq { ifr_name: [u8; 16], ifr_flags: u16, } pub struct Tap { pub file: File, } impl Tap { pub fn new(name: Option<&str>, fd: Option<RawFd>, queue_pairs: u16) -> Result<Self> { let file; if let Some(name) = name { if name.len() > 15 { return Err(format!("Open tap {} failed, name too long.", name).into()); } let mut ifr_name = [0_u8; 16]; let (left, _) = ifr_name.split_at_mut(name.len()); left.copy_from_slice(name.as_bytes()); let mut if_req = IfReq { ifr_name, ifr_flags: IFF_TAP | IFF_NO_PI | IFF_VNET_HDR, }; if queue_pairs > 1 { if_req.ifr_flags |= IFF_MULTI_QUEUE; } let file_ = OpenOptions::new() .read(true) .write(true) .custom_flags(libc::O_CLOEXEC | libc::O_NONBLOCK) .open(TUNTAP_PATH) .chain_err(|| format!("Open {} failed.", TUNTAP_PATH))?; let ret = unsafe { ioctl_with_mut_ref(&file_, TUNSETIFF(), &mut if_req) }; if ret < 0 { return Err(format!( "Failed to set tap ifr flags, error is {}", std::io::Error::last_os_error() ) .into()); } file = file_; } else if let Some(fd) = fd { file = unsafe { libc::fcntl(fd, libc::F_SETFL, libc::O_NONBLOCK); File::from_raw_fd(fd) }; } else { return Err(format!( "Open tap failed, unsupported operation, error is {}", std::io::Error::last_os_error() ) .into()); } let mut features = 0; let ret = unsafe { ioctl_with_mut_ref(&file, TUNGETFEATURES(), &mut features) }; if ret < 0 { return Err(format!( "Failed to get tap features, error is {}.", std::io::Error::last_os_error() ) .into()); } if (features & IFF_MULTI_QUEUE == 0) && queue_pairs > 1 { bail!( "Tap device doesn't support mq, but command set queue pairs {}.", queue_pairs ); } Ok(Tap { file }) } pub fn set_offload(&self, flags: u32) -> Result<()> { let ret = unsafe { ioctl_with_val(&self.file, TUNSETOFFLOAD(), flags as libc::c_ulong) }; if ret < 0 { return Err("ioctl TUNSETOFFLOAD failed.".to_string().into()); } Ok(()) } pub fn set_hdr_size(&self, len: u32) -> Result<()> { let ret = unsafe { ioctl_with_ref(&self.file, TUNSETVNETHDRSZ(), &len) }; if ret < 0 { return Err("ioctl TUNSETVNETHDRSZ failed.".to_string().into()); } Ok(()) } pub fn read(&mut self, buf: &mut [u8]) -> IoResult<usize> { self.file.read(buf) } pub fn write(&mut self, buf: &[u8]) -> IoResult<usize> { self.file.write(buf) } pub fn as_raw_fd(&self) -> RawFd { self.file.as_raw_fd() } } impl Clone for Tap { fn clone(&self) -> Self { Tap { file: self.file.try_clone().unwrap(), } } }