{"record":{"id":"4429810639194575","repo":"netbirdio/netbird","slug":"set-spoofing-s","errorCode":null,"errorMessage":"set spoofing: %s","messagePattern":"set spoofing: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"client/firewall/uspfilter/forwarder/forwarder.go","lineNumber":132,"sourceCode":"\t\ttcpip.MaskFromBytes([]byte{0, 0, 0, 0}),\n\t)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"creating default subnet: %w\", err)\n\t}\n\n\tdefaultSubnetV6, err := tcpip.NewSubnet(\n\t\ttcpip.AddrFrom16([16]byte{}),\n\t\ttcpip.MaskFromBytes(make([]byte, 16)),\n\t)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"creating default v6 subnet: %w\", err)\n\t}\n\n\tif err := s.SetPromiscuousMode(nicID, true); err != nil {\n\t\treturn nil, fmt.Errorf(\"set promiscuous mode: %s\", err)\n\t}\n\tif err := s.SetSpoofing(nicID, true); err != nil {\n\t\treturn nil, fmt.Errorf(\"set spoofing: %s\", err)\n\t}\n\n\ts.SetRouteTable([]tcpip.Route{\n\t\t{Destination: defaultSubnet, NIC: nicID},\n\t\t{Destination: defaultSubnetV6, NIC: nicID},\n\t})\n\n\tctx, cancel := context.WithCancel(context.Background())\n\tf := &Forwarder{\n\t\tlogger:        logger,\n\t\tflowLogger:    flowLogger,\n\t\tstack:         s,\n\t\tendpoint:      endpoint,\n\t\tudpForwarder:  newUDPForwarder(mtu, logger, flowLogger),\n\t\tctx:           ctx,\n\t\tcancel:        cancel,\n\t\tnetstack:      netstack,\n\t\tip:            tcpip.AddrFrom4(iface.Address().IP.As4()),","sourceCodeStart":114,"sourceCodeEnd":150,"githubUrl":"https://github.com/netbirdio/netbird/blob/93e97f4bf1ad715072dcb3fb6cdb1763431b5a9c/client/firewall/uspfilter/forwarder/forwarder.go#L114-L150","documentation":"Returned when the gVisor stack refuses SetSpoofing(nicID, true). Spoofing mode lets the NIC reply with arbitrary source addresses, which this forwarder needs because it answers on behalf of overlay peers. The stack errors when the NIC no longer exists or, on some gVisor versions, when the NIC was not created with capabilities allowing spoofing to be toggled after the fact.","triggerScenarios":"The NIC created at the top of forwarder.New was removed concurrently (engine teardown racing initialization), or the endpoint was closed between CreateNIC and SetSpoofing; a repeated New() against a half-torn-down stack also triggers it.","commonSituations":"Fast up/down or reconnection cycles (common on laptops changing networks, or mobile apps backgrounding) where Close() races the constructor; a crash in a prior lifecycle leaving the stack in a bad state.","solutions":["Ensure the previous Forwarder's cancel() and goroutine shutdown complete before creating a new one (join on a done channel)","Guard the constructor entry with the engine's lifecycle mutex so New and Stop cannot interleave","Retry construction once after teardown completes if the error indicates an unknown NIC","Inspect the wrapped %s error text to distinguish unknown-NIC races from genuine gVisor capability failures"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"if iface == nil || iface.GetWGDevice() == nil {\n    return errors.New(\"interface/device not ready\")\n}","typeGuard":"func canBuildForwarder(iface common.IFaceMapper) bool {\n    return iface != nil && iface.GetWGDevice() != nil\n}","tryCatchPattern":"f, err := forwarder.New(...)\nif err != nil && strings.Contains(err.Error(), \"set spoofing\") {\n    // NIC teardown race: wait for Stop to finish, then retry New once\n}","preventionTips":["Never run forwarder.New concurrently with Close/Stop of the previous instance","Cancel and wait for the old forwarder's context before constructing a new stack","Add a startup self-test that constructs and tears down the forwarder to surface lifecycle bugs early"],"tags":["go","netbird","gvisor","netstack","race","firewall"],"backgroundTag":null,"analyzedSha":"93e97f4bf1ad715072dcb3fb6cdb1763431b5a9c","analyzedAt":"2026-08-16T03:09:19.136Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}