From 66aa0743f087f9598835a0c13bab76535e1ae5a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20G=C3=BCntner?= Date: Mon, 22 Sep 2025 10:43:03 +0200 Subject: [PATCH] vmm: allow net devices without ip and mask MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This change enables easier integration with third-party tools by removing the requirement for a dummy IP address when configuring tap devices. The modification applies to both CLI and API interactions. Previously, cloud-hypervisor would automatically set a default static IP address (192.168.249.1) if none was provided. This could lead to: * multiple devices without explicit IP configurations would end up with the same default IP * unnecessary inclusion of this IP in firewall rules * the IP address could clash with host networking and routing This introduces a new constraint: When providing an IP, the mask must also be provided. Removes warnings introduced in #7179. Closes issue #7083. Signed-off-by: Maximilian Güntner --- option_parser/src/lib.rs | 2 +- src/main.rs | 14 --------- vmm/src/config.rs | 63 +++++++++++++++++++++++++++++++-------- vmm/src/device_manager.rs | 8 ++--- vmm/src/vm_config.rs | 22 ++------------ 5 files changed, 58 insertions(+), 51 deletions(-) diff --git a/option_parser/src/lib.rs b/option_parser/src/lib.rs index ff3d9ffff..d28c2db5e 100644 --- a/option_parser/src/lib.rs +++ b/option_parser/src/lib.rs @@ -36,7 +36,7 @@ struct OptionParserValue { requires_value: bool, } -#[derive(Error, Debug)] +#[derive(Debug, Error)] pub enum OptionParserError { #[error("unknown option: {0}")] UnknownOption(String), diff --git a/src/main.rs b/src/main.rs index 92a4b085d..9bbc4a05a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1336,20 +1336,6 @@ mod unit_tests { }"#, true, ), - ( - vec![ - "cloud-hypervisor", "--kernel", "/path/to/kernel", - "--net", - "mac=12:34:56:78:90:ab,host_mac=34:56:78:90:ab:cd,tap=tap0,ip=1.2.3.4", - ], - r#"{ - "payload": {"kernel": "/path/to/kernel"}, - "net": [ - {"mac": "12:34:56:78:90:ab", "host_mac": "34:56:78:90:ab:cd", "tap": "tap0", "ip": "1.2.3.4"} - ] - }"#, - true, - ), ( vec![ "cloud-hypervisor", "--kernel", "/path/to/kernel", diff --git a/vmm/src/config.rs b/vmm/src/config.rs index 366b369e7..d04536f60 100644 --- a/vmm/src/config.rs +++ b/vmm/src/config.rs @@ -357,6 +357,10 @@ pub enum ValidationError { InvalidIvshmemPath, #[error("Payload configuration is not bootable")] PayloadError(#[from] PayloadConfigError), + #[error("Mask provided without an IP")] + MaskProvidedWithoutIp, + #[error("IP provided without a mask")] + IpProvidedWithoutMask, } type ValidationResult = std::result::Result; @@ -1331,14 +1335,9 @@ impl NetConfig { parser.parse(net).map_err(Error::ParseNetwork)?; let tap = parser.get("tap"); - let ip = parser - .convert("ip") - .map_err(Error::ParseNetwork)? - .unwrap_or_else(default_netconfig_ip); - let mask = parser - .convert("mask") - .map_err(Error::ParseNetwork)? - .unwrap_or_else(default_netconfig_mask); + let ip = parser.convert("ip").map_err(Error::ParseNetwork)?; + let mask = parser.convert("mask").map_err(Error::ParseNetwork)?; + let mac = parser .convert("mac") .map_err(Error::ParseNetwork)? @@ -1515,6 +1514,14 @@ impl NetConfig { return Err(ValidationError::NoHardwareChecksumOffload); } + if self.mask.is_some() && self.ip.is_none() { + return Err(ValidationError::MaskProvidedWithoutIp); + } + + if self.ip.is_some() && self.mask.is_none() { + return Err(ValidationError::IpProvidedWithoutMask); + } + Ok(()) } } @@ -3177,7 +3184,6 @@ impl Drop for VmConfig { #[cfg(test)] mod tests { use std::fs::File; - use std::net::{IpAddr, Ipv4Addr}; use std::os::unix::io::AsRawFd; use net_util::MacAddr; @@ -3507,8 +3513,8 @@ mod tests { fn net_fixture() -> NetConfig { NetConfig { tap: None, - ip: IpAddr::V4(Ipv4Addr::new(192, 168, 249, 1)), - mask: IpAddr::V4(Ipv4Addr::new(255, 255, 255, 0)), + ip: None, + mask: None, mac: MacAddr::parse_str("de:ad:be:ef:12:34").unwrap(), host_mac: Some(MacAddr::parse_str("12:34:de:ad:be:ef").unwrap()), mtu: None, @@ -3550,8 +3556,8 @@ mod tests { )?, NetConfig { tap: Some("tap0".to_owned()), - ip: "192.168.100.1".parse().unwrap(), - mask: "255.255.255.128".parse().unwrap(), + ip: Some("192.168.100.1".parse().unwrap()), + mask: Some("255.255.255.128".parse().unwrap()), ..net_fixture() } ); @@ -3589,6 +3595,15 @@ mod tests { } ); + assert_eq!( + NetConfig::parse("mac=de:ad:be:ef:12:34,mask=255.255.255.0")?, + NetConfig { + mask: Some("255.255.255.0".parse().unwrap()), + host_mac: None, + ..net_fixture() + } + ); + Ok(()) } @@ -4289,6 +4304,28 @@ mod tests { Err(ValidationError::NoHardwareChecksumOffload) ); + let mut invalid_config = valid_config.clone(); + invalid_config.net = Some(vec![NetConfig { + ip: None, + mask: Some("255.255.255.0".parse().unwrap()), + ..net_fixture() + }]); + assert_eq!( + invalid_config.validate(), + Err(ValidationError::MaskProvidedWithoutIp) + ); + + let mut invalid_config = valid_config.clone(); + invalid_config.net = Some(vec![NetConfig { + ip: Some("192.1.33.7".parse().unwrap()), + mask: None, + ..net_fixture() + }]); + assert_eq!( + invalid_config.validate(), + Err(ValidationError::IpProvidedWithoutMask) + ); + let mut invalid_config = valid_config.clone(); invalid_config.fs = Some(vec![fs_fixture()]); assert_eq!( diff --git a/vmm/src/device_manager.rs b/vmm/src/device_manager.rs index 40a3d27ce..7a5adb1c3 100644 --- a/vmm/src/device_manager.rs +++ b/vmm/src/device_manager.rs @@ -2904,8 +2904,8 @@ impl DeviceManager { virtio_devices::Net::new( id.clone(), Some(tap_if_name), - Some(net_cfg.ip), - Some(net_cfg.mask), + net_cfg.ip, + net_cfg.mask, Some(net_cfg.mac), &mut net_cfg.host_mac, net_cfg.mtu, @@ -2955,8 +2955,8 @@ impl DeviceManager { virtio_devices::Net::new( id.clone(), None, - Some(net_cfg.ip), - Some(net_cfg.mask), + net_cfg.ip, + net_cfg.mask, Some(net_cfg.mac), &mut net_cfg.host_mac, net_cfg.mtu, diff --git a/vmm/src/vm_config.rs b/vmm/src/vm_config.rs index 7a581307e..76fe2d2b2 100644 --- a/vmm/src/vm_config.rs +++ b/vmm/src/vm_config.rs @@ -2,7 +2,7 @@ // // SPDX-License-Identifier: Apache-2.0 // -use std::net::{IpAddr, Ipv4Addr}; +use std::net::IpAddr; use std::path::{Path, PathBuf}; #[cfg(feature = "fw_cfg")] use std::str::FromStr; @@ -303,10 +303,8 @@ pub fn default_diskconfig_queue_size() -> u16 { pub struct NetConfig { #[serde(default = "default_netconfig_tap")] pub tap: Option, - #[serde(default = "default_netconfig_ip")] - pub ip: IpAddr, - #[serde(default = "default_netconfig_mask")] - pub mask: IpAddr, + pub ip: Option, + pub mask: Option, #[serde(default = "default_netconfig_mac")] pub mac: MacAddr, #[serde(default)] @@ -352,20 +350,6 @@ pub fn default_netconfig_tap() -> Option { None } -pub fn default_netconfig_ip() -> IpAddr { - warn!( - "Deprecation warning: No IP address provided. A default IP address is assigned. This behavior will be deprecated soon." - ); - IpAddr::V4(Ipv4Addr::new(192, 168, 249, 1)) -} - -pub fn default_netconfig_mask() -> IpAddr { - warn!( - "Deprecation warning: No network mask provided. A default network mask is assigned. This behavior will be deprecated soon." - ); - IpAddr::V4(Ipv4Addr::new(255, 255, 255, 0)) -} - pub fn default_netconfig_mac() -> MacAddr { MacAddr::local_random() }