diff --git a/internal/clientinfo/ndp.go b/internal/clientinfo/ndp.go index 0215254..9d9155d 100644 --- a/internal/clientinfo/ndp.go +++ b/internal/clientinfo/ndp.go @@ -15,6 +15,7 @@ import ( "github.com/mdlayher/ndp" "github.com/Control-D-Inc/ctrld" + ctrldnet "github.com/Control-D-Inc/ctrld/internal/net" ) // ndpDiscover provides client discovery functionality using NDP protocol. @@ -70,20 +71,23 @@ func (nd *ndpDiscover) List() []string { } // saveInfo saves ip and mac info to mapping table. -// If force is true, old ip will be removed before saving. -func (nd *ndpDiscover) saveInfo(ip, mac string, force bool) { +func (nd *ndpDiscover) saveInfo(ip, mac string) { ip = normalizeIP(ip) // Store ip => map mapping, nd.mac.Store(ip, mac) - if force { - // If there is old ip => mac mapping, delete it. - if old, ok := nd.ip.Load(mac); ok { - oldIP := old.(string) + // Do not store mac => ip mapping if new ip is a link local unicast. + if ctrldnet.IsLinkLocalUnicastIPv6(ip) { + return + } + + // If there is old ip => mac mapping, delete it. + if old, existed := nd.ip.Load(mac); existed { + oldIP := old.(string) + if oldIP != ip { nd.mac.Delete(oldIP) } } - // Store mac => ip mapping. nd.ip.Store(mac, ip) } @@ -138,7 +142,7 @@ func (nd *ndpDiscover) listenOnInterface(ctx context.Context, ifi *net.Interface for _, opt := range am.Options { if lla, ok := opt.(*ndp.LinkLayerAddress); ok { mac := lla.Addr.String() - nd.saveInfo(fromIP, mac, true) + nd.saveInfo(fromIP, mac) } } } @@ -153,7 +157,7 @@ func (nd *ndpDiscover) scanWindows(r io.Reader) { continue } if mac := parseMAC(fields[1]); mac != "" { - nd.saveInfo(fields[0], mac, true) + nd.saveInfo(fields[0], mac) } } } @@ -172,7 +176,7 @@ func (nd *ndpDiscover) scanUnix(r io.Reader) { if idx := strings.IndexByte(ip, '%'); idx != -1 { ip = ip[:idx] } - nd.saveInfo(ip, mac, true) + nd.saveInfo(ip, mac) } } } diff --git a/internal/clientinfo/ndp_linux.go b/internal/clientinfo/ndp_linux.go index 7b36ea5..ebd416f 100644 --- a/internal/clientinfo/ndp_linux.go +++ b/internal/clientinfo/ndp_linux.go @@ -24,7 +24,7 @@ func (nd *ndpDiscover) scan() { } ip := n.IP.String() mac := n.HardwareAddr.String() - nd.saveInfo(ip, mac, false) + nd.saveInfo(ip, mac) } } @@ -54,7 +54,7 @@ func (nd *ndpDiscover) subscribe(ctx context.Context) { mac := nu.HardwareAddr.String() switch nu.State { case netlink.NUD_REACHABLE: - nd.saveInfo(ip, mac, false) + nd.saveInfo(ip, mac) case netlink.NUD_FAILED: ctrld.ProxyLogger.Load().Debug().Msgf("removing NDP neighbor with failed state: %s", ip) nd.mac.Delete(ip) diff --git a/internal/clientinfo/ndp_test.go b/internal/clientinfo/ndp_test.go index 5a20555..ca924b9 100644 --- a/internal/clientinfo/ndp_test.go +++ b/internal/clientinfo/ndp_test.go @@ -45,18 +45,17 @@ ff02::c 33-33-00-00-00-0c Permanent nd.scanWindows(r) count := 0 - expectedCount := 5 + expectedCount := 6 nd.mac.Range(func(key, value any) bool { count++ return true }) - // There are 2 entries for 60-57-47-21-dd-00 in the table, but (*ndpDiscover).saveInfo - // only saves the last one, that's why the expected count number is 5. if count != expectedCount { t.Errorf("unexpected count, want %d, got: %d", expectedCount, count) } count = 0 + expectedCount = 4 nd.ip.Range(func(key, value any) bool { count++ return true diff --git a/internal/net/net.go b/internal/net/net.go index 770c3db..3a81849 100644 --- a/internal/net/net.go +++ b/internal/net/net.go @@ -115,6 +115,15 @@ func IsIPv6(ip string) bool { return parsedIP != nil && parsedIP.To4() == nil && parsedIP.To16() != nil } +// IsLinkLocalUnicastIPv6 checks if the provided IP is a link local unicast v6 address. +func IsLinkLocalUnicastIPv6(ip string) bool { + parsedIP := net.ParseIP(ip) + if parsedIP == nil || parsedIP.To4() != nil || parsedIP.To16() == nil { + return false + } + return parsedIP.To16().IsLinkLocalUnicast() +} + type parallelDialerResult struct { conn net.Conn err error