From 3aa891b29c1d108ee395df9c4b3bbb5a42d87c52 Mon Sep 17 00:00:00 2001 From: abhishek9686 Date: Sat, 20 Jun 2026 17:40:43 +0530 Subject: [PATCH] NM-363: prevent restart loop on repeated IPv6 endpoint detection --- functions/daemon.go | 6 ++++++ functions/mqpublish.go | 40 +++++++++++++++++++++++++++++++-------- ncutils/netclientutils.go | 15 +++++++++++++++ 3 files changed, 53 insertions(+), 8 deletions(-) diff --git a/functions/daemon.go b/functions/daemon.go index 3ffacbbf..ee9dd3d3 100644 --- a/functions/daemon.go +++ b/functions/daemon.go @@ -212,6 +212,12 @@ func startGoRoutines(wg *sync.WaitGroup) context.CancelFunc { } if !netclientCfg.IsStatic { + if netclientCfg.EndpointIP != nil { + config.HostPublicIP = netclientCfg.EndpointIP + } + if netclientCfg.EndpointIPv6 != nil { + config.HostPublicIP6 = netclientCfg.EndpointIPv6 + } // IPV4 config.HostPublicIP, config.WgPublicListenPort, config.HostNatType = holePunchWgPort(4, netclientCfg.ListenPort) slog.Info("wireguard public listen port: ", "port", config.WgPublicListenPort) diff --git a/functions/mqpublish.go b/functions/mqpublish.go index 4649ab3d..4960c5ef 100644 --- a/functions/mqpublish.go +++ b/functions/mqpublish.go @@ -66,6 +66,14 @@ func Checkin(ctx context.Context, wg *sync.WaitGroup) { ipTicker = time.NewTicker(time.Second * time.Duration(ipTickerIntervalSec)) defer ipTicker.Stop() + if !config.Netclient().IsStatic && config.Netclient().CurrGwNmIP == nil { + if config.HostPublicIP == nil && config.Netclient().EndpointIP != nil { + config.HostPublicIP = config.Netclient().EndpointIP + } + if config.HostPublicIP6 == nil && config.Netclient().EndpointIPv6 != nil { + config.HostPublicIP6 = config.Netclient().EndpointIPv6 + } + } err := hostServerUpdate(models.HostUpdate{Action: models.UpdateHost}) if err != nil { logger.Log(0, "could not publish endpoint change", err.Error()) @@ -102,20 +110,36 @@ func Checkin(ctx context.Context, wg *sync.WaitGroup) { if ip4 == nil && ip6 == nil { continue } - if ip4 != nil && ip4.To4() != nil && !ip4.IsUnspecified() && !config.HostPublicIP.Equal(ip4) { - slog.Debug("IP CHECKIN 1", "ipv4", ip4, "HostPublicIP", config.HostPublicIP) - config.HostPublicIP = ip4 - restart = true + knownIP4 := config.HostPublicIP + if knownIP4 == nil { + knownIP4 = config.Netclient().EndpointIP + } + if ip4 != nil && ip4.To4() != nil && !ip4.IsUnspecified() { + if !ncutils.IPsEqual(knownIP4, ip4) { + slog.Debug("IP CHECKIN 1", "ipv4", ip4, "HostPublicIP", config.HostPublicIP) + config.HostPublicIP = ip4 + restart = true + } else if config.HostPublicIP == nil { + config.HostPublicIP = ip4 + } } else if ip4 == nil && config.HostPublicIP != nil { slog.Debug("IP CHECKIN 2", "ipv4", ip4, "HostPublicIP", config.HostPublicIP) config.HostPublicIP = nil restart = true } - if ip6 != nil && ip6.To16() != nil && !ip6.IsUnspecified() && !config.HostPublicIP6.Equal(ip6) { - slog.Debug("IP CHECKIN 1", "ipv6", ip6, "HostPublicIP6", config.HostPublicIP6) - config.HostPublicIP6 = ip6 - restart = true + knownIP6 := config.HostPublicIP6 + if knownIP6 == nil { + knownIP6 = config.Netclient().EndpointIPv6 + } + if ip6 != nil && ip6.To16() != nil && !ip6.IsUnspecified() { + if !ncutils.IPsEqual(knownIP6, ip6) { + slog.Debug("IP CHECKIN 1", "ipv6", ip6, "HostPublicIP6", config.HostPublicIP6) + config.HostPublicIP6 = ip6 + restart = true + } else if config.HostPublicIP6 == nil { + config.HostPublicIP6 = ip6 + } } else if ip6 == nil && config.HostPublicIP6 != nil { slog.Debug("IP CHECKIN 2", "ipv6", ip6, "HostPublicIP6", config.HostPublicIP6) config.HostPublicIP6 = nil diff --git a/ncutils/netclientutils.go b/ncutils/netclientutils.go index eaeca687..57400469 100644 --- a/ncutils/netclientutils.go +++ b/ncutils/netclientutils.go @@ -360,6 +360,21 @@ func SetVerbosity(logLevel int) { } +// IPsEqual compares two IP addresses, normalizing IPv4 and IPv6 representations. +func IPsEqual(a, b net.IP) bool { + if a == nil && b == nil { + return true + } + if a == nil || b == nil { + return false + } + a16, b16 := a.To16(), b.To16() + if a16 == nil || b16 == nil { + return a.Equal(b) + } + return a16.Equal(b16) +} + func TraceCaller() { // Skip 1 frame to get the caller of this function pc, file, line, ok := runtime.Caller(2)