--net)| Mode | What it does |
|---|---|
user (default) |
User-mode SLIRP. Combine with --ssh-port N to forward localhost:N → guest:22. Works everywhere. |
tap |
Attach to a pre-created host TAP verbatim (--tap tap0) — the bridge/plane owns IP + forwarding. Without --tap, tap mode auto-creates like bridge and so also requires --bridge <dev>. |
bridge |
Auto-create a TAP on an existing Linux bridge (--bridge br0) via cocoon’s network/bridge. |
cni |
Auto-create a TAP inside a per-VM netns via cocoon’s network/cni (TC-redirect plane). |
tap/bridge/cni make a macOS VM join the same forwarding plane as cocoon’s Cloud
Hypervisor / Firecracker VMs on the node, so the guest can DHCP a real LAN IP from the upstream
network. With a fresh SMBIOS identity, tap and bridge use its ROM as the guest NIC MAC;
otherwise auto-created TAPs use the provider MAC and a pre-created --tap uses QEMU’s default.
Auto-creation (tap without --tap, bridge, cni) is Linux-only (needs CAP_NET_ADMIN);
user and a pre-created --tap work everywhere.
In cni mode, the guest NIC uses the MAC CNI assigned to eth0 because macspoofchk allow-lists
that address; the SMBIOS ROM identity remains unchanged.
Auto-created devices carry cocoon-macos’s own host name family (net_scope cm: bridge-mode TAPs
cm<vmid8>-<nic>, netns cm-<vmid>; a CNI-mode TAP is named tap<vmid8>-<nic> inside that netns),
so a cocoon daemon’s GC on the same node never reads a live macOS guest’s TAP or netns as an orphan
(see cocoon’s net_scope in its networking docs).
--net cni and TC redirectCNI runs QEMU inside a per-VM network namespace. cocoon’s CNI wires the netns veth to the QEMU TAP
with TC ingress redirect (mirred), not a bridge — the guest’s DHCP/traffic flows
tap → eth0 → cni0 → upstream, and it comes back the same way. The guest gets a real ToR IP; SSH
goes straight to that IP (no port-forward).
--cni-conf-dir (default /etc/cni/net.d) and --cni-bin-dir (default
/opt/cni/bin) point at a non-standard CNI installation; both are ignored by
the other net modes and are remembered in the VM record, so rm tears the
NIC down without repeating them; a clone inherits them from its source. A VM
created before the record carried them takes rm --cni-conf-dir and
rm --cni-bin-dir once.
vm clone inherits the source’s --net mode, and for bridge its bridge, unless --net or
--bridge is given. Auto-created tap/bridge/cni networking gives the clone a new TAP
(and netns under cni) and a provider-assigned MAC, except that host-bridge modes keep a fresh
SMBIOS ROM MAC when present. Without a fresh identity, user and pre-created --tap use
QEMU’s default MAC, which is not unique across VMs. A source attached to a pre-created host TAP
(--tap tap0) cannot share it, so its clone needs its own --tap; a tap source that auto-created
its TAP on a bridge clones like bridge.
vm stop leaves an auto-created TAP in place for a fast restart; once QEMU closes it the TAP has
no carrier and its bridge port forwards nothing, so bridge mode has nothing to quiesce. Under cni,
vm stop (and the stop inside vm restore --force) downs the netns eth0 so the idle TC redirect
stops storming softirqs, and vm start (or the relaunch) brings it back up. vm rm (and a failed
create) deletes the TAP and the netns; a netns or TAP left behind by a create or clone that was
killed before its record was written is reclaimed by cocoon-macos gc.
vm start (and a forced restore’s relaunch) rebuilds an auto-created TAP (under cni also its netns
and TC redirect) with the recorded MAC when the host lost it, e.g. after a reboot.
VNC exposure depends on the net mode:
--net user | tap | bridge — QEMU binds VNC loopback-only (127.0.0.1:590<vnc>). Reach it
by tunnelling: ssh -L 5901:127.0.0.1:5901 <host>.--net cni — QEMU runs in a netns, so its 127.0.0.1 bind would be unreachable off-box.
Instead VNC rides a unix socket fronted by a small host-side proxy on all interfaces
(0.0.0.0:590<vnc>). Because that is reachable off the host, --vnc-password is required for
--net cni — the launch is rejected without it.VNC is launch-scoped: it is off unless --vnc is given for that launch, it is cleared on
vm stop, and it is re-enabled per start:
cocoon-macos vm start m1 --vnc 1 --vnc-password s3cret # this run only
cocoon-macos vm stop m1 # VNC gone with the qemu it belonged to
The VNC password is never written to disk — it is read from the flag on each start.
vm start --vnc / --vnc-password on an already-running VM is a no-op: live QEMU can’t be
retargeted, so the CLI prints that the supplied VNC settings were ignored and changes nothing.
QEMU’s default None auth hangs macOS Screen Sharing. Pass --vnc-password <≤8 bytes> (applied
via the QEMU monitor post-launch) so Screen Sharing prompts and connects. The launch is rejected
(not truncated) if the password exceeds 8 bytes or contains whitespace, control characters or double
quotes — QEMU’s VNC DES auth only supports 8-byte passwords. Plain VNC clients (RealVNC/TigerVNC) work
without a password on the loopback modes.
In-guest macOS Screen Sharing (to the guest’s own IP) is not enabled headlessly — macOS requires the Screen Recording TCC grant from the GUI or MDM. Use QEMU’s built-in VNC above instead.
macOS only repaints the emulated framebuffer while the display is awake; once it sleeps (~idle),
VNC shows a blank white/black screen with just the cursor even though the guest is healthy (SSH works,
WindowServer is up). It is not a GPU/driver problem — a mouse move repaints it. The golden image’s
first-boot daemon runs pmset -a displaysleep 0 sleep 0 disablesleep 1 system-wide (covering the
pre-login loginwindow and disabling system sleep too) to keep the framebuffer painted; older images
need a setup-stage rebuild. See also the GPU/video note in Known Issues.