neigh_linux.go 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382
  1. package netlink
  2. import (
  3. "net"
  4. "syscall"
  5. "unsafe"
  6. "github.com/vishvananda/netlink/nl"
  7. "github.com/vishvananda/netns"
  8. "golang.org/x/sys/unix"
  9. )
  10. const (
  11. NDA_UNSPEC = iota
  12. NDA_DST
  13. NDA_LLADDR
  14. NDA_CACHEINFO
  15. NDA_PROBES
  16. NDA_VLAN
  17. NDA_PORT
  18. NDA_VNI
  19. NDA_IFINDEX
  20. NDA_MAX = NDA_IFINDEX
  21. )
  22. // Neighbor Cache Entry States.
  23. const (
  24. NUD_NONE = 0x00
  25. NUD_INCOMPLETE = 0x01
  26. NUD_REACHABLE = 0x02
  27. NUD_STALE = 0x04
  28. NUD_DELAY = 0x08
  29. NUD_PROBE = 0x10
  30. NUD_FAILED = 0x20
  31. NUD_NOARP = 0x40
  32. NUD_PERMANENT = 0x80
  33. )
  34. // Neighbor Flags
  35. const (
  36. NTF_USE = 0x01
  37. NTF_SELF = 0x02
  38. NTF_MASTER = 0x04
  39. NTF_PROXY = 0x08
  40. NTF_ROUTER = 0x80
  41. )
  42. type Ndmsg struct {
  43. Family uint8
  44. Index uint32
  45. State uint16
  46. Flags uint8
  47. Type uint8
  48. }
  49. func deserializeNdmsg(b []byte) *Ndmsg {
  50. var dummy Ndmsg
  51. return (*Ndmsg)(unsafe.Pointer(&b[0:unsafe.Sizeof(dummy)][0]))
  52. }
  53. func (msg *Ndmsg) Serialize() []byte {
  54. return (*(*[unsafe.Sizeof(*msg)]byte)(unsafe.Pointer(msg)))[:]
  55. }
  56. func (msg *Ndmsg) Len() int {
  57. return int(unsafe.Sizeof(*msg))
  58. }
  59. // NeighAdd will add an IP to MAC mapping to the ARP table
  60. // Equivalent to: `ip neigh add ....`
  61. func NeighAdd(neigh *Neigh) error {
  62. return pkgHandle.NeighAdd(neigh)
  63. }
  64. // NeighAdd will add an IP to MAC mapping to the ARP table
  65. // Equivalent to: `ip neigh add ....`
  66. func (h *Handle) NeighAdd(neigh *Neigh) error {
  67. return h.neighAdd(neigh, unix.NLM_F_CREATE|unix.NLM_F_EXCL)
  68. }
  69. // NeighSet will add or replace an IP to MAC mapping to the ARP table
  70. // Equivalent to: `ip neigh replace....`
  71. func NeighSet(neigh *Neigh) error {
  72. return pkgHandle.NeighSet(neigh)
  73. }
  74. // NeighSet will add or replace an IP to MAC mapping to the ARP table
  75. // Equivalent to: `ip neigh replace....`
  76. func (h *Handle) NeighSet(neigh *Neigh) error {
  77. return h.neighAdd(neigh, unix.NLM_F_CREATE|unix.NLM_F_REPLACE)
  78. }
  79. // NeighAppend will append an entry to FDB
  80. // Equivalent to: `bridge fdb append...`
  81. func NeighAppend(neigh *Neigh) error {
  82. return pkgHandle.NeighAppend(neigh)
  83. }
  84. // NeighAppend will append an entry to FDB
  85. // Equivalent to: `bridge fdb append...`
  86. func (h *Handle) NeighAppend(neigh *Neigh) error {
  87. return h.neighAdd(neigh, unix.NLM_F_CREATE|unix.NLM_F_APPEND)
  88. }
  89. // NeighAppend will append an entry to FDB
  90. // Equivalent to: `bridge fdb append...`
  91. func neighAdd(neigh *Neigh, mode int) error {
  92. return pkgHandle.neighAdd(neigh, mode)
  93. }
  94. // NeighAppend will append an entry to FDB
  95. // Equivalent to: `bridge fdb append...`
  96. func (h *Handle) neighAdd(neigh *Neigh, mode int) error {
  97. req := h.newNetlinkRequest(unix.RTM_NEWNEIGH, mode|unix.NLM_F_ACK)
  98. return neighHandle(neigh, req)
  99. }
  100. // NeighDel will delete an IP address from a link device.
  101. // Equivalent to: `ip addr del $addr dev $link`
  102. func NeighDel(neigh *Neigh) error {
  103. return pkgHandle.NeighDel(neigh)
  104. }
  105. // NeighDel will delete an IP address from a link device.
  106. // Equivalent to: `ip addr del $addr dev $link`
  107. func (h *Handle) NeighDel(neigh *Neigh) error {
  108. req := h.newNetlinkRequest(unix.RTM_DELNEIGH, unix.NLM_F_ACK)
  109. return neighHandle(neigh, req)
  110. }
  111. func neighHandle(neigh *Neigh, req *nl.NetlinkRequest) error {
  112. var family int
  113. if neigh.Family > 0 {
  114. family = neigh.Family
  115. } else {
  116. family = nl.GetIPFamily(neigh.IP)
  117. }
  118. msg := Ndmsg{
  119. Family: uint8(family),
  120. Index: uint32(neigh.LinkIndex),
  121. State: uint16(neigh.State),
  122. Type: uint8(neigh.Type),
  123. Flags: uint8(neigh.Flags),
  124. }
  125. req.AddData(&msg)
  126. ipData := neigh.IP.To4()
  127. if ipData == nil {
  128. ipData = neigh.IP.To16()
  129. }
  130. dstData := nl.NewRtAttr(NDA_DST, ipData)
  131. req.AddData(dstData)
  132. if neigh.LLIPAddr != nil {
  133. llIPData := nl.NewRtAttr(NDA_LLADDR, neigh.LLIPAddr.To4())
  134. req.AddData(llIPData)
  135. } else if neigh.Flags != NTF_PROXY || neigh.HardwareAddr != nil {
  136. hwData := nl.NewRtAttr(NDA_LLADDR, []byte(neigh.HardwareAddr))
  137. req.AddData(hwData)
  138. }
  139. if neigh.Vlan != 0 {
  140. vlanData := nl.NewRtAttr(NDA_VLAN, nl.Uint16Attr(uint16(neigh.Vlan)))
  141. req.AddData(vlanData)
  142. }
  143. if neigh.VNI != 0 {
  144. vniData := nl.NewRtAttr(NDA_VNI, nl.Uint32Attr(uint32(neigh.VNI)))
  145. req.AddData(vniData)
  146. }
  147. _, err := req.Execute(unix.NETLINK_ROUTE, 0)
  148. return err
  149. }
  150. // NeighList gets a list of IP-MAC mappings in the system (ARP table).
  151. // Equivalent to: `ip neighbor show`.
  152. // The list can be filtered by link and ip family.
  153. func NeighList(linkIndex, family int) ([]Neigh, error) {
  154. return pkgHandle.NeighList(linkIndex, family)
  155. }
  156. // NeighProxyList gets a list of neighbor proxies in the system.
  157. // Equivalent to: `ip neighbor show proxy`.
  158. // The list can be filtered by link and ip family.
  159. func NeighProxyList(linkIndex, family int) ([]Neigh, error) {
  160. return pkgHandle.NeighProxyList(linkIndex, family)
  161. }
  162. // NeighList gets a list of IP-MAC mappings in the system (ARP table).
  163. // Equivalent to: `ip neighbor show`.
  164. // The list can be filtered by link and ip family.
  165. func (h *Handle) NeighList(linkIndex, family int) ([]Neigh, error) {
  166. return h.neighList(linkIndex, family, 0)
  167. }
  168. // NeighProxyList gets a list of neighbor proxies in the system.
  169. // Equivalent to: `ip neighbor show proxy`.
  170. // The list can be filtered by link, ip family.
  171. func (h *Handle) NeighProxyList(linkIndex, family int) ([]Neigh, error) {
  172. return h.neighList(linkIndex, family, NTF_PROXY)
  173. }
  174. func (h *Handle) neighList(linkIndex, family, flags int) ([]Neigh, error) {
  175. req := h.newNetlinkRequest(unix.RTM_GETNEIGH, unix.NLM_F_DUMP)
  176. msg := Ndmsg{
  177. Family: uint8(family),
  178. Index: uint32(linkIndex),
  179. Flags: uint8(flags),
  180. }
  181. req.AddData(&msg)
  182. msgs, err := req.Execute(unix.NETLINK_ROUTE, unix.RTM_NEWNEIGH)
  183. if err != nil {
  184. return nil, err
  185. }
  186. var res []Neigh
  187. for _, m := range msgs {
  188. ndm := deserializeNdmsg(m)
  189. if linkIndex != 0 && int(ndm.Index) != linkIndex {
  190. // Ignore messages from other interfaces
  191. continue
  192. }
  193. neigh, err := NeighDeserialize(m)
  194. if err != nil {
  195. continue
  196. }
  197. res = append(res, *neigh)
  198. }
  199. return res, nil
  200. }
  201. func NeighDeserialize(m []byte) (*Neigh, error) {
  202. msg := deserializeNdmsg(m)
  203. neigh := Neigh{
  204. LinkIndex: int(msg.Index),
  205. Family: int(msg.Family),
  206. State: int(msg.State),
  207. Type: int(msg.Type),
  208. Flags: int(msg.Flags),
  209. }
  210. attrs, err := nl.ParseRouteAttr(m[msg.Len():])
  211. if err != nil {
  212. return nil, err
  213. }
  214. // This should be cached for perfomance
  215. // once per table dump
  216. link, err := LinkByIndex(neigh.LinkIndex)
  217. if err != nil {
  218. return nil, err
  219. }
  220. encapType := link.Attrs().EncapType
  221. for _, attr := range attrs {
  222. switch attr.Attr.Type {
  223. case NDA_DST:
  224. neigh.IP = net.IP(attr.Value)
  225. case NDA_LLADDR:
  226. // BUG: Is this a bug in the netlink library?
  227. // #define RTA_LENGTH(len) (RTA_ALIGN(sizeof(struct rtattr)) + (len))
  228. // #define RTA_PAYLOAD(rta) ((int)((rta)->rta_len) - RTA_LENGTH(0))
  229. attrLen := attr.Attr.Len - unix.SizeofRtAttr
  230. if attrLen == 4 && (encapType == "ipip" ||
  231. encapType == "sit" ||
  232. encapType == "gre") {
  233. neigh.LLIPAddr = net.IP(attr.Value)
  234. } else if attrLen == 16 &&
  235. encapType == "tunnel6" {
  236. neigh.IP = net.IP(attr.Value)
  237. } else {
  238. neigh.HardwareAddr = net.HardwareAddr(attr.Value)
  239. }
  240. case NDA_VLAN:
  241. neigh.Vlan = int(native.Uint16(attr.Value[0:2]))
  242. case NDA_VNI:
  243. neigh.VNI = int(native.Uint32(attr.Value[0:4]))
  244. }
  245. }
  246. return &neigh, nil
  247. }
  248. // NeighSubscribe takes a chan down which notifications will be sent
  249. // when neighbors are added or deleted. Close the 'done' chan to stop subscription.
  250. func NeighSubscribe(ch chan<- NeighUpdate, done <-chan struct{}) error {
  251. return neighSubscribeAt(netns.None(), netns.None(), ch, done, nil, false)
  252. }
  253. // NeighSubscribeAt works like NeighSubscribe plus it allows the caller
  254. // to choose the network namespace in which to subscribe (ns).
  255. func NeighSubscribeAt(ns netns.NsHandle, ch chan<- NeighUpdate, done <-chan struct{}) error {
  256. return neighSubscribeAt(ns, netns.None(), ch, done, nil, false)
  257. }
  258. // NeighSubscribeOptions contains a set of options to use with
  259. // NeighSubscribeWithOptions.
  260. type NeighSubscribeOptions struct {
  261. Namespace *netns.NsHandle
  262. ErrorCallback func(error)
  263. ListExisting bool
  264. }
  265. // NeighSubscribeWithOptions work like NeighSubscribe but enable to
  266. // provide additional options to modify the behavior. Currently, the
  267. // namespace can be provided as well as an error callback.
  268. func NeighSubscribeWithOptions(ch chan<- NeighUpdate, done <-chan struct{}, options NeighSubscribeOptions) error {
  269. if options.Namespace == nil {
  270. none := netns.None()
  271. options.Namespace = &none
  272. }
  273. return neighSubscribeAt(*options.Namespace, netns.None(), ch, done, options.ErrorCallback, options.ListExisting)
  274. }
  275. func neighSubscribeAt(newNs, curNs netns.NsHandle, ch chan<- NeighUpdate, done <-chan struct{}, cberr func(error), listExisting bool) error {
  276. s, err := nl.SubscribeAt(newNs, curNs, unix.NETLINK_ROUTE, unix.RTNLGRP_NEIGH)
  277. if err != nil {
  278. return err
  279. }
  280. if done != nil {
  281. go func() {
  282. <-done
  283. s.Close()
  284. }()
  285. }
  286. if listExisting {
  287. req := pkgHandle.newNetlinkRequest(unix.RTM_GETNEIGH,
  288. unix.NLM_F_DUMP)
  289. infmsg := nl.NewIfInfomsg(unix.AF_UNSPEC)
  290. req.AddData(infmsg)
  291. if err := s.Send(req); err != nil {
  292. return err
  293. }
  294. }
  295. go func() {
  296. defer close(ch)
  297. for {
  298. msgs, err := s.Receive()
  299. if err != nil {
  300. if cberr != nil {
  301. cberr(err)
  302. }
  303. return
  304. }
  305. for _, m := range msgs {
  306. if m.Header.Type == unix.NLMSG_DONE {
  307. continue
  308. }
  309. if m.Header.Type == unix.NLMSG_ERROR {
  310. native := nl.NativeEndian()
  311. error := int32(native.Uint32(m.Data[0:4]))
  312. if error == 0 {
  313. continue
  314. }
  315. if cberr != nil {
  316. cberr(syscall.Errno(-error))
  317. }
  318. return
  319. }
  320. neigh, err := NeighDeserialize(m.Data)
  321. if err != nil {
  322. if cberr != nil {
  323. cberr(err)
  324. }
  325. return
  326. }
  327. ch <- NeighUpdate{Type: m.Header.Type, Neigh: *neigh}
  328. }
  329. }
  330. }()
  331. return nil
  332. }