mirror of
https://github.com/seaweedfs/seaweedfs.git
synced 2026-09-12 19:16:18 +00:00
AllocateBlockVolumeResponse used bs.ListenAddr() to derive replica addresses. When the VS binds to ":port" (no explicit IP), host resolved to empty string, producing ":dataPort" as the replica address. This ":port" propagated through master assignments to both primary and replica sides. Now canonicalizes empty/wildcard host using PreferredOutboundIP() before constructing replication addresses. Also exported PreferredOutboundIP for use by the server package. This is the source fix — all downstream paths (heartbeat, API response, assignment) inherit the canonical address. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
55 lines
1.7 KiB
Go
55 lines
1.7 KiB
Go
package blockvol
|
|
|
|
import (
|
|
"net"
|
|
"strconv"
|
|
)
|
|
|
|
// canonicalizeListenerAddr resolves wildcard listener addresses to a routable
|
|
// ip:port string using the provided advertised host as the preferred IP.
|
|
//
|
|
// If the listener bound to a wildcard (":0", "0.0.0.0:port", "[::]:port"),
|
|
// the returned address uses advertisedHost instead of the wildcard.
|
|
//
|
|
// If advertisedHost is empty, falls back to preferredOutboundIP() as a
|
|
// best-effort guess. On multi-NIC hosts, the advertised host should always
|
|
// be provided to avoid selecting the wrong network interface.
|
|
func canonicalizeListenerAddr(addr net.Addr, advertisedHost string) string {
|
|
tcpAddr, ok := addr.(*net.TCPAddr)
|
|
if !ok {
|
|
return addr.String()
|
|
}
|
|
ip := tcpAddr.IP
|
|
if ip != nil && !ip.IsUnspecified() {
|
|
// Already bound to a specific IP — return as-is.
|
|
return net.JoinHostPort(ip.String(), strconv.Itoa(tcpAddr.Port))
|
|
}
|
|
// Wildcard bind — use advertised host or fallback.
|
|
host := advertisedHost
|
|
if host == "" {
|
|
host = PreferredOutboundIP()
|
|
}
|
|
if host == "" {
|
|
// Last resort: return raw address (will be ":port").
|
|
return addr.String()
|
|
}
|
|
return net.JoinHostPort(host, strconv.Itoa(tcpAddr.Port))
|
|
}
|
|
|
|
// preferredOutboundIP returns the machine's preferred outbound IP as a string.
|
|
// Uses the standard Go pattern: dial a UDP socket (no data sent), read the
|
|
// local address. Returns "" if discovery fails.
|
|
//
|
|
// This is a fallback — callers should prefer an explicitly configured
|
|
// advertised host when available.
|
|
// PreferredOutboundIP returns the machine's preferred outbound IP as a string.
|
|
func PreferredOutboundIP() string {
|
|
conn, err := net.Dial("udp", "8.8.8.8:80")
|
|
if err != nil {
|
|
return ""
|
|
}
|
|
defer conn.Close()
|
|
localAddr := conn.LocalAddr().(*net.UDPAddr)
|
|
return localAddr.IP.String()
|
|
}
|