[client] Handle UPnP routers that only support permanent leases#5826
[client] Handle UPnP routers that only support permanent leases#5826
Conversation
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (3)
✅ Files skipped from review due to trivial changes (1)
🚧 Files skipped from review as they are similar to previous changes (1)
📝 WalkthroughWalkthroughPort-forward manager now carries mapping TTL through setup and renewal; it falls back to permanent leases (TTL 0) on UPnP error 725 and adjusts renewal cadence to ttl/2 (permanent leases block until cancelled). The persisted Changes
Sequence DiagramsequenceDiagram
actor Client
participant Manager
participant Gateway as NAT Gateway
participant UPnP as UPnP Device
Client->>Manager: CreateMapping(internalPort, timeout=defaultMappingTTL)
Manager->>Gateway: DiscoverGateway()
Gateway-->>Manager: gateway
Manager->>UPnP: AddPortMapping(timeout=defaultMappingTTL)
alt Error 725 (permanent lease required)
UPnP-->>Manager: UPnP fault 725
Manager->>Manager: Detect error 725 with regex
Manager->>UPnP: AddPortMapping(timeout=0)
UPnP-->>Manager: success (ttl=0)
Manager-->>Client: (gateway, mapping.TTL=0)
else Success
UPnP-->>Manager: success (ttl=defaultMappingTTL)
Manager-->>Client: (gateway, mapping.TTL=defaultMappingTTL)
end
alt ttl > 0 (temporary lease)
Manager->>Manager: renewLoop with ticker(ttl/2)
loop Every ttl/2
Manager->>UPnP: AddPortMapping(timeout=ttl)
UPnP-->>Manager: success
end
else ttl == 0 (permanent lease)
Manager->>Manager: renewLoop blocks on cancellation
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
Suggested reviewers
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
eb5352d to
501745e
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@client/internal/engine.go`:
- Line 279: The JS build fails because portforward.NewManager is called with one
argument (services.StateManager) but the JS stub in manager_js.go currently has
signature func NewManager() *Manager causing a mismatch; update the JS stub
signature in manager_js.go to accept the same parameter type as the real
implementation (e.g., func NewManager(stateManager *statemanager.Manager)
*Manager) and return &Manager{} so calls to
portforward.NewManager(services.StateManager) compile, you can ignore the
parameter inside the stub.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 0d548c25-6985-4eb6-80ae-16bf929a51aa
📒 Files selected for processing (3)
client/internal/engine.goclient/internal/portforward/manager.goclient/internal/portforward/manager_test.go
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
client/internal/portforward/manager.go (1)
87-90: Keep unexpected setup failures above info level.This now logs every
setuperror at info, so realcreate port mappingfailures get the same treatment as the expected “no NAT found” case. Consider branching on the wrapped error source here and reserving info for the benign discover/no-gateway path.Also applies to: 150-163
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@client/internal/portforward/manager.go` around lines 87 - 90, The current handling after calling m.setup(ctx) logs all setup failures at Info level; change it to detect the benign "no gateway/no NAT found" error (e.g. by errors.Is/unwrap against the setup's sentinel error like ErrNoGateway or the underlying NAT discovery error) and log that specific case at Info, while logging any other unexpected errors at Error (or higher) and returning; apply the same branching logic to the other setup-result check around the mapping teardown block referenced (the similar handling at lines ~150-163) so only the expected no-gateway path remains Info-level and all real setup/mapping failures are logged as Error-level.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@client/internal/portforward/manager.go`:
- Around line 35-38: The permanent-lease code currently falls back to ttl=0
without persisting state, which can leave stale router rules across crashes;
update the permanent-lease path in manager.go to persist the mapping in the
State manager before enabling the ttl=0 fallback (or gate the retry until
persistence/State.Cleanup support exists). Concretely, in the code paths that
create a permanent mapping (refer to the function/method that sets ttl=0 and the
Stop() code that calls DeletePortMapping), call the existing State persistence
API to record the mapping (so Stop() can intentionally leave state on
DeletePortMapping failure and Startup can run State.Cleanup), and ensure the
retry/gating logic checks that the mapping was persisted before allowing the
ttl=0 fallback.
---
Nitpick comments:
In `@client/internal/portforward/manager.go`:
- Around line 87-90: The current handling after calling m.setup(ctx) logs all
setup failures at Info level; change it to detect the benign "no gateway/no NAT
found" error (e.g. by errors.Is/unwrap against the setup's sentinel error like
ErrNoGateway or the underlying NAT discovery error) and log that specific case
at Info, while logging any other unexpected errors at Error (or higher) and
returning; apply the same branching logic to the other setup-result check around
the mapping teardown block referenced (the similar handling at lines ~150-163)
so only the expected no-gateway path remains Info-level and all real
setup/mapping failures are logged as Error-level.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: f1fe6bde-210c-43b7-9085-c243e667ed04
📒 Files selected for processing (5)
client/internal/portforward/manager.goclient/internal/portforward/manager_test.goclient/internal/portforward/state.goclient/server/state_generic.goclient/server/state_linux.go
💤 Files with no reviewable changes (3)
- client/server/state_generic.go
- client/server/state_linux.go
- client/internal/portforward/state.go
|



Describe your changes
Some routers reject time-limited UPnP port mappings with SOAP error 725 (OnlyPermanentLeasesSupported). This adds fallback handling and cleans up dead code.
State/Cleanupcode that was never wired upsetup, let caller decide log levelIssue ticket number and link
Stack
Checklist
Documentation
Select exactly one:
Internal behavior change, no user-facing config or API changes.
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
Bug Fixes
Chores
Tests