blob: b51af7713f7cfb162ff9e8dba4e11ba46ccdc44d [file] [log] [blame]
Matteo Scandoloa4285862020-12-01 18:10:10 -08001// Copyright (c) 2017 Uber Technologies, Inc.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package utils
16
17import (
18 "errors"
19 "net"
20)
21
22// This code is borrowed from https://github.com/uber/tchannel-go/blob/dev/localip.go
23
24// scoreAddr scores how likely the given addr is to be a remote address and returns the
25// IP to use when listening. Any address which receives a negative score should not be used.
26// Scores are calculated as:
27// -1 for any unknown IP addresses.
28// +300 for IPv4 addresses
29// +100 for non-local addresses, extra +100 for "up" interaces.
30func scoreAddr(iface net.Interface, addr net.Addr) (int, net.IP) {
31 var ip net.IP
32 if netAddr, ok := addr.(*net.IPNet); ok {
33 ip = netAddr.IP
34 } else if netIP, ok := addr.(*net.IPAddr); ok {
35 ip = netIP.IP
36 } else {
37 return -1, nil
38 }
39
40 var score int
41 if ip.To4() != nil {
42 score += 300
43 }
44 if iface.Flags&net.FlagLoopback == 0 && !ip.IsLoopback() {
45 score += 100
46 if iface.Flags&net.FlagUp != 0 {
47 score += 100
48 }
49 }
50 return score, ip
51}
52
53// HostIP tries to find an IP that can be used by other machines to reach this machine.
54func HostIP() (net.IP, error) {
55 interfaces, err := net.Interfaces()
56 if err != nil {
57 return nil, err
58 }
59
60 bestScore := -1
61 var bestIP net.IP
62 // Select the highest scoring IP as the best IP.
63 for _, iface := range interfaces {
64 addrs, err := iface.Addrs()
65 if err != nil {
66 // Skip this interface if there is an error.
67 continue
68 }
69
70 for _, addr := range addrs {
71 score, ip := scoreAddr(iface, addr)
72 if score > bestScore {
73 bestScore = score
74 bestIP = ip
75 }
76 }
77 }
78
79 if bestScore == -1 {
80 return nil, errors.New("no addresses to listen on")
81 }
82
83 return bestIP, nil
84}