diff --git a/Cargo.lock b/Cargo.lock index d047cb403..f588def97 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -460,6 +460,7 @@ dependencies = [ "kvm-ioctls 0.1.0 (git+https://github.com/rust-vmm/kvm-ioctls)", "libc 0.2.54 (registry+https://github.com/rust-lang/crates.io-index)", "linux-loader 0.1.0 (git+https://github.com/sameo/linux-loader)", + "net_util 0.1.0", "pci 0.1.0", "qcow 0.1.0", "vm-allocator 0.1.0", diff --git a/src/main.rs b/src/main.rs index 64380d766..ef8dbc221 100644 --- a/src/main.rs +++ b/src/main.rs @@ -37,6 +37,12 @@ fn main() { .help("Path to VM disk image") .takes_value(true), ) + .arg( + Arg::with_name("net") + .long("net") + .help("Network parameters \"tap=,mac=\"") + .takes_value(true), + ) .arg( Arg::with_name("cpus") .long("cpus") @@ -68,6 +74,11 @@ fn main() { .expect("Missing argument: disk"); let disk_path = disk_arg.as_path(); + let mut net_params = None; + if let Some(net) = cmd_arguments.value_of("net") { + net_params = Some(net.to_string()); + } + let mut vcpus = DEFAULT_VCPUS; if let Some(cpus) = cmd_arguments.value_of("cpus") { vcpus = cpus.parse::().unwrap(); @@ -81,7 +92,8 @@ fn main() { println!("VM [{} vCPUS {} MB of memory]", vcpus, memory); println!("Booting {:?}...", kernel_path); - let vm_config = VmConfig::new(kernel_path, disk_path, cmdline, vcpus, memory).unwrap(); + let vm_config = + VmConfig::new(kernel_path, disk_path, cmdline, net_params, vcpus, memory).unwrap(); vmm::boot_kernel(vm_config).unwrap(); } diff --git a/vmm/Cargo.toml b/vmm/Cargo.toml index c8009404f..8b182604e 100755 --- a/vmm/Cargo.toml +++ b/vmm/Cargo.toml @@ -11,6 +11,7 @@ epoll = "=4.0.1" kvm-bindings = "0.1" kvm-ioctls = { git = "https://github.com/rust-vmm/kvm-ioctls" } libc = ">=0.2.39" +net_util = { path = "../net_util" } pci = {path = "../pci"} qcow = { path = "../qcow" } linux-loader = { git = "https://github.com/sameo/linux-loader" } diff --git a/vmm/src/vm.rs b/vmm/src/vm.rs index 103404538..a06991f6a 100755 --- a/vmm/src/vm.rs +++ b/vmm/src/vm.rs @@ -15,6 +15,7 @@ extern crate epoll; extern crate kvm_ioctls; extern crate libc; extern crate linux_loader; +extern crate net_util; extern crate vm_allocator; extern crate vm_memory; extern crate vm_virtio; @@ -25,6 +26,7 @@ use kvm_ioctls::*; use libc::{c_void, siginfo_t, EFD_NONBLOCK}; use linux_loader::cmdline; use linux_loader::loader::KernelLoader; +use net_util::{MacAddr, Tap}; use pci::{PciConfigIo, PciDevice, PciInterruptPin, PciRoot}; use std::ffi::CString; use std::fs::{File, OpenOptions}; @@ -153,8 +155,14 @@ pub enum Error { /// Cannot create virtio-blk device CreateVirtioBlock(io::Error), + /// Cannot create virtio-net device + CreateVirtioNet(vm_virtio::net::Error), + /// Cannot create the system allocator CreateSystemAllocator, + + /// Failed parsing network parameters + ParseNetworkParameters, } pub type Result = result::Result; @@ -219,7 +227,7 @@ pub struct VmConfig<'a> { disk_path: &'a Path, cmdline: cmdline::Cmdline, cmdline_addr: GuestAddress, - + net_params: Option, memory_size: GuestUsize, vcpu_count: u8, } @@ -229,6 +237,7 @@ impl<'a> VmConfig<'a> { kernel_path: &'a Path, disk_path: &'a Path, cmdline_str: String, + net_params: Option, vcpus: u8, memory_size: GuestUsize, ) -> Result { @@ -240,12 +249,37 @@ impl<'a> VmConfig<'a> { disk_path, cmdline, cmdline_addr: CMDLINE_OFFSET, + net_params, memory_size, vcpu_count: vcpus, }) } } +fn parse_net_params(net_params: &str) -> Result<(&str, &str)> { + // Split the parameters based on the comma delimiter + let params_list: Vec<&str> = net_params.split(',').collect(); + + let mut if_name: Option<&str> = None; + let mut mac: Option<&str> = None; + + for param in params_list.iter() { + if param.starts_with("tap=") { + if_name = Some(¶m[4..]); + } else if param.starts_with("mac=") { + mac = Some(¶m[4..]); + } + } + + if let Some(if_name) = if_name { + if let Some(mac) = mac { + return Ok((if_name, mac)); + } + } + + Err(Error::ParseNetworkParameters) +} + struct DeviceManager { io_bus: devices::Bus, mmio_bus: devices::Bus, @@ -291,18 +325,18 @@ impl DeviceManager { .open(&vm_cfg.disk_path) .map_err(Error::Disk)?; + // Add virtio-blk let virtio_block_device = vm_virtio::Block::new(raw_img, false).map_err(Error::CreateVirtioBlock)?; let virtio_block_device = Box::new(virtio_block_device); - let mut virtio_pci_device = - VirtioPciDevice::new(memory, virtio_block_device).map_err(|_| Error::VirtioDevice)?; + let mut virtio_pci_device = VirtioPciDevice::new(memory.clone(), virtio_block_device) + .map_err(|_| Error::VirtioDevice)?; let bars = virtio_pci_device .allocate_bars(allocator) .map_err(Error::AllocateBars)?; for (event, addr, _) in virtio_pci_device.ioeventfds() { let io_addr = IoEventAddress::Mmio(addr); - println!("Register ioevent at 0x{:x}", addr); vm_fd .register_ioevent(event.as_raw_fd(), &io_addr, NoDatamatch) .map_err(Error::RegisterIoevent)?; @@ -323,6 +357,44 @@ impl DeviceManager { .add_device(virtio_pci_device.clone(), &mut mmio_bus, bars) .map_err(Error::AddPciDevice)?; + // Add virtio-net if required + if let Some(net_params) = &vm_cfg.net_params { + if let Ok((tap_if_name, mac_addr)) = parse_net_params(net_params) { + let mac = MacAddr::parse_str(mac_addr).unwrap(); + let tap = Tap::open_named(tap_if_name).unwrap(); + let virtio_net_device = vm_virtio::Net::new_with_tap(tap, Some(&mac)) + .map_err(Error::CreateVirtioNet)?; + let virtio_net_device = Box::new(virtio_net_device); + let mut virtio_pci_device = VirtioPciDevice::new(memory.clone(), virtio_net_device) + .map_err(|_| Error::VirtioDevice)?; + let bars = virtio_pci_device + .allocate_bars(allocator) + .map_err(Error::AllocateBars)?; + + for (event, addr, _) in virtio_pci_device.ioeventfds() { + let io_addr = IoEventAddress::Mmio(addr); + vm_fd + .register_ioevent(event.as_raw_fd(), &io_addr, NoDatamatch) + .map_err(Error::RegisterIoevent)?; + } + + // Assign IRQ to the virtio-blk device + let irqfd = EventFd::new(EFD_NONBLOCK).map_err(Error::EventFd)?; + let irq_num = allocator.allocate_irq().ok_or(Error::AllocateIrq)?; + vm_fd + .register_irqfd(irqfd.as_raw_fd(), irq_num) + .map_err(Error::Irq)?; + // Let's use irq line INTA for now. + virtio_pci_device.assign_irq(irqfd, irq_num as u32, PciInterruptPin::IntA); + + let virtio_pci_device = Arc::new(Mutex::new(virtio_pci_device)); + + pci_root + .add_device(virtio_pci_device.clone(), &mut mmio_bus, bars) + .map_err(Error::AddPciDevice)?; + } + } + let pci = Arc::new(Mutex::new(PciConfigIo::new(pci_root))); Ok(DeviceManager {