From f0a76ad424acfd07c37aace22aecfe236b00d5fb Mon Sep 17 00:00:00 2001 From: Sebastien Boeuf Date: Mon, 8 Jul 2019 15:31:13 -0700 Subject: [PATCH] vmm: Add support for multiple virtio-net devices Until now, the VMM was only accepting a single instance of virtio-net device. This commit extends the virtio-net support by allowing several devices to be created for a single VM. Fixes #71 Signed-off-by: Sebastien Boeuf --- src/main.rs | 78 ++++++++++++++++--- .../cloud-init/openstack/latest/user_data | 2 +- vmm/src/config.rs | 25 +++--- vmm/src/vm.rs | 40 +++++----- 4 files changed, 103 insertions(+), 42 deletions(-) diff --git a/src/main.rs b/src/main.rs index 60f1d3a9f..a63ad0574 100755 --- a/src/main.rs +++ b/src/main.rs @@ -58,7 +58,8 @@ fn main() { "Network parameters \"tap=,\ ip=,mask=,mac=\"", ) - .takes_value(true), + .takes_value(true) + .min_values(1), ) .arg( Arg::with_name("rng") @@ -104,7 +105,7 @@ fn main() { .expect("Missing argument: disk. Provide at least one") .collect(); - let net = cmd_arguments.value_of("net"); + let net: Option> = cmd_arguments.values_of("net").map(|x| x.collect()); // This .unwrap() cannot fail as there is a default value defined let rng = cmd_arguments.value_of("rng").unwrap(); @@ -297,7 +298,10 @@ mod tests { .args(&["--memory", "size=512M"]) .args(&["--kernel", fw_path.as_str()]) .args(&["--disk", disks[0], disks[1]]) - .args(&["--net", "tap=,mac=,ip=192.168.2.1,mask=255.255.255.0"]) + .args(&[ + "--net", + "tap=,mac=12:34:56:78:90:ab,ip=192.168.2.1,mask=255.255.255.0", + ]) .spawn() .unwrap(); @@ -325,7 +329,10 @@ mod tests { .args(&["--memory", "size=512M"]) .args(&["--kernel", fw_path.as_str()]) .args(&["--disk", disks[0], disks[1]]) - .args(&["--net", "tap=,mac=,ip=192.168.2.1,mask=255.255.255.0"]) + .args(&[ + "--net", + "tap=,mac=12:34:56:78:90:ab,ip=192.168.2.1,mask=255.255.255.0", + ]) .spawn() .unwrap(); @@ -350,7 +357,10 @@ mod tests { .args(&["--memory", "size=5120M"]) .args(&["--kernel", fw_path.as_str()]) .args(&["--disk", disks[0], disks[1]]) - .args(&["--net", "tap=,mac=,ip=192.168.2.1,mask=255.255.255.0"]) + .args(&[ + "--net", + "tap=,mac=12:34:56:78:90:ab,ip=192.168.2.1,mask=255.255.255.0", + ]) .spawn() .unwrap(); @@ -375,7 +385,10 @@ mod tests { .args(&["--memory", "size=512M"]) .args(&["--kernel", fw_path.as_str()]) .args(&["--disk", disks[0], disks[1]]) - .args(&["--net", "tap=,mac=,ip=192.168.2.1,mask=255.255.255.0"]) + .args(&[ + "--net", + "tap=,mac=12:34:56:78:90:ab,ip=192.168.2.1,mask=255.255.255.0", + ]) .spawn() .unwrap(); @@ -413,7 +426,7 @@ mod tests { .args(&["--memory", "size=512M"]) .args(&["--kernel", kernel_path.to_str().unwrap()]) .args(&["--disk", disks[0], disks[1]]) - .args(&["--net", "tap=,mac=,ip=192.168.2.1,mask=255.255.255.0"]) + .args(&["--net", "tap=,mac=12:34:56:78:90:ab,ip=192.168.2.1,mask=255.255.255.0"]) .args(&["--cmdline", "root=PARTUUID=3cb0e0a5-925d-405e-bc55-edf0cec8f10a console=tty0 console=ttyS0,115200n8 console=hvc0 quiet init=/usr/lib/systemd/systemd-bootchart initcall_debug tsc=reliable no_timer_check noreplace-smp cryptomgr.notests rootfstype=ext4,btrfs,xfs kvm-intel.nested=1 rw"]) .spawn() .unwrap(); @@ -455,7 +468,7 @@ mod tests { .args(&["--memory", "size=512M"]) .args(&["--kernel", kernel_path.to_str().unwrap()]) .args(&["--disk", disks[0], disks[1]]) - .args(&["--net", "tap=,mac=,ip=192.168.2.1,mask=255.255.255.0"]) + .args(&["--net", "tap=,mac=12:34:56:78:90:ab,ip=192.168.2.1,mask=255.255.255.0"]) .args(&["--cmdline", "root=PARTUUID=3cb0e0a5-925d-405e-bc55-edf0cec8f10a console=tty0 console=ttyS0,115200n8 console=hvc0 quiet init=/usr/lib/systemd/systemd-bootchart initcall_debug tsc=reliable no_timer_check noreplace-smp cryptomgr.notests rootfstype=ext4,btrfs,xfs kvm-intel.nested=1 rw"]) .spawn() .unwrap(); @@ -491,7 +504,10 @@ mod tests { .args(&["--memory", "size=512M"]) .args(&["--kernel", fw_path.as_str()]) .args(&["--disk", disks[0], disks[1]]) - .args(&["--net", "tap=,mac=,ip=192.168.2.1,mask=255.255.255.0"]) + .args(&[ + "--net", + "tap=,mac=12:34:56:78:90:ab,ip=192.168.2.1,mask=255.255.255.0", + ]) .spawn() .unwrap(); @@ -538,7 +554,7 @@ mod tests { .args(&["--memory", "size=512M,file=/dev/shm"]) .args(&["--kernel", kernel_path.to_str().unwrap()]) .args(&["--disk", disks[0], disks[1]]) - .args(&["--net", "tap=,mac=,ip=192.168.2.1,mask=255.255.255.0"]) + .args(&["--net", "tap=,mac=12:34:56:78:90:ab,ip=192.168.2.1,mask=255.255.255.0"]) .args(&[ "--fs", format!( @@ -608,7 +624,7 @@ mod tests { .args(&["--memory", "size=512M"]) .args(&["--kernel", kernel_path.to_str().unwrap()]) .args(&["--disk", disks[0], disks[1]]) - .args(&["--net", "tap=,mac=,ip=192.168.2.1,mask=255.255.255.0"]) + .args(&["--net", "tap=,mac=12:34:56:78:90:ab,ip=192.168.2.1,mask=255.255.255.0"]) .args(&[ "--pmem", format!( @@ -676,7 +692,7 @@ mod tests { .args(&["--memory", "size=512M"]) .args(&["--kernel", kernel_path.to_str().unwrap()]) .args(&["--disk", disks[1]]) - .args(&["--net", "tap=,mac=,ip=192.168.2.1,mask=255.255.255.0"]) + .args(&["--net", "tap=,mac=12:34:56:78:90:ab,ip=192.168.2.1,mask=255.255.255.0"]) .args(&[ "--pmem", format!( @@ -702,4 +718,42 @@ mod tests { Ok(()) }); } + + #[test] + fn test_multiple_network_interfaces() { + test_block!(tb, "", { + let (disks, fw_path) = prepare_files(); + let mut child = Command::new("target/debug/cloud-hypervisor") + .args(&["--cpus", "1"]) + .args(&["--memory", "size=512M"]) + .args(&["--kernel", fw_path.as_str()]) + .args(&["--disk", disks[0], disks[1]]) + .args(&[ + "--net", + "tap=,mac=12:34:56:78:90:ab,ip=192.168.2.1,mask=255.255.255.0", + "tap=,mac=8a:6b:6f:5a:de:ac,ip=192.168.3.1,mask=255.255.255.0", + "tap=,mac=fe:1f:9e:e1:60:f2,ip=192.168.4.1,mask=255.255.255.0", + ]) + .spawn() + .unwrap(); + + thread::sleep(std::time::Duration::new(10, 0)); + + // 3 network interfaces + default localhost ==> 4 interfaces + aver_eq!( + tb, + ssh_command("ip -o link | wc -l") + .trim() + .parse::() + .unwrap(), + 4 + ); + + ssh_command("sudo reboot"); + thread::sleep(std::time::Duration::new(10, 0)); + let _ = child.kill(); + let _ = child.wait(); + Ok(()) + }); + } } diff --git a/test_data/cloud-init/openstack/latest/user_data b/test_data/cloud-init/openstack/latest/user_data index 44cc74c57..7f1d11b22 100644 --- a/test_data/cloud-init/openstack/latest/user_data +++ b/test_data/cloud-init/openstack/latest/user_data @@ -10,7 +10,7 @@ write_files: permissions: 0644 content: | [Match] - Name=en* + MACAddress=12:34:56:78:90:ab [Network] Address=192.168.2.2/24 diff --git a/vmm/src/config.rs b/vmm/src/config.rs index 5cfe2bb50..c395672fc 100644 --- a/vmm/src/config.rs +++ b/vmm/src/config.rs @@ -59,7 +59,7 @@ pub struct VmParams<'a> { pub kernel: &'a str, pub cmdline: Option<&'a str>, pub disks: Vec<&'a str>, - pub net: Option<&'a str>, + pub net: Option>, pub rng: &'a str, pub fs: Option>, pub pmem: Option>, @@ -192,13 +192,9 @@ pub struct NetConfig<'a> { } impl<'a> NetConfig<'a> { - pub fn parse(net: Option<&'a str>) -> Result> { - if net.is_none() { - return Ok(None); - } - + pub fn parse(net: &'a str) -> Result { // Split the parameters based on the comma delimiter - let params_list: Vec<&str> = net.unwrap().split(',').collect(); + let params_list: Vec<&str> = net.split(',').collect(); let mut tap_str: &str = ""; let mut ip_str: &str = ""; @@ -235,7 +231,7 @@ impl<'a> NetConfig<'a> { mac = MacAddr::parse_str(mac_str).map_err(Error::ParseNetMacParam)?; } - Ok(Some(NetConfig { tap, ip, mask, mac })) + Ok(NetConfig { tap, ip, mask, mac }) } } @@ -348,7 +344,7 @@ pub struct VmConfig<'a> { pub kernel: KernelConfig<'a>, pub cmdline: CmdlineConfig, pub disks: Vec>, - pub net: Option>, + pub net: Option>>, pub rng: RngConfig<'a>, pub fs: Option>>, pub pmem: Option>>, @@ -361,6 +357,15 @@ impl<'a> VmConfig<'a> { disks.push(DiskConfig::parse(disk)?); } + let mut net: Option> = None; + if let Some(net_list) = &vm_params.net { + let mut net_config_list = Vec::new(); + for item in net_list.iter() { + net_config_list.push(NetConfig::parse(item)?); + } + net = Some(net_config_list); + } + let mut fs: Option> = None; if let Some(fs_list) = &vm_params.fs { let mut fs_config_list = Vec::new(); @@ -385,7 +390,7 @@ impl<'a> VmConfig<'a> { kernel: KernelConfig::parse(vm_params.kernel)?, cmdline: CmdlineConfig::parse(vm_params.cmdline)?, disks, - net: NetConfig::parse(vm_params.net)?, + net, rng: RngConfig::parse(vm_params.rng)?, fs, pmem, diff --git a/vmm/src/vm.rs b/vmm/src/vm.rs index 869fbc4dd..a7c0a17a4 100755 --- a/vmm/src/vm.rs +++ b/vmm/src/vm.rs @@ -567,28 +567,30 @@ impl DeviceManager { } // Add virtio-net if required - if let Some(net_cfg) = &vm_cfg.net { - let mut virtio_net_device: vm_virtio::Net; + if let Some(net_list_cfg) = &vm_cfg.net { + for net_cfg in net_list_cfg.iter() { + let mut virtio_net_device: vm_virtio::Net; - if let Some(tap_if_name) = net_cfg.tap { - let tap = Tap::open_named(tap_if_name).map_err(DeviceManagerError::OpenTap)?; - virtio_net_device = vm_virtio::Net::new_with_tap(tap, Some(&net_cfg.mac)) - .map_err(DeviceManagerError::CreateVirtioNet)?; - } else { - virtio_net_device = - vm_virtio::Net::new(net_cfg.ip, net_cfg.mask, Some(&net_cfg.mac)) + if let Some(tap_if_name) = net_cfg.tap { + let tap = Tap::open_named(tap_if_name).map_err(DeviceManagerError::OpenTap)?; + virtio_net_device = vm_virtio::Net::new_with_tap(tap, Some(&net_cfg.mac)) .map_err(DeviceManagerError::CreateVirtioNet)?; - } + } else { + virtio_net_device = + vm_virtio::Net::new(net_cfg.ip, net_cfg.mask, Some(&net_cfg.mac)) + .map_err(DeviceManagerError::CreateVirtioNet)?; + } - DeviceManager::add_virtio_pci_device( - Box::new(virtio_net_device), - memory.clone(), - allocator, - vm_fd, - &mut pci, - &mut mmio_bus, - &interrupt_info, - )?; + DeviceManager::add_virtio_pci_device( + Box::new(virtio_net_device), + memory.clone(), + allocator, + vm_fd, + &mut pci, + &mut mmio_bus, + &interrupt_info, + )?; + } } // Add virtio-rng if required