wgengine

package
v1.103.0-pre Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 23, 2026 License: BSD-3-Clause Imports: 62 Imported by: 47

Documentation

Overview

Package wgengine provides the Tailscale WireGuard engine interface.

Index

Constants

This section is empty.

Variables

View Source
var ErrEngineClosing = errors.New("engine closing; no status")
View Source
var ErrNoChanges = errors.New("no changes made to Engine config")

ErrNoChanges is returned by Engine.Reconfig if no changes were made.

View Source
var HookNewBird feature.Hook[func(logf logger.Logf, socketPath string) (Bird, error)]

HookNewBird is set by the feature/bird package to construct each engine's Bird, connecting to the BIRD unix socket at socketPath. If unset, BIRD integration is not linked into the binary and Config.BIRDSocket must not be set.

View Source
var HookNewNetLogger feature.Hook[func(NetLoggerDeps) NetLogger]

HookNewNetLogger is set by the feature/netlog package to construct each engine's NetLogger. If unset, network flow logging is not linked into the binary and the engine skips all NetLogger calls.

Functions

This section is empty.

Types

type Bird added in v1.102.0

type Bird interface {
	// Reconfig recomputes from the given self node whether this node is
	// a primary subnet router. It is called near the top of every
	// [Engine.Reconfig], before its ErrNoChanges early return. It
	// reports whether the primary subnet router state changed, which
	// suppresses the engine's ErrNoChanges early return.
	Reconfig(self tailcfg.NodeView) (changed bool)

	// ReconfigDone is called at the end of [Engine.Reconfig], after the
	// router is configured, and applies any protocol state change
	// computed by the preceding Reconfig call.
	ReconfigDone()

	// Close disables the protocol and closes the connection to BIRD.
	Close()
}

Bird is the engine's handle on the BIRD Internet Routing Daemon integration, implemented by the feature/bird package. It enables the "tailscale" protocol in BIRD while this node is a primary subnet router and disables it otherwise.

Reconfig and ReconfigDone are only called from Engine.Reconfig, serialized by the engine's internal lock. Close is called from Engine.Close.

type Config added in v1.6.0

type Config struct {
	// Tun is the device used by the Engine to exchange packets with
	// the OS.
	// If nil, a fake Device that does nothing is used.
	Tun tun.Device

	// IsTAP is whether Tun is actually a TAP (Layer 2) device that'll
	// require ethernet headers.
	IsTAP bool

	// Router interfaces the Engine to the OS network stack.
	// If nil, a fake Router that does nothing is used.
	Router router.Router

	// DNS interfaces the Engine to the OS DNS resolver configuration.
	// If nil, a fake OSConfigurator that does nothing is used.
	DNS dns.OSConfigurator

	// ReconfigureVPN provides an optional hook for platforms like Android to
	// know when it's time to reconfigure their VPN implementation. Such
	// platforms can only set their entire VPN configuration (routes, DNS, etc)
	// at all once and can't make piecemeal incremental changes, so this
	// provides a hook to "flush" a batch of Router and/or DNS changes.
	ReconfigureVPN func() error

	// NetMon optionally provides an existing network monitor to re-use.
	// If nil, a new network monitor is created.
	NetMon *netmon.Monitor

	// HealthTracker, if non-nil, is the health tracker to use.
	HealthTracker *health.Tracker

	// Metrics is the usermetrics registry to use.
	// Mandatory, if not set, an error is returned.
	Metrics *usermetric.Registry

	// Dialer is the dialer to use for outbound connections.
	// If nil, a new Dialer is created.
	Dialer *tsdial.Dialer

	// ExtraRootCAs, if non-nil, specifies additional trusted root CAs for TLS
	// connections (e.g. DERP). Passed through to magicsock.
	ExtraRootCAs *x509.CertPool

	// DERPAppName, if non-empty, is an opaque app name string to
	// advertise to DERP servers for stats purposes. It is passed
	// through to magicsock.
	DERPAppName string

	// ControlKnobs is the set of control plane-provied knobs
	// to use.
	// If nil, defaults are used.
	ControlKnobs *controlknobs.Knobs

	// ListenPort is the port on which the engine will listen.
	// If zero, a port is automatically selected.
	ListenPort uint16

	// RespondToPing determines whether this engine should internally
	// reply to ICMP pings, without involving the OS.
	// Used in "fake" mode for development.
	RespondToPing bool

	// BIRDSocket, if non-empty, is the path of the BIRD unix socket to
	// configure whenever this node is a primary subnet router. It
	// requires the feature/bird package to be linked in.
	BIRDSocket string

	// SetSubsystem, if non-nil, is called for each new subsystem created, just before a successful return.
	SetSubsystem func(any)

	// DriveForLocal, if populated, will cause the engine to expose a Taildrive
	// listener at 100.100.100.100:8080.
	DriveForLocal drive.FileSystemForLocal

	// EventBus, if non-nil, is used for event publication and subscription by
	// the Engine and its subsystems.
	//
	// TODO(creachadair): As of 2025-03-19 this is optional, but is intended to
	// become required non-nil.
	EventBus *eventbus.Bus

	// ForceDiscoKey, if non-zero, forces the use of a specific disco
	// private key. This should only be used for special cases and
	// experiments, not for production. The recommended normal path is to
	// leave it zero, in which case a new disco key is generated per
	// Tailscale start and kept only in memory.
	ForceDiscoKey key.DiscoPrivate

	// OnDERPRecv, if non-nil, is called for every non-disco packet
	// received from DERP before the peer map lookup. If it returns
	// true, the packet is considered handled and is not passed to
	// WireGuard. The pkt slice is borrowed and must be copied if
	// the callee needs to retain it.
	OnDERPRecv func(regionID int, src key.NodePublic, pkt []byte) (handled bool)
}

Config is the engine configuration.

type Engine

type Engine interface {
	// Reconfig reconfigures WireGuard and makes sure it's running.
	// This also handles setting up any kernel routes.
	//
	// This is called whenever tailcontrol (the control plane)
	// sends an updated network map.
	//
	// The returned error is ErrNoChanges if no changes were made.
	Reconfig(*wgcfg.Config, *router.Config, *dns.Config) error

	// ResetAndStop resets the engine to a clean state (like calling Reconfig
	// with all pointers to zero values) and waits for it to be fully stopped,
	// with no live peers or DERPs.
	//
	// Unlike Reconfig, it does not return ErrNoChanges.
	ResetAndStop() (*Status, error)

	// SetPeerForIPFunc installs the IP-to-node lookup used by the
	// engine's internal cold paths (Ping, TSMP, pendopen diagnostics).
	// It parallels [Engine.SetPeerByIPPacketFunc] but returns richer
	// data (a full NodeView, the matched route prefix, and the IsSelf
	// flag).
	//
	// If fn is nil, those lookups fail for every IP.
	//
	// LocalBackend installs a func backed by the live nodeBackend for
	// exact-match and self addresses, with the RouteManager's outbound
	// table supplying the subnet-route / exit-node fallback; the engine
	// itself holds no peer-lookup state on this path.
	SetPeerForIPFunc(fn func(netip.Addr) (_ PeerForIP, ok bool))

	// GetFilter returns the current packet filter, if any.
	GetFilter() *filter.Filter

	// SetFilter updates the packet filter.
	SetFilter(*filter.Filter)

	// GetJailedFilter returns the current packet filter for jailed nodes,
	// if any.
	GetJailedFilter() *filter.Filter

	// SetJailedFilter updates the packet filter for jailed nodes.
	SetJailedFilter(*filter.Filter)

	// SetPeerRoutes updates the per-peer route attributes used by the
	// tun-layer data plane for per-packet NAT rewrites and
	// jailed-filter selection. native4 and native6 are this node's own
	// Tailscale addresses, and routes maps each peer's addresses and
	// routed prefixes to its attributes; it is a shared immutable
	// snapshot from [routemanager.RouteManager.Outbound].
	//
	// A nil routes table disables all per-packet peer processing;
	// callers pass nil when no current peer has any such attributes.
	SetPeerRoutes(native4, native6 netip.Addr, routes *bart.Table[*routemanager.PeerRoute])

	// SetStatusCallback sets the function to call when the
	// WireGuard status changes.
	SetStatusCallback(StatusCallback)

	// RequestStatus requests a WireGuard status update right
	// away, sent to the callback registered via SetStatusCallback.
	RequestStatus()

	// PeerByKey returns the WireGuard status of the provided peer.
	// If the peer is not found, ok is false.
	PeerByKey(key.NodePublic) (_ wgint.Peer, ok bool)

	// Close shuts down this wireguard instance, remove any routes
	// it added, etc. To bring it up again later, you'll need a
	// new Engine.
	Close()

	// Done returns a channel that is closed when the Engine's
	// Close method is called, the engine aborts with an error,
	// or it shuts down due to the closure of the underlying device.
	// You don't have to call this.
	Done() <-chan struct{}

	// SetSelfNode informs the engine of the current self node.
	// The zero (invalid) NodeView indicates no self node.
	SetSelfNode(tailcfg.NodeView)

	// UpdateStatus populates the network state using the provided
	// status builder.
	UpdateStatus(*ipnstate.StatusBuilder)

	// Ping is a request to start a ping of the given message size to the peer
	// handling the given IP, then call cb with its ping latency & method.
	//
	// If size is zero too small, it is ignored. See tailscale.PingOpts for details.
	Ping(ip netip.Addr, pingType tailcfg.PingType, size int, cb func(*ipnstate.PingResult))

	// InstallCaptureHook registers a function to be called to capture
	// packets traversing the data path. The hook can be uninstalled by
	// calling this function with a nil value.
	InstallCaptureHook(packet.CaptureCallback)

	// SetPeerByIPPacketFunc installs a callback used by wireguard-go to
	// look up which peer should handle an outbound packet by destination IP.
	SetPeerByIPPacketFunc(func(netip.Addr) (_ key.NodePublic, ok bool))

	// SetPeerConfigFunc installs the live source of per-peer WireGuard
	// configuration: given a peer's public key, fn returns the prefixes
	// the peer is currently allowed to originate traffic from, or
	// ok=false if the peer is unknown (in which case it must not exist
	// in the WireGuard device). The engine installs a single
	// [device.PeerLookupFunc] wrapping fn, so lazily-created peers
	// always see current state and the lookup func never needs to be
	// reinstalled as peers come and go.
	//
	// It is expected to be called once during LocalBackend construction,
	// before the first [Engine.Reconfig]. fn is called rarely (when
	// wireguard-go first hears from a peer it doesn't have) and may
	// acquire locks.
	SetPeerConfigFunc(fn func(key.NodePublic) (allowedIPs []netip.Prefix, ok bool))

	// SyncDevicePeer synchronizes the WireGuard device's state for a
	// single peer with the config source installed via
	// [Engine.SetPeerConfigFunc]: if the source no longer knows the
	// peer, it is removed from the device; if the peer is active in the
	// device, its allowed IPs are updated. It does O(1) work (plus the
	// config source lookup) and is intended to be called for each peer
	// added, updated, or removed by an incremental netmap delta,
	// avoiding a full [Engine.Reconfig].
	//
	// It is a no-op if no config source is installed.
	SyncDevicePeer(key.NodePublic)

	// ResetDevicePeer removes the peer from the WireGuard device,
	// discarding any session key material and in-flight handshake
	// state. If the peer is still known to the config source installed
	// via [Engine.SetPeerConfigFunc], it is lazily re-created on demand
	// with fresh state.
	//
	// LocalBackend calls it when a peer's disco key changes, which
	// means the peer restarted and its old sessions are dead.
	ResetDevicePeer(key.NodePublic)

	// SetNetLogSource installs the [NetLogSource] consulted by the
	// engine's network flow logger for node lookups and the current
	// audit logging identity.
	//
	// It is expected to be called once during LocalBackend construction,
	// before any [Engine.Reconfig] call that starts up the network logger.
	SetNetLogSource(NetLogSource)

	// SetWGPeerLookup installs the function used by the engine's
	// wireguard-go log wrapper to rewrite peer references in log lines
	// (mapping wireguard-go's "peer(XXXX…YYYY)" form to the
	// Tailscale-conventional short string form).
	//
	// It is expected to be called once during LocalBackend construction.
	// The function is called concurrently and must be safe to call with
	// no Engine locks held.
	SetWGPeerLookup(func(wgString string) (tsString string, ok bool))

	// SetPeerSessionStateFunc installs a callback used to observe WireGuard
	// peer session state transitions.
	//
	// Calls are serialized per Engine and delivered in transition order from
	// wireguard-go, while wireguard-go is holding locks. The callback must be
	// cheap and must not call back into wireguard-go.
	//
	// It does not replay current state. Callers that need a complete view should
	// set it before peers are started or lazily created, and maintain any
	// snapshots, sequence numbers, and pubsub state outside wireguard-go.
	//
	// In Tailscale, the usual implementation is
	// ipnlocal.LocalBackend.onPeerWireGuardState, installed early in
	// LocalBackend construction.
	SetPeerSessionStateFunc(func(key.NodePublic, PeerWireGuardState))

	// ProbeLocks acquires and releases the engine's internal locks so
	// that [ipnlocal.LocalBackend]'s watchdog can detect deadlocks in
	// the engine. It is otherwise a no-op.
	ProbeLocks()
}

Engine is the Tailscale WireGuard engine interface.

func NewFakeUserspaceEngine

func NewFakeUserspaceEngine(logf logger.Logf, opts ...any) (Engine, error)

NewFakeUserspaceEngine returns a new userspace engine for testing.

The opts may contain the following types:

  • int or uint16: to set the ListenPort.

func NewUserspaceEngine

func NewUserspaceEngine(logf logger.Logf, conf Config) (_ Engine, reterr error)

NewUserspaceEngine creates the named tun device and returns a Tailscale Engine running on it.

type NetLogSource added in v1.102.0

type NetLogSource interface {
	// SelfNode returns the current self node and its owning user profile.
	// The views are invalid if there is no current node.
	SelfNode() (tailcfg.NodeView, tailcfg.UserProfileView)

	// NodeByAddr returns the node assigned the given address along with
	// its owning user profile.
	// ok is false if no node is known to own addr.
	NodeByAddr(addr netip.Addr) (node tailcfg.NodeView, user tailcfg.UserProfileView, ok bool)

	// NetLogIDs returns the audit logging IDs from the current netmap,
	// along with whether exit node flows should be logged.
	// ok is false if network flow logging should not run.
	NetLogIDs() (nodeID, domainID logid.PrivateID, logExitFlows bool, ok bool)
}

NetLogSource provides the network flow logging feature what it needs from the rest of the system (in practice, ipnlocal.LocalBackend): on-demand node lookups and the current audit logging identity.

type NetLogger added in v1.102.0

type NetLogger interface {
	// Reconfig is called near the top of every [Engine.Reconfig],
	// before its ErrNoChanges early return (so identity-only changes
	// still take effect) and before the router is configured (so a
	// starting logger captures initial packets). It reports whether
	// network logging state changed, which suppresses the engine's
	// ErrNoChanges early return.
	Reconfig(routerCfg *router.Config, routerChanged bool) (changed bool)

	// ReconfigDone is called at the end of [Engine.Reconfig], after
	// the router is configured, so that a logger stopping as a result
	// of the preceding Reconfig call captures final packets. It may
	// block to flush pending messages.
	ReconfigDone()

	// Shutdown stops the logger. It may block to flush pending
	// messages, bounded by an internal upload timeout.
	Shutdown()
}

NetLogger is the engine's handle on the network flow logger lifecycle, implemented by the feature/netlog package.

Reconfig and ReconfigDone are only called from Engine.Reconfig, serialized by the engine's internal lock. Shutdown may be called concurrently with them from Engine.Close.

type NetLoggerDeps added in v1.102.0

type NetLoggerDeps struct {
	Logf   logger.Logf
	Tun    *tstun.Wrapper
	Sock   *magicsock.Conn
	NetMon *netmon.Monitor
	Health *health.Tracker
	Bus    *eventbus.Bus

	// Source returns the [NetLogSource] installed via
	// [Engine.SetNetLogSource], or nil if none has been installed yet.
	// It is a func because the source is installed after the engine
	// (and thus the NetLogger) is constructed.
	Source func() NetLogSource
}

NetLoggerDeps are the engine-owned dependencies passed to HookNewNetLogger to construct a NetLogger.

type NetworkMapCallback added in v1.4.0

type NetworkMapCallback func(*netmap.NetworkMap)

NetworkMapCallback is the type used by callbacks that hook into network map updates.

type PeerForIP added in v1.20.0

type PeerForIP struct {
	// Node is the matched node. It's always a valid value when
	// Engine.PeerForIP returns ok==true.
	Node tailcfg.NodeView

	// IsSelf is whether the Node is the local process.
	IsSelf bool

	// Route is the route that matched the IP provided
	// to Engine.PeerForIP.
	Route netip.Prefix
}

PeerForIP is the type returned by Engine.PeerForIP.

type PeerWireGuardState added in v1.102.0

type PeerWireGuardState uint8

PeerWireGuardState is the current WireGuard session state for a peer.

const (
	// PeerWireGuardStateNone means there is no handshake in progress and no
	// session key material retained for this peer.
	PeerWireGuardStateNone PeerWireGuardState = 0

	// PeerWireGuardStateHandshake means a handshake is in progress for this
	// peer, but there is not currently a usable WireGuard session.
	PeerWireGuardStateHandshake PeerWireGuardState = 1

	// PeerWireGuardStateEstablished means the peer has a completed WireGuard
	// session with usable session key material.
	PeerWireGuardStateEstablished PeerWireGuardState = 2

	// PeerWireGuardStateExpired means the peer's session key material is no
	// longer considered usable, but final key cleanup or lazy peer removal may
	// not have happened yet.
	PeerWireGuardStateExpired PeerWireGuardState = 3
)

type Status

type Status struct {
	AsOf       time.Time // the time at which the status was calculated
	Peers      []ipnstate.PeerStatusLite
	LocalAddrs []tailcfg.Endpoint // the set of possible endpoints for the magic conn
	DERPs      int                // number of active DERP connections
}

Status is the Engine status.

TODO(bradfitz): remove this, subset of ipnstate? Need to migrate users.

type StatusCallback

type StatusCallback func(*Status, error)

StatusCallback is the type of status callbacks used by Engine.SetStatusCallback.

Exactly one of Status or error is non-nil.

Directories

Path Synopsis
Create two wgengine instances and pass data through them, measuring throughput, latency, and packet loss.
Create two wgengine instances and pass data through them, measuring throughput, latency, and packet loss.
Package filter is a stateful packet filter.
Package filter is a stateful packet filter.
filtertype
Package filtertype defines the types used by wgengine/filter.
Package filtertype defines the types used by wgengine/filter.
Package magicsock implements a socket that can change its communication path while in use, actively searching for the best way to communicate.
Package magicsock implements a socket that can change its communication path while in use, actively searching for the best way to communicate.
Package netlog provides a logger that monitors a TUN device and periodically records any traffic into a log stream.
Package netlog provides a logger that monitors a TUN device and periodically records any traffic into a log stream.
Package netstack wires up gVisor's netstack into Tailscale.
Package netstack wires up gVisor's netstack into Tailscale.
gro
Package gro implements GRO for the receive (write) path into gVisor.
Package gro implements GRO for the receive (write) path into gVisor.
Package router presents an interface to manipulate the host network stack's state.
Package router presents an interface to manipulate the host network stack's state.
osrouter
Package osrouter contains OS-specific router implementations.
Package osrouter contains OS-specific router implementations.
Package wgcfg has types and a parser for representing WireGuard config.
Package wgcfg has types and a parser for representing WireGuard config.
Package wgint provides somewhat shady access to wireguard-go internals that don't (yet) have public APIs.
Package wgint provides somewhat shady access to wireguard-go internals that don't (yet) have public APIs.
Package wglog contains logging helpers for wireguard-go.
Package wglog contains logging helpers for wireguard-go.
Package winnet contains Windows-specific networking code.
Package winnet contains Windows-specific networking code.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL