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 }