Skip to content

Commit 1b604e7

Browse files
committed
better multi-queue error message + test
Signed-off-by: Egor Lazarchuk <[email protected]>
1 parent 01ecbcc commit 1b604e7

File tree

5 files changed

+77
-25
lines changed

5 files changed

+77
-25
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/devices/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ license = "Apache-2.0"
88
[dependencies]
99
event-manager = "0.3.0"
1010
libc = "0.2.117"
11+
thiserror = "1.0.32"
1112
timerfd = "1.2.0"
1213
versionize = "0.1.6"
1314
versionize_derive = "0.1.4"

src/devices/src/virtio/net/tap.rs

Lines changed: 38 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -19,16 +19,26 @@ use utils::{ioctl_ioc_nr, ioctl_iow_nr};
1919
const IFACE_NAME_MAX_LEN: usize = 16;
2020

2121
/// List of errors the tap implementation can throw.
22-
#[derive(Debug)]
22+
#[derive(Debug, thiserror::Error)]
2323
pub enum Error {
24-
/// Unable to create tap interface.
25-
CreateTap(IoError),
26-
/// Invalid interface name.
27-
InvalidIfname,
28-
/// ioctl failed.
29-
IoctlError(IoError),
30-
/// Couldn't open /dev/net/tun.
24+
/// Couldn't open /dev/net/tun
25+
#[error("Couldn't open /dev/net/tun: {0}")]
3126
OpenTun(IoError),
27+
/// Invalid interface name
28+
#[error("Invalid interface name")]
29+
InvalidIfname,
30+
/// Error while creating ifreq structure
31+
#[error(
32+
"Error while creating ifreq structure: {0}. Invalid TUN/TAP Backend provided by {1}. \
33+
Check our documentation on setting up the network devices"
34+
)]
35+
IfreqExecuteError(IoError, String),
36+
/// Error while setting the offload flags
37+
#[error("Error while setting the offload flags: {0}")]
38+
SetOffloadFlags(IoError),
39+
/// Error while setting size of the vnet header
40+
#[error("Error while setting size of the vnet header: {0}")]
41+
SetSizeOfVnetHdr(IoError),
3242
}
3343

3444
pub type Result<T> = ::std::result::Result<T, Error>;
@@ -66,6 +76,7 @@ fn build_terminated_if_name(if_name: &str) -> Result<[u8; IFACE_NAME_MAX_LEN]> {
6676
Ok(terminated_if_name)
6777
}
6878

79+
#[derive(Copy, Clone)]
6980
pub struct IfReqBuilder(ifreq);
7081

7182
impl IfReqBuilder {
@@ -87,11 +98,10 @@ impl IfReqBuilder {
8798
self
8899
}
89100

90-
pub(crate) fn execute<F: AsRawFd>(mut self, socket: &F, ioctl: u64) -> Result<ifreq> {
101+
pub(crate) fn execute<F: AsRawFd>(mut self, socket: &F, ioctl: u64) -> std::io::Result<ifreq> {
91102
// SAFETY: ioctl is safe. Called with a valid socket fd, and we check the return.
92-
let ret = unsafe { ioctl_with_mut_ref(socket, ioctl, &mut self.0) };
93-
if ret < 0 {
94-
return Err(Error::IoctlError(IoError::last_os_error()));
103+
if unsafe { ioctl_with_mut_ref(socket, ioctl, &mut self.0) } < 0 {
104+
return Err(IoError::last_os_error());
95105
}
96106

97107
Ok(self.0)
@@ -104,8 +114,6 @@ impl Tap {
104114
///
105115
/// * `if_name` - the name of the interface.
106116
pub fn open_named(if_name: &str) -> Result<Tap> {
107-
let terminated_if_name = build_terminated_if_name(if_name)?;
108-
109117
// SAFETY: Open calls are safe because we give a constant null-terminated
110118
// string and verify the result.
111119
let fd = unsafe {
@@ -117,13 +125,16 @@ impl Tap {
117125
if fd < 0 {
118126
return Err(Error::OpenTun(IoError::last_os_error()));
119127
}
128+
120129
// SAFETY: We just checked that the fd is valid.
121130
let tuntap = unsafe { File::from_raw_fd(fd) };
122131

132+
let terminated_if_name = build_terminated_if_name(if_name)?;
123133
let ifreq = IfReqBuilder::new()
124134
.if_name(&terminated_if_name)
125135
.flags((net_gen::IFF_TAP | net_gen::IFF_NO_PI | net_gen::IFF_VNET_HDR) as i16)
126-
.execute(&tuntap, TUNSETIFF())?;
136+
.execute(&tuntap, TUNSETIFF())
137+
.map_err(|io_error| Error::IfreqExecuteError(io_error, if_name.to_owned()))?;
127138

128139
Ok(Tap {
129140
tap_file: tuntap,
@@ -144,9 +155,8 @@ impl Tap {
144155
/// Set the offload flags for the tap interface.
145156
pub fn set_offload(&self, flags: c_uint) -> Result<()> {
146157
// SAFETY: ioctl is safe. Called with a valid tap fd, and we check the return.
147-
let ret = unsafe { ioctl_with_val(&self.tap_file, TUNSETOFFLOAD(), c_ulong::from(flags)) };
148-
if ret < 0 {
149-
return Err(Error::IoctlError(IoError::last_os_error()));
158+
if unsafe { ioctl_with_val(&self.tap_file, TUNSETOFFLOAD(), c_ulong::from(flags)) } < 0 {
159+
return Err(Error::SetOffloadFlags(IoError::last_os_error()));
150160
}
151161

152162
Ok(())
@@ -155,9 +165,8 @@ impl Tap {
155165
/// Set the size of the vnet hdr.
156166
pub fn set_vnet_hdr_size(&self, size: c_int) -> Result<()> {
157167
// SAFETY: ioctl is safe. Called with a valid tap fd, and we check the return.
158-
let ret = unsafe { ioctl_with_ref(&self.tap_file, TUNSETVNETHDRSZ(), &size) };
159-
if ret < 0 {
160-
return Err(Error::IoctlError(IoError::last_os_error()));
168+
if unsafe { ioctl_with_ref(&self.tap_file, TUNSETVNETHDRSZ(), &size) } < 0 {
169+
return Err(Error::SetSizeOfVnetHdr(IoError::last_os_error()));
161170
}
162171

163172
Ok(())
@@ -247,8 +256,14 @@ pub mod tests {
247256
tap_file: unsafe { File::from_raw_fd(-2) },
248257
if_name: [0x01; 16],
249258
};
250-
assert!(faulty_tap.set_vnet_hdr_size(16).is_err());
251-
assert!(faulty_tap.set_offload(0).is_err());
259+
assert_eq!(
260+
faulty_tap.set_vnet_hdr_size(16).unwrap_err().to_string(),
261+
Error::SetSizeOfVnetHdr(IoError::from_raw_os_error(9)).to_string()
262+
);
263+
assert_eq!(
264+
faulty_tap.set_offload(0).unwrap_err().to_string(),
265+
Error::SetOffloadFlags(IoError::from_raw_os_error(9)).to_string()
266+
);
252267
}
253268

254269
#[test]

src/vmm/src/vmm_config/net.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -297,7 +297,10 @@ mod tests {
297297
assert_eq!(
298298
net_builder.build(netif_2).err().unwrap().to_string(),
299299
NetworkInterfaceError::CreateNetworkDevice(devices::virtio::net::Error::TapOpen(
300-
TapError::IoctlError(std::io::Error::from_raw_os_error(16))
300+
TapError::IfreqExecuteError(
301+
std::io::Error::from_raw_os_error(16),
302+
host_dev_name_1.to_string()
303+
)
301304
))
302305
.to_string()
303306
);
@@ -321,7 +324,10 @@ mod tests {
321324
assert_eq!(
322325
net_builder.build(netif_2).err().unwrap().to_string(),
323326
NetworkInterfaceError::CreateNetworkDevice(devices::virtio::net::Error::TapOpen(
324-
TapError::IoctlError(std::io::Error::from_raw_os_error(16))
327+
TapError::IfreqExecuteError(
328+
std::io::Error::from_raw_os_error(16),
329+
host_dev_name_1.to_string()
330+
)
325331
))
326332
.to_string()
327333
);

tests/integration_tests/functional/test_net.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,3 +51,32 @@ def test_high_ingress_traffic(test_microvm_with_api, network_config):
5151
# ssh commands.
5252
exit_code, _, _ = ssh_connection.execute_command("echo success\n")
5353
assert exit_code == 0
54+
55+
56+
def test_multi_queue_unsupported(test_microvm_with_api):
57+
"""
58+
Creates multi-queue tap device and tries to add it to firecracker.
59+
60+
@type: functional
61+
"""
62+
microvm = test_microvm_with_api
63+
microvm.spawn()
64+
microvm.basic_config()
65+
66+
tapname = microvm.id[:8] + "tap1"
67+
68+
utils.run_cmd(f"ip tuntap add name {tapname} mode tap multi_queue")
69+
utils.run_cmd(f"ip link set {tapname} netns {microvm.jailer.netns}")
70+
71+
response = microvm.network.put(
72+
iface_id="eth0",
73+
host_dev_name=tapname,
74+
guest_mac="AA:FC:00:00:00:01",
75+
)
76+
77+
assert response.json()["fault_message"] == (
78+
"Could not create Network Device: Open tap device failed:"
79+
" Error while creating ifreq structure: Invalid argument (os error 22)."
80+
" Invalid TUN/TAP Backend provided by {}. Check our documentation on setting"
81+
" up the network devices"
82+
).format(tapname)

0 commit comments

Comments
 (0)