From 05446df0476c125af9ddb06b1d5141e66a94d216 Mon Sep 17 00:00:00 2001 From: Aveline <352441+ym@users.noreply.github.com> Date: Fri, 21 Nov 2025 13:26:29 +0100 Subject: [PATCH] fix(network): IPv6 addresses sorting was using wrong references (#997) --- pkg/nmlite/interface_state.go | 65 +++++++++++++++++++++++++++++++++-- pkg/nmlite/utils.go | 14 ++++---- 2 files changed, 71 insertions(+), 8 deletions(-) diff --git a/pkg/nmlite/interface_state.go b/pkg/nmlite/interface_state.go index 087cf010..efa5f087 100644 --- a/pkg/nmlite/interface_state.go +++ b/pkg/nmlite/interface_state.go @@ -2,6 +2,7 @@ package nmlite import ( "fmt" + "strings" "time" "github.com/jetkvm/kvm/internal/network/types" @@ -9,6 +10,40 @@ import ( "github.com/vishvananda/netlink" ) +type IfStateChangeReason uint + +const ( + IfStateOperStateChanged IfStateChangeReason = 1 + IfStateOnlineStateChanged IfStateChangeReason = 2 + IfStateMACAddressChanged IfStateChangeReason = 3 + IfStateIPAddressesChanged IfStateChangeReason = 4 +) + +type IfStateChangeReasons []IfStateChangeReason + +func (r IfStateChangeReason) String() string { + switch r { + case IfStateOperStateChanged: + return "oper state changed" + case IfStateOnlineStateChanged: + return "online state changed" + case IfStateMACAddressChanged: + return "MAC address changed" + case IfStateIPAddressesChanged: + return "IP addresses changed" + default: + return fmt.Sprintf("unknown change reason %d", r) + } +} + +func (rs IfStateChangeReasons) String() string { + reasons := []string{} + for _, r := range rs { + reasons = append(reasons, r.String()) + } + return strings.Join(reasons, ", ") +} + // updateInterfaceState updates the current interface state func (im *InterfaceManager) updateInterfaceState() error { nl, err := im.link() @@ -16,7 +51,10 @@ func (im *InterfaceManager) updateInterfaceState() error { return fmt.Errorf("failed to get interface: %w", err) } - var stateChanged bool + var ( + stateChanged bool + changeReasons IfStateChangeReasons + ) attrs := nl.Attrs() @@ -29,6 +67,7 @@ func (im *InterfaceManager) updateInterfaceState() error { if im.state.Up != isUp { im.state.Up = isUp stateChanged = true + changeReasons = append(changeReasons, IfStateOperStateChanged) } // Check if the interface is online @@ -36,12 +75,14 @@ func (im *InterfaceManager) updateInterfaceState() error { if im.state.Online != isOnline { im.state.Online = isOnline stateChanged = true + changeReasons = append(changeReasons, IfStateOnlineStateChanged) } // Check if the MAC address has changed if im.state.MACAddress != attrs.HardwareAddr.String() { im.state.MACAddress = attrs.HardwareAddr.String() stateChanged = true + changeReasons = append(changeReasons, IfStateMACAddressChanged) } // Update IP addresses @@ -49,6 +90,7 @@ func (im *InterfaceManager) updateInterfaceState() error { im.logger.Error().Err(err).Msg("failed to update IP addresses") } else if ipChanged { stateChanged = true + changeReasons = append(changeReasons, IfStateIPAddressesChanged) } im.state.LastUpdated = time.Now() @@ -56,7 +98,10 @@ func (im *InterfaceManager) updateInterfaceState() error { // Notify callback if state changed if stateChanged && im.onStateChange != nil { - im.logger.Debug().Interface("state", im.state).Msg("notifying state change") + im.logger.Debug(). + Stringer("changeReasons", changeReasons). + Interface("state", im.state). + Msg("notifying state change") im.onStateChange(*im.state) } @@ -80,6 +125,7 @@ func (im *InterfaceManager) updateInterfaceStateAddresses(nl *link.Link) (bool, ipv6Gateway string ipv4Ready, ipv6Ready = false, false stateChanged = false + stateChangeReason string ) routes, _ := mgr.ListDefaultRoutes(link.AfInet6) @@ -123,40 +169,55 @@ func (im *InterfaceManager) updateInterfaceStateAddresses(nl *link.Link) (bool, if !sortAndCompareStringSlices(im.state.IPv4Addresses, ipv4Addresses) { im.state.IPv4Addresses = ipv4Addresses stateChanged = true + stateChangeReason = "IPv4 addresses changed" } if !sortAndCompareIPv6AddressSlices(im.state.IPv6Addresses, ipv6Addresses) { im.state.IPv6Addresses = ipv6Addresses stateChanged = true + stateChangeReason = "IPv6 addresses changed" } if im.state.IPv4Address != ipv4Addr { im.state.IPv4Address = ipv4Addr stateChanged = true + stateChangeReason = "IPv4 address changed" } if im.state.IPv6Address != ipv6Addr { im.state.IPv6Address = ipv6Addr stateChanged = true + stateChangeReason = "IPv6 address changed" } if im.state.IPv6LinkLocal != ipv6LinkLocal { im.state.IPv6LinkLocal = ipv6LinkLocal stateChanged = true + stateChangeReason = "IPv6 link local address changed" } if im.state.IPv6Gateway != ipv6Gateway { im.state.IPv6Gateway = ipv6Gateway stateChanged = true + stateChangeReason = "IPv6 gateway changed" } if im.state.IPv4Ready != ipv4Ready { im.state.IPv4Ready = ipv4Ready stateChanged = true + stateChangeReason = "IPv4 ready state changed" } if im.state.IPv6Ready != ipv6Ready { im.state.IPv6Ready = ipv6Ready stateChanged = true + stateChangeReason = "IPv6 ready state changed" + } + + if stateChanged { + im.logger.Trace(). + Str("changeReason", stateChangeReason). + Interface("state", im.state). + Msg("interface state changed") } return stateChanged, nil diff --git a/pkg/nmlite/utils.go b/pkg/nmlite/utils.go index 49ed0078..11952d0b 100644 --- a/pkg/nmlite/utils.go +++ b/pkg/nmlite/utils.go @@ -42,17 +42,19 @@ func sortAndCompareStringSlices(a, b []string) bool { return true } +func sortIPv6AddressSlicesStable(a []types.IPv6Address) { + sort.SliceStable(a, func(i, j int) bool { + return a[i].Address.String() < a[j].Address.String() + }) +} + func sortAndCompareIPv6AddressSlices(a, b []types.IPv6Address) bool { if len(a) != len(b) { return false } - sort.SliceStable(a, func(i, j int) bool { - return a[i].Address.String() < b[j].Address.String() - }) - sort.SliceStable(b, func(i, j int) bool { - return b[i].Address.String() < a[j].Address.String() - }) + sortIPv6AddressSlicesStable(a) + sortIPv6AddressSlicesStable(b) for i := range a { if a[i].Address.String() != b[i].Address.String() {