slackhq/nebula · critical

failed to set tun device head: %w

Error message

failed to set tun device head: %w

What it means

This error is returned by the NetBSD TUN device's Activate method when the TUNSIFHEAD ioctl fails. TUNSIFHEAD enables the 4-byte address-family header on packets read/written to the tun device, which is required to carry both IPv4 and IPv6 over the same tunnel. If the kernel rejects the ioctl, the device cannot be used for dual-stack VPN traffic, so activation aborts.

Source

Thrown at overlay/tun_netbsd.go:313

			return fmt.Errorf("failed to set tun address %s: %s", cidr.Addr().String(), err)
		}
		return nil
	}

	return fmt.Errorf("unknown address type %v", cidr)
}

func (t *tun) Activate() error {
	mode := int32(unix.IFF_BROADCAST)
	err := ioctl(uintptr(t.fd), TUNSIFMODE, uintptr(unsafe.Pointer(&mode)))
	if err != nil {
		return fmt.Errorf("failed to set tun device mode: %w", err)
	}

	v := 1
	err = ioctl(uintptr(t.fd), TUNSIFHEAD, uintptr(unsafe.Pointer(&v)))
	if err != nil {
		return fmt.Errorf("failed to set tun device head: %w", err)
	}

	err = t.doIoctlByName(unix.SIOCSIFMTU, uint32(t.MTU))
	if err != nil {
		return fmt.Errorf("failed to set tun mtu: %w", err)
	}

	for i := range t.vpnNetworks {
		err = t.addIp(t.vpnNetworks[i])
		if err != nil {
			return err
		}
	}

	return t.addRoutes(false)
}

func (t *tun) doIoctlByName(ctl uintptr, value uint32) error {

View on GitHub (pinned to dd8f660c0a)

Solutions

  1. Verify the process runs as root or with the privileges needed to configure network interfaces.
  2. Confirm the fd passed to newTun is a valid NetBSD tun device (/dev/tunN) and the tun kernel module is loaded (modload if_tun).
  3. Check the wrapped errno (%w) with errors.Is to identify whether it is EBADF/ENOTTY/EPERM and fix accordingly.
  4. Rebuild/reboot the kernel with tun(4) support if the ioctl returns ENOTTY or ENODEV.
Defensive patterns

Strategy: try-catch

Validate before calling

// Before Activate: ensure we are root and the tun driver is present
if os.Geteuid() != 0 {
    return fmt.Errorf("activating tun device requires root privileges")
}
if _, err := os.Stat("/dev/tun"); err != nil {
    return fmt.Errorf("tun device not available: %w", err)
}

Try / catch

if err := t.Activate(); err != nil {
    var syscallErr syscall.Errno
    if errors.As(err, &syscallErr) {
        switch syscallErr {
        case unix.EPERM, unix.EACCES:
            // missing privileges
        case unix.ENOTTY, unix.ENODEV:
            // fd is not a tun device / driver missing
        }
    }
    return fmt.Errorf("tun activation failed: %w", err)
}

Prevention

When it happens

Trigger: Calling Activate() on a NetBSD tun where the ioctl(fd, TUNSIFHEAD, &1) call fails — e.g. the file descriptor is not a tun device, the tun driver is not loaded, or the fd lacks sufficient permissions.

Common situations: Running the VPN binary on a NetBSD host where /dev/tun was not opened correctly, the kernel lacks the tun(4) driver, or the process runs without root privileges to configure the interface.

Related errors


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