diff --git a/net/addrs/common.go b/net/addrs/common.go index bdf094e2d..4d8fd0d6c 100644 --- a/net/addrs/common.go +++ b/net/addrs/common.go @@ -20,9 +20,45 @@ type InterfaceAddr struct { Prefix int } +type InterfaceWithAddr struct { + net.Interface + cachedAddrs []net.Addr // ipv4 addresses + cachedErr error +} type InterfacesAddrs struct { - Interfaces []net.Interface - Addrs []net.Addr + Interfaces []InterfaceWithAddr + Addrs []net.Addr // addrs without attachment to specific interface. Used as a fallback mechanism +} + +func WrapInterfaces(ifaces []net.Interface) []InterfaceWithAddr { + var m = make([]InterfaceWithAddr, 0, len(ifaces)) + for i := range ifaces { + m = append(m, InterfaceWithAddr{ + Interface: ifaces[i], + }) + } + return m +} + +// GetAddr returns ipv4 only addresses for interface or cached one if set +func (i InterfaceWithAddr) GetAddr() []net.Addr { + if i.cachedAddrs != nil { + return i.cachedAddrs + } + if i.cachedErr != nil { + return nil + } + i.cachedAddrs, i.cachedErr = i.Addrs() + // filter-out ipv6 + i.cachedAddrs = slice.Filter(i.cachedAddrs, func(addr net.Addr) bool { + if ip, ok := addr.(*net.IPNet); ok { + if ip.IP.To4() == nil { + return false + } + } + return true + }) + return i.cachedAddrs } func (i InterfacesAddrs) Equal(other InterfacesAddrs) bool { @@ -71,7 +107,7 @@ func parseInterfaceName(name string) (prefix string, bus int, num int64) { } func (i InterfacesAddrs) SortWithPriority(priority []string) { - less := func(a, b net.Interface) bool { + less := func(a, b InterfaceWithAddr) bool { aPrefix, aBus, aNum := parseInterfaceName(a.Name) bPrefix, bBus, bNum := parseInterfaceName(b.Name) @@ -100,7 +136,7 @@ func (i InterfacesAddrs) SortWithPriority(priority []string) { return false } } - slices.SortFunc(i.Interfaces, func(a, b net.Interface) int { + slices.SortFunc(i.Interfaces, func(a, b InterfaceWithAddr) int { if less(a, b) { return -1 } @@ -108,6 +144,14 @@ func (i InterfacesAddrs) SortWithPriority(priority []string) { }) } +func (i InterfacesAddrs) NetInterfaces() []net.Interface { + var s = make([]net.Interface, 0, len(i.Interfaces)) + for _, iface := range i.Interfaces { + s = append(s, iface.Interface) + } + return s +} + func getStrings(i InterfacesAddrs) (allStrings []string) { for _, i := range i.Interfaces { allStrings = append(allStrings, i.Name) @@ -118,3 +162,47 @@ func getStrings(i InterfacesAddrs) (allStrings []string) { slices.Sort(allStrings) return } + +func (i InterfacesAddrs) GetInterfaceByAddr(addr net.Addr) (net.Interface, bool) { + for _, iface := range i.Interfaces { + for _, addrInIface := range iface.GetAddr() { + if addr.String() == addrInIface.String() { + return iface.Interface, true + } + } + } + return net.Interface{}, false +} + +func (i InterfacesAddrs) SortIPsLikeInterfaces(ips []net.IP) { + slices.SortFunc(ips, func(a, b net.IP) int { + pa, _ := i.findInterfacePosByIP(a) + pb, _ := i.findInterfacePosByIP(b) + + if pa == -1 && pb != -1 { + return 1 + } + if pa != -1 && pb == -1 { + return -1 + } + if pa < pb { + return -1 + } else if pa > pb { + return 1 + } + return 0 + }) +} + +func (i InterfacesAddrs) findInterfacePosByIP(ip net.IP) (pos int, equal bool) { + for position, iface := range i.Interfaces { + for _, addr := range iface.GetAddr() { + if ni, ok := addr.(*net.IPNet); ok { + if ni.Contains(ip) { + return position, ni.IP.Equal(ip) + } + } + } + } + return -1, false +} diff --git a/net/addrs/interface.go b/net/addrs/interface.go index df78206e2..48fc6f728 100644 --- a/net/addrs/interface.go +++ b/net/addrs/interface.go @@ -32,9 +32,9 @@ func GetInterfacesAddrs() (iAddrs InterfacesAddrs, err error) { if err != nil { return } - iAddrs.Interfaces = ifaces + iAddrs.Interfaces = WrapInterfaces(ifaces) - iAddrs.Interfaces = slice.Filter(iAddrs.Interfaces, func(iface net.Interface) bool { + iAddrs.Interfaces = slice.Filter(iAddrs.Interfaces, func(iface InterfaceWithAddr) bool { return iface.Flags&net.FlagUp != 0 && iface.Flags&net.FlagMulticast != 0 }) return diff --git a/space/spacecore/localdiscovery/localdiscovery.go b/space/spacecore/localdiscovery/localdiscovery.go index c20b058d5..e1518b15a 100644 --- a/space/spacecore/localdiscovery/localdiscovery.go +++ b/space/spacecore/localdiscovery/localdiscovery.go @@ -200,7 +200,7 @@ func (l *localDiscovery) startServer() (err error) { l.peerId, l.ipv4, // do not include ipv6 addresses, because they are disabled nil, - l.interfacesAddrs.Interfaces, + l.interfacesAddrs.NetInterfaces(), zeroconf.TTL(60), zeroconf.ServerSelectIPTraffic(zeroconf.IPv4), // disable ipv6 for now zeroconf.WriteTimeout(time.Second*1), @@ -223,6 +223,7 @@ func (l *localDiscovery) readAnswers(ch chan *zeroconf.ServiceEntry) { continue } var portAddrs []string + l.interfacesAddrs.SortIPsLikeInterfaces(entry.AddrIPv4) for _, a := range entry.AddrIPv4 { portAddrs = append(portAddrs, fmt.Sprintf("%s:%d", a.String(), entry.Port)) } @@ -251,7 +252,7 @@ func (l *localDiscovery) browse(ctx context.Context, ch chan *zeroconf.ServiceEn newAddrs.SortWithPriority(interfacesSortPriority) if err := zeroconf.Browse(ctx, serviceName, mdnsDomain, ch, zeroconf.ClientWriteTimeout(time.Second*1), - zeroconf.SelectIfaces(newAddrs.Interfaces), + zeroconf.SelectIfaces(newAddrs.NetInterfaces()), zeroconf.SelectIPTraffic(zeroconf.IPv4)); err != nil { log.Error("browsing failed", zap.Error(err)) } @@ -266,7 +267,7 @@ func (l *localDiscovery) notifyPeerToPeerStatus(newAddrs addrs.InterfacesAddrs) } func (l *localDiscovery) notifyP2PNotPossible(newAddrs addrs.InterfacesAddrs) bool { - return len(newAddrs.Interfaces) == 0 || addrs.IsLoopBack(newAddrs.Interfaces) + return len(newAddrs.Interfaces) == 0 || addrs.IsLoopBack(newAddrs.NetInterfaces()) } func (l *localDiscovery) executeHook(hook Hook) {