Compare commits
8 Commits
debug-and-
...
merged-fix
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6efc1a61fe | ||
|
|
4a45e40578 | ||
|
|
85ad236f6d | ||
|
|
e54ab4b5e1 | ||
|
|
23977c6409 | ||
|
|
a73cb99045 | ||
|
|
ccf40fefaf | ||
|
|
b2548a4037 |
@@ -118,6 +118,8 @@ type Conn struct {
|
||||
|
||||
// debug purpose
|
||||
dumpState *stateDump
|
||||
|
||||
endpointUpdater *endpointUpdater
|
||||
}
|
||||
|
||||
// NewConn creates a new not opened Conn to the remote peer.
|
||||
@@ -141,6 +143,11 @@ func NewConn(config ConnConfig, services ServiceDependencies) (*Conn, error) {
|
||||
statusRelay: worker.NewAtomicStatus(),
|
||||
statusICE: worker.NewAtomicStatus(),
|
||||
dumpState: newStateDump(config.Key, connLog, services.StatusRecorder),
|
||||
endpointUpdater: &endpointUpdater{
|
||||
log: connLog,
|
||||
wgConfig: config.WgConfig,
|
||||
initiator: isWireGuardInitiator(config),
|
||||
},
|
||||
}
|
||||
|
||||
return conn, nil
|
||||
@@ -250,7 +257,7 @@ func (conn *Conn) Close(signalToRemote bool) {
|
||||
conn.wgProxyICE = nil
|
||||
}
|
||||
|
||||
if err := conn.removeWgPeer(); err != nil {
|
||||
if err := conn.endpointUpdater.removeWgPeer(); err != nil {
|
||||
conn.Log.Errorf("failed to remove wg endpoint: %v", err)
|
||||
}
|
||||
|
||||
@@ -377,13 +384,12 @@ func (conn *Conn) onICEConnectionIsReady(priority conntype.ConnPriority, iceConn
|
||||
}
|
||||
|
||||
conn.Log.Infof("configure WireGuard endpoint to: %s", ep.String())
|
||||
if err = conn.configureWGEndpoint(ep, iceConnInfo.RosenpassPubKey); err != nil {
|
||||
if err = conn.endpointUpdater.configureWGEndpoint(ep, iceConnInfo.RosenpassPubKey); err != nil {
|
||||
conn.handleConfigurationFailure(err, wgProxy)
|
||||
return
|
||||
}
|
||||
wgConfigWorkaround()
|
||||
|
||||
|
||||
if conn.wgProxyRelay != nil {
|
||||
conn.Log.Debugf("redirect packages from relayed conn to WireGuard")
|
||||
conn.wgProxyRelay.RedirectAs(ep)
|
||||
@@ -417,7 +423,7 @@ func (conn *Conn) onICEStateDisconnected() {
|
||||
conn.dumpState.SwitchToRelay()
|
||||
conn.wgProxyRelay.Work()
|
||||
|
||||
if err := conn.configureWGEndpoint(conn.wgProxyRelay.EndpointAddr(), conn.rosenpassRemoteKey); err != nil {
|
||||
if err := conn.endpointUpdater.configureWGEndpoint(conn.wgProxyRelay.EndpointAddr(), conn.rosenpassRemoteKey); err != nil {
|
||||
conn.Log.Errorf("failed to switch to relay conn: %v", err)
|
||||
}
|
||||
|
||||
@@ -486,7 +492,7 @@ func (conn *Conn) onRelayConnectionIsReady(rci RelayConnInfo) {
|
||||
}
|
||||
|
||||
wgProxy.Work()
|
||||
if err := conn.configureWGEndpoint(wgProxy.EndpointAddr(), rci.rosenpassPubKey); err != nil {
|
||||
if err := conn.endpointUpdater.configureWGEndpoint(wgProxy.EndpointAddr(), rci.rosenpassPubKey); err != nil {
|
||||
if err := wgProxy.CloseConn(); err != nil {
|
||||
conn.Log.Warnf("Failed to close relay connection: %v", err)
|
||||
}
|
||||
@@ -554,17 +560,6 @@ func (conn *Conn) onGuardEvent() {
|
||||
}
|
||||
}
|
||||
|
||||
func (conn *Conn) configureWGEndpoint(addr *net.UDPAddr, remoteRPKey []byte) error {
|
||||
presharedKey := conn.presharedKey(remoteRPKey)
|
||||
return conn.config.WgConfig.WgInterface.UpdatePeer(
|
||||
conn.config.WgConfig.RemoteKey,
|
||||
conn.config.WgConfig.AllowedIps,
|
||||
defaultWgKeepAlive,
|
||||
addr,
|
||||
presharedKey,
|
||||
)
|
||||
}
|
||||
|
||||
func (conn *Conn) updateRelayStatus(relayServerAddr string, rosenpassPubKey []byte) {
|
||||
peerState := State{
|
||||
PubKey: conn.config.Key,
|
||||
@@ -707,10 +702,6 @@ func (conn *Conn) isICEActive() bool {
|
||||
return (conn.currentConnPriority == conntype.ICEP2P || conn.currentConnPriority == conntype.ICETurn) && conn.statusICE.Get() == worker.StatusConnected
|
||||
}
|
||||
|
||||
func (conn *Conn) removeWgPeer() error {
|
||||
return conn.config.WgConfig.WgInterface.RemovePeer(conn.config.WgConfig.RemoteKey)
|
||||
}
|
||||
|
||||
func (conn *Conn) handleConfigurationFailure(err error, wgProxy wgproxy.Proxy) {
|
||||
conn.Log.Warnf("Failed to update wg peer configuration: %v", err)
|
||||
if wgProxy != nil {
|
||||
@@ -791,6 +782,10 @@ func isController(config ConnConfig) bool {
|
||||
return config.LocalKey > config.Key
|
||||
}
|
||||
|
||||
func isWireGuardInitiator(config ConnConfig) bool {
|
||||
return isController(config)
|
||||
}
|
||||
|
||||
func isRosenpassEnabled(remoteRosenpassPubKey []byte) bool {
|
||||
return remoteRosenpassPubKey != nil
|
||||
}
|
||||
|
||||
88
client/internal/peer/endpoint.go
Normal file
88
client/internal/peer/endpoint.go
Normal file
@@ -0,0 +1,88 @@
|
||||
package peer
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// fallbackDelay could be const but because of testing it is a var
|
||||
var fallbackDelay = 5 * time.Second
|
||||
|
||||
type endpointUpdater struct {
|
||||
log *logrus.Entry
|
||||
wgConfig WgConfig
|
||||
initiator bool
|
||||
|
||||
cancelFunc func()
|
||||
configUpdateMutex sync.Mutex
|
||||
}
|
||||
|
||||
// configureWGEndpoint sets up the WireGuard endpoint configuration.
|
||||
// The initiator immediately configures the endpoint, while the non-initiator
|
||||
// waits for a fallback period before configuring to avoid handshake congestion.
|
||||
func (e *endpointUpdater) configureWGEndpoint(addr *net.UDPAddr, remoteRPKey []byte) error {
|
||||
if e.initiator {
|
||||
return e.updateWireGuardPeer(addr, remoteRPKey)
|
||||
}
|
||||
|
||||
// prevent to run new update while cancel the previous update
|
||||
e.configUpdateMutex.Lock()
|
||||
if e.cancelFunc != nil {
|
||||
e.cancelFunc()
|
||||
}
|
||||
e.configUpdateMutex.Unlock()
|
||||
|
||||
var ctx context.Context
|
||||
ctx, e.cancelFunc = context.WithCancel(context.Background())
|
||||
go e.scheduleDelayedUpdate(ctx, addr, remoteRPKey)
|
||||
|
||||
return e.updateWireGuardPeer(nil, remoteRPKey)
|
||||
}
|
||||
|
||||
func (e *endpointUpdater) removeWgPeer() error {
|
||||
e.configUpdateMutex.Lock()
|
||||
defer e.configUpdateMutex.Unlock()
|
||||
|
||||
if e.cancelFunc != nil {
|
||||
e.cancelFunc()
|
||||
}
|
||||
|
||||
return e.wgConfig.WgInterface.RemovePeer(e.wgConfig.RemoteKey)
|
||||
}
|
||||
|
||||
// scheduleDelayedUpdate waits for the fallback period before updating the endpoint
|
||||
func (e *endpointUpdater) scheduleDelayedUpdate(ctx context.Context, addr *net.UDPAddr, remoteRPKey []byte) {
|
||||
t := time.NewTimer(fallbackDelay)
|
||||
defer t.Stop()
|
||||
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
case <-t.C:
|
||||
e.configUpdateMutex.Lock()
|
||||
defer e.configUpdateMutex.Unlock()
|
||||
|
||||
if ctx.Err() != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if err := e.updateWireGuardPeer(addr, remoteRPKey); err != nil {
|
||||
e.log.Errorf("failed to update WireGuard peer, address: %s, error: %v", addr, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (e *endpointUpdater) updateWireGuardPeer(endpoint *net.UDPAddr, remoteRPKey []byte) error {
|
||||
// todo add, "presharedKey := e.presharedKey(remote)"
|
||||
return e.wgConfig.WgInterface.UpdatePeer(
|
||||
e.wgConfig.RemoteKey,
|
||||
e.wgConfig.AllowedIps,
|
||||
defaultWgKeepAlive,
|
||||
endpoint,
|
||||
e.wgConfig.PreSharedKey,
|
||||
)
|
||||
}
|
||||
@@ -33,6 +33,7 @@ type WGWatcher struct {
|
||||
ctx context.Context
|
||||
ctxCancel context.CancelFunc
|
||||
ctxLock sync.Mutex
|
||||
enabled time.Time
|
||||
}
|
||||
|
||||
func NewWGWatcher(log *log.Entry, wgIfaceStater WGInterfaceStater, peerKey string, stateDump *stateDump) *WGWatcher {
|
||||
@@ -48,6 +49,7 @@ func NewWGWatcher(log *log.Entry, wgIfaceStater WGInterfaceStater, peerKey strin
|
||||
func (w *WGWatcher) EnableWgWatcher(parentCtx context.Context, onDisconnectedFn func()) {
|
||||
w.log.Debugf("enable WireGuard watcher")
|
||||
w.ctxLock.Lock()
|
||||
w.enabled = time.Now()
|
||||
|
||||
if w.ctx != nil && w.ctx.Err() == nil {
|
||||
w.log.Errorf("WireGuard watcher already enabled")
|
||||
@@ -87,6 +89,8 @@ func (w *WGWatcher) DisableWgWatcher() {
|
||||
func (w *WGWatcher) periodicHandshakeCheck(ctx context.Context, ctxCancel context.CancelFunc, onDisconnectedFn func(), initialHandshake time.Time) {
|
||||
w.log.Infof("WireGuard watcher started")
|
||||
|
||||
debugTicker := time.NewTicker(time.Second)
|
||||
|
||||
timer := time.NewTimer(wgHandshakeOvertime)
|
||||
defer timer.Stop()
|
||||
defer ctxCancel()
|
||||
@@ -95,15 +99,28 @@ func (w *WGWatcher) periodicHandshakeCheck(ctx context.Context, ctxCancel contex
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-debugTicker.C:
|
||||
handshake, err := w.wgState()
|
||||
if err != nil {
|
||||
w.log.Errorf("failed to read wg stats: %v", err)
|
||||
continue
|
||||
}
|
||||
if !handshake.IsZero() {
|
||||
w.log.Infof("first wg handshake detected at: %s, %s", handshake, time.Since(w.enabled))
|
||||
debugTicker.Stop()
|
||||
}
|
||||
case <-timer.C:
|
||||
handshake, ok := w.handshakeCheck(lastHandshake)
|
||||
if !ok {
|
||||
onDisconnectedFn()
|
||||
return
|
||||
}
|
||||
if lastHandshake.IsZero() {
|
||||
w.log.Infof("first wg handshake detected at: %s", handshake)
|
||||
}
|
||||
/*
|
||||
// todo: put it back if remove debug ticker
|
||||
if lastHandshake.IsZero() {
|
||||
w.log.Infof("first wg handshake detected at: %s", handshake)
|
||||
}
|
||||
*/
|
||||
|
||||
lastHandshake = *handshake
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"os"
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
@@ -31,42 +32,100 @@ type Win32_BIOS struct {
|
||||
SerialNumber string
|
||||
}
|
||||
|
||||
// GetInfo retrieves and parses the system information
|
||||
func GetInfo(ctx context.Context) *Info {
|
||||
osName, osVersion := getOSNameAndVersion()
|
||||
buildVersion := getBuildVersion()
|
||||
// CachedStaticInfo holds all the static system information that never changes
|
||||
type CachedStaticInfo struct {
|
||||
OSName string
|
||||
OSVersion string
|
||||
KernelVersion string
|
||||
SystemSerialNumber string
|
||||
SystemProductName string
|
||||
SystemManufacturer string
|
||||
Environment Environment // Assuming this is from your StaticInfo struct
|
||||
GoOS string
|
||||
CPUs int
|
||||
Kernel string
|
||||
Platform string
|
||||
}
|
||||
|
||||
var (
|
||||
cachedStaticInfo *CachedStaticInfo
|
||||
staticInfoOnce sync.Once
|
||||
)
|
||||
|
||||
func init() {
|
||||
go initStaticInfo()
|
||||
}
|
||||
|
||||
// initStaticInfo initializes all static system information once
|
||||
func initStaticInfo() {
|
||||
staticInfoOnce.Do(func() {
|
||||
log.Debugf("initializing static system information (one-time operation)")
|
||||
start := time.Now()
|
||||
|
||||
// Get OS info
|
||||
osName, osVersion := getOSNameAndVersion()
|
||||
buildVersion := getBuildVersion()
|
||||
|
||||
// Get hardware info
|
||||
si := updateStaticInfo()
|
||||
|
||||
cachedStaticInfo = &CachedStaticInfo{
|
||||
OSName: osName,
|
||||
OSVersion: osVersion,
|
||||
KernelVersion: buildVersion,
|
||||
SystemSerialNumber: si.SystemSerialNumber,
|
||||
SystemProductName: si.SystemProductName,
|
||||
SystemManufacturer: si.SystemManufacturer,
|
||||
Environment: si.Environment,
|
||||
GoOS: runtime.GOOS,
|
||||
CPUs: runtime.NumCPU(),
|
||||
Kernel: "windows",
|
||||
Platform: "unknown",
|
||||
}
|
||||
|
||||
log.Debugf("static system information initialized in %s", time.Since(start))
|
||||
})
|
||||
}
|
||||
|
||||
// GetInfo retrieves system information (static info cached, dynamic info fresh)
|
||||
func GetInfo(ctx context.Context) *Info {
|
||||
initStaticInfo()
|
||||
log.Debugf("gathering dynamic system information")
|
||||
start := time.Now()
|
||||
|
||||
// Only gather dynamic information that might change
|
||||
log.Debugf("gathering networkAddresses")
|
||||
addrs, err := networkAddresses()
|
||||
if err != nil {
|
||||
log.Warnf("failed to discover network addresses: %s", err)
|
||||
}
|
||||
|
||||
start := time.Now()
|
||||
si := updateStaticInfo()
|
||||
if time.Since(start) > 1*time.Second {
|
||||
log.Warnf("updateStaticInfo took %s", time.Since(start))
|
||||
}
|
||||
|
||||
gio := &Info{
|
||||
Kernel: "windows",
|
||||
OSVersion: osVersion,
|
||||
Platform: "unknown",
|
||||
OS: osName,
|
||||
GoOS: runtime.GOOS,
|
||||
CPUs: runtime.NumCPU(),
|
||||
KernelVersion: buildVersion,
|
||||
NetworkAddresses: addrs,
|
||||
SystemSerialNumber: si.SystemSerialNumber,
|
||||
SystemProductName: si.SystemProductName,
|
||||
SystemManufacturer: si.SystemManufacturer,
|
||||
Environment: si.Environment,
|
||||
}
|
||||
|
||||
log.Debugf("gathering Hostname")
|
||||
systemHostname, _ := os.Hostname()
|
||||
gio.Hostname = extractDeviceName(ctx, systemHostname)
|
||||
gio.NetbirdVersion = version.NetbirdVersion()
|
||||
gio.UIVersion = extractUserAgent(ctx)
|
||||
|
||||
// Create Info struct using cached static info + fresh dynamic info
|
||||
gio := &Info{
|
||||
// Static information (cached)
|
||||
Kernel: cachedStaticInfo.Kernel,
|
||||
OSVersion: cachedStaticInfo.OSVersion,
|
||||
Platform: cachedStaticInfo.Platform,
|
||||
OS: cachedStaticInfo.OSName,
|
||||
GoOS: cachedStaticInfo.GoOS,
|
||||
CPUs: cachedStaticInfo.CPUs,
|
||||
KernelVersion: cachedStaticInfo.KernelVersion,
|
||||
SystemSerialNumber: cachedStaticInfo.SystemSerialNumber,
|
||||
SystemProductName: cachedStaticInfo.SystemProductName,
|
||||
SystemManufacturer: cachedStaticInfo.SystemManufacturer,
|
||||
Environment: cachedStaticInfo.Environment,
|
||||
|
||||
// Dynamic information (fresh each call)
|
||||
NetworkAddresses: addrs,
|
||||
Hostname: extractDeviceName(ctx, systemHostname),
|
||||
NetbirdVersion: version.NetbirdVersion(), // This might change with updates
|
||||
UIVersion: extractUserAgent(ctx), // This might change
|
||||
}
|
||||
|
||||
log.Debugf("dynamic system information gathered in %s", time.Since(start))
|
||||
return gio
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user