netutil: GetDefaultHost for getting the default IP of the host machine

This commit is contained in:
Anthony Romano 2016-08-12 15:34:41 -07:00
parent 7b11c288fe
commit d23392ed8e
2 changed files with 161 additions and 0 deletions

28
pkg/netutil/routes.go Normal file
View File

@ -0,0 +1,28 @@
// 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 !linux !386,!amd64
package netutil
import (
"fmt"
"runtime"
)
// GetDefaultHost fetches the a resolvable name that corresponds
// to the machine's default routable interface
func GetDefaultHost() (string, error) {
return "", fmt.Errorf("default host not supported on %s_%s", runtime.GOOS, runtime.GOARCH)
}

133
pkg/netutil/routes_linux.go Normal file
View File

@ -0,0 +1,133 @@
// 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 linux
// +build 386 amd64
// TODO support native endian but without using "unsafe"
package netutil
import (
"bytes"
"encoding/binary"
"fmt"
"net"
"syscall"
)
var errNoDefaultRoute = fmt.Errorf("could not find default route")
func GetDefaultHost() (string, error) {
rmsg, rerr := getDefaultRoute()
if rerr != nil {
return "", rerr
}
attrs, aerr := syscall.ParseNetlinkRouteAttr(rmsg)
if aerr != nil {
return "", aerr
}
oif := uint32(0)
for _, attr := range attrs {
if attr.Attr.Type == syscall.RTA_PREFSRC {
return net.IP(attr.Value).String(), nil
}
if attr.Attr.Type == syscall.RTA_OIF {
oif = binary.LittleEndian.Uint32(attr.Value)
}
}
if oif == 0 {
return "", errNoDefaultRoute
}
// prefsrc not detected, fall back to getting address from iface
ifmsg, ierr := getIface(oif)
if ierr != nil {
return "", ierr
}
attrs, aerr = syscall.ParseNetlinkRouteAttr(ifmsg)
if aerr != nil {
return "", aerr
}
for _, attr := range attrs {
if attr.Attr.Type == syscall.RTA_SRC {
return net.IP(attr.Value).String(), nil
}
}
return "", errNoDefaultRoute
}
func getDefaultRoute() (*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
}
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, binary.LittleEndian, &rtmsg); rerr != nil {
continue
}
if rtmsg.Dst_len == 0 {
// zero-length Dst_len implies default route
return &m, nil
}
}
return nil, errNoDefaultRoute
}
func getIface(idx uint32) (*syscall.NetlinkMessage, error) {
dat, err := syscall.NetlinkRIB(syscall.RTM_GETADDR, syscall.AF_UNSPEC)
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, binary.LittleEndian, &ifaddrmsg); rerr != nil {
continue
}
if ifaddrmsg.Index == idx {
return &m, nil
}
}
return nil, errNoDefaultRoute
}