slackhq/nebula · error

newTunFromFd not supported in NetBSD

Error message

newTunFromFd not supported in NetBSD

What it means

newTunFromFd on NetBSD is an unimplemented stub that always returns this error. Passing an existing tun fd to the overlay constructor is simply not supported on NetBSD, unlike Linux. The only supported path there is newTun, which opens /dev/tunN by name.

Source

Thrown at overlay/tun_netbsd.go:74

	Vltime    uint32
	Pltime    uint32
}

type tun struct {
	Device      string
	vpnNetworks []netip.Prefix
	MTU         int
	Routes      atomic.Pointer[[]Route]
	routeTree   atomic.Pointer[bart.Table[routing.Gateways]]
	l           *slog.Logger
	f           *os.File
	fd          int
}

var deviceNameRE = regexp.MustCompile(`^tun[0-9]+$`)

func newTunFromFd(_ *config.C, _ *slog.Logger, _ int, _ []netip.Prefix) (*tun, error) {
	return nil, fmt.Errorf("newTunFromFd not supported in NetBSD")
}

func newTun(c *config.C, l *slog.Logger, vpnNetworks []netip.Prefix, _ bool) (*tun, error) {
	// Try to open tun device
	var err error
	deviceName := c.GetString("tun.dev", "")
	if deviceName == "" {
		return nil, fmt.Errorf("a device name in the format of /dev/tunN must be specified")
	}
	if !deviceNameRE.MatchString(deviceName) {
		return nil, fmt.Errorf("a device name in the format of /dev/tunN must be specified")
	}

	fd, err := unix.Open("/dev/"+deviceName, os.O_RDWR, 0)
	if err != nil {
		return nil, err
	}

View on GitHub (pinned to dd8f660c0a)

Solutions

  1. On NetBSD, configure a device name (tun.dev) so newTun (the /dev/tunN path) is used instead of fd handoff.
  2. Remove or gate fd-injection logic behind a Linux-only build/runtime check.
  3. If fd handoff is required, implement newTunFromFd for NetBSD upstream or use an OS with support (Linux).

Example fix

// before: unconditional fd handoff
t, err := overlay.NewTunFromFd(c, l, fd, networks)
// after
if runtime.GOOS == "netbsd" {
    t, err = overlay.NewTun(c, l, networks, false)
} else {
    t, err = overlay.NewTunFromFd(c, l, fd, networks)
}
Defensive patterns

Strategy: fallback

Validate before calling

// choose constructor by platform before calling
if runtime.GOOS == "netbsd" {
    // fd path unsupported; must use name-based newTun
    return errors.New("fd-based tun creation unsupported on netbsd; set tun.dev")
}

Try / catch

t, err := tryNewTunFromFd(c, l, fd, networks)
if err != nil && strings.Contains(err.Error(), "newTunFromFd not supported") {
    t, err = newTun(c, l, networks, false) // name-based fallback
}

Prevention

When it happens

Trigger: Any call path that constructs the tun from a pre-opened file descriptor while running on NetBSD — e.g. handoff/hot-reload flows or embedders that create the tun device themselves and pass the fd in.

Common situations: Deploying a fd-passing setup (systemd socket handoff, custom embedding, zero-downtime reload) that works on Linux onto a NetBSD host.

Related errors


AI-assisted analysis of slackhq/nebula@dd8f660c0a (2026-09-03). Data as JSON: /api/errors/b38ffb95a8771e7d. Report an issue: GitHub.