[PR #7059] [client] Do not misroute WireGuard packets to the STUN handler #29505

Open
opened 2026-08-05 08:08:11 -04:00 by saavagebueno · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/netbirdio/netbird/pull/7059
Author: @lixmal
Created: 8/4/2026
Status: 🔄 Open

Base: mainHead: ice-stun-wg-demux


📝 Commits (3)

  • 7099f59 Do not misroute WireGuard packets to the STUN handler
  • e950a94 Clear the size of a packet consumed by the STUN handler
  • d8fc0fd Require a minimum length before classifying a packet as WireGuard

📊 Changes

2 files changed (+267 additions, -14 deletions)

View changed files

📝 client/iface/bind/ice_bind.go (+52 -14)
client/iface/bind/stun_filter_test.go (+215 -0)

📄 Description

Describe your changes

Packets are classified as STUN by looking only at the magic cookie, which sits at the same offset as the WireGuard receiver index. That index is a random uint32 drawn fresh on every handshake, so a session can draw the cookie value, and while that keypair lives every inbound packet of the session is handed to the STUN handler instead of to WireGuard.

The odds are 1 in 2^32 per session. Since an active session rekeys every 120 seconds, a peer with 50 active sessions draws roughly 25 indices per minute, which puts the expected interval for any single client in the range of centuries. It scales linearly with the number of sessions in existence, however, so it is worth fixing rather than dismissing. The consequence is asymmetric and self-healing, which makes it hard to attribute: outbound traffic keeps flowing because it carries the remote index, while inbound traffic is dropped for up to one rekey interval, and then the next handshake draws a new index and the peer recovers on its own. The handshake watcher does not cover it: only messages carrying our own index in that field are affected, which is transport data and cookie replies, while a handshake response carries the responder's index there and ours further in. Handshakes therefore keep succeeding on schedule and the peer reports itself connected with a recent handshake while nothing inbound arrives. A handshake initiation whose sender index collides is dropped the same way, but WireGuard retries every 5 seconds with a fresh index, so that case clears on the next attempt.

  • Reject WireGuard-shaped packets before the STUN cookie check, which is exact rather than heuristic because the low byte of a STUN message type is non-zero for every method in use
  • Classify on the received length instead of the buffer capacity, so leftover bytes from an earlier packet cannot decide the outcome
  • Clear the reported size of a packet consumed by the STUN handler, so WireGuard no longer processes that buffer again under the previous packet's length and endpoint
  • Skip counting a packet too short to hold a WireGuard header as peer activity

Only the userspace path is affected. In kernel mode the equivalent check lives in a BPF filter on a raw socket, which receives a copy rather than consuming the packet, so a collision there costs a log line instead of the traffic.

Trivial fix, no behavior or API change.

Stack

Checklist

  • Is it a bug fix
  • Is a typo/documentation fix
  • Is a feature enhancement
  • It is a refactor
  • Created tests that fail without the change (if possible)
  • I ran and tested this change locally — I did not rely on CI to find out whether it works
  • This PR has a single purpose (not a fix + refactor + feature in one)
  • This change is a trivial fix, OR it links an issue the NetBird team agreed on beforehand. Changes to the public API, gRPC protocols, functionality behavior, CLI / service flags, or new features always need that agreement first. See CONTRIBUTING.md.

By submitting this pull request, you confirm that you have read and agree to the terms of the Contributor License Agreement.

Documentation

Select exactly one:

  • I added/updated documentation for this change
  • Documentation is not needed for this change (internal packet classification, no user-visible surface)

Docs PR URL (required if "docs added" is checked)

Paste the PR link from https://github.com/netbirdio/docs here:

https://github.com/netbirdio/docs/pull/__

Summary by CodeRabbit

  • Bug Fixes
    • Improved network packet handling by safely validating short, malformed, and partially received packets.
    • Improved distinction between WireGuard traffic and STUN messages, including packets with overlapping header patterns.
    • Prevented stale data from previously processed packets from affecting subsequent network activity detection.
    • Improved handling and reporting of invalid STUN messages.

🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/netbirdio/netbird/pull/7059 **Author:** [@lixmal](https://github.com/lixmal) **Created:** 8/4/2026 **Status:** 🔄 Open **Base:** `main` ← **Head:** `ice-stun-wg-demux` --- ### 📝 Commits (3) - [`7099f59`](https://github.com/netbirdio/netbird/commit/7099f5907b9d632fb3fd1419128af6839e26c651) Do not misroute WireGuard packets to the STUN handler - [`e950a94`](https://github.com/netbirdio/netbird/commit/e950a9487fbc3e0402e4863dd7b85e9f1a7f2a29) Clear the size of a packet consumed by the STUN handler - [`d8fc0fd`](https://github.com/netbirdio/netbird/commit/d8fc0fdbe45ea5ad4dcff12d044d814c7dfe82fe) Require a minimum length before classifying a packet as WireGuard ### 📊 Changes **2 files changed** (+267 additions, -14 deletions) <details> <summary>View changed files</summary> 📝 `client/iface/bind/ice_bind.go` (+52 -14) ➕ `client/iface/bind/stun_filter_test.go` (+215 -0) </details> ### 📄 Description ## Describe your changes Packets are classified as STUN by looking only at the magic cookie, which sits at the same offset as the WireGuard receiver index. That index is a random uint32 drawn fresh on every handshake, so a session can draw the cookie value, and while that keypair lives every inbound packet of the session is handed to the STUN handler instead of to WireGuard. The odds are 1 in 2^32 per session. Since an active session rekeys every 120 seconds, a peer with 50 active sessions draws roughly 25 indices per minute, which puts the expected interval for any single client in the range of centuries. It scales linearly with the number of sessions in existence, however, so it is worth fixing rather than dismissing. The consequence is asymmetric and self-healing, which makes it hard to attribute: outbound traffic keeps flowing because it carries the remote index, while inbound traffic is dropped for up to one rekey interval, and then the next handshake draws a new index and the peer recovers on its own. The handshake watcher does not cover it: only messages carrying our own index in that field are affected, which is transport data and cookie replies, while a handshake response carries the responder's index there and ours further in. Handshakes therefore keep succeeding on schedule and the peer reports itself connected with a recent handshake while nothing inbound arrives. A handshake initiation whose sender index collides is dropped the same way, but WireGuard retries every 5 seconds with a fresh index, so that case clears on the next attempt. - Reject WireGuard-shaped packets before the STUN cookie check, which is exact rather than heuristic because the low byte of a STUN message type is non-zero for every method in use - Classify on the received length instead of the buffer capacity, so leftover bytes from an earlier packet cannot decide the outcome - Clear the reported size of a packet consumed by the STUN handler, so WireGuard no longer processes that buffer again under the previous packet's length and endpoint - Skip counting a packet too short to hold a WireGuard header as peer activity Only the userspace path is affected. In kernel mode the equivalent check lives in a BPF filter on a raw socket, which receives a copy rather than consuming the packet, so a collision there costs a log line instead of the traffic. ## Issue ticket number and link Trivial fix, no behavior or API change. ## Stack <!-- branch-stack --> ### Checklist - [x] Is it a bug fix - [ ] Is a typo/documentation fix - [ ] Is a feature enhancement - [ ] It is a refactor - [x] Created tests that fail without the change (if possible) - [x] I ran and tested this change locally — I did not rely on CI to find out whether it works - [x] This PR has a single purpose (not a fix + refactor + feature in one) - [x] This change is a trivial fix, **OR** it links an issue the NetBird team agreed on beforehand. Changes to the public API, gRPC protocols, functionality behavior, CLI / service flags, or new features always need that agreement first. See [CONTRIBUTING.md](https://github.com/netbirdio/netbird/blob/main/CONTRIBUTING.md#ticket-first-pr-second). > By submitting this pull request, you confirm that you have read and agree to the terms of the [Contributor License Agreement](https://github.com/netbirdio/netbird/blob/main/CONTRIBUTOR_LICENSE_AGREEMENT.md). ## Documentation Select exactly one: - [ ] I added/updated documentation for this change - [x] Documentation is **not needed** for this change (internal packet classification, no user-visible surface) ### Docs PR URL (required if "docs added" is checked) Paste the PR link from https://github.com/netbirdio/docs here: https://github.com/netbirdio/docs/pull/__ <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **Bug Fixes** - Improved network packet handling by safely validating short, malformed, and partially received packets. - Improved distinction between WireGuard traffic and STUN messages, including packets with overlapping header patterns. - Prevented stale data from previously processed packets from affecting subsequent network activity detection. - Improved handling and reporting of invalid STUN messages. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
saavagebueno added the pull-request label 2026-08-05 08:08:11 -04:00
Sign in to join this conversation.
No Label pull-request
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: DYNR/netbird#29505