From 574485ec69c1d1a7be8765f5da24d9c23803cb96 Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Tue, 7 Sep 2021 19:29:42 -0700 Subject: [PATCH] better IP v6 support --- weed/command/filer.go | 7 +- weed/command/master.go | 7 +- weed/command/master_follower.go | 5 +- weed/command/msg_broker.go | 3 +- weed/command/volume.go | 10 +- weed/filer/filechunk_manifest.go | 5 + weed/topology/data_node.go | 8 +- weed/topology/rack.go | 4 +- weed/topology/store_replicate.go | 2 +- weed/util/network.go | 25 ++++- weed/wdclient/net2/ip.go | 177 ------------------------------- 11 files changed, 46 insertions(+), 207 deletions(-) delete mode 100644 weed/wdclient/net2/ip.go diff --git a/weed/command/filer.go b/weed/command/filer.go index b1237cc3f..9a27978be 100644 --- a/weed/command/filer.go +++ b/weed/command/filer.go @@ -4,7 +4,6 @@ import ( "fmt" "net/http" "os" - "strconv" "strings" "time" @@ -207,7 +206,7 @@ func (fo *FilerOptions) startFiler() { } if *fo.publicPort != 0 { - publicListeningAddress := *fo.bindIp + ":" + strconv.Itoa(*fo.publicPort) + publicListeningAddress := util.JoinHostPort(*fo.bindIp, *fo.publicPort) glog.V(0).Infoln("Start Seaweed filer server", util.Version(), "public at", publicListeningAddress) publicListener, e := util.NewListener(publicListeningAddress, 0) if e != nil { @@ -222,7 +221,7 @@ func (fo *FilerOptions) startFiler() { glog.V(0).Infof("Start Seaweed Filer %s at %s:%d", util.Version(), *fo.ip, *fo.port) filerListener, e := util.NewListener( - *fo.bindIp+":"+strconv.Itoa(*fo.port), + util.JoinHostPort(*fo.bindIp, *fo.port), time.Duration(10)*time.Second, ) if e != nil { @@ -231,7 +230,7 @@ func (fo *FilerOptions) startFiler() { // starting grpc server grpcPort := *fo.port + 10000 - grpcL, err := util.NewListener(*fo.bindIp+":"+strconv.Itoa(grpcPort), 0) + grpcL, err := util.NewListener(util.JoinHostPort(*fo.bindIp, grpcPort), 0) if err != nil { glog.Fatalf("failed to listen on grpc port %d: %v", grpcPort, err) } diff --git a/weed/command/master.go b/weed/command/master.go index 4eb43ee09..2605f6f4b 100644 --- a/weed/command/master.go +++ b/weed/command/master.go @@ -7,7 +7,6 @@ import ( "net/http" "os" "sort" - "strconv" "strings" "time" @@ -116,7 +115,7 @@ func startMaster(masterOption MasterOptions, masterWhiteList []string) { r := mux.NewRouter() ms := weed_server.NewMasterServer(r, masterOption.toMasterOption(masterWhiteList), peers) - listeningAddress := *masterOption.ipBind + ":" + strconv.Itoa(*masterOption.port) + listeningAddress := util.JoinHostPort(*masterOption.ipBind, *masterOption.port) glog.V(0).Infof("Start Seaweed Master %s at %s", util.Version(), listeningAddress) masterListener, e := util.NewListener(listeningAddress, 0) if e != nil { @@ -132,7 +131,7 @@ func startMaster(masterOption MasterOptions, masterWhiteList []string) { r.HandleFunc("/cluster/status", raftServer.StatusHandler).Methods("GET") // starting grpc server grpcPort := *masterOption.port + 10000 - grpcL, err := util.NewListener(*masterOption.ipBind+":"+strconv.Itoa(grpcPort), 0) + grpcL, err := util.NewListener(util.JoinHostPort(*masterOption.ipBind, grpcPort), 0) if err != nil { glog.Fatalf("master failed to listen on grpc port %d: %v", grpcPort, err) } @@ -163,7 +162,7 @@ func startMaster(masterOption MasterOptions, masterWhiteList []string) { func checkPeers(masterIp string, masterPort int, peers string) (masterAddress string, cleanedPeers []string) { glog.V(0).Infof("current: %s:%d peers:%s", masterIp, masterPort, peers) - masterAddress = masterIp + ":" + strconv.Itoa(masterPort) + masterAddress = util.JoinHostPort(masterIp, masterPort) if peers != "" { cleanedPeers = strings.Split(peers, ",") } diff --git a/weed/command/master_follower.go b/weed/command/master_follower.go index b628f7abf..48548a389 100644 --- a/weed/command/master_follower.go +++ b/weed/command/master_follower.go @@ -13,7 +13,6 @@ import ( "github.com/gorilla/mux" "google.golang.org/grpc/reflection" "net/http" - "strconv" "strings" "time" ) @@ -114,7 +113,7 @@ func startMasterFollower(masterOptions MasterOptions) { r := mux.NewRouter() ms := weed_server.NewMasterServer(r, option, masters) - listeningAddress := *masterOptions.ipBind + ":" + strconv.Itoa(*masterOptions.port) + listeningAddress := util.JoinHostPort(*masterOptions.ipBind, *masterOptions.port) glog.V(0).Infof("Start Seaweed Master %s at %s", util.Version(), listeningAddress) masterListener, e := util.NewListener(listeningAddress, 0) if e != nil { @@ -123,7 +122,7 @@ func startMasterFollower(masterOptions MasterOptions) { // starting grpc server grpcPort := *masterOptions.port + 10000 - grpcL, err := util.NewListener(*masterOptions.ipBind+":"+strconv.Itoa(grpcPort), 0) + grpcL, err := util.NewListener(util.JoinHostPort(*masterOptions.ipBind, grpcPort), 0) if err != nil { glog.Fatalf("master failed to listen on grpc port %d: %v", grpcPort, err) } diff --git a/weed/command/msg_broker.go b/weed/command/msg_broker.go index db0b4148d..403bbe317 100644 --- a/weed/command/msg_broker.go +++ b/weed/command/msg_broker.go @@ -3,7 +3,6 @@ package command import ( "context" "fmt" - "strconv" "time" "google.golang.org/grpc/reflection" @@ -100,7 +99,7 @@ func (msgBrokerOpt *MessageBrokerOptions) startQueueServer() bool { }, grpcDialOption) // start grpc listener - grpcL, err := util.NewListener(":"+strconv.Itoa(*msgBrokerOpt.port), 0) + grpcL, err := util.NewListener(util.JoinHostPort("", *msgBrokerOpt.port), 0) if err != nil { glog.Fatalf("failed to listen on grpc port %d: %v", *msgBrokerOpt.port, err) } diff --git a/weed/command/volume.go b/weed/command/volume.go index 235eff11b..3278107f5 100644 --- a/weed/command/volume.go +++ b/weed/command/volume.go @@ -194,7 +194,7 @@ func (v VolumeServerOptions) startVolumeServer(volumeFolders, maxVolumeCounts, v *v.publicPort = *v.port } if *v.publicUrl == "" { - *v.publicUrl = *v.ip + ":" + strconv.Itoa(*v.publicPort) + *v.publicUrl = util.JoinHostPort(*v.ip, *v.publicPort) } volumeMux := http.NewServeMux() @@ -308,7 +308,7 @@ func (v VolumeServerOptions) isSeparatedPublicPort() bool { func (v VolumeServerOptions) startGrpcService(vs volume_server_pb.VolumeServerServer) *grpc.Server { grpcPort := *v.port + 10000 - grpcL, err := util.NewListener(*v.bindIp+":"+strconv.Itoa(grpcPort), 0) + grpcL, err := util.NewListener(util.JoinHostPort(*v.bindIp, grpcPort), 0) if err != nil { glog.Fatalf("failed to listen on grpc port %d: %v", grpcPort, err) } @@ -324,7 +324,7 @@ func (v VolumeServerOptions) startGrpcService(vs volume_server_pb.VolumeServerSe } func (v VolumeServerOptions) startPublicHttpService(handler http.Handler) httpdown.Server { - publicListeningAddress := *v.bindIp + ":" + strconv.Itoa(*v.publicPort) + publicListeningAddress := util.JoinHostPort(*v.bindIp, *v.publicPort) glog.V(0).Infoln("Start Seaweed volume server", util.Version(), "public at", publicListeningAddress) publicListener, e := util.NewListener(publicListeningAddress, time.Duration(*v.idleConnectionTimeout)*time.Second) if e != nil { @@ -351,7 +351,7 @@ func (v VolumeServerOptions) startClusterHttpService(handler http.Handler) httpd keyFile = viper.GetString("https.volume.key") } - listeningAddress := *v.bindIp + ":" + strconv.Itoa(*v.port) + listeningAddress := util.JoinHostPort(*v.bindIp, *v.port) glog.V(0).Infof("Start Seaweed volume server %s at %s", util.Version(), listeningAddress) listener, e := util.NewListener(listeningAddress, time.Duration(*v.idleConnectionTimeout)*time.Second) if e != nil { @@ -373,7 +373,7 @@ func (v VolumeServerOptions) startClusterHttpService(handler http.Handler) httpd } func (v VolumeServerOptions) startTcpService(volumeServer *weed_server.VolumeServer) { - listeningAddress := *v.bindIp + ":" + strconv.Itoa(*v.port+20000) + listeningAddress := util.JoinHostPort(*v.bindIp,*v.port+20000) glog.V(0).Infoln("Start Seaweed volume server", util.Version(), "tcp at", listeningAddress) listener, e := util.NewListener(listeningAddress, 0) if e != nil { diff --git a/weed/filer/filechunk_manifest.go b/weed/filer/filechunk_manifest.go index 00dbf1fd6..32008271b 100644 --- a/weed/filer/filechunk_manifest.go +++ b/weed/filer/filechunk_manifest.go @@ -6,6 +6,8 @@ import ( "github.com/chrislusf/seaweedfs/weed/wdclient" "io" "math" + "net/url" + "strings" "time" "github.com/golang/protobuf/proto" @@ -108,6 +110,9 @@ func retriedFetchChunkData(urlStrings []string, cipherKey []byte, isGzipped bool for waitTime := time.Second; waitTime < util.RetryWaitTime; waitTime += waitTime / 2 { for _, urlString := range urlStrings { receivedData = receivedData[:0] + if strings.Contains(urlString, "%") { + urlString = url.PathEscape(urlString) + } shouldRetry, err = util.ReadUrlAsStream(urlString+"?readDeleted=true", cipherKey, isGzipped, isFullChunk, offset, size, func(data []byte) { receivedData = append(receivedData, data...) }) diff --git a/weed/topology/data_node.go b/weed/topology/data_node.go index 3f0234ca8..cd3249c98 100644 --- a/weed/topology/data_node.go +++ b/weed/topology/data_node.go @@ -2,14 +2,12 @@ package topology import ( "fmt" + "github.com/chrislusf/seaweedfs/weed/glog" "github.com/chrislusf/seaweedfs/weed/pb/master_pb" + "github.com/chrislusf/seaweedfs/weed/storage" "github.com/chrislusf/seaweedfs/weed/storage/needle" "github.com/chrislusf/seaweedfs/weed/storage/types" "github.com/chrislusf/seaweedfs/weed/util" - "strconv" - - "github.com/chrislusf/seaweedfs/weed/glog" - "github.com/chrislusf/seaweedfs/weed/storage" ) type DataNode struct { @@ -207,7 +205,7 @@ func (dn *DataNode) MatchLocation(ip string, port int) bool { } func (dn *DataNode) Url() string { - return dn.Ip + ":" + strconv.Itoa(dn.Port) + return util.JoinHostPort(dn.Ip, dn.Port) } func (dn *DataNode) ToMap() interface{} { diff --git a/weed/topology/rack.go b/weed/topology/rack.go index 8eb2a717c..9c77285c3 100644 --- a/weed/topology/rack.go +++ b/weed/topology/rack.go @@ -3,7 +3,7 @@ package topology import ( "github.com/chrislusf/seaweedfs/weed/pb/master_pb" "github.com/chrislusf/seaweedfs/weed/storage/types" - "strconv" + "github.com/chrislusf/seaweedfs/weed/util" "time" ) @@ -38,7 +38,7 @@ func (r *Rack) GetOrCreateDataNode(ip string, port int, publicUrl string, maxVol return dn } } - dn := NewDataNode(ip + ":" + strconv.Itoa(port)) + dn := NewDataNode(util.JoinHostPort(ip, port)) dn.Ip = ip dn.Port = port dn.PublicUrl = publicUrl diff --git a/weed/topology/store_replicate.go b/weed/topology/store_replicate.go index 2f225b36f..85c3a48ab 100644 --- a/weed/topology/store_replicate.go +++ b/weed/topology/store_replicate.go @@ -179,7 +179,7 @@ func getWritableRemoteReplications(s *storage.Store, grpcDialOption grpc.DialOpt // not on local store, or has replications lookupResult, lookupErr := operation.LookupVolumeId(masterFn, grpcDialOption, volumeId.String()) if lookupErr == nil { - selfUrl := s.Ip + ":" + strconv.Itoa(s.Port) + selfUrl := util.JoinHostPort(s.Ip, s.Port) for _, location := range lookupResult.Locations { if location.Url != selfUrl { remoteLocations = append(remoteLocations, location) diff --git a/weed/util/network.go b/weed/util/network.go index f9fad7771..43b4a794d 100644 --- a/weed/util/network.go +++ b/weed/util/network.go @@ -15,6 +15,18 @@ func DetectedHostAddress() string { return "" } + if v4Address := selectIpV4(netInterfaces, true); v4Address != ""{ + return v4Address + } + + if v6Address := selectIpV4(netInterfaces, false); v6Address != ""{ + return v6Address + } + + return "localhost" +} + +func selectIpV4(netInterfaces []net.Interface, isIpV4 bool) string { for _, netInterface := range netInterfaces { if (netInterface.Flags & net.FlagUp) == 0 { continue @@ -26,14 +38,19 @@ func DetectedHostAddress() string { for _, a := range addrs { if ipNet, ok := a.(*net.IPNet); ok && !ipNet.IP.IsLoopback() { - if ipNet.IP.To4() != nil { - return ipNet.IP.String() + if isIpV4 { + if ipNet.IP.To4() != nil { + return ipNet.IP.String() + } + } else { + if ipNet.IP.To16() != nil { + return ipNet.IP.String() + } } } } } - - return "localhost" + return "" } func JoinHostPort(host string, port int) string { diff --git a/weed/wdclient/net2/ip.go b/weed/wdclient/net2/ip.go deleted file mode 100644 index ff5e3b24e..000000000 --- a/weed/wdclient/net2/ip.go +++ /dev/null @@ -1,177 +0,0 @@ -package net2 - -import ( - "fmt" - "log" - "net" - "os" - "strings" - "sync" -) - -var myHostname string -var myHostnameOnce sync.Once - -// Like os.Hostname but caches first successful result, making it cheap to call it -// over and over. -// It will also crash whole process if fetching Hostname fails! -func MyHostname() string { - myHostnameOnce.Do(func() { - var err error - myHostname, err = os.Hostname() - if err != nil { - log.Fatal(err) - } - }) - return myHostname -} - -var myIp4 *net.IPAddr -var myIp4Once sync.Once - -// Resolves `MyHostname()` to an Ip4 address. Caches first successful result, making it -// cheap to call it over and over. -// It will also crash whole process if resolving the IP fails! -func MyIp4() *net.IPAddr { - myIp4Once.Do(func() { - var err error - myIp4, err = net.ResolveIPAddr("ip4", MyHostname()) - if err != nil { - log.Fatal(err) - } - }) - return myIp4 -} - -var myIp6 *net.IPAddr -var myIp6Once sync.Once - -// Resolves `MyHostname()` to an Ip6 address. Caches first successful result, making it -// cheap to call it over and over. -// It will also crash whole process if resolving the IP fails! -func MyIp6() *net.IPAddr { - myIp6Once.Do(func() { - var err error - myIp6, err = net.ResolveIPAddr("ip6", MyHostname()) - if err != nil { - log.Fatal(err) - } - }) - return myIp6 -} - -// This returns the list of local ip addresses which other hosts can connect -// to (NOTE: Loopback ip is ignored). -// Also resolves Hostname to an address and adds it to the list too, so -// IPs from /etc/hosts can work too. -func GetLocalIPs() ([]*net.IP, error) { - hostname, err := os.Hostname() - if err != nil { - return nil, fmt.Errorf("Failed to lookup hostname: %v", err) - } - // Resolves IP Address from Hostname, this way overrides in /etc/hosts - // can work too for IP resolution. - ipInfo, err := net.ResolveIPAddr("ip4", hostname) - if err != nil { - return nil, fmt.Errorf("Failed to resolve ip: %v", err) - } - ips := []*net.IP{&ipInfo.IP} - - // TODO(zviad): Is rest of the code really necessary? - addrs, err := net.InterfaceAddrs() - if err != nil { - return nil, fmt.Errorf("Failed to get interface addresses: %v", err) - } - for _, addr := range addrs { - ipnet, ok := addr.(*net.IPNet) - if !ok { - continue - } - - if ipnet.IP.IsLoopback() { - continue - } - - ips = append(ips, &ipnet.IP) - } - return ips, nil -} - -var localhostIPNets []*net.IPNet - -func init() { - for _, mask := range []string{"127.0.0.1/8", "::1/128"} { - _, ipnet, err := net.ParseCIDR(mask) - if err != nil { - panic(err) - } - localhostIPNets = append(localhostIPNets, ipnet) - } -} - -func IsLocalhostIp(ipStr string) bool { - ip := net.ParseIP(ipStr) - if ip == nil { - return false - } - for _, ipnet := range localhostIPNets { - if ipnet.Contains(ip) { - return true - } - } - return false -} - -// Given a host string, return true if the host is an ip (v4/v6) localhost. -func IsLocalhost(host string) bool { - return IsLocalhostIp(host) || - host == "localhost" || - host == "ip6-localhost" || - host == "ipv6-localhost" -} - -// Resolves hostnames in addresses to actual IP4 addresses. Skips all invalid addresses -// and all addresses that can't be resolved. -// `addrs` are assumed to be of form: [":", ...] -// Returns an error in addition to resolved addresses if not all resolutions succeed. -func ResolveIP4s(addrs []string) ([]string, error) { - resolvedAddrs := make([]string, 0, len(addrs)) - var lastErr error - - for _, server := range addrs { - hostPort := strings.Split(server, ":") - if len(hostPort) != 2 { - lastErr = fmt.Errorf("Skipping invalid address: %s", server) - continue - } - - ip, err := net.ResolveIPAddr("ip4", hostPort[0]) - if err != nil { - lastErr = err - continue - } - resolvedAddrs = append(resolvedAddrs, ip.IP.String()+":"+hostPort[1]) - } - return resolvedAddrs, lastErr -} - -func LookupValidAddrs() (map[string]bool, error) { - hostName, err := os.Hostname() - if err != nil { - return nil, err - } - addrs, err := net.LookupHost(hostName) - if err != nil { - return nil, err - } - validAddrs := make(map[string]bool) - validAddrs[hostName] = true - for _, addr := range addrs { - validAddrs[addr] = true - } - // Special case localhost/127.0.0.1 so that this works on devVMs. It should - // have no affect in production. - validAddrs["127.0.0.1"] = true - validAddrs["localhost"] = true - return validAddrs, nil -}