Skip to content

Commit 29f2486

Browse files
Alexandra Iordachexibz
authored andcommitted
Fix network interfaces API updates before boot
Changing the MAC address resulted in error 400 because the inner tap object was already taken. Added support to leave the tap in place and update the other fields, unless the interface's name changes. Fixes firecracker-microvm#354 Signed-off-by: Alexandra Iordache <[email protected]>
1 parent 8bfe0d3 commit 29f2486

File tree

1 file changed

+106
-44
lines changed

1 file changed

+106
-44
lines changed

vmm/src/device_config/net.rs

Lines changed: 106 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
use std::collections::linked_list::{self, LinkedList};
21
use std::mem;
32
use std::rc::Rc;
43
use std::result;
@@ -43,6 +42,13 @@ impl NetworkInterfaceConfig {
4342
})
4443
}
4544

45+
fn update_from_body(&mut self, mut body: NetworkInterfaceBody) {
46+
self.id = Rc::new(mem::replace(&mut body.iface_id, String::new()));
47+
self.rx_rate_limiter = body.rx_rate_limiter.take();
48+
self.tx_rate_limiter = body.tx_rate_limiter.take();
49+
self.body = body;
50+
}
51+
4652
pub fn id_as_str(&self) -> &str {
4753
self.id.as_str()
4854
}
@@ -57,36 +63,61 @@ impl NetworkInterfaceConfig {
5763
}
5864

5965
pub struct NetworkInterfaceConfigs {
60-
// We use just a list for now, since we only add interfaces as this point.
61-
if_list: LinkedList<NetworkInterfaceConfig>,
66+
if_list: Vec<NetworkInterfaceConfig>,
6267
}
6368

6469
impl NetworkInterfaceConfigs {
6570
pub fn new() -> Self {
6671
NetworkInterfaceConfigs {
67-
if_list: LinkedList::new(),
72+
if_list: Vec::new(),
6873
}
6974
}
7075

7176
pub fn put(&mut self, body: NetworkInterfaceBody) -> result::Result<SyncOkStatus, SyncError> {
77+
match self.if_list
78+
.iter()
79+
.position(|netif| netif.id.as_str() == body.iface_id.as_str())
80+
{
81+
Some(index) => self.update(index, body),
82+
None => self.create(body),
83+
}
84+
}
85+
86+
pub fn iter_mut(&mut self) -> ::std::slice::IterMut<NetworkInterfaceConfig> {
87+
self.if_list.iter_mut()
88+
}
89+
90+
fn create(&mut self, body: NetworkInterfaceBody) -> result::Result<SyncOkStatus, SyncError> {
91+
self.validate_unique_mac(&body.guest_mac)?;
7292
let cfg = NetworkInterfaceConfig::try_from_body(body).map_err(SyncError::OpenTap)?;
73-
for device_config in self.if_list.iter_mut() {
74-
if device_config.id_as_str() == cfg.id_as_str() {
75-
device_config.tap = cfg.tap;
76-
device_config.body = cfg.body.clone();
77-
return Ok(SyncOkStatus::Updated);
78-
}
93+
self.if_list.push(cfg);
94+
Ok(SyncOkStatus::Created)
95+
}
7996

80-
if cfg.guest_mac().is_some() && device_config.guest_mac() == cfg.guest_mac() {
81-
return Err(SyncError::GuestMacAddressInUse);
82-
}
97+
fn update(
98+
&mut self,
99+
index: usize,
100+
body: NetworkInterfaceBody,
101+
) -> result::Result<SyncOkStatus, SyncError> {
102+
if self.if_list[index].body.host_dev_name != body.host_dev_name {
103+
// This is a new tap device which replaces the one at the specified ID.
104+
self.if_list.remove(index);
105+
self.create(body)?;
106+
} else {
107+
// The same tap device is being updated.
108+
self.validate_unique_mac(&body.guest_mac)?;
109+
self.if_list[index].update_from_body(body);
83110
}
84-
self.if_list.push_back(cfg);
85-
Ok(SyncOkStatus::Created)
111+
Ok(SyncOkStatus::Updated)
86112
}
87113

88-
pub fn iter_mut(&mut self) -> linked_list::IterMut<NetworkInterfaceConfig> {
89-
self.if_list.iter_mut()
114+
fn validate_unique_mac(&self, mac: &Option<MacAddr>) -> result::Result<(), SyncError> {
115+
for device_config in self.if_list.iter() {
116+
if mac.is_some() && mac == &device_config.body.guest_mac {
117+
return Err(SyncError::GuestMacAddressInUse);
118+
}
119+
}
120+
Ok(())
90121
}
91122
}
92123

@@ -96,37 +127,68 @@ mod tests {
96127
use api_server::request::sync::DeviceState;
97128
use net_util::MacAddr;
98129

130+
fn make_netif(id: &str, name: &str, mac: MacAddr) -> NetworkInterfaceBody {
131+
NetworkInterfaceBody {
132+
iface_id: String::from(id),
133+
state: DeviceState::Attached,
134+
host_dev_name: String::from(name),
135+
guest_mac: Some(mac),
136+
rx_rate_limiter: Some(RateLimiterDescription::default()),
137+
tx_rate_limiter: Some(RateLimiterDescription::default()),
138+
}
139+
}
140+
141+
fn make_netif_cfg(body: NetworkInterfaceBody, id: &str) -> NetworkInterfaceConfig {
142+
NetworkInterfaceConfig {
143+
body: body,
144+
id: Rc::new(String::from(id)),
145+
tap: None,
146+
tx_rate_limiter: None,
147+
rx_rate_limiter: None,
148+
}
149+
}
150+
99151
#[test]
100152
fn test_put() {
101153
let mut netif_configs = NetworkInterfaceConfigs::new();
102154
assert!(netif_configs.if_list.is_empty());
103155

104-
if let Ok(mac) = MacAddr::parse_str("01:23:45:67:89:0A") {
105-
let mut netif_body = NetworkInterfaceBody {
106-
iface_id: String::from("foo"),
107-
state: DeviceState::Attached,
108-
host_dev_name: String::from("bar"),
109-
guest_mac: Some(mac.clone()),
110-
rx_rate_limiter: Some(RateLimiterDescription::default()),
111-
tx_rate_limiter: Some(RateLimiterDescription::default()),
112-
};
113-
assert!(netif_configs.put(netif_body.clone()).is_ok());
114-
assert_eq!(netif_configs.if_list.len(), 1);
115-
116-
netif_body.host_dev_name = String::from("baz");
117-
assert!(netif_configs.put(netif_body).is_ok());
118-
assert_eq!(netif_configs.if_list.len(), 1);
119-
120-
let other_netif_body = NetworkInterfaceBody {
121-
iface_id: String::from("bar"),
122-
state: DeviceState::Attached,
123-
host_dev_name: String::from("foo"),
124-
guest_mac: Some(mac.clone()),
125-
rx_rate_limiter: None,
126-
tx_rate_limiter: None,
127-
};
128-
assert!(netif_configs.put(other_netif_body).is_err());
129-
assert_eq!(netif_configs.if_list.len(), 1);
130-
}
156+
let mac1 = MacAddr::parse_str("01:23:45:67:89:0A").unwrap();
157+
let mac2 = MacAddr::parse_str("23:45:67:89:0A:01").unwrap();
158+
let mac3 = MacAddr::parse_str("45:67:89:0A:01:23").unwrap();
159+
160+
// Add an interface.
161+
let mut netif_body = make_netif("foo", "bar", mac1);
162+
netif_configs
163+
.if_list
164+
.push(make_netif_cfg(netif_body.clone(), "foo"));
165+
assert_eq!(netif_configs.if_list.len(), 1);
166+
167+
// Update MAC.
168+
netif_body.guest_mac = Some(mac2.clone());
169+
assert!(netif_configs.put(netif_body).is_ok());
170+
assert_eq!(netif_configs.if_list.len(), 1);
171+
172+
// Try to add another interface with the same MAC.
173+
let mut other_netif_body = make_netif("bar", "foo", mac2.clone());
174+
assert!(netif_configs.put(other_netif_body.clone()).is_err());
175+
assert_eq!(netif_configs.if_list.len(), 1);
176+
177+
// Add another interface.
178+
other_netif_body.guest_mac = Some(mac3);
179+
netif_configs
180+
.if_list
181+
.push(make_netif_cfg(other_netif_body.clone(), "foo"));
182+
assert_eq!(netif_configs.if_list.len(), 2);
183+
184+
// Try to update with an unavailable name.
185+
other_netif_body.host_dev_name = String::from("baz");
186+
assert!(netif_configs.put(other_netif_body.clone()).is_err());
187+
assert_eq!(netif_configs.if_list.len(), 2);
188+
189+
// Try to update with an unavailable MAC.
190+
other_netif_body.guest_mac = Some(mac2);
191+
assert!(netif_configs.put(other_netif_body).is_err());
192+
assert_eq!(netif_configs.if_list.len(), 2);
131193
}
132194
}

0 commit comments

Comments
 (0)