mirror of
https://github.com/netbirdio/netbird.git
synced 2026-04-24 19:26:39 +00:00
* [client] Suppress ICE signaling and periodic offers in force-relay mode When NB_FORCE_RELAY is enabled, skip WorkerICE creation entirely, suppress ICE credentials in offer/answer messages, disable the periodic ICE candidate monitor, and fix isConnectedOnAllWay to only check relay status so the guard stops sending unnecessary offers. * [client] Dynamically suppress ICE based on remote peer's offer credentials Track whether the remote peer includes ICE credentials in its offers/answers. When remote stops sending ICE credentials, skip ICE listener dispatch, suppress ICE credentials in responses, and exclude ICE from the guard connectivity check. When remote resumes sending ICE credentials, re-enable all ICE behavior. * [client] Fix nil SessionID panic and force ICE teardown on relay-only transition Fix nil pointer dereference in signalOfferAnswer when SessionID is nil (relay-only offers). Close stale ICE agent immediately when remote peer stops sending ICE credentials to avoid traffic black-hole during the ICE disconnect timeout. * [client] Add relay-only fallback check when ICE is unavailable Ensure the relay connection is supported with the peer when ICE is disabled to prevent connectivity issues. * [client] Add tri-state connection status to guard for smarter ICE retry (#5828) * [client] Add tri-state connection status to guard for smarter ICE retry Refactor isConnectedOnAllWay to return a ConnStatus enum (Connected, Disconnected, PartiallyConnected) instead of a boolean. When relay is up but ICE is not (PartiallyConnected), limit ICE offers to 3 retries with exponential backoff then fall back to hourly attempts, reducing unnecessary signaling traffic. Fully disconnected peers continue to retry aggressively. External events (relay/ICE disconnect, signal/relay reconnect) reset retry state to give ICE a fresh chance. * [client] Clarify guard ICE retry state and trace log trigger Split iceRetryState.attempt into shouldRetry (pure predicate) and enterHourlyMode (explicit state transition) so the caller in reconnectLoopWithRetry reads top-to-bottom. Restore the original trace-log behavior in isConnectedOnAllWay so it only logs on full disconnection, not on the new PartiallyConnected state. * [client] Extract pure evalConnStatus and add unit tests Split isConnectedOnAllWay into a thin method that snapshots state and a pure evalConnStatus helper that takes a connStatusInputs struct, so the tri-state decision logic can be exercised without constructing full Worker or Handshaker objects. Add table-driven tests covering force-relay, ICE-unavailable and fully-available code paths, plus unit tests for iceRetryState budget/hourly transitions and reset. * [client] Improve grammar in logs and refactor ICE credential checks
62 lines
1.5 KiB
Go
62 lines
1.5 KiB
Go
package guard
|
|
|
|
import (
|
|
"time"
|
|
|
|
log "github.com/sirupsen/logrus"
|
|
)
|
|
|
|
const (
|
|
// maxICERetries is the maximum number of ICE offer attempts when relay is connected
|
|
maxICERetries = 3
|
|
// iceRetryInterval is the periodic retry interval after ICE retries are exhausted
|
|
iceRetryInterval = 1 * time.Hour
|
|
)
|
|
|
|
// iceRetryState tracks the limited ICE retry attempts when relay is already connected.
|
|
// After maxICERetries attempts it switches to a periodic hourly retry.
|
|
type iceRetryState struct {
|
|
log *log.Entry
|
|
retries int
|
|
hourly *time.Ticker
|
|
}
|
|
|
|
func (s *iceRetryState) reset() {
|
|
s.retries = 0
|
|
if s.hourly != nil {
|
|
s.hourly.Stop()
|
|
s.hourly = nil
|
|
}
|
|
}
|
|
|
|
// shouldRetry reports whether the caller should send another ICE offer on this tick.
|
|
// Returns false when the per-cycle retry budget is exhausted and the caller must switch
|
|
// to the hourly ticker via enterHourlyMode + hourlyC.
|
|
func (s *iceRetryState) shouldRetry() bool {
|
|
if s.hourly != nil {
|
|
s.log.Debugf("hourly ICE retry attempt")
|
|
return true
|
|
}
|
|
|
|
s.retries++
|
|
if s.retries <= maxICERetries {
|
|
s.log.Debugf("ICE retry attempt %d/%d", s.retries, maxICERetries)
|
|
return true
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
// enterHourlyMode starts the hourly retry ticker. Must be called after shouldRetry returns false.
|
|
func (s *iceRetryState) enterHourlyMode() {
|
|
s.log.Infof("ICE retries exhausted (%d/%d), switching to hourly retry", maxICERetries, maxICERetries)
|
|
s.hourly = time.NewTicker(iceRetryInterval)
|
|
}
|
|
|
|
func (s *iceRetryState) hourlyC() <-chan time.Time {
|
|
if s.hourly == nil {
|
|
return nil
|
|
}
|
|
return s.hourly.C
|
|
}
|