This document covers the lifecycle management of the core tunnel system in Firestack, specifically focusing on the rtunnel implementation which orchestrates the gvisor-based network stack and all subsystems. The tunnel is the top-level abstraction that brings together the DNS resolver, proxy system, protocol handlers, and network stack into a cohesive unit.
This page documents:
SeamlessEndpoint architecture allowing for "hot" FD swapping.Stat() with KeyedBarrier caching.Sources: intra/tunnel.go1-123 tunnel/tunnel.go1-66
The tunnel system is built around the Tunnel interface which extends the base tunnel.Tunnel interface with Firestack-specific capabilities. The concrete implementation is rtunnel, which manages all subsystem lifecycles and coordinates their interactions.
The Tunnel interface intra/tunnel.go74-119 defines the contract for an Intra session, including component accessors and lifecycle management methods.
The rtunnel struct intra/tunnel.go121-137 maintains references to all major subsystems:
Sources: intra/tunnel.go59-137 tunnel/tunnel.go49-66
Firestack provides multiple entry points for tunnel creation. The high-level NewTunnel functions are the primary way to instantiate the rtunnel orchestrator.
| Function | File | Description |
|---|---|---|
NewTunnel | intra/tunnel.go155 | Helper that calls NewTunnel2 using the same value for linkmtu and tunmtu. |
NewTunnel2 | intra/tunnel.go159 | Core initialization sequence with distinct control over TUN device MTU vs. physical link MTU. |
NewGTunnel | tunnel/tunnel.go173 | Low-level gvisor stack and SeamlessEndpoint initialization. |
Sources: intra/tunnel.go155-162 tunnel/tunnel.go173-221
The initialization process is protected by an 8-second timeout (mktunTimeout) intra/tunnel.go51 to prevent indefinite hangs. The sequence follows a strict order to ensure proper dependency resolution.
Initialization Sequence Diagram
Key Implementation Details:
core.EitherOr intra/tunnel.go190 to race between completion and mktunTimeout intra/tunnel.go51dtr.kickstart(proxies) intra/tunnel.go205 ensures the bootstrap resolver is ready, which may involve waiting for proxy availability.tunnel.NewGTunnel tunnel/tunnel.go173-221 creates the gvisor stack and attaches protocol handlers. It utilizes netstack.NewEndpoint tunnel/tunnel.go184 for the link layer.gtunnel starts a waitForEndpoint goroutine tunnel/tunnel.go95-146 to monitor the health of the SeamlessEndpoint. It checks if the endpoint stays up for at least 3 seconds (uptimeThreshold) tunnel/tunnel.go100Sources: intra/tunnel.go155-269 tunnel/tunnel.go95-146 tunnel/tunnel.go173-221
The tunnel supports runtime reconfiguration without full teardown, enabling adaptation to network changes (e.g., switching from WiFi to LTE).
The magiclink implementation of SeamlessEndpoint intra/netstack/seamless.go68-77 allows swapping the underlying file descriptor (TUN device) without destroying the network stack.
Swap Logic intra/netstack/seamless.go158-213:
Swap(fd, mtu) on the existing endpoint intra/netstack/seamless.go163fdbasedInjectableEndpoint via newFdbasedInjectableEndpoint intra/netstack/seamless.go180NetworkDispatcher intra/netstack/seamless.go192Tango (atomic swap) to replace the old endpoint with the new one intra/netstack/seamless.go198The underlying endpoint intra/netstack/fdbased.go70-112 handles the actual packet dispatching from the TUN FD to the network stack via a linkDispatcher intra/netstack/dispatchers.go174-180
Sources: intra/netstack/seamless.go68-77 intra/netstack/seamless.go158-213 intra/netstack/fdbased.go70-112 intra/netstack/dispatchers.go174-180
Firestack distinguishes between the TUN device MTU and the underlying Link MTU.
SetLinkMtu intra/tunnel.go101 updates the link MTU hint used by proxies (e.g., WireGuard). It uses an atomic.Int32 intra/tunnel.go133 to track changes and returns didchange if the value was updated.
Sources: intra/tunnel.go101 intra/tunnel.go133
Restart intra/tunnel.go107 performs a complete swap of the gvisor netstack while preserving higher-level subsystems (Resolver, Proxies). This is typically used when the TUN file descriptor changes significantly and a seamless swap is not desired.
Restart Workflow:
old.Disconnect() which invokes t.stack.Destroy() tunnel/tunnel.go155gtunnel with the new file descriptor via NewGTunnel tunnel/tunnel.go173rtunnel's volatile storage t intra/tunnel.go125Sources: intra/tunnel.go107 tunnel/tunnel.go148-158 tunnel/tunnel.go173-221
The tunnel provides detailed metrics through Stat(). To minimize the performance impact of collecting metrics from the gvisor stack and Go runtime, results are cached.
The tunnel uses a core.Barrier named bar intra/tunnel.go126 with a 30-second TTL (statttl) intra/tunnel.go139
Stat Collection Flow:
tunnel.Stat() tunnel/tunnel.go65 to get low-level netstack metrics.Sources: intra/tunnel.go118-126 intra/tunnel.go139 tunnel/tunnel.go65
The following diagram maps the logical components of tunnel management to their specific code identifiers and file locations.
Sources: intra/tunnel.go121-137 tunnel/tunnel.go68-78
This diagram illustrates the relationship between the gtunnel and the magiclink (SeamlessEndpoint) which wraps the underlying gVisor link endpoint.
Sources: tunnel/tunnel.go68-78 intra/netstack/seamless.go68-77 intra/netstack/seamless.go158-213
Refresh this wiki