diff options
Diffstat (limited to 'pkg/common/outbound_ip.go')
-rw-r--r-- | pkg/common/outbound_ip.go | 75 |
1 files changed, 75 insertions, 0 deletions
diff --git a/pkg/common/outbound_ip.go b/pkg/common/outbound_ip.go new file mode 100644 index 0000000..66e15e5 --- /dev/null +++ b/pkg/common/outbound_ip.go @@ -0,0 +1,75 @@ +package common + +import ( + "net" + "sort" + "strings" +) + +// GetOutboundIP returns an outbound IP address of this machine. +// It tries to access the internet and returns the local IP address of the connection. +// If the machine cannot access the internet, it returns a preferred IP address from network interfaces. +// It returns nil if no IP address is found. +func GetOutboundIP() net.IP { + // See https://stackoverflow.com/a/37382208 + conn, err := net.Dial("udp", "8.8.8.8:80") + if err == nil { + defer conn.Close() + return conn.LocalAddr().(*net.UDPAddr).IP + } + + // So the machine cannot access the internet. Pick an IP address from network interfaces. + if ifs, err := net.Interfaces(); err == nil { + type IP struct { + net.IP + net.Interface + } + var ips []IP + for _, i := range ifs { + if addrs, err := i.Addrs(); err == nil { + for _, addr := range addrs { + var ip net.IP + switch v := addr.(type) { + case *net.IPNet: + ip = v.IP + case *net.IPAddr: + ip = v.IP + } + if ip.IsGlobalUnicast() { + ips = append(ips, IP{ip, i}) + } + } + } + } + if len(ips) > 1 { + sort.Slice(ips, func(i, j int) bool { + ifi := ips[i].Interface + ifj := ips[j].Interface + + // ethernet is preferred + if vi, vj := strings.HasPrefix(ifi.Name, "e"), strings.HasPrefix(ifj.Name, "e"); vi != vj { + return vi + } + + ipi := ips[i].IP + ipj := ips[j].IP + + // IPv4 is preferred + if vi, vj := ipi.To4() != nil, ipj.To4() != nil; vi != vj { + return vi + } + + // en0 is preferred to en1 + if ifi.Name != ifj.Name { + return ifi.Name < ifj.Name + } + + // fallback + return ipi.String() < ipj.String() + }) + return ips[0].IP + } + } + + return nil +} |