xref: /cloud-hypervisor/net_util/src/open_tap.rs (revision 88a9f799449c04180c6b9a21d3b9c0c4b57e2bd6)
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