slackhq/nebula · error

FwpmFilterAdd0: 0x%x

Error message

FwpmFilterAdd0: 0x%x

What it means

addInterfaceFilter installs a PERMIT filter in the ALE layer that matches inbound traffic on a specific interface LUID, letting it bypass Windows Defender Firewall. This error wraps a non-zero return from FwpmFilterAdd0 — the filter object could not be added to the engine. The call comes from the public PermitInterface API.

Source

Thrown at wfp/wfp_windows.go:326

		// filterKey left zero: WFP assigns one when the filter is added.
		displayData:         fwpmDisplayData0{name: name, description: desc},
		flags:               fwpmFilterFlagClearActionRight,
		layerKey:            layer,
		subLayerKey:         sublayerKey,
		weight:              fwpValue0{type_: fwpUint8, value: uintptr(15)},
		numFilterConditions: 1,
		filterCondition:     &cond,
		action:              fwpmAction0{actionType: fwpActionPermit},
	}

	r1, _, _ := procFwpmFilterAdd0.Call(
		engine,
		uintptr(unsafe.Pointer(&filter)),
		0, // sd == NULL
		0, // id == NULL
	)
	if r1 != 0 {
		return fmt.Errorf("FwpmFilterAdd0: 0x%x", r1)
	}
	return nil
}

// addUDPPortFilter installs a PERMIT filter that matches (IP_PROTOCOL == UDP) AND (IP_LOCAL_PORT == port).
// FWP_UINT8 and FWP_UINT16 are <= 32 bits so they live inline in the FWP_VALUE0 union.
func addUDPPortFilter(engine uintptr, sublayerKey, layer windows.GUID, port uint16) error {
	name, _ := windows.UTF16PtrFromString("Nebula allow UDP port inbound")
	desc, _ := windows.UTF16PtrFromString("Permits inbound UDP to a nebula listener port")

	conds := [2]fwpmFilterCondition0{
		{
			fieldKey:  fwpmConditionIPProtocol,
			matchType: fwpMatchEqual,
			conditionValue: fwpValue0{
				type_: fwpUint8,
				value: uintptr(ipprotoUDP),
			},

View on GitHub (pinned to dd8f660c0a)

Solutions

  1. Decode the hex win32 code for the exact FWP_E_* cause
  2. Verify the interface LUID is current (Get-NetAdapter / netsh interface show interface) and re-fetch it immediately before calling
  3. Run the process as Administrator
  4. Retry — transient transaction conflicts with other WFP agents resolve on re-attempt
  5. Ensure PermitUDPPort/newSession succeeded and the Session has not been Closed before calling PermitInterface

Example fix

// before
luid := staleLuid // cached from long ago
if err := w.PermitInterface(luid); err != nil { ... }
// after — refresh LUID at call time
adapter, _ := net.InterfaceByName(name)
luid := uint64(adapter.Index)
if err := w.PermitInterface(luid); err != nil {
	return fmt.Errorf("permit interface: %w", err)
}
Defensive patterns

Strategy: validation

Validate before calling

func interfaceExists(luid uint64) error {
	ifaces, err := net.Interfaces()
	if err != nil { return err }
	for _, ifc := range ifaces {
		if uint64(ifc.Index) == luid { return nil }
	}
	return fmt.Errorf("interface with LUID %d not found", luid)
}
// call before PermitInterface
if err := interfaceExists(luid); err != nil { return err }

Try / catch

if err := w.PermitInterface(luid); err != nil {
	var winErr syscall.Errno
	if errors.As(err, &winErr) && winErr == 0x80320009 { // FWP_E_TXN_IN_PROGRESS
		time.Sleep(200 * time.Millisecond)
		return w.PermitInterface(luid)
	}
	return err
}

Prevention

When it happens

Trigger: Calling PermitInterface where FwpmFilterAdd0 fails: invalid/unknown interface LUID, filter conditions rejected (FWP_E_INVALID_PARAMETER), access denied on the engine handle, transaction conflict with another WFP client, or the sublayer/session was already closed via Close().

Common situations: Passing an LUID that no longer exists (interface unplugged/renamed between lookup and call), running without admin rights, an EDR/VPN product holding the BFE transaction, or calling after the Session was closed.

Related errors


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