Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@
- New optional `version` field to PUT requests towards `/mmds/config` to
configure MMDS version. Accepted values are `V1` and `V2` and default is
`V1`. Known limitation: `V2` does not currently work after snapshot load.
- Mandatory `network_interfaces` field to PUT requests towards
`/mmds/config` which contains a list of network interface IDs capable of
forwarding packets to MMDS.

### Changed

Expand All @@ -49,6 +52,10 @@
`X-metadata-token` header when using V2.
- Allow `PUT` requests to MMDS in order to generate a session token
to be used for future `GET` requests when version 2 is used.
- Remove `allow_mmds_requests` field from the request body that attaches network
interfaces. Specifying interfaces that allow forwarding requests to MMDS is done
by adding the network interface's ID to the `network_interfaces` field of PUT
`/mmds/config` request's body.

### Fixed

Expand Down
6 changes: 4 additions & 2 deletions src/api_server/src/parsed_request.rs
Original file line number Diff line number Diff line change
Expand Up @@ -836,7 +836,10 @@ pub(crate) mod tests {
assert!(ParsedRequest::try_from_request(&req).is_ok());

// `/mmds/config`
let body = "{\"ipv4_address\":\"169.254.170.2\"}";
let body = "{ \
\"ipv4_address\": \"169.254.170.2\", \
\"network_interfaces\": [\"iface0\"] \
}";
sender
.write_all(http_request("PUT", "/mmds/config", Some(&body)).as_bytes())
.unwrap();
Expand All @@ -853,7 +856,6 @@ pub(crate) mod tests {
\"iface_id\": \"string\", \
\"guest_mac\": \"12:34:56:78:9a:BC\", \
\"host_dev_name\": \"string\", \
\"allow_mmds_requests\": true, \
\"rx_rate_limiter\": { \
\"bandwidth\": { \
\"size\": 0, \
Expand Down
29 changes: 24 additions & 5 deletions src/api_server/src/request/mmds.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,19 +73,38 @@ mod tests {

// Test `config` path.
let body = r#"{
"ipv4_address": "169.254.170.2"
"version": "V2",
"ipv4_address": "169.254.170.2",
"network_interfaces": []
}"#;
let config_path = "config";
assert!(parse_put_mmds(&Body::new(body), Some(&config_path)).is_ok());

let body = r#"{
"ipv4_address": ""
"network_interfaces": []
}"#;
let config_path = "config";
assert!(parse_put_mmds(&Body::new(body), Some(&config_path)).is_ok());

let body = r#"{
"version": "foo",
"ipv4_address": "169.254.170.2",
"network_interfaces": []
}"#;
let config_path = "config";
assert!(parse_put_mmds(&Body::new(body), Some(&config_path)).is_err());

let body = r#"{
"version": "V2"
}"#;
let config_path = "config";
assert!(parse_put_mmds(&Body::new(body), Some(&config_path)).is_err());

// Equivalent to reset the mmds configuration.
let empty_body = r#"{}"#;
assert!(parse_put_mmds(&Body::new(empty_body), Some(&config_path)).is_ok());
let body = r#"{
"ipv4_address": "",
"network_interfaces": []
}"#;
assert!(parse_put_mmds(&Body::new(body), Some(&config_path)).is_err());

let invalid_config_body = r#"{
"invalid_config": "invalid_value"
Expand Down
3 changes: 1 addition & 2 deletions src/api_server/src/request/net.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,8 +74,7 @@ mod tests {
let body = r#"{
"iface_id": "foo",
"host_dev_name": "bar",
"guest_mac": "12:34:56:78:9A:BC",
"allow_mmds_requests": false
"guest_mac": "12:34:56:78:9A:BC"
}"#;
// 1. Exercise infamous "The id from the path does not match id from the body!".
assert!(parse_put_net(&Body::new(body), Some(&"bar")).is_err());
Expand Down
22 changes: 14 additions & 8 deletions src/api_server/swagger/firecracker.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -963,6 +963,8 @@ definitions:
type: object
description:
Defines the MMDS configuration.
required:
- network_interfaces
properties:
version:
description: Enumeration indicating the MMDS version to be configured.
Expand All @@ -971,6 +973,18 @@ definitions:
- V1
- V2
default: V1
network_interfaces:
description:
List of the network interface IDs capable of forwarding packets to
the MMDS. Network interface IDs mentioned must be valid at the time
of this request. The net device model will reply to HTTP GET requests
sent to the MMDS address via the interfaces mentioned. In this
case, both ARP requests and TCP segments heading to `ipv4_address`
are intercepted by the device model, and do not reach the associated
TAP device.
type: array
items:
type: string
ipv4_address:
type: string
format: "169.254.([1-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-4]).([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])"
Expand All @@ -985,14 +999,6 @@ definitions:
- host_dev_name
- iface_id
properties:
allow_mmds_requests:
type: boolean
description:
If this field is set, the device model will reply to HTTP GET
requests sent to the MMDS address via this interface. In this case,
both ARP requests for 169.254.169.254 and TCP segments heading to the
same address are intercepted by the device model, and do not reach
the associated TAP device.
guest_mac:
type: string
host_dev_name:
Expand Down
17 changes: 10 additions & 7 deletions src/devices/src/virtio/net/device.rs
Original file line number Diff line number Diff line change
Expand Up @@ -220,18 +220,21 @@ impl Net {
self.tap.if_name_as_str().to_string()
}

/// Says if this device supports MMDS.
pub fn mmds_enabled(&self) -> bool {
self.mmds_ns.is_some()
}

/// Sets MMDS endpoint IPv4 address, if the device supports MMDS.
pub fn set_mmds_ipv4_addr(&mut self, ipv4_addr: Ipv4Addr) {
/// Configures the `MmdsNetworkStack` to allow device to forward MMDS requests.
/// If the device already supports MMDS, updates the IPv4 address.
pub fn configure_mmds_network_stack(&mut self, ipv4_addr: Ipv4Addr) {
if let Some(mmds_ns) = self.mmds_ns.as_mut() {
mmds_ns.set_ipv4_addr(ipv4_addr);
} else {
self.mmds_ns = Some(MmdsNetworkStack::new_with_defaults(Some(ipv4_addr)))
}
}

/// Disables the `MmdsNetworkStack` to prevent device to forward MMDS requests.
pub fn disable_mmds_network_stack(&mut self) {
self.mmds_ns = None
}

/// Provides a reference to the configured RX rate limiter.
pub fn rx_rate_limiter(&self) -> &RateLimiter {
&self.rx_rate_limiter
Expand Down
1 change: 0 additions & 1 deletion src/vmm/src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1273,7 +1273,6 @@ pub mod tests {
guest_mac: None,
rx_rate_limiter: None,
tx_rate_limiter: None,
allow_mmds_requests: true,
};

let mut cmdline = default_kernel_cmdline();
Expand Down
1 change: 0 additions & 1 deletion src/vmm/src/device_manager/persist.rs
Original file line number Diff line number Diff line change
Expand Up @@ -569,7 +569,6 @@ mod tests {
guest_mac: None,
rx_rate_limiter: None,
tx_rate_limiter: None,
allow_mmds_requests: true,
};
insert_net_device(
&mut vmm,
Expand Down
1 change: 0 additions & 1 deletion src/vmm/src/persist.rs
Original file line number Diff line number Diff line change
Expand Up @@ -564,7 +564,6 @@ mod tests {
guest_mac: None,
rx_rate_limiter: None,
tx_rate_limiter: None,
allow_mmds_requests: true,
};
insert_net_device(
&mut vmm,
Expand Down
92 changes: 55 additions & 37 deletions src/vmm/src/resources.rs
Original file line number Diff line number Diff line change
Expand Up @@ -301,18 +301,8 @@ impl VmResources {
&mut self,
body: NetworkInterfaceConfig,
) -> Result<NetworkInterfaceError> {
self.net_builder.build(body).map(|net_device| {
// Update `Net` device `MmdsNetworkStack` IPv4 address.
match &self.mmds_config {
Some(cfg) => cfg.ipv4_addr().map_or((), |ipv4_addr| {
net_device
.lock()
.expect("Poisoned lock")
.set_mmds_ipv4_addr(ipv4_addr);
}),
None => (),
};
})
let _ = self.net_builder.build(body)?;
Ok(())
Comment on lines +304 to +305
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit:

Suggested change
let _ = self.net_builder.build(body)?;
Ok(())
let _ = self.net_builder.build(body)

}

/// Sets a vsock device to be attached when the VM starts.
Expand All @@ -326,28 +316,15 @@ impl VmResources {
config: MmdsConfig,
instance_id: &str,
) -> Result<MmdsConfigError> {
// Check IPv4 address validity.
let ipv4_addr = match config.ipv4_addr() {
Some(ipv4_addr) if is_link_local_valid(ipv4_addr) => Ok(ipv4_addr),
None => Ok(MmdsNetworkStack::default_ipv4_addr()),
_ => Err(MmdsConfigError::InvalidIpv4Addr),
}?;

// Update existing built network device `MmdsNetworkStack` IPv4 address.
for net_device in self.net_builder.iter_mut() {
net_device
.lock()
.expect("Poisoned lock")
.set_mmds_ipv4_addr(ipv4_addr);
}

self.set_mmds_network_stack_config(&config)?;
self.set_mmds_version(config.version, instance_id)?;

self.mmds_config = Some(MmdsConfig {
version: config.version,
ipv4_address: config
.ipv4_addr()
.or_else(|| Some(MmdsNetworkStack::default_ipv4_addr())),
network_interfaces: config.network_interfaces,
});

Ok(())
Expand All @@ -366,6 +343,47 @@ impl VmResources {
mmds_lock.set_aad(instance_id);
Ok(())
}

// Updates MMDS Network Stack for network interfaces to allow forwarding
// requests to MMDS (or not).
fn set_mmds_network_stack_config(&mut self, config: &MmdsConfig) -> Result<MmdsConfigError> {
// Check IPv4 address validity.
let ipv4_addr = match config.ipv4_addr() {
Some(ipv4_addr) if is_link_local_valid(ipv4_addr) => Ok(ipv4_addr),
None => Ok(MmdsNetworkStack::default_ipv4_addr()),
_ => Err(MmdsConfigError::InvalidIpv4Addr),
}?;

let network_interfaces = config.network_interfaces();
// Ensure that at least one network ID is specified.
if network_interfaces.is_empty() {
return Err(MmdsConfigError::EmptyNetworkIfaceList);
}

// Ensure all interface IDs specified correspond to existing net devices.
if !network_interfaces.iter().all(|id| {
self.net_builder
.iter()
.map(|device| device.lock().expect("Poisoned lock").id().clone())
.any(|x| &x == id)
}) {
return Err(MmdsConfigError::InvalidNetworkInterfaceId);
}

// Create `MmdsNetworkStack` and configure the IPv4 address for
// existing built network devices whose names are defined in the
// network interface ID list.
for net_device in self.net_builder.iter_mut() {
let mut net_device_lock = net_device.lock().expect("Poisoned lock");
if network_interfaces.contains(net_device_lock.id()) {
net_device_lock.configure_mmds_network_stack(ipv4_addr);
} else {
net_device_lock.disable_mmds_network_stack();
}
}

Ok(())
}
}

impl From<&VmResources> for VmmConfig {
Expand Down Expand Up @@ -421,7 +439,6 @@ mod tests {
guest_mac: Some(MacAddr::parse_str("01:23:45:67:89:0a").unwrap()),
rx_rate_limiter: Some(RateLimiterConfig::default()),
tx_rate_limiter: Some(RateLimiterConfig::default()),
allow_mmds_requests: false,
}
}

Expand Down Expand Up @@ -751,8 +768,7 @@ mod tests {
"network-interfaces": [
{{
"iface_id": "netif",
"host_dev_name": "hostname8",
"allow_mmds_requests": true
"host_dev_name": "hostname8"
}}
],
"machine-config": {{
Expand All @@ -761,17 +777,18 @@ mod tests {
"ht_enabled": false
}},
"mmds-config": {{
"ipv4_address": "169.254.170.2"
"version": "V2",
"ipv4_address": "169.254.170.2",
"network_interfaces": ["netif"]
}}
}}"#,
kernel_file.as_path().to_str().unwrap(),
rootfs_file.as_path().to_str().unwrap(),
);
assert!(VmResources::from_json(json.as_str(), &default_instance_info).is_ok());

// Test all configuration, this time trying to configure the MMDS with an
// empty body. It will make it access the code path in which it sets the
// default MMDS configuration.
// Test all configuration, this time trying to set default configuration
// for version and IPv4 address.
let kernel_file = TempFile::new().unwrap();
json = format!(
r#"{{
Expand All @@ -795,16 +812,17 @@ mod tests {
"network-interfaces": [
{{
"iface_id": "netif",
"host_dev_name": "hostname9",
"allow_mmds_requests": true
"host_dev_name": "hostname9"
}}
],
"machine-config": {{
"vcpu_count": 2,
"mem_size_mib": 1024,
"ht_enabled": false
}},
"mmds-config": {{}}
"mmds-config": {{
"network_interfaces": ["netif"]
}}
}}"#,
kernel_file.as_path().to_str().unwrap(),
rootfs_file.as_path().to_str().unwrap(),
Expand Down
Loading