mirror of
https://github.com/netbirdio/netbird.git
synced 2026-04-16 15:26:40 +00:00
* Refactor WG endpoint setup with role-based proxy activation For relay connections, the controller (initiator) now activates the wgProxy before configuring the WG endpoint, while the non-controller (responder) configures the endpoint first with a delayed update, then activates the proxy after. This prevents the responder from sending traffic through the proxy before WireGuard is ready to receive it, avoiding handshake congestion when both sides try to initiate simultaneously. For ICE connections, pass hasRelayBackup as the setEndpointNow flag so the responder sets the endpoint immediately when a relay fallback exists (avoiding the delayed update path since relay is already available as backup). On ICE disconnect with relay fallback, remove the duplicate wgProxyRelay.Work() calls — the relay proxy is already active from initial setup, so re-activating it is unnecessary. In EndpointUpdater, split ConfigureWGEndpoint into explicit configureAsInitiator and configureAsResponder paths, and add the setEndpointNow parameter to let the caller control whether the responder applies the endpoint immediately or defers it. Add unused SwitchWGEndpoint and RemoveEndpointAddress methods. Remove the wgConfigWorkaround sleep from the relay setup path. * Fix redundant wgProxyRelay.Work() call during relay fallback setup * Simplify WireGuard endpoint configuration by removing unused parameters and redundant logic
141 lines
3.5 KiB
Go
141 lines
3.5 KiB
Go
package peer
|
|
|
|
import (
|
|
"context"
|
|
"net"
|
|
"sync"
|
|
"time"
|
|
|
|
"github.com/sirupsen/logrus"
|
|
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
|
)
|
|
|
|
const (
|
|
defaultWgKeepAlive = 25 * time.Second
|
|
fallbackDelay = 5 * time.Second
|
|
)
|
|
|
|
type EndpointUpdater struct {
|
|
log *logrus.Entry
|
|
wgConfig WgConfig
|
|
initiator bool
|
|
|
|
// mu protects cancelFunc
|
|
mu sync.Mutex
|
|
cancelFunc func()
|
|
updateWg sync.WaitGroup
|
|
}
|
|
|
|
func NewEndpointUpdater(log *logrus.Entry, wgConfig WgConfig, initiator bool) *EndpointUpdater {
|
|
return &EndpointUpdater{
|
|
log: log,
|
|
wgConfig: wgConfig,
|
|
initiator: initiator,
|
|
}
|
|
}
|
|
|
|
func (e *EndpointUpdater) ConfigureWGEndpoint(addr *net.UDPAddr, presharedKey *wgtypes.Key) error {
|
|
e.mu.Lock()
|
|
defer e.mu.Unlock()
|
|
|
|
if e.initiator {
|
|
e.log.Debugf("configure up WireGuard as initiator")
|
|
return e.configureAsInitiator(addr, presharedKey)
|
|
}
|
|
|
|
e.log.Debugf("configure up WireGuard as responder")
|
|
return e.configureAsResponder(addr, presharedKey)
|
|
}
|
|
|
|
func (e *EndpointUpdater) SwitchWGEndpoint(addr *net.UDPAddr, presharedKey *wgtypes.Key) error {
|
|
e.mu.Lock()
|
|
defer e.mu.Unlock()
|
|
|
|
// prevent to run new update while cancel the previous update
|
|
e.waitForCloseTheDelayedUpdate()
|
|
|
|
return e.updateWireGuardPeer(addr, presharedKey)
|
|
}
|
|
|
|
func (e *EndpointUpdater) RemoveWgPeer() error {
|
|
e.mu.Lock()
|
|
defer e.mu.Unlock()
|
|
|
|
e.waitForCloseTheDelayedUpdate()
|
|
return e.wgConfig.WgInterface.RemovePeer(e.wgConfig.RemoteKey)
|
|
}
|
|
|
|
func (e *EndpointUpdater) RemoveEndpointAddress() error {
|
|
e.mu.Lock()
|
|
defer e.mu.Unlock()
|
|
|
|
e.waitForCloseTheDelayedUpdate()
|
|
return e.wgConfig.WgInterface.RemoveEndpointAddress(e.wgConfig.RemoteKey)
|
|
}
|
|
|
|
func (e *EndpointUpdater) configureAsInitiator(addr *net.UDPAddr, presharedKey *wgtypes.Key) error {
|
|
if err := e.updateWireGuardPeer(addr, presharedKey); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (e *EndpointUpdater) configureAsResponder(addr *net.UDPAddr, presharedKey *wgtypes.Key) error {
|
|
// prevent to run new update while cancel the previous update
|
|
e.waitForCloseTheDelayedUpdate()
|
|
|
|
e.log.Debugf("configure up WireGuard and wait for handshake")
|
|
var ctx context.Context
|
|
ctx, e.cancelFunc = context.WithCancel(context.Background())
|
|
e.updateWg.Add(1)
|
|
go e.scheduleDelayedUpdate(ctx, addr, presharedKey)
|
|
|
|
if err := e.updateWireGuardPeer(nil, presharedKey); err != nil {
|
|
e.waitForCloseTheDelayedUpdate()
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (e *EndpointUpdater) waitForCloseTheDelayedUpdate() {
|
|
if e.cancelFunc == nil {
|
|
return
|
|
}
|
|
|
|
e.cancelFunc()
|
|
e.cancelFunc = nil
|
|
e.updateWg.Wait()
|
|
}
|
|
|
|
// scheduleDelayedUpdate waits for the fallback period before updating the endpoint
|
|
func (e *EndpointUpdater) scheduleDelayedUpdate(ctx context.Context, addr *net.UDPAddr, presharedKey *wgtypes.Key) {
|
|
defer e.updateWg.Done()
|
|
t := time.NewTimer(fallbackDelay)
|
|
defer t.Stop()
|
|
|
|
select {
|
|
case <-ctx.Done():
|
|
return
|
|
case <-t.C:
|
|
if err := e.updateWireGuardPeer(addr, presharedKey); err != nil {
|
|
e.log.Errorf("failed to update WireGuard peer, address: %s, error: %v", addr, err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func (e *EndpointUpdater) updateWireGuardPeer(endpoint *net.UDPAddr, presharedKey *wgtypes.Key) error {
|
|
return e.wgConfig.WgInterface.UpdatePeer(
|
|
e.wgConfig.RemoteKey,
|
|
e.wgConfig.AllowedIps,
|
|
defaultWgKeepAlive,
|
|
endpoint,
|
|
presharedKey,
|
|
)
|
|
}
|
|
|
|
// wgConfigWorkaround is a workaround for the issue with WireGuard configuration update
|
|
// When update a peer configuration in near to each other time, the second update can be ignored by WireGuard
|
|
func wgConfigWorkaround() {
|
|
time.Sleep(100 * time.Millisecond)
|
|
}
|