|
|
@@ -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
|
|
|
+}
|