WireGuard Kernel Module Silently Overwrites AllowedIPs in Trie, Breaking Peer Routing Without Errors

The WireGuard kernel module stores AllowedIPs in a single prefix trie per device rather than per peer, causing exact-match insertions of identical CIDR prefixes to reassign nodes and remove them from the previous peer's list. This behavior silently drops routing for affected peers while handshakes and inbound traffic continue, leading to one-way connectivity failures and frame errors. The issue affects road-warrior setups using 0.0.0.0/0, mesh networks, Kubernetes CNI plugins such as Cilium and Calico, and network operating systems including VyOS and OPNsense. No warning is emitted by wg, wg-quick, or the kernel on overwrite, and the longest-prefix-match lookup ensures only equal-length prefixes collide. The root cause resides in allowedips.c where rcu_assign_pointer redirects the trie node and list_move_tail detaches it from the original peer. The same logic appears across Linux, wireguard-go, wireguard-nt, FreeBSD, and OpenBSD implementations.

HabrTranslated from Russian

Read at source

The WireGuard kernel module does not preserve AllowedIPs entries for peers when duplicate prefixes are inserted. A server with multiple peers can lose routing information for earlier peers after a new peer is added with an overlapping AllowedIPs value, even though the on-disk configuration remains correct.

In the reported case, wg show displayed one peer with allowed ips: (none) while the other retained 10.100.0.0/24. The affected peer continued to complete handshakes and deliver inbound packets, yet outbound traffic failed after decryption, incrementing rx_frame_errors on the interface.

Root Cause in the AllowedIPs Trie

AllowedIPs are stored in a single struct allowedips trie belonging to the wg_device. Each peer holds only a list of back-references to trie nodes. When an exact match on both prefix and CIDR length occurs during insertion, the code executes:

  • rcu_assign_pointer(node->peer, peer) — redirects the node to the new peer
  • list_move_tail — removes the node from the previous peer’s allowedips_list

The operation returns success (0) with no warning, and neither wg nor wg-quick performs overlap checks.

Observed Symptoms and Affected Systems

The problem manifests differently depending on prefix length:

  • Two peers with identical 10.100.0.0/24 — the first peer loses its entry
  • One peer with 10.100.0.0/24 and another with 10.100.0.5/32 — both entries remain because lengths differ

Real-world reports include OpenWrt users adding mobile peers with 0.0.0.0/0, VyOS and OPNsense issues from 2020–2025, and Kubernetes clusters using Cilium or Calico where pod CIDRs are generated automatically.

Packet Handling and Diagnostics

Inbound packets from an orphaned peer are dropped after decryption with the message “Packet has unallowed src IP”, incrementing frame errors. Outbound packets to an address without a peer return ENOKEY (“Required key not available”) and increment tx_errors. Dynamic debug can surface the source-address message, but the overwrite itself produces no log entry.

The same trie-reassignment logic exists in wireguard-go, wireguard-nt, FreeBSD, and OpenBSD implementations, confirming the behavior is not Linux-specific.

Meridian digestThe week's most consequential security stories, distilled into one briefing.See the digest

More in Vulnerabilities & Exploits

Telegram for Android Patches Voice Call Vulnerability Enabling Code Execution
Yandex Patches Millions of Stations After eMudhra Certificate Keys Compromised
Cisco ASA and FTD Face DoS Vulnerability in SSL VPN Already Exploited in the Wild