mirror of
https://github.com/cloud-hypervisor/cloud-hypervisor.git
synced 2026-08-05 02:19:16 +00:00
In case the host MAC address associated with a TAP device wasn't explicitly provided by the user, Cloud Hypervisor would get the host MAC associated by default with this TAP device and store it through the network config. Problem is, in the context of a snapshot/restore, that meant the network config provided by the user was different on the destination host compared to the source host. This was causing an issue when Cloud Hypervisor wasn't started with CAP_NET_ADMIN permissions as it couldn't set the host MAC address on the destination, while the source never needed these permissions since the MAC was automatically allocated by the kernel. We're fixing this issue by setting the host MAC address when it's explicitly requested by the user through the network config, and making the host MAC immutable so that it can't be changed at runtime. Signed-off-by: Sebastien Boeuf <sboeuf@meta.com>
144 lines
5.1 KiB
Rust
144 lines
5.1 KiB
Rust
// Copyright (c) 2020 Intel Corporation. All rights reserved.
|
|
//
|
|
// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
|
|
|
|
use std::net::IpAddr;
|
|
use std::path::Path;
|
|
use std::{fs, io, num, result};
|
|
|
|
use log::warn;
|
|
use thiserror::Error;
|
|
|
|
use super::{MacAddr, Tap, TapError, vnet_hdr_len};
|
|
|
|
#[derive(Error, Debug)]
|
|
pub enum Error {
|
|
#[error("Failed to convert an hexadecimal string into an integer")]
|
|
ConvertHexStringToInt(#[source] num::ParseIntError),
|
|
#[error("Error related to the multiqueue support (no support TAP side)")]
|
|
MultiQueueNoTapSupport,
|
|
#[error("Error related to the multiqueue support (no support device side)")]
|
|
MultiQueueNoDeviceSupport,
|
|
#[error("Failed to read the TAP flags from sysfs")]
|
|
ReadSysfsTunFlags(#[source] io::Error),
|
|
#[error("Open tap device failed")]
|
|
TapOpen(#[source] TapError),
|
|
#[error("Setting tap IP and/or netmask failed")]
|
|
TapSetIpNetmask(#[source] TapError),
|
|
#[error("Setting MAC address failed")]
|
|
TapSetMac(#[source] TapError),
|
|
#[error("Setting vnet header size failed")]
|
|
TapSetVnetHdrSize(#[source] TapError),
|
|
#[error("Setting MTU failed")]
|
|
TapSetMtu(#[source] TapError),
|
|
#[error("Enabling tap interface failed")]
|
|
TapEnable(#[source] TapError),
|
|
}
|
|
|
|
type Result<T> = result::Result<T, Error>;
|
|
|
|
fn check_mq_support(if_name: &Option<&str>, queue_pairs: usize) -> Result<()> {
|
|
if let Some(tap_name) = if_name {
|
|
let mq = queue_pairs > 1;
|
|
let path = format!("/sys/class/net/{tap_name}/tun_flags");
|
|
// interface does not exist, check is not required
|
|
if !Path::new(&path).exists() {
|
|
return Ok(());
|
|
}
|
|
let tun_flags_str = fs::read_to_string(path).map_err(Error::ReadSysfsTunFlags)?;
|
|
let tun_flags = i32::from_str_radix(tun_flags_str.trim().trim_start_matches("0x"), 16)
|
|
.map_err(Error::ConvertHexStringToInt)?;
|
|
if (tun_flags & libc::IFF_MULTI_QUEUE != 0) && !mq {
|
|
return Err(Error::MultiQueueNoDeviceSupport);
|
|
} else if (tun_flags & libc::IFF_MULTI_QUEUE == 0) && mq {
|
|
return Err(Error::MultiQueueNoTapSupport);
|
|
}
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
/// Opens a Tap device and configures it.
|
|
///
|
|
/// Afterward, further RX queues can be opened with a common config.
|
|
fn open_tap_rx_q_0(
|
|
if_name: Option<&str>,
|
|
ip_addr: Option<IpAddr>,
|
|
netmask: Option<IpAddr>,
|
|
host_mac: Option<MacAddr>,
|
|
mtu: Option<u16>,
|
|
num_rx_q: usize,
|
|
flags: Option<i32>,
|
|
) -> Result<Tap> {
|
|
// Check if the given interface exists before we create it.
|
|
let tap_exists = if_name.is_some_and(|n| Path::new(&format!("/sys/class/net/{n}")).exists());
|
|
|
|
let tap = match if_name {
|
|
Some(name) => Tap::open_named(name, num_rx_q, flags).map_err(Error::TapOpen)?,
|
|
// Create a new Tap device in Linux, if none was specified.
|
|
None => Tap::new(num_rx_q).map_err(Error::TapOpen)?,
|
|
};
|
|
// Don't overwrite ip configuration of existing interfaces:
|
|
if tap_exists {
|
|
warn!(
|
|
"Tap {} already exists. IP configuration will not be overwritten.",
|
|
if_name.unwrap_or_default()
|
|
);
|
|
} else if let Some(ip) = ip_addr {
|
|
tap.set_ip_addr(ip, netmask)
|
|
.map_err(Error::TapSetIpNetmask)?;
|
|
}
|
|
if let Some(mac) = host_mac {
|
|
tap.set_mac_addr(mac).map_err(Error::TapSetMac)?;
|
|
}
|
|
if let Some(mtu) = mtu {
|
|
tap.set_mtu(mtu as i32).map_err(Error::TapSetMtu)?;
|
|
}
|
|
tap.enable().map_err(Error::TapEnable)?;
|
|
|
|
tap.set_vnet_hdr_size(vnet_hdr_len() as i32)
|
|
.map_err(Error::TapSetVnetHdrSize)?;
|
|
|
|
Ok(tap)
|
|
}
|
|
|
|
/// Create a new virtio network device with the given IP address and
|
|
/// netmask.
|
|
pub fn open_tap(
|
|
if_name: Option<&str>,
|
|
ip_addr: Option<IpAddr>,
|
|
netmask: Option<IpAddr>,
|
|
host_mac: Option<MacAddr>,
|
|
mtu: Option<u16>,
|
|
num_rx_q: usize,
|
|
flags: Option<i32>,
|
|
) -> Result<Vec<Tap>> {
|
|
let mut taps: Vec<Tap> = Vec::new();
|
|
let mut ifname: String = String::new();
|
|
|
|
// In case the tap interface already exists, check if the number of
|
|
// queues is appropriate. The tap might not support multiqueue while
|
|
// the number of queues indicates the user expects multiple queues, or
|
|
// on the contrary, the tap might support multiqueue while the number
|
|
// of queues indicates the user doesn't expect multiple queues.
|
|
check_mq_support(&if_name, num_rx_q)?;
|
|
|
|
for i in 0..num_rx_q {
|
|
let tap: Tap;
|
|
if i == 0 {
|
|
// Special handling is required for the first RX queue, such as
|
|
// configuring the device. Subsequent iterations will then use the
|
|
// same device.
|
|
tap = open_tap_rx_q_0(if_name, ip_addr, netmask, host_mac, mtu, num_rx_q, flags)?;
|
|
// Set the name of the tap device we open in subsequent iterations.
|
|
ifname = tap.if_name_as_str().to_string();
|
|
} else {
|
|
tap = Tap::open_named(ifname.as_str(), num_rx_q, flags).map_err(Error::TapOpen)?;
|
|
|
|
tap.set_vnet_hdr_size(vnet_hdr_len() as i32)
|
|
.map_err(Error::TapSetVnetHdrSize)?;
|
|
}
|
|
taps.push(tap);
|
|
}
|
|
Ok(taps)
|
|
}
|