vmm/vmm_config/
net.rs

1// Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2// SPDX-License-Identifier: Apache-2.0
3
4use std::convert::TryInto;
5use std::ops::Deref;
6use std::sync::{Arc, Mutex};
7
8use serde::{Deserialize, Serialize};
9
10use super::RateLimiterConfig;
11use crate::VmmError;
12use crate::devices::virtio::net::{Net, TapError};
13use crate::utils::net::mac::MacAddr;
14
15/// This struct represents the strongly typed equivalent of the json body from net iface
16/// related requests.
17#[derive(Debug, PartialEq, Eq, Deserialize, Serialize)]
18#[serde(deny_unknown_fields)]
19pub struct NetworkInterfaceConfig {
20    /// ID of the guest network interface.
21    pub iface_id: String,
22    /// Host level path for the guest network interface.
23    pub host_dev_name: String,
24    /// Guest MAC address.
25    pub guest_mac: Option<MacAddr>,
26    /// Rate Limiter for received packages.
27    pub rx_rate_limiter: Option<RateLimiterConfig>,
28    /// Rate Limiter for transmitted packages.
29    pub tx_rate_limiter: Option<RateLimiterConfig>,
30}
31
32impl From<&Net> for NetworkInterfaceConfig {
33    fn from(net: &Net) -> Self {
34        let rx_rl: RateLimiterConfig = net.rx_rate_limiter().into();
35        let tx_rl: RateLimiterConfig = net.tx_rate_limiter().into();
36        NetworkInterfaceConfig {
37            iface_id: net.id().clone(),
38            host_dev_name: net.iface_name(),
39            guest_mac: net.guest_mac().copied(),
40            rx_rate_limiter: rx_rl.into_option(),
41            tx_rate_limiter: tx_rl.into_option(),
42        }
43    }
44}
45
46/// The data fed into a network iface update request. Currently, only the RX and TX rate limiters
47/// can be updated.
48#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
49#[serde(deny_unknown_fields)]
50pub struct NetworkInterfaceUpdateConfig {
51    /// The net iface ID, as provided by the user at iface creation time.
52    pub iface_id: String,
53    /// New RX rate limiter config. Only provided data will be updated. I.e. if any optional data
54    /// is missing, it will not be nullified, but left unchanged.
55    pub rx_rate_limiter: Option<RateLimiterConfig>,
56    /// New TX rate limiter config. Only provided data will be updated. I.e. if any optional data
57    /// is missing, it will not be nullified, but left unchanged.
58    pub tx_rate_limiter: Option<RateLimiterConfig>,
59}
60
61/// Errors associated with the operations allowed on a net device.
62#[derive(Debug, thiserror::Error, displaydoc::Display)]
63pub enum NetworkInterfaceError {
64    /// Could not create the network device: {0}
65    CreateNetworkDevice(#[from] crate::devices::virtio::net::NetError),
66    /// Cannot create the rate limiter: {0}
67    CreateRateLimiter(#[from] std::io::Error),
68    /// Unable to update the net device: {0}
69    DeviceUpdate(#[from] VmmError),
70    /// The MAC address is already in use: {0}
71    GuestMacAddressInUse(String),
72    /// Cannot open/create the tap device: {0}
73    OpenTap(#[from] TapError),
74}
75
76/// Builder for a list of network devices.
77#[derive(Debug, Default)]
78pub struct NetBuilder {
79    net_devices: Vec<Arc<Mutex<Net>>>,
80}
81
82impl NetBuilder {
83    /// Creates an empty list of Network Devices.
84    pub fn new() -> Self {
85        NetBuilder {
86            // List of built network devices.
87            net_devices: Vec::new(),
88        }
89    }
90
91    /// Returns a immutable iterator over the network devices.
92    pub fn iter(&self) -> ::std::slice::Iter<'_, Arc<Mutex<Net>>> {
93        self.net_devices.iter()
94    }
95
96    /// Adds an existing network device in the builder.
97    pub fn add_device(&mut self, device: Arc<Mutex<Net>>) {
98        self.net_devices.push(device);
99    }
100
101    /// Builds a network device based on a network interface config. Keeps a device reference
102    /// in the builder's internal list.
103    pub fn build(
104        &mut self,
105        netif_config: NetworkInterfaceConfig,
106    ) -> Result<Arc<Mutex<Net>>, NetworkInterfaceError> {
107        if let Some(ref mac_address) = netif_config.guest_mac {
108            let mac_conflict = |net: &Arc<Mutex<Net>>| {
109                let net = net.lock().expect("Poisoned lock");
110                // Check if another net dev has same MAC.
111                Some(mac_address) == net.guest_mac() && &netif_config.iface_id != net.id()
112            };
113            // Validate there is no Mac conflict.
114            // No need to validate host_dev_name conflict. In such a case,
115            // an error will be thrown during device creation anyway.
116            if self.net_devices.iter().any(mac_conflict) {
117                return Err(NetworkInterfaceError::GuestMacAddressInUse(
118                    mac_address.to_string(),
119                ));
120            }
121        }
122
123        // If this is an update, just remove the old one.
124        if let Some(index) = self
125            .net_devices
126            .iter()
127            .position(|net| net.lock().expect("Poisoned lock").id() == &netif_config.iface_id)
128        {
129            self.net_devices.swap_remove(index);
130        }
131
132        // Add new device.
133        let net = Arc::new(Mutex::new(Self::create_net(netif_config)?));
134        self.net_devices.push(net.clone());
135
136        Ok(net)
137    }
138
139    /// Creates a Net device from a NetworkInterfaceConfig.
140    pub fn create_net(cfg: NetworkInterfaceConfig) -> Result<Net, NetworkInterfaceError> {
141        let rx_rate_limiter = cfg
142            .rx_rate_limiter
143            .map(super::RateLimiterConfig::try_into)
144            .transpose()
145            .map_err(NetworkInterfaceError::CreateRateLimiter)?;
146        let tx_rate_limiter = cfg
147            .tx_rate_limiter
148            .map(super::RateLimiterConfig::try_into)
149            .transpose()
150            .map_err(NetworkInterfaceError::CreateRateLimiter)?;
151
152        // Create and return the Net device
153        crate::devices::virtio::net::Net::new(
154            cfg.iface_id,
155            &cfg.host_dev_name,
156            cfg.guest_mac,
157            rx_rate_limiter.unwrap_or_default(),
158            tx_rate_limiter.unwrap_or_default(),
159        )
160        .map_err(NetworkInterfaceError::CreateNetworkDevice)
161    }
162
163    /// Returns a vec with the structures used to configure the net devices.
164    pub fn configs(&self) -> Vec<NetworkInterfaceConfig> {
165        let mut ret = vec![];
166        for net in &self.net_devices {
167            ret.push(NetworkInterfaceConfig::from(net.lock().unwrap().deref()));
168        }
169        ret
170    }
171}
172
173#[cfg(test)]
174mod tests {
175    use std::str::FromStr;
176
177    use super::*;
178    use crate::rate_limiter::RateLimiter;
179
180    impl NetBuilder {
181        pub(crate) fn len(&self) -> usize {
182            self.net_devices.len()
183        }
184    }
185
186    fn create_netif(id: &str, name: &str, mac: &str) -> NetworkInterfaceConfig {
187        NetworkInterfaceConfig {
188            iface_id: String::from(id),
189            host_dev_name: String::from(name),
190            guest_mac: Some(MacAddr::from_str(mac).unwrap()),
191            rx_rate_limiter: RateLimiterConfig::default().into_option(),
192            tx_rate_limiter: RateLimiterConfig::default().into_option(),
193        }
194    }
195
196    impl Clone for NetworkInterfaceConfig {
197        fn clone(&self) -> Self {
198            NetworkInterfaceConfig {
199                iface_id: self.iface_id.clone(),
200                host_dev_name: self.host_dev_name.clone(),
201                guest_mac: self.guest_mac,
202                rx_rate_limiter: None,
203                tx_rate_limiter: None,
204            }
205        }
206    }
207
208    #[test]
209    fn test_insert() {
210        let mut net_builder = NetBuilder::new();
211
212        let id_1 = "id_1";
213        let mut host_dev_name_1 = "dev1";
214        let mut guest_mac_1 = "01:23:45:67:89:0a";
215
216        // Test create.
217        let netif_1 = create_netif(id_1, host_dev_name_1, guest_mac_1);
218        net_builder.build(netif_1).unwrap();
219        assert_eq!(net_builder.net_devices.len(), 1);
220
221        // Test update mac address (this test does not modify the tap).
222        guest_mac_1 = "01:23:45:67:89:0b";
223        let netif_1 = create_netif(id_1, host_dev_name_1, guest_mac_1);
224
225        net_builder.build(netif_1).unwrap();
226        assert_eq!(net_builder.net_devices.len(), 1);
227
228        // Test update host_dev_name (the tap will be updated).
229        host_dev_name_1 = "dev2";
230        let netif_1 = create_netif(id_1, host_dev_name_1, guest_mac_1);
231        net_builder.build(netif_1).unwrap();
232        assert_eq!(net_builder.net_devices.len(), 1);
233    }
234
235    #[test]
236    fn test_insert_error_cases() {
237        let mut net_builder = NetBuilder::new();
238
239        let id_1 = "id_1";
240        let host_dev_name_1 = "dev3";
241        let guest_mac_1 = "01:23:45:67:89:0a";
242
243        // Adding the first valid network config.
244        let netif_1 = create_netif(id_1, host_dev_name_1, guest_mac_1);
245        net_builder.build(netif_1).unwrap();
246
247        // Error Cases for CREATE
248        // Error Case: Add new network config with the same mac as netif_1.
249        let id_2 = "id_2";
250        let host_dev_name_2 = "dev4";
251        let guest_mac_2 = "01:23:45:67:89:0b";
252
253        let netif_2 = create_netif(id_2, host_dev_name_2, guest_mac_1);
254        let expected_error = NetworkInterfaceError::GuestMacAddressInUse(guest_mac_1.into());
255        assert_eq!(
256            net_builder.build(netif_2).err().unwrap().to_string(),
257            expected_error.to_string()
258        );
259        assert_eq!(net_builder.net_devices.len(), 1);
260
261        // Error Case: Add new network config with the same dev_host_name as netif_1.
262        let netif_2 = create_netif(id_2, host_dev_name_1, guest_mac_2);
263        assert_eq!(
264            net_builder.build(netif_2).err().unwrap().to_string(),
265            NetworkInterfaceError::CreateNetworkDevice(
266                crate::devices::virtio::net::NetError::TapOpen(TapError::IfreqExecuteError(
267                    std::io::Error::from_raw_os_error(16),
268                    host_dev_name_1.to_string()
269                ))
270            )
271            .to_string()
272        );
273        assert_eq!(net_builder.net_devices.len(), 1);
274
275        // Adding the second valid network config.
276        let netif_2 = create_netif(id_2, host_dev_name_2, guest_mac_2);
277        net_builder.build(netif_2).unwrap();
278
279        // Error Cases for UPDATE
280        // Error Case: Update netif_2 mac using the same mac as netif_1.
281        let netif_2 = create_netif(id_2, host_dev_name_2, guest_mac_1);
282        let expected_error = NetworkInterfaceError::GuestMacAddressInUse(guest_mac_1.into());
283        assert_eq!(
284            net_builder.build(netif_2).err().unwrap().to_string(),
285            expected_error.to_string()
286        );
287
288        // Error Case: Update netif_2 dev_host_name using the same dev_host_name as netif_1.
289        let netif_2 = create_netif(id_2, host_dev_name_1, guest_mac_2);
290        assert_eq!(
291            net_builder.build(netif_2).err().unwrap().to_string(),
292            NetworkInterfaceError::CreateNetworkDevice(
293                crate::devices::virtio::net::NetError::TapOpen(TapError::IfreqExecuteError(
294                    std::io::Error::from_raw_os_error(16),
295                    host_dev_name_1.to_string()
296                ))
297            )
298            .to_string()
299        );
300    }
301
302    #[test]
303    fn test_net_config() {
304        let net_id = "id";
305        let host_dev_name = "dev";
306        let guest_mac = "01:23:45:67:89:0b";
307
308        let net_if_cfg = create_netif(net_id, host_dev_name, guest_mac);
309        assert_eq!(
310            net_if_cfg.guest_mac.unwrap(),
311            MacAddr::from_str(guest_mac).unwrap()
312        );
313
314        let mut net_builder = NetBuilder::new();
315        net_builder.build(net_if_cfg.clone()).unwrap();
316        assert_eq!(net_builder.net_devices.len(), 1);
317
318        let configs = net_builder.configs();
319        assert_eq!(configs.len(), 1);
320        assert_eq!(configs.first().unwrap(), &net_if_cfg);
321    }
322
323    #[test]
324    fn test_add_device() {
325        let mut net_builder = NetBuilder::new();
326        let net_id = "test_id";
327        let host_dev_name = "dev";
328        let guest_mac = "01:23:45:67:89:0b";
329
330        let net = Net::new(
331            net_id.to_string(),
332            host_dev_name,
333            Some(MacAddr::from_str(guest_mac).unwrap()),
334            RateLimiter::default(),
335            RateLimiter::default(),
336        )
337        .unwrap();
338
339        net_builder.add_device(Arc::new(Mutex::new(net)));
340        assert_eq!(net_builder.net_devices.len(), 1);
341        assert_eq!(
342            net_builder
343                .net_devices
344                .pop()
345                .unwrap()
346                .lock()
347                .unwrap()
348                .deref()
349                .id(),
350            net_id
351        );
352    }
353}