1 // Copyright (c) 2020 Intel Corporation. All rights reserved. 2 // 3 // SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause 4 5 use super::{vnet_hdr_len, MacAddr, Tap, TapError}; 6 use std::net::Ipv4Addr; 7 use std::path::Path; 8 use std::{fs, io}; 9 10 #[derive(Debug)] 11 pub enum Error { 12 /// Failed to convert an hexadecimal string into an integer. 13 ConvertHexStringToInt(std::num::ParseIntError), 14 /// Error related to the multiqueue support (no support TAP side). 15 MultiQueueNoTapSupport, 16 /// Error related to the multiqueue support (no support device side). 17 MultiQueueNoDeviceSupport, 18 /// Failed to read the TAP flags from sysfs. 19 ReadSysfsTunFlags(io::Error), 20 /// Open tap device failed. 21 TapOpen(TapError), 22 /// Setting tap IP failed. 23 TapSetIp(TapError), 24 /// Setting tap netmask failed. 25 TapSetNetmask(TapError), 26 /// Setting MAC address failed 27 TapSetMac(TapError), 28 /// Getting MAC address failed 29 TapGetMac(TapError), 30 /// Setting vnet header size failed. 31 TapSetVnetHdrSize(TapError), 32 /// Enabling tap interface failed. 33 TapEnable(TapError), 34 } 35 36 type Result<T> = std::result::Result<T, Error>; 37 38 fn check_mq_support(if_name: &Option<&str>, queue_pairs: usize) -> Result<()> { 39 if let Some(tap_name) = if_name { 40 let mq = queue_pairs > 1; 41 let path = format!("/sys/class/net/{}/tun_flags", tap_name); 42 // interface does not exist, check is not required 43 if !Path::new(&path).exists() { 44 return Ok(()); 45 } 46 let tun_flags_str = fs::read_to_string(path).map_err(Error::ReadSysfsTunFlags)?; 47 let tun_flags = u32::from_str_radix(tun_flags_str.trim().trim_start_matches("0x"), 16) 48 .map_err(Error::ConvertHexStringToInt)?; 49 if (tun_flags & net_gen::IFF_MULTI_QUEUE != 0) && !mq { 50 return Err(Error::MultiQueueNoDeviceSupport); 51 } else if (tun_flags & net_gen::IFF_MULTI_QUEUE == 0) && mq { 52 return Err(Error::MultiQueueNoTapSupport); 53 } 54 } 55 Ok(()) 56 } 57 58 /// Create a new virtio network device with the given IP address and 59 /// netmask. 60 pub fn open_tap( 61 if_name: Option<&str>, 62 ip_addr: Option<Ipv4Addr>, 63 netmask: Option<Ipv4Addr>, 64 host_mac: &mut Option<MacAddr>, 65 num_rx_q: usize, 66 flags: Option<i32>, 67 ) -> Result<Vec<Tap>> { 68 let mut taps: Vec<Tap> = Vec::new(); 69 let mut ifname: String = String::new(); 70 let vnet_hdr_size = vnet_hdr_len() as i32; 71 72 // In case the tap interface already exists, check if the number of 73 // queues is appropriate. The tap might not support multiqueue while 74 // the number of queues indicates the user expects multiple queues, or 75 // on the contrary, the tap might support multiqueue while the number 76 // of queues indicates the user doesn't expect multiple queues. 77 check_mq_support(&if_name, num_rx_q)?; 78 79 for i in 0..num_rx_q { 80 let tap: Tap; 81 if i == 0 { 82 tap = match if_name { 83 Some(name) => Tap::open_named(name, num_rx_q, flags).map_err(Error::TapOpen)?, 84 None => Tap::new(num_rx_q).map_err(Error::TapOpen)?, 85 }; 86 if let Some(ip) = ip_addr { 87 tap.set_ip_addr(ip).map_err(Error::TapSetIp)?; 88 } 89 if let Some(mask) = netmask { 90 tap.set_netmask(mask).map_err(Error::TapSetNetmask)?; 91 } 92 if let Some(mac) = host_mac { 93 tap.set_mac_addr(*mac).map_err(Error::TapSetMac)? 94 } else { 95 *host_mac = Some(tap.get_mac_addr().map_err(Error::TapGetMac)?) 96 } 97 tap.enable().map_err(Error::TapEnable)?; 98 99 tap.set_vnet_hdr_size(vnet_hdr_size) 100 .map_err(Error::TapSetVnetHdrSize)?; 101 102 ifname = String::from_utf8(tap.get_if_name()).unwrap(); 103 } else { 104 tap = Tap::open_named(ifname.as_str(), num_rx_q, flags).map_err(Error::TapOpen)?; 105 106 tap.set_vnet_hdr_size(vnet_hdr_size) 107 .map_err(Error::TapSetVnetHdrSize)?; 108 } 109 taps.push(tap); 110 } 111 Ok(taps) 112 } 113