Switch from blacklist to whitelist: only known-safe keys are preserved.
Fixes host data leaking into VM context menus (e.g. recent files via
Actions groups) and cross-VM window grouping (via StartupWMClass).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
New VM home directories are created by copying /var/lib/vmsilo/home-template
on first start, allowing users to seed dotfiles and configs. The template
directory is created via tmpfiles and owned by the configured user.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Shares a host directory as /home/user in guest VMs via virtiofs, enabled
by default. Accepts true (/shared/<vmname>), a custom path string, or
false to disable. Host directory is created with correct uid:gid ownership
at VM start.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Allows each VM to choose its Wayland proxy. Defaults to wayland-proxy-virtwl
(existing behavior). Setting waylandProxy = "sommelier" uses the ChromeOS
sommelier compositor instead.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Drop the vm- prefix, add dash separator between VM name and interface
index, and remove the 10-character VM name limit. Long names that would
exceed IFNAMSIZ (15 chars) are truncated with VM ID appended for
uniqueness (e.g., bankingsupe3-22 for id=3).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When type = "tap" but no tap sub-options are set, iface.tap is null.
Guard all iface.tap.* accesses so assertions fire cleanly instead of
crashing during evaluation.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Allow TAP interfaces to be added to a named host bridge
(networking.bridges) instead of being assigned a host IP.
Only one of tap.hostAddress or tap.bridge may be set.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Allow TAP interfaces without a host-side IP address. This supports
use cases like bridged networking where the host doesn't need an IP
on the TAP interface.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
TAP interfaces were named tap-<vmname>, which collides when a VM has
multiple TAP interfaces. Use vm-<vmname><ifIndex> instead, where ifIndex
is the PCI slot number (22-36). Add assertion that VM names are at most
10 characters to stay within the 15-char Linux interface name limit.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
/run is tmpfs, so placing the ephemeral disk there defeats the purpose
of moving writes off RAM. Use /var/lib/vmsilo/ which is on real disk.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
VM root overlay writes now go to a sparse qcow2 disk instead of tmpfs,
reducing host RAM usage. The host creates the qcow2 at VM start and
deletes it at stop. The guest formats it as ext4 with discard support.
Adds rootOverlay option (type: qcow2/tmpfs, size: default 10G) with
tmpfs available as fallback for the original behavior.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Use lib.attrValues to convert interfaces attrset to list for
lib.concatMap and lib.any operations.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The assertions were still using list operations (lib.imap0, lib.length)
on vm.network.interfaces after it was changed to an attrset.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Change network.interfaces from list to attrset where keys become
guest-visible interface names. Names are passed to the guest via
vmsilo.ifname=<name>,<mac> kernel parameters and applied at early
boot via udev rules.
- Add sortedInterfaceList helper for deterministic PCI slot assignment
- Update all interface iteration to use sorted attrset
- Add vmsilo-ifname-rules initrd service to generate udev rules
- MAC addresses now generated from vmName-userIfName hash
BREAKING: network.interfaces syntax changes from list to attrset:
Before: interfaces = [{ type = "tap"; ... }];
After: interfaces = { wan = { type = "tap"; ... }; };
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Extract routeSubmodule type shared between routes/v6Routes (options.nix)
- Add isVmSwitchIface helper to unify interface type checking
- Rename isBdfGlobal to isBdf and remove duplicate local definition
- Add parseCIDR helper for consistent CIDR parsing across 4 locations
- Add resolveOptionalConfig helper for tri-state GPU/sound config
- Add assertions for tap.hostAddress and CIDR format validation
- Extract kernelParamHelper for /proc/cmdline parsing in guest scripts
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Remove unused Rust code found by review: EthernetFrame struct,
extract_dest_mac(), MAX_FRAME_SIZE, MAX_TSO_FRAME, PacketForwarder.our_ip,
EgressBuffer.routes, and redundant serde_json dev-dependency.
Fix MAC generation to use interface name (e.g. enp0s22) instead of stale
idx+16 offset that diverged when PCI slot was changed to idx+22.
Fix README.md: wrong socket path, non-existent vmNetwork.router option
(now receiveBroadcast/routes), stale PCI offset examples, incomplete
vm-switch networking example. Add --quiet flag to CLI docs in both
README.md and CLAUDE.md.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add vmNetwork.routes option (list of CIDR strings) that gets written
atomically to the vm-switch config directory. Enables declaring subnets
reachable through a VM for longest-prefix-match routing.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The router/client role distinction in vm-switch is no longer meaningful
with L3 IP-based forwarding. All VMs now use client.ip/client.mac/
client.sock uniformly; the only behavioral difference is the
receive_broadcast flag file.
- Remove VmRole enum and all role-based logic from Rust code
- Rename NixOS option vmNetwork.router to vmNetwork.receiveBroadcast
- Remove "exactly one router per network" assertion
- Update bufferbloat-test, documentation, and all tests
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace removed initialCredits with bufferSize and add fqCodelTarget,
fqCodelInterval, fqCodelLimit, fqCodelQuantum options.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Activation script now writes:
<vm>/<role>.ip - IPv4 address from interface config
<vm>/<role>.mac - MAC address
<vm>/receive_broadcast - flag for router VMs
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
PCI addresses use hex notation (00:16.0, 00:1a.0) but the code was
outputting decimal. Also start at slot 0x16 (22 decimal) to leave
more room for crosvm auto-assigned devices.
- Use lib.toHexString for PCI address generation
- Change base offset from 16 to 22
- Replace hardcoded values with helper function calls
- Update CLAUDE.md documentation
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Design for changing vm-switch from L2 (MAC-based) to L3 (IP-based)
forwarding. Key changes:
- Forward packets based on destination IP instead of MAC
- ARP proxy responds with real peer MACs
- ACL via peers/ directory (structural enforcement)
- Anti-spoofing via source IP validation on ingress
- Broadcast limited to 255.255.255.255
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
crosvm's --vhost-user type=net doesn't accept a mac parameter;
the MAC address is provided by the vhost-user backend (vm-switch).
Also fix pci-device -> pci-address parameter name.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Replace fragmented hostNetworking (boolean) and vmNetwork (attrsOf) with
a unified network option containing nameservers and an interfaces list.
Each interface specifies type (tap/vm-switch), addressing, and routing.
Key changes:
- PCI-based interface naming (ifIndex = position + 16) for predictable
names like enp0s16, enp0s17
- MAC generation now based on SHA1("${vmName}-${ifIndex}")
- Kernel param systemd.hostname= replaces custom vmsilo.hostname=
- Remove net.ifnames=0 and set-hostname service (systemd handles both)
- Enable predictable interface names in guest
- Add assertions for interface validation
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Replace the imperative configure-network service with systemd's built-in
network generator. This parses ip=, rd.route=, and nameserver= kernel
parameters and generates .network files that networkd manages natively.
Benefits:
- network-online.target now waits correctly for network configuration
- Consistent with NixOS/systemd patterns
- Removes imperative shell script
- Adds nameserver configuration support
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Previously, icon copying only searched within each application package's
own icons. Apps like konsole that use icons from system themes (breeze)
would have missing icons in the VM desktop menu.
Now searches breeze-icons and hicolor-icon-theme as fallback after
package-bundled icons, ensuring icons are found and copied with the
VM-prefixed name regardless of their source.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The user-facing parameter is now the initial credit budget (how many
bytes can be in-flight before backpressure pauses TX), not the raw
ring capacity. Capacity is derived: initial_credits + CREDIT_HEADROOM.
Export CREDIT_HEADROOM constant from ring.rs (requires entry_size to
be const fn). Default --initial-credits is 128k, giving ~259k capacity.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When push() wraps around, it costs entry_size + sentinel_waste (up to
2x the frame size). With only 1x headroom, push() could fail despite
positive credits when free space was split across the wrap point.
Double the headroom reservation to 2 * entry_size(MAX_TSO_FRAME) and
increase the default --buffer-size from 128k to 256k so initial credits
remain usable (~131KB with the new headroom).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The ring buffer data region is allocated at a fixed 512 KiB but the new
--buffer-size flag limits how much of it may be used (default 128 KiB).
Accepts plain bytes ("65536") or k/K suffix ("128k"). This lets operators
tune memory vs. burst-absorption per peer without changing the underlying
shared-memory layout.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Worker processes now set their title to "vm-switch(<network>): <vm>"
by overwriting the argv buffer and setting prctl(PR_SET_NAME). Add
-n/--name CLI option to specify the network name.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
autoShutdown is a submodule with enable (bool) and after (int seconds).
autoStart (bool) adds wantedBy multi-user.target to boot VM automatically.
Kernel parameters renamed to match: autoShutdown.enable, autoShutdown.after.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace the thread-based vhost-user backend architecture with a
fork-based process model where each VM gets its own child process.
This enables strong isolation between VMs handling untrusted network
traffic, with multiple layers of defense in depth.
Process model:
- Main process watches config directory and orchestrates child lifecycle
- One child process forked per VM, running as vhost-user net backend
- Children communicate via SOCK_SEQPACKET control channel with SCM_RIGHTS
- Automatic child restart on crash/disconnect, with peer notification
- Ping/pong heartbeat monitoring for worker health (1s interval, 100ms timeout)
- SIGCHLD handling integrated into tokio event loop
Inter-process packet forwarding:
- Lock-free SPSC ring buffers in shared memory (memfd + mmap)
- 64-slot rings (~598KB each) with atomic head/tail, no locks in datapath
- Eventfd signaling for empty-to-non-empty transitions
- Main orchestrates buffer exchange: GetBuffer -> BufferReady -> PutBuffer
- Zero-copy path: producers write directly into consumer's shared memory
Namespace sandbox (applied before tokio, single-threaded):
- User namespace: unprivileged outside, UID 0 inside
- PID namespace: main is PID 1, children invisible to host
- Mount namespace: minimal tmpfs root with /config, /dev, /proc, /tmp
- IPC namespace: isolated System V IPC
- Network namespace: empty, communication only via inherited FDs
- Controllable via --no-sandbox flag
Seccomp BPF filtering (two-tier whitelist):
- Main filter: allows fork, socket creation, inotify, openat
- Child filter: strict subset - no fork, no socket, no file open
- Child filter applied after vhost setup, before event loop
- Modes: kill (default), trap (SIGSYS debug), log, disabled
Also adds vm-switch service dependencies to VM units in the NixOS
module so VMs wait for their network switch before starting.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Pass color via wayland security context app_id (vmsilo:<name>:<color>)
so kwin can style window decorations per-VM.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Override kwin when qubes-lite is enabled to apply kde-decoration.patch,
which colors window decorations based on vmsilo:<vmname>:<color> security
context from wayland-proxy-virtwl.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add completion for --ssh and --root options, and ensure VM name
completion works after options are specified.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>