netbirdio/netbird · error

setting fwmark failed: %w

Error message

setting fwmark failed: %w

What it means

Returned by rawsocket.prepareSenderRawSocket when nbnet.SetSocketOpt(fd) fails. SetSocketOpt only acts when AdvancedRouting() is enabled, and then performs syscall.SetsockoptInt(fd, SOL_SOCKET, SO_MARK, ControlPlaneMark). SO_MARK requires CAP_NET_ADMIN on kernels < 5.17 and CAP_NET_RAW on 5.17+. The socket being marked is the raw socket used to fake packet source addresses for the WG proxy, so a failed mark aborts socket creation and with it eBPF proxy Listen() or SrcFaker construction.

Source

Thrown at client/iface/wgproxy/rawsocket/rawsocket.go:69

		}
	}

	// Bind the socket to the "lo" interface.
	err = syscall.SetsockoptString(fd, syscall.SOL_SOCKET, syscall.SO_BINDTODEVICE, "lo")
	if err != nil {
		if closeErr := syscall.Close(fd); closeErr != nil {
			log.Warnf("failed to close raw socket fd: %v", closeErr)
		}
		return nil, fmt.Errorf("binding to lo interface failed: %w", err)
	}

	// Set the fwmark on the socket.
	err = nbnet.SetSocketOpt(fd)
	if err != nil {
		if closeErr := syscall.Close(fd); closeErr != nil {
			log.Warnf("failed to close raw socket fd: %v", closeErr)
		}
		return nil, fmt.Errorf("setting fwmark failed: %w", err)
	}

	// Convert the file descriptor to a PacketConn.
	file := os.NewFile(uintptr(fd), fmt.Sprintf("fd %d", fd))
	if file == nil {
		if closeErr := syscall.Close(fd); closeErr != nil {
			log.Warnf("failed to close raw socket fd: %v", closeErr)
		}
		return nil, fmt.Errorf("converting fd to file failed")
	}
	packetConn, err := net.FilePacketConn(file)
	if err != nil {
		if closeErr := file.Close(); closeErr != nil {
			log.Warnf("failed to close file: %v", closeErr)
		}
		return nil, fmt.Errorf("converting file to packet conn failed: %w", err)
	}

View on GitHub (pinned to 93e97f4bf1)

Solutions

  1. Run the agent as root or grant CAP_NET_RAW and CAP_NET_ADMIN (kernel >= 5.17 needs only CAP_NET_RAW): `sudo setcap cap_net_raw,cap_net_admin+ep ./netbird` or run via the packaged service
  2. For containers, start with NET_ADMIN capability or run the container privileged like the official image does
  3. If SO_MARK is genuinely unavailable, disable advanced routing for the agent so AdvancedRouting() returns false and the call becomes a no-op
  4. Verify with a quick probe: `python3 -c "import socket; s=socket.socket(2,3,255); s.setsockopt(1,36,0x162)"` succeeding means caps are fine
Defensive patterns

Strategy: validation

Validate before calling

// capability preflight before creating the proxy
func canMarkSockets() bool {
    fd, err := syscall.Socket(syscall.AF_INET, syscall.SOCK_RAW, syscall.IPPROTO_RAW)
    if err != nil {
        return false
    }
    defer syscall.Close(fd)
    return syscall.SetsockoptInt(fd, syscall.SOL_SOCKET, syscall.SO_MARK, 0x162) == nil
}

Try / catch

if _, err := rawsocket.PrepareSenderRawSocketIPv4(); err != nil {
    if strings.Contains(err.Error(), "setting fwmark failed") {
        return fmt.Errorf("raw socket needs CAP_NET_ADMIN (or CAP_NET_RAW on linux >= 5.17): %w", err)
    }
    return err
}

Prevention

When it happens

Trigger: Agent (or a test binary) run without root and without CAP_NET_ADMIN/CAP_NET_RAW while advanced routing is active (routing table != default or the env toggle enabled); container dropping capabilities; seccomp filtering setsockopt(SO_MARK).

Common situations: Running netbird or its unit/integration harness as a non-root user; Docker/podman containers started without --cap-add=NET_ADMIN; hardening profiles (systemd ProtectKernelDefaults-adjacent, AppArmor) blocking SO_MARK.

Related errors


AI-assisted analysis of netbirdio/netbird@93e97f4bf1 (2026-08-16). Data as JSON: /api/errors/c8d084d48d02eb0c. Report an issue: GitHub.