2020-11-13 17:36:07 +00:00
|
|
|
// Copyright 2019 the Kilo authors
|
|
|
|
//
|
|
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
// you may not use this file except in compliance with the License.
|
|
|
|
// You may obtain a copy of the License at
|
|
|
|
//
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
//
|
|
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
// See the License for the specific language governing permissions and
|
|
|
|
// limitations under the License.
|
|
|
|
|
2021-09-30 15:26:50 +00:00
|
|
|
//go:build linux
|
2020-11-13 17:36:07 +00:00
|
|
|
// +build linux
|
|
|
|
|
|
|
|
package mesh
|
|
|
|
|
|
|
|
import (
|
|
|
|
"errors"
|
|
|
|
"fmt"
|
|
|
|
"net"
|
|
|
|
"sort"
|
|
|
|
|
|
|
|
"github.com/vishvananda/netlink"
|
|
|
|
)
|
|
|
|
|
|
|
|
// getIP returns a private and public IP address for the local node.
|
|
|
|
// It selects the private IP address in the following order:
|
|
|
|
// - private IP to which hostname resolves
|
|
|
|
// - private IP assigned to interface of default route
|
|
|
|
// - private IP assigned to local interface
|
2021-01-24 13:19:01 +00:00
|
|
|
// - nil if no private IP was found
|
2020-11-13 17:36:07 +00:00
|
|
|
// It selects the public IP address in the following order:
|
|
|
|
// - public IP to which hostname resolves
|
|
|
|
// - public IP assigned to interface of default route
|
|
|
|
// - public IP assigned to local interface
|
|
|
|
// - private IP to which hostname resolves
|
|
|
|
// - private IP assigned to interface of default route
|
|
|
|
// - private IP assigned to local interface
|
|
|
|
// - if no IP was found, return nil and an error.
|
|
|
|
func getIP(hostname string, ignoreIfaces ...int) (*net.IPNet, *net.IPNet, error) {
|
|
|
|
ignore := make(map[string]struct{})
|
|
|
|
for i := range ignoreIfaces {
|
|
|
|
if ignoreIfaces[i] == 0 {
|
|
|
|
// Only ignore valid interfaces.
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
iface, err := net.InterfaceByIndex(ignoreIfaces[i])
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, fmt.Errorf("failed to find interface %d: %v", ignoreIfaces[i], err)
|
|
|
|
}
|
|
|
|
ips, err := ipsForInterface(iface)
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
for _, ip := range ips {
|
|
|
|
ignore[ip.String()] = struct{}{}
|
|
|
|
ignore[oneAddressCIDR(ip.IP).String()] = struct{}{}
|
|
|
|
}
|
|
|
|
}
|
2021-08-22 11:40:55 +00:00
|
|
|
|
2020-11-13 17:36:07 +00:00
|
|
|
var hostPriv, hostPub []*net.IPNet
|
|
|
|
{
|
|
|
|
// Check IPs to which hostname resolves first.
|
|
|
|
ips := ipsForHostname(hostname)
|
|
|
|
for _, ip := range ips {
|
|
|
|
ok, mask, err := assignedToInterface(ip)
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, fmt.Errorf("failed to search locally assigned addresses: %v", err)
|
|
|
|
}
|
|
|
|
if !ok {
|
|
|
|
continue
|
|
|
|
}
|
2021-08-22 11:40:55 +00:00
|
|
|
if isLocal(ip.IP) {
|
|
|
|
continue
|
|
|
|
}
|
2020-11-13 17:36:07 +00:00
|
|
|
ip.Mask = mask
|
|
|
|
if isPublic(ip.IP) {
|
|
|
|
hostPub = append(hostPub, ip)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
hostPriv = append(hostPriv, ip)
|
|
|
|
}
|
|
|
|
sortIPs(hostPriv)
|
|
|
|
sortIPs(hostPub)
|
|
|
|
}
|
|
|
|
|
|
|
|
var defaultPriv, defaultPub []*net.IPNet
|
|
|
|
{
|
|
|
|
// Check IPs on interface for default route next.
|
|
|
|
iface, err := defaultInterface()
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
ips, err := ipsForInterface(iface)
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
for _, ip := range ips {
|
|
|
|
if isLocal(ip.IP) {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if isPublic(ip.IP) {
|
|
|
|
defaultPub = append(defaultPub, ip)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
defaultPriv = append(defaultPriv, ip)
|
|
|
|
}
|
|
|
|
sortIPs(defaultPriv)
|
|
|
|
sortIPs(defaultPub)
|
|
|
|
}
|
|
|
|
|
|
|
|
var interfacePriv, interfacePub []*net.IPNet
|
|
|
|
{
|
|
|
|
// Finally look for IPs on all interfaces.
|
|
|
|
ips, err := ipsForAllInterfaces()
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
for _, ip := range ips {
|
|
|
|
if isLocal(ip.IP) {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if isPublic(ip.IP) {
|
|
|
|
interfacePub = append(interfacePub, ip)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
interfacePriv = append(interfacePriv, ip)
|
|
|
|
}
|
|
|
|
sortIPs(interfacePriv)
|
|
|
|
sortIPs(interfacePub)
|
|
|
|
}
|
|
|
|
|
|
|
|
var priv, pub, tmpPriv, tmpPub []*net.IPNet
|
|
|
|
tmpPriv = append(tmpPriv, hostPriv...)
|
|
|
|
tmpPriv = append(tmpPriv, defaultPriv...)
|
|
|
|
tmpPriv = append(tmpPriv, interfacePriv...)
|
|
|
|
tmpPub = append(tmpPub, hostPub...)
|
|
|
|
tmpPub = append(tmpPub, defaultPub...)
|
|
|
|
tmpPub = append(tmpPub, interfacePub...)
|
|
|
|
for i := range tmpPriv {
|
|
|
|
if _, ok := ignore[tmpPriv[i].String()]; ok {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
priv = append(priv, tmpPriv[i])
|
|
|
|
}
|
|
|
|
for i := range tmpPub {
|
|
|
|
if _, ok := ignore[tmpPub[i].String()]; ok {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
pub = append(pub, tmpPub[i])
|
|
|
|
}
|
|
|
|
if len(priv) == 0 && len(pub) == 0 {
|
|
|
|
return nil, nil, errors.New("no valid IP was found")
|
|
|
|
}
|
|
|
|
if len(priv) == 0 {
|
2021-01-24 13:19:01 +00:00
|
|
|
// If no private IPs were found, use nil.
|
|
|
|
priv = append(priv, nil)
|
2020-11-13 17:36:07 +00:00
|
|
|
}
|
|
|
|
if len(pub) == 0 {
|
|
|
|
pub = priv
|
|
|
|
}
|
|
|
|
return priv[0], pub[0], nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func assignedToInterface(ip *net.IPNet) (bool, net.IPMask, error) {
|
|
|
|
links, err := netlink.LinkList()
|
|
|
|
if err != nil {
|
|
|
|
return false, nil, fmt.Errorf("failed to list interfaces: %v", err)
|
|
|
|
}
|
|
|
|
// Sort the links for stability.
|
|
|
|
sort.Slice(links, func(i, j int) bool {
|
|
|
|
return links[i].Attrs().Name < links[j].Attrs().Name
|
|
|
|
})
|
|
|
|
for _, link := range links {
|
|
|
|
addrs, err := netlink.AddrList(link, netlink.FAMILY_ALL)
|
|
|
|
if err != nil {
|
|
|
|
return false, nil, fmt.Errorf("failed to list addresses for %s: %v", link.Attrs().Name, err)
|
|
|
|
}
|
|
|
|
// Sort the IPs for stability.
|
|
|
|
sort.Slice(addrs, func(i, j int) bool {
|
|
|
|
return addrs[i].String() < addrs[j].String()
|
|
|
|
})
|
|
|
|
for i := range addrs {
|
|
|
|
if ip.IP.Equal(addrs[i].IP) {
|
|
|
|
return true, addrs[i].Mask, nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false, nil, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// ipsForHostname returns a slice of IPs to which the
|
|
|
|
// given hostname resolves.
|
|
|
|
func ipsForHostname(hostname string) []*net.IPNet {
|
|
|
|
if ip := net.ParseIP(hostname); ip != nil {
|
|
|
|
return []*net.IPNet{oneAddressCIDR(ip)}
|
|
|
|
}
|
|
|
|
ips, err := net.LookupIP(hostname)
|
|
|
|
if err != nil {
|
|
|
|
// Most likely the hostname is not resolvable.
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
nets := make([]*net.IPNet, len(ips))
|
|
|
|
for i := range ips {
|
|
|
|
nets[i] = oneAddressCIDR(ips[i])
|
|
|
|
}
|
|
|
|
return nets
|
|
|
|
}
|
|
|
|
|
|
|
|
// ipsForAllInterfaces returns a slice of IPs assigned to all the
|
|
|
|
// interfaces on the host.
|
|
|
|
func ipsForAllInterfaces() ([]*net.IPNet, error) {
|
|
|
|
ifaces, err := net.Interfaces()
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("failed to list interfaces: %v", err)
|
|
|
|
}
|
|
|
|
var nets []*net.IPNet
|
|
|
|
for _, iface := range ifaces {
|
|
|
|
ips, err := ipsForInterface(&iface)
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("failed to list addresses for %s: %v", iface.Name, err)
|
|
|
|
}
|
|
|
|
nets = append(nets, ips...)
|
|
|
|
}
|
|
|
|
return nets, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// ipsForInterface returns a slice of IPs assigned to the given interface.
|
|
|
|
func ipsForInterface(iface *net.Interface) ([]*net.IPNet, error) {
|
|
|
|
link, err := netlink.LinkByIndex(iface.Index)
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("failed to get link: %s", err)
|
|
|
|
}
|
|
|
|
addrs, err := netlink.AddrList(link, netlink.FAMILY_ALL)
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("failed to list addresses for %s: %v", iface.Name, err)
|
|
|
|
}
|
|
|
|
var ips []*net.IPNet
|
|
|
|
for _, a := range addrs {
|
|
|
|
if a.IPNet != nil {
|
|
|
|
ips = append(ips, a.IPNet)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return ips, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// interfacesForIP returns a slice of interfaces withthe given IP.
|
|
|
|
func interfacesForIP(ip *net.IPNet) ([]net.Interface, error) {
|
|
|
|
ifaces, err := net.Interfaces()
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("failed to list interfaces: %v", err)
|
|
|
|
}
|
|
|
|
var interfaces []net.Interface
|
|
|
|
for _, iface := range ifaces {
|
|
|
|
ips, err := ipsForInterface(&iface)
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("failed to list addresses for %s: %v", iface.Name, err)
|
|
|
|
}
|
|
|
|
for i := range ips {
|
|
|
|
if ip.IP.Equal(ips[i].IP) {
|
|
|
|
interfaces = append(interfaces, iface)
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if len(interfaces) == 0 {
|
|
|
|
return nil, fmt.Errorf("no interface has %s assigned", ip.String())
|
|
|
|
}
|
|
|
|
return interfaces, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// defaultInterface returns the interface for the default route of the host.
|
|
|
|
func defaultInterface() (*net.Interface, error) {
|
|
|
|
routes, err := netlink.RouteList(nil, netlink.FAMILY_ALL)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, route := range routes {
|
|
|
|
if route.Dst == nil || route.Dst.String() == "0.0.0.0/0" || route.Dst.String() == "::/0" {
|
|
|
|
if route.LinkIndex <= 0 {
|
|
|
|
return nil, errors.New("failed to determine interface of route")
|
|
|
|
}
|
|
|
|
return net.InterfaceByIndex(route.LinkIndex)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil, errors.New("failed to find default route")
|
|
|
|
}
|