[PR #5971] [MERGED] [client] Drop DNS probes for passive health projection #28835

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

📋 Pull Request Information

Original PR: https://github.com/netbirdio/netbird/pull/5971
Author: @lixmal
Created: 4/23/2026
Status: Merged
Merged: 5/15/2026
Merged by: @lixmal

Base: mainHead: drop-dns-probes


📝 Commits (8)

  • c102592 [client] Drop DNS probes for passive health projection
  • d0f9d80 Harden race fan-out and fix lint
  • db2a62b [client] Add system DNS fallback for Windows, systemd-resolved, NetworkManager (#6000)
  • 5c9aabf Merge branch 'main' into drop-dns-probes
  • a8671e5 Merge remote-tracking branch 'origin/main' into drop-dns-probes
  • 98144e0 Restore value receivers on androidHostManager and reorder iosHostManager methods
  • e8a3e3f Merge remote-tracking branch 'origin/main' into drop-dns-probes
  • 7859ba1 Propagate EDE meta through race result on upstream short-circuit

📊 Changes

22 files changed (+2268 additions, -847 deletions)

View changed files

📝 client/internal/connect.go (+0 -2)
📝 client/internal/dns/host.go (+12 -0)
📝 client/internal/dns/host_android.go (+17 -2)
📝 client/internal/dns/host_ios.go (+9 -0)
📝 client/internal/dns/host_windows.go (+114 -7)
📝 client/internal/dns/hosts_dns_holder.go (+1 -0)
📝 client/internal/dns/local/local.go (+0 -2)
📝 client/internal/dns/mock_server.go (+3 -6)
📝 client/internal/dns/network_manager_unix.go (+206 -5)
📝 client/internal/dns/server.go (+548 -330)
📝 client/internal/dns/server_android.go (+1 -1)
📝 client/internal/dns/server_test.go (+621 -77)
📝 client/internal/dns/systemd_linux.go (+147 -4)
📝 client/internal/dns/upstream.go (+373 -308)
📝 client/internal/dns/upstream_android.go (+3 -2)
📝 client/internal/dns/upstream_general.go (+3 -2)
📝 client/internal/dns/upstream_ios.go (+12 -5)
📝 client/internal/dns/upstream_test.go (+152 -75)
📝 client/internal/engine.go (+2 -14)
📝 client/internal/routemanager/manager.go (+34 -0)

...and 2 more files

📄 Description

Describe your changes

Replaces the synthetic DNS probe with a passive health model driven by real query outcomes, and merges overlapping nameserver groups into a single handler that races them in parallel.

  • Drop active probes (ProbeAvailability, NB_SKIP_DNS_PROBE, exponential-backoff reactivation). No more synthetic SOA traffic and no more deactivate/reactivate dance on upstream failure.
  • Merge every nameserver group targeting the same domain into one handler with multiple inner "races". Within a race upstreams are tried in order; when more than one race exists they fan out in parallel and the first valid answer wins.
  • Record per-upstream outcomes (UpstreamHealth) on every query and project them into NSGroupState every 10s: a group is Healthy if any seen upstream has a recent success, Unhealthy if any has a recent failure with no fresher success, Undecided otherwise.
  • Suppress the "Nameserver group unreachable" event during peer startup: if every upstream is reachable only through the overlay (routed prefix or the WG subnet itself) and no peer has yet reported Connected for it, hold the warning for a grace window that scales with the number of selected routes. Public DNS, connected-overlay DNS, and previously-healthy groups fire immediately.
  • Add GetActiveClientRoutes on route-manager so the DNS layer can tell "routed but no live peer" apart from "routed and reachable".
  • Clear health projection state on Stop so a subsequent Start doesn't inherit sticky flags (everHealthy, warningActive) from the prior session.

Addresses the long-standing "Unable to reach one or more DNS servers" false-positive reported in #5727 and the community forum. Supersedes #5625 (which took the narrower approach of skipping the probe for overlay IPs).

Handler layout

Two nameserver groups targeting example.com:

Group Upstreams (in order)
A 10.0.0.1, 10.0.0.2
B 10.0.1.1

collapse into one handler with two inner races:

upstreamResolverBase(example.com)
  ├── race A: [10.0.0.1, 10.0.0.2]   // sequential fallback
  └── race B: [10.0.1.1]             // parallel with A

Per query: A and B run concurrently. A tries 10.0.0.1, falls back to 10.0.0.2 on failure. First valid answer from either race wins; the loser is cancelled.

Health event transitions

Current verdict Next verdict Warning event Recovery event
Healthy / Undecided Unhealthy (public or connected-overlay upstream) emit --
Healthy / Undecided Unhealthy (overlay only, no connected peer, never healthy) held for grace window --
Healthy Unhealthy (overlay only, no connected peer, was healthy before) emit --
Unhealthy (warning emitted) Unhealthy silent --
Unhealthy (warning emitted) Healthy -- emit
Unhealthy (warning held) Healthy -- silent

Closes #5727
Supersedes #5625

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)

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 (explain why)

Internal refactor of client-side DNS health handling. No user-facing configuration or API change.

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

  • New Features

    • DNS queries now race across nameserver groups and return the first successful response.
    • Routing now uses both "selected" and a new "active" route source.
  • Improvements

    • Upstream health is recorded from real query outcomes and projected into per-group states with timed warnings and recovery events.
    • Nameserver groups for the same domain are merged; projection state is cleared on stop.
    • Eager probing removed in favor of continuous health projection.
  • Tests

    • Expanded tests for race behavior, health projection, event timing, and recovery.

🔄 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/5971 **Author:** [@lixmal](https://github.com/lixmal) **Created:** 4/23/2026 **Status:** ✅ Merged **Merged:** 5/15/2026 **Merged by:** [@lixmal](https://github.com/lixmal) **Base:** `main` ← **Head:** `drop-dns-probes` --- ### 📝 Commits (8) - [`c102592`](https://github.com/netbirdio/netbird/commit/c102592735ffd664c15bd7ba40bcfee8808461ce) [client] Drop DNS probes for passive health projection - [`d0f9d80`](https://github.com/netbirdio/netbird/commit/d0f9d80c3a3fc9ece7438cf430f152aeab7fc69c) Harden race fan-out and fix lint - [`db2a62b`](https://github.com/netbirdio/netbird/commit/db2a62bf2911f059306d0d7de5e55e234f159f7f) [client] Add system DNS fallback for Windows, systemd-resolved, NetworkManager (#6000) - [`5c9aabf`](https://github.com/netbirdio/netbird/commit/5c9aabf4bcbb8a8551e559930767c576e4168bcc) Merge branch 'main' into drop-dns-probes - [`a8671e5`](https://github.com/netbirdio/netbird/commit/a8671e5248175cebf62c8ac8f006e1e9db743187) Merge remote-tracking branch 'origin/main' into drop-dns-probes - [`98144e0`](https://github.com/netbirdio/netbird/commit/98144e099688cbe89f4b8055ea72646361253484) Restore value receivers on androidHostManager and reorder iosHostManager methods - [`e8a3e3f`](https://github.com/netbirdio/netbird/commit/e8a3e3f24b7636a927ed61d9bf0d777311b95d14) Merge remote-tracking branch 'origin/main' into drop-dns-probes - [`7859ba1`](https://github.com/netbirdio/netbird/commit/7859ba1b78c4253e786b527b3be549ae88713262) Propagate EDE meta through race result on upstream short-circuit ### 📊 Changes **22 files changed** (+2268 additions, -847 deletions) <details> <summary>View changed files</summary> 📝 `client/internal/connect.go` (+0 -2) 📝 `client/internal/dns/host.go` (+12 -0) 📝 `client/internal/dns/host_android.go` (+17 -2) 📝 `client/internal/dns/host_ios.go` (+9 -0) 📝 `client/internal/dns/host_windows.go` (+114 -7) 📝 `client/internal/dns/hosts_dns_holder.go` (+1 -0) 📝 `client/internal/dns/local/local.go` (+0 -2) 📝 `client/internal/dns/mock_server.go` (+3 -6) 📝 `client/internal/dns/network_manager_unix.go` (+206 -5) 📝 `client/internal/dns/server.go` (+548 -330) 📝 `client/internal/dns/server_android.go` (+1 -1) 📝 `client/internal/dns/server_test.go` (+621 -77) 📝 `client/internal/dns/systemd_linux.go` (+147 -4) 📝 `client/internal/dns/upstream.go` (+373 -308) 📝 `client/internal/dns/upstream_android.go` (+3 -2) 📝 `client/internal/dns/upstream_general.go` (+3 -2) 📝 `client/internal/dns/upstream_ios.go` (+12 -5) 📝 `client/internal/dns/upstream_test.go` (+152 -75) 📝 `client/internal/engine.go` (+2 -14) 📝 `client/internal/routemanager/manager.go` (+34 -0) _...and 2 more files_ </details> ### 📄 Description ## Describe your changes Replaces the synthetic DNS probe with a passive health model driven by real query outcomes, and merges overlapping nameserver groups into a single handler that races them in parallel. - Drop active probes (`ProbeAvailability`, `NB_SKIP_DNS_PROBE`, exponential-backoff reactivation). No more synthetic SOA traffic and no more deactivate/reactivate dance on upstream failure. - Merge every nameserver group targeting the same domain into one handler with multiple inner "races". Within a race upstreams are tried in order; when more than one race exists they fan out in parallel and the first valid answer wins. - Record per-upstream outcomes (`UpstreamHealth`) on every query and project them into `NSGroupState` every 10s: a group is Healthy if any seen upstream has a recent success, Unhealthy if any has a recent failure with no fresher success, Undecided otherwise. - Suppress the "Nameserver group unreachable" event during peer startup: if every upstream is reachable only through the overlay (routed prefix or the WG subnet itself) and no peer has yet reported Connected for it, hold the warning for a grace window that scales with the number of selected routes. Public DNS, connected-overlay DNS, and previously-healthy groups fire immediately. - Add `GetActiveClientRoutes` on route-manager so the DNS layer can tell "routed but no live peer" apart from "routed and reachable". - Clear health projection state on `Stop` so a subsequent Start doesn't inherit sticky flags (everHealthy, warningActive) from the prior session. Addresses the long-standing "Unable to reach one or more DNS servers" false-positive reported in #5727 and the community forum. Supersedes #5625 (which took the narrower approach of skipping the probe for overlay IPs). ### Handler layout Two nameserver groups targeting `example.com`: | Group | Upstreams (in order) | | --- | --- | | A | 10.0.0.1, 10.0.0.2 | | B | 10.0.1.1 | collapse into one handler with two inner races: ``` upstreamResolverBase(example.com) ├── race A: [10.0.0.1, 10.0.0.2] // sequential fallback └── race B: [10.0.1.1] // parallel with A ``` Per query: A and B run concurrently. A tries 10.0.0.1, falls back to 10.0.0.2 on failure. First valid answer from either race wins; the loser is cancelled. ### Health event transitions | Current verdict | Next verdict | Warning event | Recovery event | | --- | --- | --- | --- | | Healthy / Undecided | Unhealthy (public or connected-overlay upstream) | emit | -- | | Healthy / Undecided | Unhealthy (overlay only, no connected peer, never healthy) | held for grace window | -- | | Healthy | Unhealthy (overlay only, no connected peer, was healthy before) | emit | -- | | Unhealthy (warning emitted) | Unhealthy | silent | -- | | Unhealthy (warning emitted) | Healthy | -- | emit | | Unhealthy (warning held) | Healthy | -- | silent | ## Issue ticket number and link Closes #5727 Supersedes #5625 ## Stack <!-- branch-stack --> ### Checklist - [ ] Is it a bug fix - [ ] Is a typo/documentation fix - [ ] Is a feature enhancement - [x] It is a refactor - [x] Created tests that fail without the change (if possible) > 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 (explain why) Internal refactor of client-side DNS health handling. No user-facing configuration or API change. ### 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 * **New Features** * DNS queries now race across nameserver groups and return the first successful response. * Routing now uses both "selected" and a new "active" route source. * **Improvements** * Upstream health is recorded from real query outcomes and projected into per-group states with timed warnings and recovery events. * Nameserver groups for the same domain are merged; projection state is cleared on stop. * Eager probing removed in favor of continuous health projection. * **Tests** * Expanded tests for race behavior, health projection, event timing, and recovery. <!-- 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:07:00 -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#28835