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
2 changes: 1 addition & 1 deletion nvx-rs/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ testing = ["dep:getrandom", "dep:serde", "dep:serde_json", "dep:nix", "dep:windo
# NVX's own `nvx-rs` binary, the command-line interface that runs NVX's microVMs and sandboxes,
# compiles egress policies, looks up artifacts, and downloads releases. It adds no API.
cli = ["host"]
# The serde form of `Hypervisor`. The `host` feature enables it.
# The serde forms of `Hypervisor` and `net::Ipv4Cidr`. The `host` feature enables it.
serde = ["dep:serde"]
# The programs that run in Linux guests: the `guest` module and the `nvx-*` guest binaries. Build
# them with `--no-default-features --features guest` and the `guest` profile; see README.md.
Expand Down
30 changes: 23 additions & 7 deletions nvx-rs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ changes only when the module's purpose does.
| [`nvx::exec`](#nvxexec) | The execution engine: the streams, credit, and outcome of an execution's two sides, without I/O, for any framing |
| [`nvx::host::runtime`](#nvxhostruntime) | `Runtime`: what every launch shares, the runtime's files and their identity, the hypervisor and its probe, and the launch options |
| [`nvx::host::vm`](#nvxhostvm) | The directory of a VM, private to the current user, with the fixed names of the files and endpoints of its launches |
| [`nvx::host::config`](#nvxhostconfig) | `VmConfig`: a VM's processors, memory, root, view, export, and boot console as one value, checked without reading the host |
| [`nvx::host::config`](#nvxhostconfig) | `VmConfig`: a VM's processors, memory, root, view, network, export, boot console, and cold-boot settings as one value, checked without reading the host |
| [`nvx::host::artifacts`](#nvxhostartifacts) | Locating the OpenVMM executable and the guest artifacts that a VM runs, and their runtime identity |
| [`nvx::host::launch`](#nvxhostlaunch) | `LaunchSpec` and `VmmLaunch`: the OpenVMM command line of a managed microVM, its checks, and the inputs that it shares with an adapter's launch |
| [`nvx::host::network`](#nvxhostnetwork) | The one lowering of a network posture into OpenVMM options, for the CLI and external orchestrators |
Expand Down Expand Up @@ -332,6 +332,11 @@ an egress rule; `is_valid_hostname` and `is_valid_guest_network`, the forms of a
VM's hostname and guest address, and `guest_gateway`, the gateway of a guest
address.

`Ipv4Cidr`: a guest address, checked when it is built, which `Ipv4Cidr::new` and
`Ipv4Cidr::parse` refuse unless OpenVMM accepts it, as `is_valid_guest_network`
does; it displays and parses as `ADDRESS/PREFIX`, such as `10.0.0.2/24`, and
with the `serde` feature it serializes as that text.

### `nvx::gpt`

`Disk::read`: the protective MBR, primary header, and primary partition entry
Expand Down Expand Up @@ -450,10 +455,11 @@ file; a caller that reconnects after a restart keeps it.
### `nvx::host::config`

`VmConfig`: the configuration of a VM as one value, its processors, its memory
in MiB, its `RootConfig`, the `View` that its workload has of it, the
`ExportPlan` of the host paths that it exports, if any, and the
`ConsoleAttachment` of its boot console, a `Listener` for a client or
`Disconnected`; the console's device exists either way.
in MiB, its `RootConfig`, the `View` that its workload has of it, its
`NetworkConfig` if it has a NIC, the `ExportPlan` of the host paths that it
exports, if any, the `ConsoleAttachment` of its boot console, a `Listener` for a
client or `Disconnected`, whose device exists either way, and a `Boot`, what
only a cold boot takes.

A root is `Blockless`, the guest's own system from its initramfs, or `Layered`,
a `LayeredRoot`: the path of an image and its `RootImage`, EROFS or a GPT disk,
Expand All @@ -463,13 +469,23 @@ upper, a scratch image with its `Journal` policy, or RAM up to a cap of 1 to 90
percent of the VM's memory, 50 when the caller has no other
(`UpperConfig::DEFAULT_MAX_MEMORY_PERCENT`).

A `NetworkConfig` holds the `NetworkPolicy` that OpenVMM enforces and the NIC's
address, an `nvx::net::Ipv4Cidr`, if the caller names one: without one, a cold
boot takes `DEFAULT_GUEST_NETWORK`, `10.0.0.2/24`, which `nvx::host::launch`
also exports, and a restore the snapshot's. A `NicConfig` is the NIC that a VM
settled on, with its address. A `Boot` holds the caller's own kernel
command-line tokens, to which NVX adds those of what it lowers: a VM that boots
needs one, and a VM that a snapshot restores takes none.

`VmConfig::check` checks the whole value without reading the host: it opens no
path and checks no support of the host. It fails with an `nvx::Error` of kind
`Unsupported` that names the first refusal: no processor or no memory; a layered
root without an image or a scratch path, with a bound of an EROFS image or of 0,
or with a RAM upper's cap outside 1 to 90 percent; a hostname that an activation
record refuses; or an export that `ExportPlan::check` refuses for the guest
targets that the root reserves.
targets that the root reserves. The network's policy and the boot's command line
are checked where they are lowered, with the guest's address and NVX's own
tokens.

### `nvx::host::artifacts`

Expand Down Expand Up @@ -2618,7 +2634,7 @@ caller owner.
| `bundled` | Stages a release at build time; see [Bundled artifacts](#bundled-artifacts) |
| `testing` | NVX's own `nvx-fake-openvmm` binary, a fake OpenVMM for tests; see [Test hooks](#test-hooks). It adds no API |
| `cli` | NVX's own `nvx-rs` binary; it adds no API, and enables `host`. See [The nvx-rs CLI](#the-nvx-rs-cli) |
| `serde` | The serde form of `Hypervisor` |
| `serde` | The serde forms of `Hypervisor` and `net::Ipv4Cidr` |
| `guest` | The `guest` module and the guest binaries; see [Guest binaries](#guest-binaries) |

Without default features, the crate has only `protocol`, `activation`, `exec`,
Expand Down
15 changes: 15 additions & 0 deletions nvx-rs/public-api/linux.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1750,15 +1750,23 @@ fn nvx::host::artifacts::openvmm_executable
mod nvx::host::cli (hidden)
fn nvx::host::cli::main (hidden)
mod nvx::host::config
struct nvx::host::config::Boot
field nvx::host::config::Boot::command_line
enum nvx::host::config::ConsoleAttachment
variant nvx::host::config::ConsoleAttachment::Disconnected
variant nvx::host::config::ConsoleAttachment::Listener
const nvx::host::config::DEFAULT_GUEST_NETWORK
struct nvx::host::config::LayeredRoot
field nvx::host::config::LayeredRoot::format
field nvx::host::config::LayeredRoot::gpt_first_partition_bound
field nvx::host::config::LayeredRoot::image
field nvx::host::config::LayeredRoot::overlay
field nvx::host::config::LayeredRoot::upper
struct nvx::host::config::NetworkConfig
field nvx::host::config::NetworkConfig::guest_network
field nvx::host::config::NetworkConfig::policy
struct nvx::host::config::NicConfig
field nvx::host::config::NicConfig::guest_network
enum nvx::host::config::RootConfig
variant nvx::host::config::RootConfig::Blockless
variant nvx::host::config::RootConfig::Layered
Expand All @@ -1771,10 +1779,12 @@ variant nvx::host::config::UpperConfig::Scratch
field nvx::host::config::UpperConfig::Scratch::journal
field nvx::host::config::UpperConfig::Scratch::path
struct nvx::host::config::VmConfig
field nvx::host::config::VmConfig::boot
method nvx::host::config::VmConfig::check
field nvx::host::config::VmConfig::console
field nvx::host::config::VmConfig::filesystem
field nvx::host::config::VmConfig::memory_mib
field nvx::host::config::VmConfig::network
field nvx::host::config::VmConfig::processors
field nvx::host::config::VmConfig::root
field nvx::host::config::VmConfig::view
Expand Down Expand Up @@ -3170,6 +3180,11 @@ method nvx::net::Cidr::is_ipv6
method nvx::net::Cidr::parse
method nvx::net::Cidr::parse_masked
method nvx::net::Cidr::subtract
struct nvx::net::Ipv4Cidr
method nvx::net::Ipv4Cidr::address
method nvx::net::Ipv4Cidr::new
method nvx::net::Ipv4Cidr::parse
method nvx::net::Ipv4Cidr::prefix_len
mod nvx::net::cidr
struct nvx::net::cidr::Cidr
method nvx::net::cidr::Cidr::contains
Expand Down
15 changes: 15 additions & 0 deletions nvx-rs/public-api/windows.txt
Original file line number Diff line number Diff line change
Expand Up @@ -676,15 +676,23 @@ fn nvx::host::artifacts::openvmm_executable
mod nvx::host::cli (hidden)
fn nvx::host::cli::main (hidden)
mod nvx::host::config
struct nvx::host::config::Boot
field nvx::host::config::Boot::command_line
enum nvx::host::config::ConsoleAttachment
variant nvx::host::config::ConsoleAttachment::Disconnected
variant nvx::host::config::ConsoleAttachment::Listener
const nvx::host::config::DEFAULT_GUEST_NETWORK
struct nvx::host::config::LayeredRoot
field nvx::host::config::LayeredRoot::format
field nvx::host::config::LayeredRoot::gpt_first_partition_bound
field nvx::host::config::LayeredRoot::image
field nvx::host::config::LayeredRoot::overlay
field nvx::host::config::LayeredRoot::upper
struct nvx::host::config::NetworkConfig
field nvx::host::config::NetworkConfig::guest_network
field nvx::host::config::NetworkConfig::policy
struct nvx::host::config::NicConfig
field nvx::host::config::NicConfig::guest_network
enum nvx::host::config::RootConfig
variant nvx::host::config::RootConfig::Blockless
variant nvx::host::config::RootConfig::Layered
Expand All @@ -697,10 +705,12 @@ variant nvx::host::config::UpperConfig::Scratch
field nvx::host::config::UpperConfig::Scratch::journal
field nvx::host::config::UpperConfig::Scratch::path
struct nvx::host::config::VmConfig
field nvx::host::config::VmConfig::boot
method nvx::host::config::VmConfig::check
field nvx::host::config::VmConfig::console
field nvx::host::config::VmConfig::filesystem
field nvx::host::config::VmConfig::memory_mib
field nvx::host::config::VmConfig::network
field nvx::host::config::VmConfig::processors
field nvx::host::config::VmConfig::root
field nvx::host::config::VmConfig::view
Expand Down Expand Up @@ -2095,6 +2105,11 @@ method nvx::net::Cidr::is_ipv6
method nvx::net::Cidr::parse
method nvx::net::Cidr::parse_masked
method nvx::net::Cidr::subtract
struct nvx::net::Ipv4Cidr
method nvx::net::Ipv4Cidr::address
method nvx::net::Ipv4Cidr::new
method nvx::net::Ipv4Cidr::parse
method nvx::net::Ipv4Cidr::prefix_len
mod nvx::net::cidr
struct nvx::net::cidr::Cidr
method nvx::net::cidr::Cidr::contains
Expand Down
41 changes: 40 additions & 1 deletion nvx-rs/src/host/config/mod.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,22 @@
//! The configuration of a VM, as one value that [`VmConfig::check`] checks without reading the
//! host: the VM's processors and memory, its [`RootConfig`], the [`View`] that its workload has
//! of it, the host paths that it exports, and its [`ConsoleAttachment`].
//! of it, its [`NetworkConfig`], the host paths that it exports, its [`ConsoleAttachment`], and
//! what only its cold boot takes, a [`Boot`].

use std::ops::RangeInclusive;
use std::path::PathBuf;

use super::filesystem::{ExportPlan, ReservedTargets};
use super::network::NetworkPolicy;
use crate::activation::{self, Journal, MAX_HOSTNAME_BYTES, OverlayMode, View};
use crate::error::{Error, Result};
use crate::net::Ipv4Cidr;
use crate::protocol::root::{ImageFormat, RootImage};

/// The guest address of a VM's NIC at a cold boot whose configuration names none, in the form
/// that [`Ipv4Cidr::parse`] reads.
pub const DEFAULT_GUEST_NETWORK: &str = "10.0.0.2/24";

/// The caps of a RAM upper that [`VmConfig::check`] accepts, in percent of the VM's memory.
const RAM_UPPER_CAPS: RangeInclusive<u8> = 1..=90;

Expand All @@ -24,10 +31,15 @@ pub struct VmConfig {
pub root: RootConfig,
/// The view that the VM's workload has of it.
pub view: View,
/// The VM's network, if it has a NIC.
pub network: Option<NetworkConfig>,
/// The host paths that the VM exports to its guest, if any.
pub filesystem: Option<ExportPlan>,
/// Where the output of the VM's boot console goes.
pub console: ConsoleAttachment,
/// What only a cold boot takes: a VM that boots needs it, and one that a snapshot restores
/// takes none.
pub boot: Option<Boot>,
}

/// The root of a VM.
Expand Down Expand Up @@ -86,6 +98,30 @@ pub enum ConsoleAttachment {
Disconnected,
}

/// The network of a VM: the address of its NIC, and the policy that OpenVMM enforces on it.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct NetworkConfig {
/// The NIC's static address, if the caller names one. Without one, a cold boot takes
/// [`DEFAULT_GUEST_NETWORK`], and a restore the snapshot's.
pub guest_network: Option<Ipv4Cidr>,
/// The policy that OpenVMM enforces on the VM's traffic.
pub policy: NetworkPolicy,
}

/// The NIC that a VM has, with the static address that it settled on.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct NicConfig {
/// The NIC's static address.
pub guest_network: Ipv4Cidr,
}

/// What only a VM's cold boot takes.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Boot {
/// The caller's own kernel command-line tokens, to which NVX adds those of what it lowers.
pub command_line: String,
}

impl VmConfig {
/// Checks the whole configuration without reading the host: it opens no path, and checks no
/// support of the host.
Expand All @@ -96,6 +132,9 @@ impl VmConfig {
/// without a path, or with a RAM upper whose cap is not 1 to 90 percent; a hostname that is
/// empty, longer than [`MAX_HOSTNAME_BYTES`], or holds a NUL; and an export that
/// [`ExportPlan::check`] refuses for the root, whose error is the source.
///
/// The NIC's address is checked when it is built. The network's policy and the boot's command
/// line are checked where they are lowered, with the guest's address and NVX's own tokens.
pub fn check(&self) -> Result<()> {
if self.processors == 0 {
return Err(Error::unsupported("a VM needs at least one processor"));
Expand Down
39 changes: 39 additions & 0 deletions nvx-rs/src/host/config/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use super::*;
use crate::ErrorKind;
use crate::activation::MachineIdMode;
use crate::host::filesystem::{Bind, ExportChild, Owner};
use crate::host::network::Access;
use crate::uuid::Uuid;

/// Returns the path of `name` in a directory that doesn't exist.
Expand All @@ -24,8 +25,10 @@ fn blockless() -> VmConfig {
hostname: b"box".to_vec(),
machine_id: MachineIdMode::Image,
},
network: None,
filesystem: None,
console: ConsoleAttachment::Listener,
boot: None,
}
}

Expand Down Expand Up @@ -105,6 +108,42 @@ fn blockless_and_layered_configurations_pass_with_either_console() {
}
}

#[test]
fn configurations_pass_with_or_without_a_nic_address_and_a_boot() {
let networks = [
None,
Some(NetworkConfig {
guest_network: None,
policy: NetworkPolicy::new(Access::Deny),
}),
Some(NetworkConfig {
guest_network: Some(Ipv4Cidr::parse(DEFAULT_GUEST_NETWORK).unwrap()),
policy: NetworkPolicy::new(Access::Allow),
}),
];
let boots = [
None,
Some(Boot {
command_line: String::new(),
}),
Some(Boot {
command_line: "quiet loglevel=0".to_owned(),
}),
];
for config in [blockless(), layered(erofs())] {
for network in &networks {
for boot in &boots {
let config = VmConfig {
network: network.clone(),
boot: boot.clone(),
..config.clone()
};
config.check().unwrap();
}
}
}
}

#[test]
fn a_ram_upper_holds_1_to_90_percent_of_the_memory_and_half_by_default() {
assert_eq!(UpperConfig::DEFAULT_MAX_MEMORY_PERCENT, 50);
Expand Down
4 changes: 2 additions & 2 deletions nvx-rs/src/host/launch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ use crate::net::{is_valid_guest_network, is_valid_hostname};
use crate::protocol::WORKLOAD_ACCOUNT_TOKEN;
use crate::protocol::cmdline::{self, Parameter};

pub use super::config::DEFAULT_GUEST_NETWORK;

/// Bytes of the x86 kernel command line available to caller tokens, including the NUL.
pub const COMMAND_LINE_BUDGET: usize = 1024;
/// Longest Windows process command line, in UTF-16 code units, including the terminating NUL.
Expand All @@ -49,8 +51,6 @@ pub const MAX_TIMEOUT: Duration = Duration::from_secs(30 * 24 * 60 * 60);
pub const DEFAULT_MEMORY_MIB: u32 = 256;
/// Default workload hostname.
pub const DEFAULT_HOSTNAME: &str = "nvx-sandbox";
/// Default guest network address.
pub const DEFAULT_GUEST_NETWORK: &str = "10.0.0.2/24";
/// The name of the kernel parameter that sets the workload hostname, which follows the extra
/// kernel parameters on every command line.
const HOSTNAME_PARAMETER: &str = "hostname";
Expand Down
3 changes: 2 additions & 1 deletion nvx-rs/src/host/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@
//! [`LaunchOptions`](runtime::LaunchOptions).
//! - [`config`] holds the configuration of a VM as one value, a [`VmConfig`](config::VmConfig),
//! which it checks without reading the host: the VM's processors and memory, its root, the view
//! that its workload has of it, its export, and its boot console.
//! that its workload has of it, its network, its export, its boot console, and what only its
//! cold boot takes.
//! - [`launch`] builds OpenVMM's command line from a [`LaunchSpec`](launch::LaunchSpec): the
//! managed lifecycle, the workload identity, the kernel command line, and the network and
//! filesystem options.
Expand Down
2 changes: 1 addition & 1 deletion nvx-rs/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@
//! console without running a VM. It adds no API.
//! - `cli`: NVX's own `nvx-rs` binary, the command-line interface that runs NVX's microVMs and
//! sandboxes. It adds no API, and enables `host`.
//! - `serde`: the serde form of [`Hypervisor`].
//! - `serde`: the serde forms of [`Hypervisor`] and [`net::Ipv4Cidr`].
//! - `guest`: the `guest` module and the guest binaries, which the `guest` profile builds as
//! static `x86_64-unknown-linux-musl` executables.

Expand Down
Loading
Loading