[PR #5805] [CLOSED] [client] Fix ICE reconnection loop and buffer candidates before agent initialization #23968

Closed
opened 2026-08-05 06:08:04 -04:00 by saavagebueno · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/netbirdio/netbird/pull/5805
Author: @MichaelUray
Created: 4/6/2026
Status: Closed

Base: mainHead: fix/ice-guard-loop-and-candidate-buffer


📝 Commits (3)

  • e179501 fix(client): prevent ICE reconnection loop when peers share the same public IP
  • 3adbc13 fix(client): address CodeRabbit review on ICE candidate buffer and session ID handling
  • 90b59d9 fix(client): address maintainer review - drop candidate buffer, call OnRemoteCandidate synchronously

📊 Changes

2 files changed (+87 additions, -32 deletions)

View changed files

📝 client/internal/engine.go (+1 -1)
📝 client/internal/peer/worker_ice.go (+86 -31)

📄 Description

Summary

  • Guard Loop Fix: When both peers are behind the same NAT (same public IP), their connectivity guards fire simultaneously every ~12s, each sending a new offer with a new session ID. This cancels the other side's ICE agent before it can finish negotiating, creating an infinite reconnection loop. Fix: skip new offers while agentConnecting == true to let the current ICE attempt complete.
  • Candidate Buffering: Remote ICE candidates that arrive before the local ICE agent is created were silently dropped. Add a pendingCandidates buffer that collects early candidates and flushes them once the agent is ready.

Both fixes are tightly coupled — without the buffer, the guard loop fix alone would still miss early candidates.

Checklist

  • Bug fix
  • Create tests that fail without the change: ICE connections between peers behind the same NAT consistently failed before this fix
  • Documentation not needed — internal ICE behavior change, no user-facing API change

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

Fixes #3669 — Unable to connect to netbird peer on different LAN with the same public IP
Fixes #5672 — ICE Agent is not initialized yet on android app
Related #2703 — Multiple peers behind NAT getting relayed
Related #3339 — Same LAN cannot successfully P2P
Related #4031 — No P2P/direct connection, only relayed
Related #4225 — Netbird Self-Hosted P2P not working
Related #5670 — macOS ICE agent never initializes (arm64)

Summary by CodeRabbit

  • Bug Fixes
    • Buffer early remote connection candidates with a bounded cap to avoid lost candidates and limit memory use.
    • Expand and queue applicable server-reflexive candidates for later delivery instead of dropping them.
    • Prevent rapid reconnection churn by skipping replacement of a recently-started agent while it’s still connecting (adds a short protection window).
    • Flush buffered candidates into a newly created connection and clear the buffer on close to prevent stale data.

🔄 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/5805 **Author:** [@MichaelUray](https://github.com/MichaelUray) **Created:** 4/6/2026 **Status:** ❌ Closed **Base:** `main` ← **Head:** `fix/ice-guard-loop-and-candidate-buffer` --- ### 📝 Commits (3) - [`e179501`](https://github.com/netbirdio/netbird/commit/e1795017d3a9712d25545a82a483b5c7e2156fc6) fix(client): prevent ICE reconnection loop when peers share the same public IP - [`3adbc13`](https://github.com/netbirdio/netbird/commit/3adbc13b793ec0d81f9b133f6552e9a0d8726f7d) fix(client): address CodeRabbit review on ICE candidate buffer and session ID handling - [`90b59d9`](https://github.com/netbirdio/netbird/commit/90b59d944572ee26f597994912b988beb2760fbe) fix(client): address maintainer review - drop candidate buffer, call OnRemoteCandidate synchronously ### 📊 Changes **2 files changed** (+87 additions, -32 deletions) <details> <summary>View changed files</summary> 📝 `client/internal/engine.go` (+1 -1) 📝 `client/internal/peer/worker_ice.go` (+86 -31) </details> ### 📄 Description ## Summary - **Guard Loop Fix**: When both peers are behind the same NAT (same public IP), their connectivity guards fire simultaneously every ~12s, each sending a new offer with a new session ID. This cancels the other side's ICE agent before it can finish negotiating, creating an infinite reconnection loop. Fix: skip new offers while `agentConnecting == true` to let the current ICE attempt complete. - **Candidate Buffering**: Remote ICE candidates that arrive before the local ICE agent is created were silently dropped. Add a `pendingCandidates` buffer that collects early candidates and flushes them once the agent is ready. Both fixes are tightly coupled — without the buffer, the guard loop fix alone would still miss early candidates. ## Checklist - [x] Bug fix - [x] Create tests that fail without the change: ICE connections between peers behind the same NAT consistently failed before this fix - [x] Documentation not needed — internal ICE behavior change, no user-facing API change By submitting this pull request, I confirm that I have read and agree to the terms of the [Contributor License Agreement](https://github.com/netbirdio/netbird/blob/main/CONTRIBUTOR_LICENSE_AGREEMENT.md). ## Related Issues Fixes #3669 — Unable to connect to netbird peer on different LAN with the same public IP Fixes #5672 — ICE Agent is not initialized yet on android app Related #2703 — Multiple peers behind NAT getting relayed Related #3339 — Same LAN cannot successfully P2P Related #4031 — No P2P/direct connection, only relayed Related #4225 — Netbird Self-Hosted P2P not working Related #5670 — macOS ICE agent never initializes (arm64) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Bug Fixes** * Buffer early remote connection candidates with a bounded cap to avoid lost candidates and limit memory use. * Expand and queue applicable server-reflexive candidates for later delivery instead of dropping them. * Prevent rapid reconnection churn by skipping replacement of a recently-started agent while it’s still connecting (adds a short protection window). * Flush buffered candidates into a newly created connection and clear the buffer on close to prevent stale data. <!-- 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 06:08:04 -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#23968