tailscale/tailscale

NetworkManager IPv4 DNS addresses encoded with native endianness instead of network byte order

Summary

  • Context: The NetworkManager DNS integration code encodes IPv4 addresses as uint32 values for NetworkManager's DBus API.

  • Bug: The code uses binary.NativeEndian instead of binary.BigEndian to convert IPv4 addresses to uint32.

  • Actual vs. expected: On little-endian systems (x86/AMD64), IPv4 addresses are byte-swapped, causing NetworkManager to receive incorrect DNS server addresses instead of network byte order (big-endian) values as specified in its API documentation.

  • Impact: DNS queries are sent to incorrect IP addresses, causing resolution failures for users with custom DNS configurations on NetworkManager-managed Linux systems.

Code with bug

File: net/dns/nm.go

dnsv4 = append(
    dnsv4,
    binary.NativeEndian.Uint32(b[12:]), // <-- BUG 🔴 should be BigEndian for network byte order
)

NetworkManager's DBus API documentation at developer.gnome.org/NetworkManager/stable/settings-ipv4.html explicitly states that IPv4 addresses must be "in network byte order" (big-endian).

Example

On little-endian systems (x86/AMD64), the byte-swapping produces incorrect addresses:

Input IP

Bytes

Current (NativeEndian)

Expected (BigEndian)

NetworkManager Interprets As

192.168.1.1

0xc0 0xa8 0x01 0x01

0x0101A8C0

0xC0A80101

1.1.168.192 (wrong)

10.0.0.1

0x0a 0x00 0x00 0x01

0x0100000A

0x0A000001

1.0.0.10 (wrong)

100.100.100.100

0x64 0x64 0x64 0x64

0x64646464

0x64646464

100.100.100.100 (correct)

The bug has remained undetected because the most commonly used Tailscale DNS server IP (100.100.100.100) is a palindrome that produces the same encoding regardless of byte order. Additionally, NetworkManager mode is less common than other DNS management modes like systemd-resolved.

Recommended fix

Change binary.NativeEndian to binary.BigEndian:

dnsv4 = append(
    dnsv4,
    binary.BigEndian.Uint32(b[12:]), // <-- FIX 🟢 ensures network byte order
)

This aligns with NetworkManager's API requirements and the consistent pattern used throughout the codebase for network protocol encoding (e.g., net/packet/ip4.go).