[PR #95] [CLOSED] fix(relay): re-notify server on reconnect with unchanged endpoint (clients stuck not registered) #1202

Closed
opened 2026-07-14 19:53:39 -05:00 by GiteaMirror · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/fosrl/gerbil/pull/95
Author: @DanivosYoun
Created: 6/13/2026
Status: Closed

Base: mainHead: fix/holepunch-dedup-skips-reregistration


📝 Commits (1)

  • bed4a6e fix(relay): re-notify server on reconnect with unchanged endpoint

📊 Changes

1 file changed (+14 additions, -2 deletions)

View changed files

📝 relay/relay.go (+14 -2)

📄 Description

Problem

Since the hole-punch endpoint dedup (lastEndpointCache) was added in 1.4.x, a reconnecting client can get stuck connected but not registered indefinitely.

relay/relay.go skips notifyServer() (POST /gerbil/update-hole-punch) whenever the client's endpoint state is unchanged from a cached value:

if cached, ok := s.lastEndpointCache.Load(cacheKey); ok && cached.(cachedEndpointState) == newState {
    // Endpoint unchanged - skip the HTTP call but still clear stale sessions.
    s.clearSessionsForIP(endpoint.IP)
    continue   // notifyServer() skipped
}

notifyServer() is not just an idempotent update — it is what (re)registers the client and returns the ProxyMapping that establishes routing. A reconnecting client (e.g. olm) commonly presents the same endpoint (same NAT IP:port + a persistent public key/token) while the server has already torn down its session on the previous disconnect. The dedup then suppresses the needed re-registration, so the server never returns a fresh ProxyMapping and the client stays in "Registering" forever. Because the cache is in-process, every reconnect from the same endpoint keeps hitting the dedup → the failure is permanent until gerbil restarts or the client's endpoint changes.

Observed with gerbil 1.4.0/1.4.1 + olm 1.6.0 (latest) on a remote exit node; olm /status shows connected: true, registered: false, networkSettings: {}. gerbil 1.3.1 is unaffected (no dedup).

Fix

Invalidate the cache entry in the dedup path (where sessions are already being cleared), so the next hole-punch falls through to notifyServer() and re-establishes routing. notifyServer() is already asynchronous, so steady-state load stays bounded; this restores the pre-1.4 behaviour of re-notifying on repeated hole-punches.

Reproduce

  1. gerbil 1.4.x on a (remote) exit node.
  2. Connect an olm client once (primes lastEndpointCache).
  3. Disconnect and reconnect from the same NAT endpoint with the same key.
  4. Client stays connected: true, registered: false; no ProxyMapping is created.
  • Restarting gerbil lets exactly one subsequent connect register again, then it breaks on the next reconnect — confirming the dedup is the cause.

Note

Filed as a hotfix PR; a companion issue with the full analysis follows. An alternative (kept-effective dedup) would be to skip only when an active ProxyMapping for the client still exists; happy to adjust to whichever the maintainers prefer.

🤖 Generated with Claude Code


🔄 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/fosrl/gerbil/pull/95 **Author:** [@DanivosYoun](https://github.com/DanivosYoun) **Created:** 6/13/2026 **Status:** ❌ Closed **Base:** `main` ← **Head:** `fix/holepunch-dedup-skips-reregistration` --- ### 📝 Commits (1) - [`bed4a6e`](https://github.com/fosrl/gerbil/commit/bed4a6eb09dc854684a957d454e65906875037a3) fix(relay): re-notify server on reconnect with unchanged endpoint ### 📊 Changes **1 file changed** (+14 additions, -2 deletions) <details> <summary>View changed files</summary> 📝 `relay/relay.go` (+14 -2) </details> ### 📄 Description ## Problem Since the hole-punch endpoint dedup (`lastEndpointCache`) was added in 1.4.x, a reconnecting client can get stuck **connected but not registered** indefinitely. `relay/relay.go` skips `notifyServer()` (`POST /gerbil/update-hole-punch`) whenever the client's endpoint state is unchanged from a cached value: ```go if cached, ok := s.lastEndpointCache.Load(cacheKey); ok && cached.(cachedEndpointState) == newState { // Endpoint unchanged - skip the HTTP call but still clear stale sessions. s.clearSessionsForIP(endpoint.IP) continue // notifyServer() skipped } ``` `notifyServer()` is not just an idempotent update — it is what (re)registers the client and returns the `ProxyMapping` that establishes routing. A reconnecting client (e.g. **olm**) commonly presents the **same** endpoint (same NAT IP:port + a persistent public key/token) while the server has already torn down its session on the previous disconnect. The dedup then suppresses the needed re-registration, so the server never returns a fresh `ProxyMapping` and the client stays in "Registering" forever. Because the cache is in-process, every reconnect from the same endpoint keeps hitting the dedup → the failure is **permanent** until gerbil restarts or the client's endpoint changes. Observed with gerbil **1.4.0/1.4.1** + olm **1.6.0** (latest) on a remote exit node; olm `/status` shows `connected: true, registered: false, networkSettings: {}`. **gerbil 1.3.1 is unaffected** (no dedup). ## Fix Invalidate the cache entry in the dedup path (where sessions are already being cleared), so the next hole-punch falls through to `notifyServer()` and re-establishes routing. `notifyServer()` is already asynchronous, so steady-state load stays bounded; this restores the pre-1.4 behaviour of re-notifying on repeated hole-punches. ## Reproduce 1. gerbil 1.4.x on a (remote) exit node. 2. Connect an olm client once (primes `lastEndpointCache`). 3. Disconnect and reconnect from the same NAT endpoint with the same key. 4. Client stays `connected: true, registered: false`; no `ProxyMapping` is created. - Restarting gerbil lets exactly one subsequent connect register again, then it breaks on the next reconnect — confirming the dedup is the cause. ## Note Filed as a hotfix PR; a companion issue with the full analysis follows. An alternative (kept-effective dedup) would be to skip only when an active `ProxyMapping` for the client still exists; happy to adjust to whichever the maintainers prefer. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
GiteaMirror added the pull-request label 2026-07-14 19:53:39 -05:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/gerbil#1202