routes_linux.go 5.6 KB

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