...

Source file src/go.etcd.io/etcd/pkg/v3/netutil/routes_linux.go

Documentation: go.etcd.io/etcd/pkg/v3/netutil

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

View as plain text