{"record":{"id":"ba6a7b355bc6416b","repo":"netbirdio/netbird","slug":"userspace-packet-filtering-not-handled-on-this-dev","errorCode":null,"errorMessage":"userspace packet filtering not handled on this device","messagePattern":"userspace packet filtering not handled on this device","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"client/iface/iface.go","lineNumber":255,"sourceCode":"\tif err := w.waitUntilRemoved(); err != nil {\n\t\tlog.Warnf(\"failed to remove WireGuard interface %s: %v\", w.Name(), err)\n\t\tif err := w.Destroy(); err != nil {\n\t\t\tresult = multierror.Append(result, fmt.Errorf(\"failed to remove WireGuard interface %s: %w\", w.Name(), err))\n\t\t\treturn errors.FormatErrorOrNil(result)\n\t\t}\n\t\tlog.Infof(\"interface %s successfully removed\", w.Name())\n\t}\n\n\treturn errors.FormatErrorOrNil(result)\n}\n\n// SetFilter sets packet filters for the userspace implementation\nfunc (w *WGIface) SetFilter(filter device.PacketFilter) error {\n\tw.mu.Lock()\n\tdefer w.mu.Unlock()\n\n\tif w.tun.FilteredDevice() == nil {\n\t\treturn fmt.Errorf(\"userspace packet filtering not handled on this device\")\n\t}\n\n\tw.filter = filter\n\n\tw.tun.FilteredDevice().SetFilter(filter)\n\treturn nil\n}\n\n// GetFilter returns packet filter used by interface if it uses userspace device implementation\nfunc (w *WGIface) GetFilter() device.PacketFilter {\n\tw.mu.Lock()\n\tdefer w.mu.Unlock()\n\n\treturn w.filter\n}\n\n// GetDevice to interact with raw device (with filtering)\nfunc (w *WGIface) GetDevice() *device.FilteredDevice {","sourceCodeStart":237,"sourceCodeEnd":273,"githubUrl":"https://github.com/netbirdio/netbird/blob/93e97f4bf1ad715072dcb3fb6cdb1763431b5a9c/client/iface/iface.go#L237-L273","documentation":"SetFilter() programs packet filtering inside the userspace (wireguard-go) data path, which only exists when the interface uses a userspace/netstack TUN whose FilteredDevice() is non-nil. Kernel-mode devices (TunKernelDevice) return nil from FilteredDevice(), so calling SetFilter there returns this error: kernel-mode filtering belongs to the OS firewall (nftables/iptables/pf/WFP), not to the in-process device.","triggerScenarios":"Calling WGIface.SetFilter() on an interface created in kernel mode (Linux with kernel WireGuard), or from code paths that assume the userspace data path (packet-filter DNS hook, capture support) running against a kernel device.","commonSituations":"Linux agent with kernel WireGuard enabled; platform integrations reusing SetFilter unconditionally; tests with fake TUN devices whose FilteredDevice() returns nil.","solutions":["Gate the call on the interface mode: only program in-process filters for userspace/netstack interfaces","Use the firewall manager for kernel-mode filtering","Check for the nil filtered device before calling (the same condition the error tests)","Switch the interface to userspace mode if in-process filtering is required on that platform"],"exampleFix":"// before\nif err := wgi.SetFilter(filter); err != nil {\n    return err // blows up on kernel-mode interfaces\n}\n\n// after: kernel mode relies on the OS firewall\nif !kernelMode {\n    if err := wgi.SetFilter(filter); err != nil {\n        return err\n    }\n}\n// kernel mode: apply equivalent rules via the firewall manager","handlingStrategy":"type-guard","validationCode":null,"typeGuard":"// SetFilter only succeeds when a userspace filtered device exists;\n// kernel-mode interfaces must filter via the OS firewall instead.\nfunc supportsInProcessFilter(ifaceMode string) bool {\n    return ifaceMode == \"userspace\" || ifaceMode == \"netstack\"\n}","tryCatchPattern":"if err := wgi.SetFilter(f); err != nil {\n    if strings.Contains(err.Error(), \"not handled on this device\") {\n        // kernel mode: apply the same policy via the firewall manager\n        return firewallMgr.Apply(f)\n    }\n    return err\n}","preventionTips":["Know the interface mode before calling userspace-only APIs","Route kernel-mode filtering through the OS firewall backends","Centralize platform capability checks instead of scattering them at call sites"],"tags":["go","netbird","packet-filter","kernel-mode","userspace"],"backgroundTag":null,"analyzedSha":"93e97f4bf1ad715072dcb3fb6cdb1763431b5a9c","analyzedAt":"2026-08-16T03:09:19.136Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}