Skip to content

guestnet: add a private userspace network for each VM guest - #13

Closed
bradfitz wants to merge 1 commit into
mainfrom
bradfitz/guestnet
Closed

bradfitz wants to merge 1 commit into
mainfrom
bradfitz/guestnet

Conversation

@bradfitz

@bradfitz bradfitz commented Nov 3, 2026

Copy link
Copy Markdown
Member

This is the network for mantsana, a macOS CI VM backend replacing Tart
(see the DESIGN.md that follows), but nothing in it is macOS-specific.

A guest's virtual NIC hands us raw Ethernet frames (on macOS over a
SOCK_DGRAM socketpair to a VZFileHandleNetworkDeviceAttachment) and they
all terminate in a gVisor netstack owned by the guest's Net, so a guest
can only reach what we connect it to, and can't see other guests:

  • The gateway, IMDS (169.254.169.254) and DNS (169.254.169.253)
    addresses are on our NIC, so gVisor answers ARP and ICMP for them,
    and the guest needs no route or DNS changes to reach them. ARP for
    any other address is dropped: the NIC is promiscuous, and answering
    the guest's DHCP duplicate-address probe makes it decline its lease.
  • DHCP is answered from the frame loop with one fixed lease, the same
    in every slot, so a VM memory snapshot restores anywhere.
  • Each TCP SYN and UDP flow is dispatched by destination to a
    registered Service (a guest-facing server, or a forward to a
    host-side address), to DNS (any port 53) or NTP (any port 123)
    interception, or to egress dialed from the host, subject to a deny
    list. Every flow and DNS query is logged.
  • The host reaches the guest only by DialGuest; there is no host
    route to it.

guestnettest provides a fake guest stack for tests of code built on it.

Updates tailscale/corp#39994

This is the network for mantsana, a macOS CI VM backend replacing Tart
(see the DESIGN.md that follows), but nothing in it is macOS-specific.

A guest's virtual NIC hands us raw Ethernet frames (on macOS over a
SOCK_DGRAM socketpair to a VZFileHandleNetworkDeviceAttachment) and they
all terminate in a gVisor netstack owned by the guest's Net, so a guest
can only reach what we connect it to, and can't see other guests:

  - The gateway, IMDS (169.254.169.254) and DNS (169.254.169.253)
    addresses are on our NIC, so gVisor answers ARP and ICMP for them,
    and the guest needs no route or DNS changes to reach them. ARP for
    any other address is dropped: the NIC is promiscuous, and answering
    the guest's DHCP duplicate-address probe makes it decline its lease.
  - DHCP is answered from the frame loop with one fixed lease, the same
    in every slot, so a VM memory snapshot restores anywhere.
  - Each TCP SYN and UDP flow is dispatched by destination to a
    registered Service (a guest-facing server, or a forward to a
    host-side address), to DNS (any port 53) or NTP (any port 123)
    interception, or to egress dialed from the host, subject to a deny
    list. Every flow and DNS query is logged.
  - The host reaches the guest only by DialGuest; there is no host
    route to it.

guestnettest provides a fake guest stack for tests of code built on it.

Updates tailscale/corp#39994

Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
Change-Id: I5b359a459ae52218e97cef3b74ecc14a806e0cc7
@bradfitz
bradfitz requested a review from a team November 3, 2026 18:28
@bradfitz bradfitz closed this Nov 4, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant