| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251 | // Copyright 2016 The etcd 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.// +build linuxpackage netutilimport (	"bytes"	"encoding/binary"	"fmt"	"net"	"sort"	"syscall"	"go.etcd.io/etcd/pkg/cpuutil")var errNoDefaultRoute = fmt.Errorf("could not find default route")var errNoDefaultHost = fmt.Errorf("could not find default host")var errNoDefaultInterface = fmt.Errorf("could not find default interface")// GetDefaultHost obtains the first IP address of machine from the routing table and returns the IP address as string.// An IPv4 address is preferred to an IPv6 address for backward compatibility.func GetDefaultHost() (string, error) {	rmsgs, rerr := getDefaultRoutes()	if rerr != nil {		return "", rerr	}	// prioritize IPv4	if rmsg, ok := rmsgs[syscall.AF_INET]; ok {		if host, err := chooseHost(syscall.AF_INET, rmsg); host != "" || err != nil {			return host, err		}		delete(rmsgs, syscall.AF_INET)	}	// sort so choice is deterministic	var families []int	for family := range rmsgs {		families = append(families, int(family))	}	sort.Ints(families)	for _, f := range families {		family := uint8(f)		if host, err := chooseHost(family, rmsgs[family]); host != "" || err != nil {			return host, err		}	}	return "", errNoDefaultHost}func chooseHost(family uint8, rmsg *syscall.NetlinkMessage) (string, error) {	host, oif, err := parsePREFSRC(rmsg)	if host != "" || err != nil {		return host, err	}	// prefsrc not detected, fall back to getting address from iface	ifmsg, ierr := getIfaceAddr(oif, family)	if ierr != nil {		return "", ierr	}	attrs, aerr := syscall.ParseNetlinkRouteAttr(ifmsg)	if aerr != nil {		return "", aerr	}	for _, attr := range attrs {		// search for RTA_DST because ipv6 doesn't have RTA_SRC		if attr.Attr.Type == syscall.RTA_DST {			return net.IP(attr.Value).String(), nil		}	}	return "", nil}func getDefaultRoutes() (map[uint8]*syscall.NetlinkMessage, error) {	dat, err := syscall.NetlinkRIB(syscall.RTM_GETROUTE, syscall.AF_UNSPEC)	if err != nil {		return nil, err	}	msgs, msgErr := syscall.ParseNetlinkMessage(dat)	if msgErr != nil {		return nil, msgErr	}	routes := make(map[uint8]*syscall.NetlinkMessage)	rtmsg := syscall.RtMsg{}	for _, m := range msgs {		if m.Header.Type != syscall.RTM_NEWROUTE {			continue		}		buf := bytes.NewBuffer(m.Data[:syscall.SizeofRtMsg])		if rerr := binary.Read(buf, cpuutil.ByteOrder(), &rtmsg); rerr != nil {			continue		}		if rtmsg.Dst_len == 0 && rtmsg.Table == syscall.RT_TABLE_MAIN {			// zero-length Dst_len implies default route			msg := m			routes[rtmsg.Family] = &msg		}	}	if len(routes) > 0 {		return routes, nil	}	return nil, errNoDefaultRoute}// Used to get an address of interface.func getIfaceAddr(idx uint32, family uint8) (*syscall.NetlinkMessage, error) {	dat, err := syscall.NetlinkRIB(syscall.RTM_GETADDR, int(family))	if err != nil {		return nil, err	}	msgs, msgErr := syscall.ParseNetlinkMessage(dat)	if msgErr != nil {		return nil, msgErr	}	ifaddrmsg := syscall.IfAddrmsg{}	for _, m := range msgs {		if m.Header.Type != syscall.RTM_NEWADDR {			continue		}		buf := bytes.NewBuffer(m.Data[:syscall.SizeofIfAddrmsg])		if rerr := binary.Read(buf, cpuutil.ByteOrder(), &ifaddrmsg); rerr != nil {			continue		}		if ifaddrmsg.Index == idx {			return &m, nil		}	}	return nil, fmt.Errorf("could not find address for interface index %v", idx)}// Used to get a name of interface.func getIfaceLink(idx uint32) (*syscall.NetlinkMessage, error) {	dat, err := syscall.NetlinkRIB(syscall.RTM_GETLINK, syscall.AF_UNSPEC)	if err != nil {		return nil, err	}	msgs, msgErr := syscall.ParseNetlinkMessage(dat)	if msgErr != nil {		return nil, msgErr	}	ifinfomsg := syscall.IfInfomsg{}	for _, m := range msgs {		if m.Header.Type != syscall.RTM_NEWLINK {			continue		}		buf := bytes.NewBuffer(m.Data[:syscall.SizeofIfInfomsg])		if rerr := binary.Read(buf, cpuutil.ByteOrder(), &ifinfomsg); rerr != nil {			continue		}		if ifinfomsg.Index == int32(idx) {			return &m, nil		}	}	return nil, fmt.Errorf("could not find link for interface index %v", idx)}// GetDefaultInterfaces gets names of interfaces and returns a map[interface]families.func GetDefaultInterfaces() (map[string]uint8, error) {	interfaces := make(map[string]uint8)	rmsgs, rerr := getDefaultRoutes()	if rerr != nil {		return interfaces, rerr	}	for family, rmsg := range rmsgs {		_, oif, err := parsePREFSRC(rmsg)		if err != nil {			return interfaces, err		}		ifmsg, ierr := getIfaceLink(oif)		if ierr != nil {			return interfaces, ierr		}		attrs, aerr := syscall.ParseNetlinkRouteAttr(ifmsg)		if aerr != nil {			return interfaces, aerr		}		for _, attr := range attrs {			if attr.Attr.Type == syscall.IFLA_IFNAME {				// key is an interface name				// possible values: 2 - AF_INET, 10 - AF_INET6, 12 - dualstack				interfaces[string(attr.Value[:len(attr.Value)-1])] += family			}		}	}	if len(interfaces) > 0 {		return interfaces, nil	}	return interfaces, errNoDefaultInterface}// parsePREFSRC returns preferred source address and output interface index (RTA_OIF).func parsePREFSRC(m *syscall.NetlinkMessage) (host string, oif uint32, err error) {	var attrs []syscall.NetlinkRouteAttr	attrs, err = syscall.ParseNetlinkRouteAttr(m)	if err != nil {		return "", 0, err	}	for _, attr := range attrs {		if attr.Attr.Type == syscall.RTA_PREFSRC {			host = net.IP(attr.Value).String()		}		if attr.Attr.Type == syscall.RTA_OIF {			oif = cpuutil.ByteOrder().Uint32(attr.Value)		}		if host != "" && oif != uint32(0) {			break		}	}	if oif == 0 {		err = errNoDefaultRoute	}	return host, oif, err}
 |