{"record":{"id":"8d23e92775fa9de8","repo":"slackhq/nebula","slug":"newtunfromfd-not-supported-in-freebsd","errorCode":null,"errorMessage":"newTunFromFd not supported in FreeBSD","messagePattern":"newTunFromFd not supported in FreeBSD","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"overlay/tun_freebsd.go","lineNumber":282,"sourceCode":"\t\t\tifreq := ifreqDestroy{Name: t.deviceBytes()}\n\t\t\terr = ioctl(uintptr(s), syscall.SIOCIFDESTROY, uintptr(unsafe.Pointer(&ifreq)))\n\t\t}\n\t\tif err != nil {\n\t\t\tt.l.Error(\"Error destroying tunnel\", \"error\", err)\n\t\t}\n\t}()\n\n\t// wait up to 1 second so we start blocking at the ioctl\n\tselect {\n\tcase <-c:\n\tcase <-time.After(1 * time.Second):\n\t}\n\n\treturn nil\n}\n\nfunc newTunFromFd(_ *config.C, _ *slog.Logger, _ int, _ []netip.Prefix) (*tun, error) {\n\treturn nil, fmt.Errorf(\"newTunFromFd not supported in FreeBSD\")\n}\n\nfunc newTun(c *config.C, l *slog.Logger, vpnNetworks []netip.Prefix, _ bool) (*tun, error) {\n\t// Try to open existing tun device\n\tvar fd int\n\tvar err error\n\tdeviceName := c.GetString(\"tun.dev\", \"\")\n\tif deviceName != \"\" {\n\t\tfd, err = unix.Open(\"/dev/\"+deviceName, os.O_RDWR, 0)\n\t}\n\tif errors.Is(err, fs.ErrNotExist) || deviceName == \"\" {\n\t\t// If the device doesn't already exist, request a new one and rename it\n\t\tfd, err = unix.Open(\"/dev/tun\", os.O_RDWR, 0)\n\t}\n\tif err != nil {\n\t\treturn nil, err\n\t}\n","sourceCodeStart":264,"sourceCodeEnd":300,"githubUrl":"https://github.com/slackhq/nebula/blob/dd8f660c0ac37903ec4080ca4d3c861ba9342ceb/overlay/tun_freebsd.go#L264-L300","documentation":"newTunFromFd is the constructor that adopts a caller-supplied file descriptor for a tun device. On FreeBSD this constructor is intentionally unimplemented and always returns this error, because the platform tun code manages its own device lifecycle (opening /dev/tun, nonblocking setup, shutdown pipe) and cannot safely adopt a foreign fd.","triggerScenarios":"Calling the library's device constructor that takes an existing fd (newTunFromFd with a *config.C, logger, int fd, and prefix list) on a FreeBSD build. Any code path that requests fd-based device creation on darwin-freeBSD platforms triggers it unconditionally.","commonSituations":"Porting Linux code that opens its own /dev/tun fd and hands it to the library; using a config option like tun.fd or a 'dev' pre-opened descriptor; test harnesses that pre-create tun fds for fast setup.","solutions":["Do not pass a pre-opened fd on FreeBSD; use newTun() (config-driven) which opens /dev/tun itself","Remove or gate the fd-based construction path behind a runtime.GOOS check","If you must share an fd, open the device via newTun and extract its fd instead of the reverse"],"exampleFix":"// before\ntunDev, err := newTunFromFd(cfg, logger, myFd, prefixes)\n// after\nif runtime.GOOS == \"freebsd\" {\n    tunDev, err = newTun(cfg, logger, prefixes, false)\n} else {\n    tunDev, err = newTunFromFd(cfg, logger, myFd, prefixes)\n}","handlingStrategy":"validation","validationCode":"if runtime.GOOS == \"freebsd\" && usingFdConstruction {\n    return errors.New(\"fd-based tun construction is unsupported on freebsd; use config-driven newTun\")\n}","typeGuard":"func fdTunSupported() bool { return runtime.GOOS != \"freebsd\" }","tryCatchPattern":"tunDev, err := newTunFromFd(cfg, log, fd, prefixes)\nif err != nil && strings.Contains(err.Error(), \"not supported in FreeBSD\") {\n    tunDev, err = newTun(cfg, log, prefixes, false)\n}\nif err != nil { return err }","preventionTips":["Branch device construction on runtime.GOOS before choosing newTunFromFd","Use config-driven tun creation as the portable path","Add a build-tag or startup assertion for fd-based tun usage","Keep the fd-based path behind a Linux/darwin-only feature flag"],"tags":["freebsd","tun","unsupported-platform","file-descriptor"],"backgroundTag":"unsupported-platform-operation","analyzedSha":"dd8f660c0ac37903ec4080ca4d3c861ba9342ceb","analyzedAt":"2026-09-03T11:13:55.444Z","contentChangedAt":"2026-09-03T11:13:55.444Z","schemaVersion":2},"datasetVersion":"2026-09-10T17:17:09.494Z"}