route_linux.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452
  1. package netlink
  2. import (
  3. "fmt"
  4. "net"
  5. "syscall"
  6. "github.com/vishvananda/netlink/nl"
  7. )
  8. // RtAttr is shared so it is in netlink_linux.go
  9. const (
  10. SCOPE_UNIVERSE Scope = syscall.RT_SCOPE_UNIVERSE
  11. SCOPE_SITE Scope = syscall.RT_SCOPE_SITE
  12. SCOPE_LINK Scope = syscall.RT_SCOPE_LINK
  13. SCOPE_HOST Scope = syscall.RT_SCOPE_HOST
  14. SCOPE_NOWHERE Scope = syscall.RT_SCOPE_NOWHERE
  15. )
  16. const (
  17. RT_FILTER_PROTOCOL uint64 = 1 << (1 + iota)
  18. RT_FILTER_SCOPE
  19. RT_FILTER_TYPE
  20. RT_FILTER_TOS
  21. RT_FILTER_IIF
  22. RT_FILTER_OIF
  23. RT_FILTER_DST
  24. RT_FILTER_SRC
  25. RT_FILTER_GW
  26. RT_FILTER_TABLE
  27. )
  28. const (
  29. FLAG_ONLINK NextHopFlag = syscall.RTNH_F_ONLINK
  30. FLAG_PERVASIVE NextHopFlag = syscall.RTNH_F_PERVASIVE
  31. )
  32. var testFlags = []flagString{
  33. {f: FLAG_ONLINK, s: "onlink"},
  34. {f: FLAG_PERVASIVE, s: "pervasive"},
  35. }
  36. func (r *Route) ListFlags() []string {
  37. var flags []string
  38. for _, tf := range testFlags {
  39. if r.Flags&int(tf.f) != 0 {
  40. flags = append(flags, tf.s)
  41. }
  42. }
  43. return flags
  44. }
  45. // RouteAdd will add a route to the system.
  46. // Equivalent to: `ip route add $route`
  47. func RouteAdd(route *Route) error {
  48. return pkgHandle.RouteAdd(route)
  49. }
  50. // RouteAdd will add a route to the system.
  51. // Equivalent to: `ip route add $route`
  52. func (h *Handle) RouteAdd(route *Route) error {
  53. req := h.newNetlinkRequest(syscall.RTM_NEWROUTE, syscall.NLM_F_CREATE|syscall.NLM_F_EXCL|syscall.NLM_F_ACK)
  54. return h.routeHandle(route, req, nl.NewRtMsg())
  55. }
  56. // RouteDel will delete a route from the system.
  57. // Equivalent to: `ip route del $route`
  58. func RouteDel(route *Route) error {
  59. return pkgHandle.RouteDel(route)
  60. }
  61. // RouteDel will delete a route from the system.
  62. // Equivalent to: `ip route del $route`
  63. func (h *Handle) RouteDel(route *Route) error {
  64. req := h.newNetlinkRequest(syscall.RTM_DELROUTE, syscall.NLM_F_ACK)
  65. return h.routeHandle(route, req, nl.NewRtDelMsg())
  66. }
  67. func (h *Handle) routeHandle(route *Route, req *nl.NetlinkRequest, msg *nl.RtMsg) error {
  68. if (route.Dst == nil || route.Dst.IP == nil) && route.Src == nil && route.Gw == nil {
  69. return fmt.Errorf("one of Dst.IP, Src, or Gw must not be nil")
  70. }
  71. family := -1
  72. var rtAttrs []*nl.RtAttr
  73. if route.Dst != nil && route.Dst.IP != nil {
  74. dstLen, _ := route.Dst.Mask.Size()
  75. msg.Dst_len = uint8(dstLen)
  76. dstFamily := nl.GetIPFamily(route.Dst.IP)
  77. family = dstFamily
  78. var dstData []byte
  79. if dstFamily == FAMILY_V4 {
  80. dstData = route.Dst.IP.To4()
  81. } else {
  82. dstData = route.Dst.IP.To16()
  83. }
  84. rtAttrs = append(rtAttrs, nl.NewRtAttr(syscall.RTA_DST, dstData))
  85. }
  86. if route.Src != nil {
  87. srcFamily := nl.GetIPFamily(route.Src)
  88. if family != -1 && family != srcFamily {
  89. return fmt.Errorf("source and destination ip are not the same IP family")
  90. }
  91. family = srcFamily
  92. var srcData []byte
  93. if srcFamily == FAMILY_V4 {
  94. srcData = route.Src.To4()
  95. } else {
  96. srcData = route.Src.To16()
  97. }
  98. // The commonly used src ip for routes is actually PREFSRC
  99. rtAttrs = append(rtAttrs, nl.NewRtAttr(syscall.RTA_PREFSRC, srcData))
  100. }
  101. if route.Gw != nil {
  102. gwFamily := nl.GetIPFamily(route.Gw)
  103. if family != -1 && family != gwFamily {
  104. return fmt.Errorf("gateway, source, and destination ip are not the same IP family")
  105. }
  106. family = gwFamily
  107. var gwData []byte
  108. if gwFamily == FAMILY_V4 {
  109. gwData = route.Gw.To4()
  110. } else {
  111. gwData = route.Gw.To16()
  112. }
  113. rtAttrs = append(rtAttrs, nl.NewRtAttr(syscall.RTA_GATEWAY, gwData))
  114. }
  115. if len(route.MultiPath) > 0 {
  116. buf := []byte{}
  117. for _, nh := range route.MultiPath {
  118. rtnh := &nl.RtNexthop{
  119. RtNexthop: syscall.RtNexthop{
  120. Hops: uint8(nh.Hops),
  121. Ifindex: int32(nh.LinkIndex),
  122. Len: uint16(syscall.SizeofRtNexthop),
  123. },
  124. }
  125. var gwData []byte
  126. if nh.Gw != nil {
  127. gwFamily := nl.GetIPFamily(nh.Gw)
  128. if family != -1 && family != gwFamily {
  129. return fmt.Errorf("gateway, source, and destination ip are not the same IP family")
  130. }
  131. var gw *nl.RtAttr
  132. if gwFamily == FAMILY_V4 {
  133. gw = nl.NewRtAttr(syscall.RTA_GATEWAY, []byte(nh.Gw.To4()))
  134. } else {
  135. gw = nl.NewRtAttr(syscall.RTA_GATEWAY, []byte(nh.Gw.To16()))
  136. }
  137. gwData := gw.Serialize()
  138. rtnh.Len += uint16(len(gwData))
  139. }
  140. buf = append(buf, rtnh.Serialize()...)
  141. buf = append(buf, gwData...)
  142. }
  143. rtAttrs = append(rtAttrs, nl.NewRtAttr(syscall.RTA_MULTIPATH, buf))
  144. }
  145. if route.Table > 0 {
  146. if route.Table >= 256 {
  147. msg.Table = syscall.RT_TABLE_UNSPEC
  148. b := make([]byte, 4)
  149. native.PutUint32(b, uint32(route.Table))
  150. rtAttrs = append(rtAttrs, nl.NewRtAttr(syscall.RTA_TABLE, b))
  151. } else {
  152. msg.Table = uint8(route.Table)
  153. }
  154. }
  155. if route.Priority > 0 {
  156. b := make([]byte, 4)
  157. native.PutUint32(b, uint32(route.Priority))
  158. rtAttrs = append(rtAttrs, nl.NewRtAttr(syscall.RTA_PRIORITY, b))
  159. }
  160. if route.Tos > 0 {
  161. msg.Tos = uint8(route.Tos)
  162. }
  163. if route.Protocol > 0 {
  164. msg.Protocol = uint8(route.Protocol)
  165. }
  166. if route.Type > 0 {
  167. msg.Type = uint8(route.Type)
  168. }
  169. msg.Flags = uint32(route.Flags)
  170. msg.Scope = uint8(route.Scope)
  171. msg.Family = uint8(family)
  172. req.AddData(msg)
  173. for _, attr := range rtAttrs {
  174. req.AddData(attr)
  175. }
  176. var (
  177. b = make([]byte, 4)
  178. native = nl.NativeEndian()
  179. )
  180. native.PutUint32(b, uint32(route.LinkIndex))
  181. req.AddData(nl.NewRtAttr(syscall.RTA_OIF, b))
  182. _, err := req.Execute(syscall.NETLINK_ROUTE, 0)
  183. return err
  184. }
  185. // RouteList gets a list of routes in the system.
  186. // Equivalent to: `ip route show`.
  187. // The list can be filtered by link and ip family.
  188. func RouteList(link Link, family int) ([]Route, error) {
  189. return pkgHandle.RouteList(link, family)
  190. }
  191. // RouteList gets a list of routes in the system.
  192. // Equivalent to: `ip route show`.
  193. // The list can be filtered by link and ip family.
  194. func (h *Handle) RouteList(link Link, family int) ([]Route, error) {
  195. var routeFilter *Route
  196. if link != nil {
  197. routeFilter = &Route{
  198. LinkIndex: link.Attrs().Index,
  199. }
  200. }
  201. return h.RouteListFiltered(family, routeFilter, RT_FILTER_OIF)
  202. }
  203. // RouteListFiltered gets a list of routes in the system filtered with specified rules.
  204. // All rules must be defined in RouteFilter struct
  205. func RouteListFiltered(family int, filter *Route, filterMask uint64) ([]Route, error) {
  206. return pkgHandle.RouteListFiltered(family, filter, filterMask)
  207. }
  208. // RouteListFiltered gets a list of routes in the system filtered with specified rules.
  209. // All rules must be defined in RouteFilter struct
  210. func (h *Handle) RouteListFiltered(family int, filter *Route, filterMask uint64) ([]Route, error) {
  211. req := h.newNetlinkRequest(syscall.RTM_GETROUTE, syscall.NLM_F_DUMP)
  212. infmsg := nl.NewIfInfomsg(family)
  213. req.AddData(infmsg)
  214. msgs, err := req.Execute(syscall.NETLINK_ROUTE, syscall.RTM_NEWROUTE)
  215. if err != nil {
  216. return nil, err
  217. }
  218. var res []Route
  219. for _, m := range msgs {
  220. msg := nl.DeserializeRtMsg(m)
  221. if msg.Flags&syscall.RTM_F_CLONED != 0 {
  222. // Ignore cloned routes
  223. continue
  224. }
  225. if msg.Table != syscall.RT_TABLE_MAIN {
  226. if filter == nil || filter != nil && filterMask&RT_FILTER_TABLE == 0 {
  227. // Ignore non-main tables
  228. continue
  229. }
  230. }
  231. route, err := deserializeRoute(m)
  232. if err != nil {
  233. return nil, err
  234. }
  235. if filter != nil {
  236. switch {
  237. case filterMask&RT_FILTER_TABLE != 0 && route.Table != filter.Table:
  238. continue
  239. case filterMask&RT_FILTER_PROTOCOL != 0 && route.Protocol != filter.Protocol:
  240. continue
  241. case filterMask&RT_FILTER_SCOPE != 0 && route.Scope != filter.Scope:
  242. continue
  243. case filterMask&RT_FILTER_TYPE != 0 && route.Type != filter.Type:
  244. continue
  245. case filterMask&RT_FILTER_TOS != 0 && route.Tos != filter.Tos:
  246. continue
  247. case filterMask&RT_FILTER_OIF != 0 && route.LinkIndex != filter.LinkIndex:
  248. continue
  249. case filterMask&RT_FILTER_IIF != 0 && route.ILinkIndex != filter.ILinkIndex:
  250. continue
  251. case filterMask&RT_FILTER_GW != 0 && !route.Gw.Equal(filter.Gw):
  252. continue
  253. case filterMask&RT_FILTER_SRC != 0 && !route.Src.Equal(filter.Src):
  254. continue
  255. case filterMask&RT_FILTER_DST != 0 && filter.Dst != nil:
  256. if route.Dst == nil {
  257. continue
  258. }
  259. aMaskLen, aMaskBits := route.Dst.Mask.Size()
  260. bMaskLen, bMaskBits := filter.Dst.Mask.Size()
  261. if !(route.Dst.IP.Equal(filter.Dst.IP) && aMaskLen == bMaskLen && aMaskBits == bMaskBits) {
  262. continue
  263. }
  264. }
  265. }
  266. res = append(res, route)
  267. }
  268. return res, nil
  269. }
  270. // deserializeRoute decodes a binary netlink message into a Route struct
  271. func deserializeRoute(m []byte) (Route, error) {
  272. msg := nl.DeserializeRtMsg(m)
  273. attrs, err := nl.ParseRouteAttr(m[msg.Len():])
  274. if err != nil {
  275. return Route{}, err
  276. }
  277. route := Route{
  278. Scope: Scope(msg.Scope),
  279. Protocol: int(msg.Protocol),
  280. Table: int(msg.Table),
  281. Type: int(msg.Type),
  282. Tos: int(msg.Tos),
  283. Flags: int(msg.Flags),
  284. }
  285. native := nl.NativeEndian()
  286. for _, attr := range attrs {
  287. switch attr.Attr.Type {
  288. case syscall.RTA_GATEWAY:
  289. route.Gw = net.IP(attr.Value)
  290. case syscall.RTA_PREFSRC:
  291. route.Src = net.IP(attr.Value)
  292. case syscall.RTA_DST:
  293. route.Dst = &net.IPNet{
  294. IP: attr.Value,
  295. Mask: net.CIDRMask(int(msg.Dst_len), 8*len(attr.Value)),
  296. }
  297. case syscall.RTA_OIF:
  298. route.LinkIndex = int(native.Uint32(attr.Value[0:4]))
  299. case syscall.RTA_IIF:
  300. route.ILinkIndex = int(native.Uint32(attr.Value[0:4]))
  301. case syscall.RTA_PRIORITY:
  302. route.Priority = int(native.Uint32(attr.Value[0:4]))
  303. case syscall.RTA_TABLE:
  304. route.Table = int(native.Uint32(attr.Value[0:4]))
  305. case syscall.RTA_MULTIPATH:
  306. parseRtNexthop := func(value []byte) (*NexthopInfo, []byte, error) {
  307. if len(value) < syscall.SizeofRtNexthop {
  308. return nil, nil, fmt.Errorf("Lack of bytes")
  309. }
  310. nh := nl.DeserializeRtNexthop(value)
  311. if len(value) < int(nh.RtNexthop.Len) {
  312. return nil, nil, fmt.Errorf("Lack of bytes")
  313. }
  314. info := &NexthopInfo{
  315. LinkIndex: int(nh.RtNexthop.Ifindex),
  316. Hops: int(nh.RtNexthop.Hops),
  317. }
  318. attrs, err := nl.ParseRouteAttr(value[syscall.SizeofRtNexthop:int(nh.RtNexthop.Len)])
  319. if err != nil {
  320. return nil, nil, err
  321. }
  322. for _, attr := range attrs {
  323. switch attr.Attr.Type {
  324. case syscall.RTA_GATEWAY:
  325. info.Gw = net.IP(attr.Value)
  326. }
  327. }
  328. return info, value[int(nh.RtNexthop.Len):], nil
  329. }
  330. rest := attr.Value
  331. for len(rest) > 0 {
  332. info, buf, err := parseRtNexthop(rest)
  333. if err != nil {
  334. return route, err
  335. }
  336. route.MultiPath = append(route.MultiPath, info)
  337. rest = buf
  338. }
  339. }
  340. }
  341. return route, nil
  342. }
  343. // RouteGet gets a route to a specific destination from the host system.
  344. // Equivalent to: 'ip route get'.
  345. func RouteGet(destination net.IP) ([]Route, error) {
  346. return pkgHandle.RouteGet(destination)
  347. }
  348. // RouteGet gets a route to a specific destination from the host system.
  349. // Equivalent to: 'ip route get'.
  350. func (h *Handle) RouteGet(destination net.IP) ([]Route, error) {
  351. req := h.newNetlinkRequest(syscall.RTM_GETROUTE, syscall.NLM_F_REQUEST)
  352. family := nl.GetIPFamily(destination)
  353. var destinationData []byte
  354. var bitlen uint8
  355. if family == FAMILY_V4 {
  356. destinationData = destination.To4()
  357. bitlen = 32
  358. } else {
  359. destinationData = destination.To16()
  360. bitlen = 128
  361. }
  362. msg := &nl.RtMsg{}
  363. msg.Family = uint8(family)
  364. msg.Dst_len = bitlen
  365. req.AddData(msg)
  366. rtaDst := nl.NewRtAttr(syscall.RTA_DST, destinationData)
  367. req.AddData(rtaDst)
  368. msgs, err := req.Execute(syscall.NETLINK_ROUTE, syscall.RTM_NEWROUTE)
  369. if err != nil {
  370. return nil, err
  371. }
  372. var res []Route
  373. for _, m := range msgs {
  374. route, err := deserializeRoute(m)
  375. if err != nil {
  376. return nil, err
  377. }
  378. res = append(res, route)
  379. }
  380. return res, nil
  381. }
  382. // RouteSubscribe takes a chan down which notifications will be sent
  383. // when routes are added or deleted. Close the 'done' chan to stop subscription.
  384. func RouteSubscribe(ch chan<- RouteUpdate, done <-chan struct{}) error {
  385. s, err := nl.Subscribe(syscall.NETLINK_ROUTE, syscall.RTNLGRP_IPV4_ROUTE, syscall.RTNLGRP_IPV6_ROUTE)
  386. if err != nil {
  387. return err
  388. }
  389. if done != nil {
  390. go func() {
  391. <-done
  392. s.Close()
  393. }()
  394. }
  395. go func() {
  396. defer close(ch)
  397. for {
  398. msgs, err := s.Receive()
  399. if err != nil {
  400. return
  401. }
  402. for _, m := range msgs {
  403. route, err := deserializeRoute(m.Data)
  404. if err != nil {
  405. return
  406. }
  407. ch <- RouteUpdate{Type: m.Header.Type, Route: route}
  408. }
  409. }
  410. }()
  411. return nil
  412. }