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