From 1b8b5ac179b7f4bab411d5b6b86a34cf838ca6b5 Mon Sep 17 00:00:00 2001 From: Rob Bradford Date: Fri, 15 May 2020 10:00:38 +0100 Subject: [PATCH] vhost-user_net, vm-virtio, vmm: Permit host MAC address setting Add a new "host_mac" parameter to "--net" and "--net-backend" and use this to set the MAC address on the tap interface. If no address is given one is randomly assigned and is stored in the config. Support for vhost-user-net self spawning was also included. Fixes: #1177 Signed-off-by: Rob Bradford --- src/main.rs | 48 +++++++++++++++++++-------------------- vhost_user_net/src/lib.rs | 21 ++++++++++++++--- vm-virtio/src/net.rs | 4 +++- vm-virtio/src/net_util.rs | 6 +++++ vmm/src/config.rs | 24 ++++++++++++++++---- vmm/src/device_manager.rs | 11 +++++++-- 6 files changed, 79 insertions(+), 35 deletions(-) diff --git a/src/main.rs b/src/main.rs index 4b41903ce..b1659e21e 100755 --- a/src/main.rs +++ b/src/main.rs @@ -798,11 +798,11 @@ mod unit_tests { false, ), ( - vec!["cloud-hypervisor", "--kernel", "/path/to/kernel", "--net", "mac=12:34:56:78:90:ab"], + vec!["cloud-hypervisor", "--kernel", "/path/to/kernel", "--net", "mac=12:34:56:78:90:ab,host_mac=34:56:78:90:ab:cd"], r#"{ "kernel": {"path": "/path/to/kernel"}, "net": [ - {"mac": "12:34:56:78:90:ab"} + {"mac": "12:34:56:78:90:ab", "host_mac": "34:56:78:90:ab:cd"} ] }"#, true, @@ -811,12 +811,12 @@ mod unit_tests { vec![ "cloud-hypervisor", "--kernel", "/path/to/kernel", "--net", - "mac=12:34:56:78:90:ab,tap=tap0", + "mac=12:34:56:78:90:ab,host_mac=34:56:78:90:ab:cd,tap=tap0", ], r#"{ "kernel": {"path": "/path/to/kernel"}, "net": [ - {"mac": "12:34:56:78:90:ab", "tap": "tap0"} + {"mac": "12:34:56:78:90:ab", "host_mac": "34:56:78:90:ab:cd", "tap": "tap0"} ] }"#, true, @@ -825,12 +825,12 @@ mod unit_tests { vec![ "cloud-hypervisor", "--kernel", "/path/to/kernel", "--net", - "mac=12:34:56:78:90:ab,tap=tap0,ip=1.2.3.4", + "mac=12:34:56:78:90:ab,host_mac=34:56:78:90:ab:cd,tap=tap0,ip=1.2.3.4", ], r#"{ "kernel": {"path": "/path/to/kernel"}, "net": [ - {"mac": "12:34:56:78:90:ab", "tap": "tap0", "ip": "1.2.3.4"} + {"mac": "12:34:56:78:90:ab", "host_mac": "34:56:78:90:ab:cd", "tap": "tap0", "ip": "1.2.3.4"} ] }"#, true, @@ -839,12 +839,12 @@ mod unit_tests { vec![ "cloud-hypervisor", "--kernel", "/path/to/kernel", "--net", - "mac=12:34:56:78:90:ab,tap=tap0,ip=1.2.3.4,mask=5.6.7.8", + "mac=12:34:56:78:90:ab,host_mac=34:56:78:90:ab:cd,tap=tap0,ip=1.2.3.4,mask=5.6.7.8", ], r#"{ "kernel": {"path": "/path/to/kernel"}, "net": [ - {"mac": "12:34:56:78:90:ab", "tap": "tap0", "ip": "1.2.3.4", "mask": "5.6.7.8"} + {"mac": "12:34:56:78:90:ab", "host_mac": "34:56:78:90:ab:cd", "tap": "tap0", "ip": "1.2.3.4", "mask": "5.6.7.8"} ] }"#, true, @@ -853,12 +853,12 @@ mod unit_tests { vec![ "cloud-hypervisor", "--kernel", "/path/to/kernel", "--net", - "mac=12:34:56:78:90:ab,tap=tap0,ip=1.2.3.4,mask=5.6.7.8,num_queues=4", + "mac=12:34:56:78:90:ab,host_mac=34:56:78:90:ab:cd,tap=tap0,ip=1.2.3.4,mask=5.6.7.8,num_queues=4", ], r#"{ "kernel": {"path": "/path/to/kernel"}, "net": [ - {"mac": "12:34:56:78:90:ab", "tap": "tap0", "ip": "1.2.3.4", "mask": "5.6.7.8", "num_queues": 4} + {"mac": "12:34:56:78:90:ab", "host_mac": "34:56:78:90:ab:cd", "tap": "tap0", "ip": "1.2.3.4", "mask": "5.6.7.8", "num_queues": 4} ] }"#, true, @@ -867,12 +867,12 @@ mod unit_tests { vec![ "cloud-hypervisor", "--kernel", "/path/to/kernel", "--net", - "mac=12:34:56:78:90:ab,tap=tap0,ip=1.2.3.4,mask=5.6.7.8,num_queues=4,queue_size=128", + "mac=12:34:56:78:90:ab,host_mac=34:56:78:90:ab:cd,tap=tap0,ip=1.2.3.4,mask=5.6.7.8,num_queues=4,queue_size=128", ], r#"{ "kernel": {"path": "/path/to/kernel"}, "net": [ - {"mac": "12:34:56:78:90:ab", "tap": "tap0", "ip": "1.2.3.4", "mask": "5.6.7.8", "num_queues": 4, "queue_size": 128} + {"mac": "12:34:56:78:90:ab", "host_mac": "34:56:78:90:ab:cd", "tap": "tap0", "ip": "1.2.3.4", "mask": "5.6.7.8", "num_queues": 4, "queue_size": 128} ] }"#, true, @@ -881,12 +881,12 @@ mod unit_tests { vec![ "cloud-hypervisor", "--kernel", "/path/to/kernel", "--net", - "mac=12:34:56:78:90:ab,tap=tap0,ip=1.2.3.4,mask=5.6.7.8,num_queues=2,queue_size=256", + "mac=12:34:56:78:90:ab,host_mac=34:56:78:90:ab:cd,tap=tap0,ip=1.2.3.4,mask=5.6.7.8,num_queues=2,queue_size=256", ], r#"{ "kernel": {"path": "/path/to/kernel"}, "net": [ - {"mac": "12:34:56:78:90:ab", "tap": "tap0", "ip": "1.2.3.4", "mask": "5.6.7.8"} + {"mac": "12:34:56:78:90:ab", "host_mac": "34:56:78:90:ab:cd", "tap": "tap0", "ip": "1.2.3.4", "mask": "5.6.7.8"} ] }"#, true, @@ -895,12 +895,12 @@ mod unit_tests { vec![ "cloud-hypervisor", "--kernel", "/path/to/kernel", "--net", - "mac=12:34:56:78:90:ab,tap=tap0,ip=1.2.3.4,mask=5.6.7.8", + "mac=12:34:56:78:90:ab,host_mac=34:56:78:90:ab:cd,tap=tap0,ip=1.2.3.4,mask=5.6.7.8", ], r#"{ "kernel": {"path": "/path/to/kernel"}, "net": [ - {"mac": "12:34:56:78:90:ab", "tap": "tap0", "ip": "1.2.3.4", "mask": "5.6.7.8", "num_queues": 2, "queue_size": 256} + {"mac": "12:34:56:78:90:ab", "host_mac": "34:56:78:90:ab:cd", "tap": "tap0", "ip": "1.2.3.4", "mask": "5.6.7.8", "num_queues": 2, "queue_size": 256} ] }"#, true, @@ -909,12 +909,12 @@ mod unit_tests { vec![ "cloud-hypervisor", "--kernel", "/path/to/kernel", "--net", - "mac=12:34:56:78:90:ab,tap=tap0,ip=1.2.3.4,mask=5.6.7.8,num_queues=2,queue_size=256,iommu=on", + "mac=12:34:56:78:90:ab,host_mac=34:56:78:90:ab:cd,tap=tap0,ip=1.2.3.4,mask=5.6.7.8,num_queues=2,queue_size=256,iommu=on", ], r#"{ "kernel": {"path": "/path/to/kernel"}, "net": [ - {"mac": "12:34:56:78:90:ab", "tap": "tap0", "ip": "1.2.3.4", "mask": "5.6.7.8", "num_queues": 2, "queue_size": 256, "iommu": true} + {"mac": "12:34:56:78:90:ab", "host_mac": "34:56:78:90:ab:cd", "tap": "tap0", "ip": "1.2.3.4", "mask": "5.6.7.8", "num_queues": 2, "queue_size": 256, "iommu": true} ] }"#, false, @@ -923,12 +923,12 @@ mod unit_tests { vec![ "cloud-hypervisor", "--kernel", "/path/to/kernel", "--net", - "mac=12:34:56:78:90:ab,tap=tap0,ip=1.2.3.4,mask=5.6.7.8,num_queues=2,queue_size=256,iommu=on", + "mac=12:34:56:78:90:ab,host_mac=34:56:78:90:ab:cd,tap=tap0,ip=1.2.3.4,mask=5.6.7.8,num_queues=2,queue_size=256,iommu=on", ], r#"{ "kernel": {"path": "/path/to/kernel"}, "net": [ - {"mac": "12:34:56:78:90:ab", "tap": "tap0", "ip": "1.2.3.4", "mask": "5.6.7.8", "num_queues": 2, "queue_size": 256, "iommu": true} + {"mac": "12:34:56:78:90:ab", "host_mac": "34:56:78:90:ab:cd", "tap": "tap0", "ip": "1.2.3.4", "mask": "5.6.7.8", "num_queues": 2, "queue_size": 256, "iommu": true} ], "iommu": true }"#, @@ -938,23 +938,23 @@ mod unit_tests { vec![ "cloud-hypervisor", "--kernel", "/path/to/kernel", "--net", - "mac=12:34:56:78:90:ab,tap=tap0,ip=1.2.3.4,mask=5.6.7.8,num_queues=2,queue_size=256,iommu=off", + "mac=12:34:56:78:90:ab,host_mac=34:56:78:90:ab:cd,tap=tap0,ip=1.2.3.4,mask=5.6.7.8,num_queues=2,queue_size=256,iommu=off", ], r#"{ "kernel": {"path": "/path/to/kernel"}, "net": [ - {"mac": "12:34:56:78:90:ab", "tap": "tap0", "ip": "1.2.3.4", "mask": "5.6.7.8", "num_queues": 2, "queue_size": 256, "iommu": false} + {"mac": "12:34:56:78:90:ab", "host_mac": "34:56:78:90:ab:cd", "tap": "tap0", "ip": "1.2.3.4", "mask": "5.6.7.8", "num_queues": 2, "queue_size": 256, "iommu": false} ] }"#, true, ), ( - vec!["cloud-hypervisor", "--kernel", "/path/to/kernel", "--memory", "shared=true", "--net", "mac=12:34:56:78:90:ab,vhost_user=true,socket=/tmp/socket"], + vec!["cloud-hypervisor", "--kernel", "/path/to/kernel", "--memory", "shared=true", "--net", "mac=12:34:56:78:90:ab,host_mac=34:56:78:90:ab:cd,vhost_user=true,socket=/tmp/socket"], r#"{ "kernel": {"path": "/path/to/kernel"}, "memory" : { "shared": true, "size": 536870912 }, "net": [ - {"mac": "12:34:56:78:90:ab", "vhost_user": true, "vhost_socket": "/tmp/socket"} + {"mac": "12:34:56:78:90:ab", "host_mac": "34:56:78:90:ab:cd", "vhost_user": true, "vhost_socket": "/tmp/socket"} ] }"#, true, diff --git a/vhost_user_net/src/lib.rs b/vhost_user_net/src/lib.rs index 6a4b5aa26..e2c9ad255 100644 --- a/vhost_user_net/src/lib.rs +++ b/vhost_user_net/src/lib.rs @@ -16,7 +16,7 @@ extern crate vmm; use epoll; use libc::{self, EAGAIN, EFD_NONBLOCK}; use log::*; -use net_util::Tap; +use net_util::{MacAddr, Tap}; use std::fmt; use std::io::Read; use std::io::{self}; @@ -221,13 +221,20 @@ pub struct VhostUserNetBackend { impl VhostUserNetBackend { fn new( ip_addr: Ipv4Addr, + host_mac: MacAddr, netmask: Ipv4Addr, num_queues: usize, queue_size: u16, ifname: Option<&str>, ) -> Result { - let mut taps = open_tap(ifname, Some(ip_addr), Some(netmask), num_queues / 2) - .map_err(Error::OpenTap)?; + let mut taps = open_tap( + ifname, + Some(ip_addr), + Some(netmask), + Some(host_mac), + num_queues / 2, + ) + .map_err(Error::OpenTap)?; let mut queues_per_thread = Vec::new(); let mut threads = Vec::new(); @@ -351,6 +358,7 @@ impl VhostUserBackend for VhostUserNetBackend { pub struct VhostUserNetBackendConfig { pub ip: Ipv4Addr, + pub host_mac: MacAddr, pub mask: Ipv4Addr, pub socket: String, pub num_queues: usize, @@ -365,6 +373,7 @@ impl VhostUserNetBackendConfig { parser .add("tap") .add("ip") + .add("host_mac") .add("mask") .add("queue_size") .add("num_queues") @@ -377,6 +386,10 @@ impl VhostUserNetBackendConfig { .convert("ip") .map_err(Error::FailedConfigParse)? .unwrap_or_else(|| Ipv4Addr::new(192, 168, 100, 1)); + let host_mac = parser + .convert("host_mac") + .map_err(Error::FailedConfigParse)? + .unwrap_or_else(MacAddr::local_random); let mask = parser .convert("mask") .map_err(Error::FailedConfigParse)? @@ -393,6 +406,7 @@ impl VhostUserNetBackendConfig { Ok(VhostUserNetBackendConfig { ip, + host_mac, mask, socket, num_queues, @@ -420,6 +434,7 @@ pub fn start_net_backend(backend_command: &str) { let net_backend = Arc::new(RwLock::new( VhostUserNetBackend::new( backend_config.ip, + backend_config.host_mac, backend_config.mask, backend_config.num_queues, backend_config.queue_size, diff --git a/vm-virtio/src/net.rs b/vm-virtio/src/net.rs index 65bc99bd4..6e67991d3 100644 --- a/vm-virtio/src/net.rs +++ b/vm-virtio/src/net.rs @@ -392,11 +392,13 @@ impl Net { ip_addr: Option, netmask: Option, guest_mac: Option, + host_mac: Option, iommu: bool, num_queues: usize, queue_size: u16, ) -> Result { - let taps = open_tap(if_name, ip_addr, netmask, num_queues / 2).map_err(Error::OpenTap)?; + let taps = open_tap(if_name, ip_addr, netmask, host_mac, num_queues / 2) + .map_err(Error::OpenTap)?; Self::new_with_tap(id, taps, guest_mac, iommu, num_queues, queue_size) } diff --git a/vm-virtio/src/net_util.rs b/vm-virtio/src/net_util.rs index 6f8aaf71a..dfe04b83d 100644 --- a/vm-virtio/src/net_util.rs +++ b/vm-virtio/src/net_util.rs @@ -119,6 +119,8 @@ pub enum Error { TapSetIp(TapError), /// Setting tap netmask failed. TapSetNetmask(TapError), + /// Setting MAC address failed + TapSetMac(TapError), /// Setting tap interface offload flags failed. TapSetOffload(TapError), /// Setting vnet header size failed. @@ -531,6 +533,7 @@ pub fn open_tap( if_name: Option<&str>, ip_addr: Option, netmask: Option, + host_mac: Option, num_rx_q: usize, ) -> Result> { let mut taps: Vec = Vec::new(); @@ -558,6 +561,9 @@ pub fn open_tap( if let Some(mask) = netmask { tap.set_netmask(mask).map_err(Error::TapSetNetmask)?; } + if let Some(mac) = host_mac { + tap.set_mac_addr(mac).map_err(Error::TapSetMac)? + } tap.enable().map_err(Error::TapEnable)?; tap.set_offload(flag).map_err(Error::TapSetOffload)?; diff --git a/vmm/src/config.rs b/vmm/src/config.rs index c94fa32bd..33969c55d 100644 --- a/vmm/src/config.rs +++ b/vmm/src/config.rs @@ -701,6 +701,8 @@ pub struct NetConfig { pub mask: Ipv4Addr, #[serde(default = "default_netconfig_mac")] pub mac: MacAddr, + #[serde(default = "default_netconfig_mac")] + pub host_mac: MacAddr, #[serde(default)] pub iommu: bool, #[serde(default = "default_netconfig_num_queues")] @@ -745,6 +747,7 @@ impl Default for NetConfig { ip: default_netconfig_ip(), mask: default_netconfig_mask(), mac: default_netconfig_mac(), + host_mac: default_netconfig_mac(), iommu: false, num_queues: default_netconfig_num_queues(), queue_size: default_netconfig_queue_size(), @@ -769,6 +772,7 @@ impl NetConfig { .add("ip") .add("mask") .add("mac") + .add("host_mac") .add("iommu") .add("queue_size") .add("num_queues") @@ -790,6 +794,10 @@ impl NetConfig { .convert("mac") .map_err(Error::ParseNetwork)? .unwrap_or_else(default_netconfig_mac); + let host_mac = parser + .convert("host_mac") + .map_err(Error::ParseNetwork)? + .unwrap_or_else(default_netconfig_mac); let iommu = parser .convert::("iommu") .map_err(Error::ParseNetwork)? @@ -816,6 +824,7 @@ impl NetConfig { ip, mask, mac, + host_mac, iommu, num_queues, queue_size, @@ -1641,17 +1650,19 @@ mod tests { fn test_net_parsing() -> Result<()> { // mac address is random assert_eq!( - NetConfig::parse("mac=de:ad:be:ef:12:34")?, + NetConfig::parse("mac=de:ad:be:ef:12:34,host_mac=12:34:de:ad:be:ef")?, NetConfig { mac: MacAddr::parse_str("de:ad:be:ef:12:34").unwrap(), + host_mac: MacAddr::parse_str("12:34:de:ad:be:ef").unwrap(), ..Default::default() } ); assert_eq!( - NetConfig::parse("mac=de:ad:be:ef:12:34,id=mynet0")?, + NetConfig::parse("mac=de:ad:be:ef:12:34,host_mac=12:34:de:ad:be:ef,id=mynet0")?, NetConfig { mac: MacAddr::parse_str("de:ad:be:ef:12:34").unwrap(), + host_mac: MacAddr::parse_str("12:34:de:ad:be:ef").unwrap(), id: Some("mynet0".to_owned()), ..Default::default() } @@ -1659,10 +1670,11 @@ mod tests { assert_eq!( NetConfig::parse( - "mac=de:ad:be:ef:12:34,tap=tap0,ip=192.168.100.1,mask=255.255.255.128" + "mac=de:ad:be:ef:12:34,host_mac=12:34:de:ad:be:ef,tap=tap0,ip=192.168.100.1,mask=255.255.255.128" )?, NetConfig { mac: MacAddr::parse_str("de:ad:be:ef:12:34").unwrap(), + host_mac: MacAddr::parse_str("12:34:de:ad:be:ef").unwrap(), tap: Some("tap0".to_owned()), ip: "192.168.100.1".parse().unwrap(), mask: "255.255.255.128".parse().unwrap(), @@ -1671,9 +1683,10 @@ mod tests { ); assert_eq!( - NetConfig::parse("mac=de:ad:be:ef:12:34,vhost_user=true,socket=/tmp/socket")?, + NetConfig::parse("mac=de:ad:be:ef:12:34,host_mac=12:34:de:ad:be:ef,vhost_user=true,socket=/tmp/socket")?, NetConfig { mac: MacAddr::parse_str("de:ad:be:ef:12:34").unwrap(), + host_mac: MacAddr::parse_str("12:34:de:ad:be:ef").unwrap(), vhost_user: true, vhost_socket: Some("/tmp/socket".to_owned()), ..Default::default() @@ -1681,9 +1694,10 @@ mod tests { ); assert_eq!( - NetConfig::parse("mac=de:ad:be:ef:12:34,num_queues=4,queue_size=1024,iommu=on")?, + NetConfig::parse("mac=de:ad:be:ef:12:34,host_mac=12:34:de:ad:be:ef,num_queues=4,queue_size=1024,iommu=on")?, NetConfig { mac: MacAddr::parse_str("de:ad:be:ef:12:34").unwrap(), + host_mac: MacAddr::parse_str("12:34:de:ad:be:ef").unwrap(), num_queues: 4, queue_size: 1024, iommu: true, diff --git a/vmm/src/device_manager.rs b/vmm/src/device_manager.rs index 19da383eb..68542b320 100644 --- a/vmm/src/device_manager.rs +++ b/vmm/src/device_manager.rs @@ -1463,8 +1463,13 @@ impl DeviceManager { .args(&[ "--net-backend", &format!( - "ip={},mask={},socket={},num_queues={},queue_size={}", - net_cfg.ip, net_cfg.mask, &sock, net_cfg.num_queues, net_cfg.queue_size + "ip={},mask={},socket={},num_queues={},queue_size={},host_mac={}", + net_cfg.ip, + net_cfg.mask, + &sock, + net_cfg.num_queues, + net_cfg.queue_size, + net_cfg.host_mac ), ]) .spawn() @@ -1529,6 +1534,7 @@ impl DeviceManager { None, None, Some(net_cfg.mac), + Some(net_cfg.host_mac), net_cfg.iommu, net_cfg.num_queues, net_cfg.queue_size, @@ -1543,6 +1549,7 @@ impl DeviceManager { Some(net_cfg.ip), Some(net_cfg.mask), Some(net_cfg.mac), + Some(net_cfg.host_mac), net_cfg.iommu, net_cfg.num_queues, net_cfg.queue_size,