slackhq/nebula · error
unable to discover link_addr for tun interface
Error message
unable to discover link_addr for tun interface
What it means
Activate() looks up the link-layer address (MAC/link_addr) of the tun interface by device name before assigning IPs. If getLinkAddr succeeds but returns no entry, the interface does not exist in the system interface list, and this error is returned. It is not wrapped because the plain-text cause is the whole story: no link_addr could be discovered for t.Device.
Source
Thrown at overlay/tun_freebsd.go:486
return nil
}
return fmt.Errorf("unknown address type %v", cidr)
}
func (t *tun) Activate() error {
// Setup our default MTU
err := t.setMTU()
if err != nil {
return err
}
linkAddr, err := getLinkAddr(t.Device)
if err != nil {
return err
}
if linkAddr == nil {
return fmt.Errorf("unable to discover link_addr for tun interface")
}
t.linkAddr = linkAddr
for i := range t.vpnNetworks {
err := t.addIp(t.vpnNetworks[i])
if err != nil {
return err
}
}
return t.addRoutes(false)
}
func (t *tun) setMTU() error {
// Set the MTU on the device
s, err := unix.Socket(unix.AF_INET, unix.SOCK_DGRAM, unix.IPPROTO_IP)
if err != nil {
return errView on GitHub (pinned to dd8f660c0a)
Solutions
- Verify the interface exists: run `ifconfig` and match the configured device name exactly
- Don't hardcode the tun name in config; let the library open the next free device and use its reported Device
- Check jail/vnet confinement — the interface may exist on the host but not in the jail's network stack
- Re-run device creation if the interface was torn down; ensure Activate is called right after newTun
Example fix
// before
# config
tun: tun9 # device may not exist
// after
# omit explicit device name or verify with:
_, err := net.InterfaceByName("tun9")
if err != nil { /* recreate device or clear config */ } Defensive patterns
Strategy: validation
Validate before calling
if _, err := net.InterfaceByName(deviceName); err != nil {
return fmt.Errorf("tun device %q does not exist before Activate: %w", deviceName, err)
} Try / catch
if err := dev.Activate(); err != nil {
if strings.Contains(err.Error(), "unable to discover link_addr") {
return fmt.Errorf("tun interface missing (name mismatch or destroyed): %w", err)
}
return err
} Prevention
- Don't hardcode tun names in config; let the library pick and report the device
- Call Activate immediately after newTun to avoid races with device teardown
- Check jail/vnet visibility of the interface when running confined
- Verify with `ifconfig` that the configured device name exists exactly
When it happens
Trigger: getLinkAddr(t.Device) returns (nil, nil) — the interface name configured for the tun (e.g. from config 'dev' / 'tun') does not correspond to any existing interface at the time Activate runs.
Common situations: Config points at a device name that was never created (e.g. 'tun0' when the kernel allocated 'tun1'); the device was destroyed between open and Activate; running in a jail/vnet without the tun interface visible; name mismatch after OS upgrade.
Related errors
- failed to set tun address %s: %s
- unable to determine IP version from packet
- newTunFromFd not supported in FreeBSD
- failed to set tun device as nonblocking: %w
- failed to create shutdown pipe: %w
AI-assisted analysis of slackhq/nebula@dd8f660c0a (2026-09-03).
Data as JSON: /api/errors/2411fe11214f5058.
Report an issue: GitHub.