[PR #6339] [MERGED] [relay, client] Fall back to WebSocket relay transport on oversized QUIC datagrams #28054

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

📋 Pull Request Information

Original PR: https://github.com/netbirdio/netbird/pull/6339
Author: @lixmal
Created: 6/4/2026
Status: Merged
Merged: 6/9/2026
Merged by: @lixmal

Base: mainHead: relay-quic-ws-datagram-fallback


📝 Commits (6)

  • 3435267 Add relay transport selection and fall back to WebSocket on oversized QUIC datagrams
  • fbd97d6 Raise wasm build size limit to 60MB
  • 36f346c Log skipped relay fallback and bail sequential dial on cancelled context
  • ac70eb2 Make relay datagram fallback transport-agnostic via a dialer capability
  • 3bad4c9 Run relay datagram fallback once per connection and annotate the datagram-sized marker
  • d50241b Move nonDatagramSized below the transportFallback methods

📊 Changes

15 files changed (+664 additions, -23 deletions)

View changed files

📝 .github/workflows/wasm-build-validation.yml (+2 -2)
📝 shared/relay/client/client.go (+68 -5)
shared/relay/client/dialer/capability.go (+18 -0)
📝 shared/relay/client/dialer/net/err.go (+5 -0)
📝 shared/relay/client/dialer/quic/conn.go (+14 -6)
📝 shared/relay/client/dialer/quic/quic.go (+30 -0)
📝 shared/relay/client/dialer/race_dialer.go (+39 -0)
📝 shared/relay/client/dialer/race_dialer_test.go (+63 -0)
📝 shared/relay/client/dialers_generic.go (+36 -5)
shared/relay/client/dialers_generic_test.go (+101 -0)
📝 shared/relay/client/dialers_js.go (+5 -1)
📝 shared/relay/client/manager.go (+12 -4)
📝 shared/relay/client/picker.go (+2 -0)
shared/relay/client/transport.go (+129 -0)
shared/relay/client/transport_test.go (+140 -0)

📄 Description

Describe your changes

Relayed traffic over QUIC is carried in DATAGRAM frames, which must fit a single QUIC packet. When the path MTU between a client and a relay is too small to carry a full-size overlay packet (overlay packet + WireGuard overhead + relay framing), the relay write fails with "DATAGRAM frame too large" and every full-size packet to that relayed peer is silently dropped, so bulk transfers stall while the tunnel still looks up. The race dialer made this intermittent: the same relay would sometimes connect over QUIC (broken) and sometimes over WebSocket (fine).

  • Add NB_RELAY_TRANSPORT to select the relay transport: auto (default, race QUIC and WebSocket), quic, ws, and prefer-quic / prefer-ws which try one transport first and fall back to the other on connect failure instead of racing.
  • On the first oversized QUIC datagram, record a WebSocket fallback for that relay and reconnect over WebSocket. The preference is shared across the relay manager so it survives reconnects of both the home relay and foreign relays, with a window that grows on repeated failures. When the transport is pinned to quic, the fallback is skipped.
  • Map the QUIC datagram-too-large error to a single warning that includes the datagram size and the path budget, instead of logging it twice per packet.
  • Log the QUIC path MTU discovery result and connection close reason, scoped to the relay address, to make relay path issues diagnosable.

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)
  • This change does not modify the public API, gRPC protocols, functionality behavior, CLI / service flags, or introduce a new feature — OR I have discussed it with the NetBird team beforehand (link the issue / Slack thread in the description). 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 (explain why)

A new opt-in env var for an edge transport behavior; default behavior is unchanged.

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

    • Env var to control relay transport mode (QUIC, WebSocket, auto, prefer).
    • Automatic fallback from QUIC datagrams to WebSocket with per-server sticky backoff (backoff grows, caps) and safer reconnects after oversized-datagram events.
    • Optional sequential dialing so transports can be tried in order instead of raced.
    • Improved QUIC connection tracing and detection of datagram-capable transports.
  • Tests

    • Added tests for sequential dialing, transport-mode parsing, sticky fallback, and datagram-too-large handling.
  • Chores

    • Raised Wasm build size check threshold.

🔄 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/6339 **Author:** [@lixmal](https://github.com/lixmal) **Created:** 6/4/2026 **Status:** ✅ Merged **Merged:** 6/9/2026 **Merged by:** [@lixmal](https://github.com/lixmal) **Base:** `main` ← **Head:** `relay-quic-ws-datagram-fallback` --- ### 📝 Commits (6) - [`3435267`](https://github.com/netbirdio/netbird/commit/3435267a23bdda15f3f800374aa626709225a678) Add relay transport selection and fall back to WebSocket on oversized QUIC datagrams - [`fbd97d6`](https://github.com/netbirdio/netbird/commit/fbd97d6da5652f855a89274e47f2a2b22a821924) Raise wasm build size limit to 60MB - [`36f346c`](https://github.com/netbirdio/netbird/commit/36f346c0fe15c03f62359cd69264620fa546bd2c) Log skipped relay fallback and bail sequential dial on cancelled context - [`ac70eb2`](https://github.com/netbirdio/netbird/commit/ac70eb2c8e867e33f3461f15809ba92ad69e24c8) Make relay datagram fallback transport-agnostic via a dialer capability - [`3bad4c9`](https://github.com/netbirdio/netbird/commit/3bad4c939bb74323408e8a816ab129f813350c56) Run relay datagram fallback once per connection and annotate the datagram-sized marker - [`d50241b`](https://github.com/netbirdio/netbird/commit/d50241b75d3e4c8157c51830e9f77b26c579f580) Move nonDatagramSized below the transportFallback methods ### 📊 Changes **15 files changed** (+664 additions, -23 deletions) <details> <summary>View changed files</summary> 📝 `.github/workflows/wasm-build-validation.yml` (+2 -2) 📝 `shared/relay/client/client.go` (+68 -5) ➕ `shared/relay/client/dialer/capability.go` (+18 -0) 📝 `shared/relay/client/dialer/net/err.go` (+5 -0) 📝 `shared/relay/client/dialer/quic/conn.go` (+14 -6) 📝 `shared/relay/client/dialer/quic/quic.go` (+30 -0) 📝 `shared/relay/client/dialer/race_dialer.go` (+39 -0) 📝 `shared/relay/client/dialer/race_dialer_test.go` (+63 -0) 📝 `shared/relay/client/dialers_generic.go` (+36 -5) ➕ `shared/relay/client/dialers_generic_test.go` (+101 -0) 📝 `shared/relay/client/dialers_js.go` (+5 -1) 📝 `shared/relay/client/manager.go` (+12 -4) 📝 `shared/relay/client/picker.go` (+2 -0) ➕ `shared/relay/client/transport.go` (+129 -0) ➕ `shared/relay/client/transport_test.go` (+140 -0) </details> ### 📄 Description ## Describe your changes Relayed traffic over QUIC is carried in DATAGRAM frames, which must fit a single QUIC packet. When the path MTU between a client and a relay is too small to carry a full-size overlay packet (overlay packet + WireGuard overhead + relay framing), the relay write fails with "DATAGRAM frame too large" and every full-size packet to that relayed peer is silently dropped, so bulk transfers stall while the tunnel still looks up. The race dialer made this intermittent: the same relay would sometimes connect over QUIC (broken) and sometimes over WebSocket (fine). - Add `NB_RELAY_TRANSPORT` to select the relay transport: `auto` (default, race QUIC and WebSocket), `quic`, `ws`, and `prefer-quic` / `prefer-ws` which try one transport first and fall back to the other on connect failure instead of racing. - On the first oversized QUIC datagram, record a WebSocket fallback for that relay and reconnect over WebSocket. The preference is shared across the relay manager so it survives reconnects of both the home relay and foreign relays, with a window that grows on repeated failures. When the transport is pinned to `quic`, the fallback is skipped. - Map the QUIC datagram-too-large error to a single warning that includes the datagram size and the path budget, instead of logging it twice per packet. - Log the QUIC path MTU discovery result and connection close reason, scoped to the relay address, to make relay path issues diagnosable. ## Issue ticket number and link ## Stack <!-- branch-stack --> ### Checklist - [x] Is it a bug fix - [ ] Is a typo/documentation fix - [x] Is a feature enhancement - [ ] It is a refactor - [x] Created tests that fail without the change (if possible) - [ ] This change does **not** modify the public API, gRPC protocols, functionality behavior, CLI / service flags, or introduce a new feature — **OR** I have discussed it with the NetBird team beforehand (link the issue / Slack thread in the description). See [CONTRIBUTING.md](https://github.com/netbirdio/netbird/blob/main/CONTRIBUTING.md#discuss-changes-with-the-netbird-team-first). > 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) A new opt-in env var for an edge transport behavior; default behavior is unchanged. ### 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** * Env var to control relay transport mode (QUIC, WebSocket, auto, prefer). * Automatic fallback from QUIC datagrams to WebSocket with per-server sticky backoff (backoff grows, caps) and safer reconnects after oversized-datagram events. * Optional sequential dialing so transports can be tried in order instead of raced. * Improved QUIC connection tracing and detection of datagram-capable transports. * **Tests** * Added tests for sequential dialing, transport-mode parsing, sticky fallback, and datagram-too-large handling. * **Chores** * Raised Wasm build size check threshold. <!-- 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 07:09:36 -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#28054