slackhq/nebula · error

failed to get tun device link: %s

Error message

failed to get tun device link: %s

What it means

Raised in tun.Activate when netlink.LinkByName(t.Device) cannot find the tun device's link. Nebula needs the link to read its interface index (deviceIndex) for subsequent route setup. It means the kernel has no link registered under the configured device name.

Source

Thrown at overlay/tun_linux.go:463

	s, err := unix.Socket(
		unix.AF_INET, //because everything we use t.ioctlFd for is address family independent, this is fine
		unix.SOCK_DGRAM,
		unix.IPPROTO_IP,
	)
	if err != nil {
		return err
	}
	t.ioctlFd = uintptr(s)

	// Set the device name
	ifrf := ifReq{Name: devName}
	if err = ioctl(t.ioctlFd, unix.SIOCGIFFLAGS, uintptr(unsafe.Pointer(&ifrf))); err != nil {
		return fmt.Errorf("failed to set tun device name: %s", err)
	}

	link, err := netlink.LinkByName(t.Device)
	if err != nil {
		return fmt.Errorf("failed to get tun device link: %s", err)
	}

	t.deviceIndex = link.Attrs().Index

	// Setup our default MTU
	t.setMTU()

	// Set the transmit queue length
	ifrq := ifreqQLEN{Name: devName, Value: int32(t.TXQueueLen)}
	if err = ioctl(t.ioctlFd, unix.SIOCSIFTXQLEN, uintptr(unsafe.Pointer(&ifrq))); err != nil {
		// If we can't set the queue length nebula will still work but it may lead to packet loss
		t.l.Error("Failed to set tun tx queue length", "error", err)
	}

	const modeNone = 1
	if err = netlink.LinkSetIP6AddrGenMode(link, modeNone); err != nil {
		t.l.Warn("Failed to disable link local address generation", "error", err)
	}

View on GitHub (pinned to dd8f660c0a)

Solutions

  1. Confirm the device name: run ip link and compare with tun.dev config; fix the config typo.
  2. Ensure the tun kernel module is loaded (modprobe tun) and /dev/net/tun exists.
  3. Prevent external daemons (NetworkManager, systemd-networkd) from deleting the interface (mark it unmanaged).
  4. Recreate the tun device and retry Activate().

Example fix

// before (config)
tun.dev: tun9
// after — name must match the created device
# ip link  -> device is tun0
tun.dev: tun0
Defensive patterns

Strategy: validation

Validate before calling

func linkExists(name string) bool {
    _, err := netlink.LinkByName(name)
    return err == nil
}
// call before Activate: if !linkExists(t.Device) { recreate tun }

Try / catch

if err := t.Activate(netstack); err != nil {
    if strings.Contains(err.Error(), "failed to get tun device link") {
        log.Error("tun link missing — check tun.dev and kernel tun module", "cause", err)
    }
}

Prevention

When it happens

Trigger: Calling Activate() when the tun interface named by t.Device doesn't exist in the kernel's link table — the TUNSETIFF/dev attach didn't register it, or it was removed.

Common situations: Config typo in tun.dev; device deleted by another process or network manager before activation; using a name not matching the created device; kernel without tun module loaded (tun not in /dev).

Related errors


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