vmm: Add support for letting the VMM create the TAP interface

Until now, the only way to get some networking with cloud-hypervisor
was to let the user create a TAP interface first, and then to provide
the name of this interface to the VMM.

This patch extend the previous behavior by adding the support for the
creation of a brand new TAP interface from the VMM itself. In case no
interface name is provided through "tap=<if_name>", we will assume
the user wants the VMM to create and set the interface on its behalf,
no matter the value of other parameters (ip, mask, and mac).
In this same scenario, because the user expects the VMM to create the
TAP interface, he can also provide the associated IP address and subnet
mask associated with it. In case those values are not provided, some
default ones will be picked.

No matter the value of "tap", the MAC address will always be set, and
if no value is provided, the VMM will come up with a default value for
it.

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
This commit is contained in:
Sebastien Boeuf 2019-05-09 11:21:15 -07:00 committed by Samuel Ortiz
parent 0c4c330843
commit 5934f30fde
2 changed files with 67 additions and 17 deletions

View File

@ -40,7 +40,7 @@ fn main() {
.arg(
Arg::with_name("net")
.long("net")
.help("Network parameters \"tap=<if_name>,mac=<mac_addr>\"")
.help("Network parameters \"tap=<if_name>,ip=<ip_addr>,mask=<net_mask>,mac=<mac_addr>\"")
.takes_value(true),
)
.arg(
@ -81,8 +81,12 @@ fn main() {
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());
if cmd_arguments.is_present("net") {
if let Some(net) = cmd_arguments.value_of("net") {
net_params = Some(net.to_string());
} else {
net_params = Some(String::new())
}
}
let rng_path = match cmd_arguments.occurrences_of("rng") {

View File

@ -31,6 +31,7 @@ use pci::{PciConfigIo, PciDevice, PciInterruptPin, PciRoot};
use std::ffi::CString;
use std::fs::{File, OpenOptions};
use std::io::{self, stdout};
use std::net::Ipv4Addr;
use std::os::unix::io::{AsRawFd, RawFd};
use std::path::Path;
use std::sync::{Arc, Barrier, Mutex};
@ -166,6 +167,9 @@ pub enum Error {
/// Failed parsing network parameters
ParseNetworkParameters,
/// Cannot open tap interface
OpenTap(net_util::TapError),
}
pub type Result<T> = result::Result<T, Error>;
@ -262,28 +266,59 @@ impl<'a> VmConfig<'a> {
}
}
fn parse_net_params(net_params: &str) -> Result<(&str, &str)> {
#[derive(Debug)]
struct NetParams<'a> {
tap_if_name: Option<&'a str>,
ip_addr: Ipv4Addr,
net_mask: Ipv4Addr,
mac_addr: MacAddr,
}
fn parse_net_params(net_params: &str) -> Result<NetParams> {
// 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;
let mut tap: &str = "";
let mut ip: &str = "";
let mut mask: &str = "";
let mut mac: &str = "";
for param in params_list.iter() {
if param.starts_with("tap=") {
if_name = Some(&param[4..]);
tap = &param[4..];
} else if param.starts_with("ip=") {
ip = &param[3..];
} else if param.starts_with("mask=") {
mask = &param[5..];
} else if param.starts_with("mac=") {
mac = Some(&param[4..]);
mac = &param[4..];
}
}
if let Some(if_name) = if_name {
if let Some(mac) = mac {
return Ok((if_name, mac));
}
let mut tap_if_name: Option<&str> = None;
let mut ip_addr: Ipv4Addr = "192.168.249.1".parse().unwrap();
let mut net_mask: Ipv4Addr = "255.255.255.0".parse().unwrap();
let mut mac_addr: MacAddr = MacAddr::parse_str("12:34:56:78:90:ab").unwrap();
if !tap.is_empty() {
tap_if_name = Some(tap);
}
if !ip.is_empty() {
ip_addr = ip.parse().unwrap();
}
if !mask.is_empty() {
net_mask = mask.parse().unwrap();
}
if !mac.is_empty() {
mac_addr = MacAddr::parse_str(mac).unwrap();
}
Err(Error::ParseNetworkParameters)
Ok(NetParams {
tap_if_name,
ip_addr,
net_mask,
mac_addr,
})
}
struct DeviceManager {
@ -346,11 +381,22 @@ impl DeviceManager {
// 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))
if let Ok(net_params) = parse_net_params(net_params) {
let mut virtio_net_device: vm_virtio::Net;
if let Some(tap_if_name) = net_params.tap_if_name {
let tap = Tap::open_named(tap_if_name).map_err(Error::OpenTap)?;
virtio_net_device =
vm_virtio::Net::new_with_tap(tap, Some(&net_params.mac_addr))
.map_err(Error::CreateVirtioNet)?;
} else {
virtio_net_device = vm_virtio::Net::new(
net_params.ip_addr,
net_params.net_mask,
Some(&net_params.mac_addr),
)
.map_err(Error::CreateVirtioNet)?;
}
DeviceManager::add_virtio_pci_device(
Box::new(virtio_net_device),