go.etcd.io/etcd@v3.3.27+incompatible/pkg/netutil/routes_linux.go (about)

     1  // Copyright 2016 The etcd Authors
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  // +build linux
    16  
    17  package netutil
    18  
    19  import (
    20  	"bytes"
    21  	"encoding/binary"
    22  	"fmt"
    23  	"net"
    24  	"sort"
    25  	"syscall"
    26  
    27  	"github.com/coreos/etcd/pkg/cpuutil"
    28  )
    29  
    30  var errNoDefaultRoute = fmt.Errorf("could not find default route")
    31  var errNoDefaultHost = fmt.Errorf("could not find default host")
    32  var errNoDefaultInterface = fmt.Errorf("could not find default interface")
    33  
    34  // GetDefaultHost obtains the first IP address of machine from the routing table and returns the IP address as string.
    35  // An IPv4 address is preferred to an IPv6 address for backward compatibility.
    36  func GetDefaultHost() (string, error) {
    37  	rmsgs, rerr := getDefaultRoutes()
    38  	if rerr != nil {
    39  		return "", rerr
    40  	}
    41  
    42  	// prioritize IPv4
    43  	if rmsg, ok := rmsgs[syscall.AF_INET]; ok {
    44  		if host, err := chooseHost(syscall.AF_INET, rmsg); host != "" || err != nil {
    45  			return host, err
    46  		}
    47  		delete(rmsgs, syscall.AF_INET)
    48  	}
    49  
    50  	// sort so choice is deterministic
    51  	var families []int
    52  	for family := range rmsgs {
    53  		families = append(families, int(family))
    54  	}
    55  	sort.Ints(families)
    56  
    57  	for _, f := range families {
    58  		family := uint8(f)
    59  		if host, err := chooseHost(family, rmsgs[family]); host != "" || err != nil {
    60  			return host, err
    61  		}
    62  	}
    63  
    64  	return "", errNoDefaultHost
    65  }
    66  
    67  func chooseHost(family uint8, rmsg *syscall.NetlinkMessage) (string, error) {
    68  	host, oif, err := parsePREFSRC(rmsg)
    69  	if host != "" || err != nil {
    70  		return host, err
    71  	}
    72  
    73  	// prefsrc not detected, fall back to getting address from iface
    74  	ifmsg, ierr := getIfaceAddr(oif, family)
    75  	if ierr != nil {
    76  		return "", ierr
    77  	}
    78  
    79  	attrs, aerr := syscall.ParseNetlinkRouteAttr(ifmsg)
    80  	if aerr != nil {
    81  		return "", aerr
    82  	}
    83  
    84  	for _, attr := range attrs {
    85  		// search for RTA_DST because ipv6 doesn't have RTA_SRC
    86  		if attr.Attr.Type == syscall.RTA_DST {
    87  			return net.IP(attr.Value).String(), nil
    88  		}
    89  	}
    90  
    91  	return "", nil
    92  }
    93  
    94  func getDefaultRoutes() (map[uint8]*syscall.NetlinkMessage, error) {
    95  	dat, err := syscall.NetlinkRIB(syscall.RTM_GETROUTE, syscall.AF_UNSPEC)
    96  	if err != nil {
    97  		return nil, err
    98  	}
    99  
   100  	msgs, msgErr := syscall.ParseNetlinkMessage(dat)
   101  	if msgErr != nil {
   102  		return nil, msgErr
   103  	}
   104  
   105  	routes := make(map[uint8]*syscall.NetlinkMessage)
   106  	rtmsg := syscall.RtMsg{}
   107  	for _, m := range msgs {
   108  		if m.Header.Type != syscall.RTM_NEWROUTE {
   109  			continue
   110  		}
   111  		buf := bytes.NewBuffer(m.Data[:syscall.SizeofRtMsg])
   112  		if rerr := binary.Read(buf, cpuutil.ByteOrder(), &rtmsg); rerr != nil {
   113  			continue
   114  		}
   115  		if rtmsg.Dst_len == 0 && rtmsg.Table == syscall.RT_TABLE_MAIN {
   116  			// zero-length Dst_len implies default route
   117  			msg := m
   118  			routes[rtmsg.Family] = &msg
   119  		}
   120  	}
   121  
   122  	if len(routes) > 0 {
   123  		return routes, nil
   124  	}
   125  
   126  	return nil, errNoDefaultRoute
   127  }
   128  
   129  // Used to get an address of interface.
   130  func getIfaceAddr(idx uint32, family uint8) (*syscall.NetlinkMessage, error) {
   131  	dat, err := syscall.NetlinkRIB(syscall.RTM_GETADDR, int(family))
   132  	if err != nil {
   133  		return nil, err
   134  	}
   135  
   136  	msgs, msgErr := syscall.ParseNetlinkMessage(dat)
   137  	if msgErr != nil {
   138  		return nil, msgErr
   139  	}
   140  
   141  	ifaddrmsg := syscall.IfAddrmsg{}
   142  	for _, m := range msgs {
   143  		if m.Header.Type != syscall.RTM_NEWADDR {
   144  			continue
   145  		}
   146  		buf := bytes.NewBuffer(m.Data[:syscall.SizeofIfAddrmsg])
   147  		if rerr := binary.Read(buf, cpuutil.ByteOrder(), &ifaddrmsg); rerr != nil {
   148  			continue
   149  		}
   150  		if ifaddrmsg.Index == idx {
   151  			return &m, nil
   152  		}
   153  	}
   154  
   155  	return nil, fmt.Errorf("could not find address for interface index %v", idx)
   156  
   157  }
   158  
   159  // Used to get a name of interface.
   160  func getIfaceLink(idx uint32) (*syscall.NetlinkMessage, error) {
   161  	dat, err := syscall.NetlinkRIB(syscall.RTM_GETLINK, syscall.AF_UNSPEC)
   162  	if err != nil {
   163  		return nil, err
   164  	}
   165  
   166  	msgs, msgErr := syscall.ParseNetlinkMessage(dat)
   167  	if msgErr != nil {
   168  		return nil, msgErr
   169  	}
   170  
   171  	ifinfomsg := syscall.IfInfomsg{}
   172  	for _, m := range msgs {
   173  		if m.Header.Type != syscall.RTM_NEWLINK {
   174  			continue
   175  		}
   176  		buf := bytes.NewBuffer(m.Data[:syscall.SizeofIfInfomsg])
   177  		if rerr := binary.Read(buf, cpuutil.ByteOrder(), &ifinfomsg); rerr != nil {
   178  			continue
   179  		}
   180  		if ifinfomsg.Index == int32(idx) {
   181  			return &m, nil
   182  		}
   183  	}
   184  
   185  	return nil, fmt.Errorf("could not find link for interface index %v", idx)
   186  }
   187  
   188  // GetDefaultInterfaces gets names of interfaces and returns a map[interface]families.
   189  func GetDefaultInterfaces() (map[string]uint8, error) {
   190  	interfaces := make(map[string]uint8)
   191  	rmsgs, rerr := getDefaultRoutes()
   192  	if rerr != nil {
   193  		return interfaces, rerr
   194  	}
   195  
   196  	for family, rmsg := range rmsgs {
   197  		_, oif, err := parsePREFSRC(rmsg)
   198  		if err != nil {
   199  			return interfaces, err
   200  		}
   201  
   202  		ifmsg, ierr := getIfaceLink(oif)
   203  		if ierr != nil {
   204  			return interfaces, ierr
   205  		}
   206  
   207  		attrs, aerr := syscall.ParseNetlinkRouteAttr(ifmsg)
   208  		if aerr != nil {
   209  			return interfaces, aerr
   210  		}
   211  
   212  		for _, attr := range attrs {
   213  			if attr.Attr.Type == syscall.IFLA_IFNAME {
   214  				// key is an interface name
   215  				// possible values: 2 - AF_INET, 10 - AF_INET6, 12 - dualstack
   216  				interfaces[string(attr.Value[:len(attr.Value)-1])] += family
   217  			}
   218  		}
   219  	}
   220  	if len(interfaces) > 0 {
   221  		return interfaces, nil
   222  	}
   223  	return interfaces, errNoDefaultInterface
   224  }
   225  
   226  // parsePREFSRC returns preferred source address and output interface index (RTA_OIF).
   227  func parsePREFSRC(m *syscall.NetlinkMessage) (host string, oif uint32, err error) {
   228  	var attrs []syscall.NetlinkRouteAttr
   229  	attrs, err = syscall.ParseNetlinkRouteAttr(m)
   230  	if err != nil {
   231  		return "", 0, err
   232  	}
   233  
   234  	for _, attr := range attrs {
   235  		if attr.Attr.Type == syscall.RTA_PREFSRC {
   236  			host = net.IP(attr.Value).String()
   237  		}
   238  		if attr.Attr.Type == syscall.RTA_OIF {
   239  			oif = cpuutil.ByteOrder().Uint32(attr.Value)
   240  		}
   241  		if host != "" && oif != uint32(0) {
   242  			break
   243  		}
   244  	}
   245  
   246  	if oif == 0 {
   247  		err = errNoDefaultRoute
   248  	}
   249  	return host, oif, err
   250  }