neigh_linux.go 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499
  1. package netlink
  2. import (
  3. "errors"
  4. "fmt"
  5. "net"
  6. "syscall"
  7. "unsafe"
  8. "github.com/vishvananda/netlink/nl"
  9. "github.com/vishvananda/netns"
  10. "golang.org/x/sys/unix"
  11. )
  12. const (
  13. NDA_UNSPEC = iota
  14. NDA_DST
  15. NDA_LLADDR
  16. NDA_CACHEINFO
  17. NDA_PROBES
  18. NDA_VLAN
  19. NDA_PORT
  20. NDA_VNI
  21. NDA_IFINDEX
  22. NDA_MASTER
  23. NDA_LINK_NETNSID
  24. NDA_SRC_VNI
  25. NDA_PROTOCOL
  26. NDA_NH_ID
  27. NDA_FDB_EXT_ATTRS
  28. NDA_FLAGS_EXT
  29. NDA_MAX = NDA_FLAGS_EXT
  30. )
  31. // Neighbor Cache Entry States.
  32. const (
  33. NUD_NONE = 0x00
  34. NUD_INCOMPLETE = 0x01
  35. NUD_REACHABLE = 0x02
  36. NUD_STALE = 0x04
  37. NUD_DELAY = 0x08
  38. NUD_PROBE = 0x10
  39. NUD_FAILED = 0x20
  40. NUD_NOARP = 0x40
  41. NUD_PERMANENT = 0x80
  42. )
  43. // Neighbor Flags
  44. const (
  45. NTF_USE = 0x01
  46. NTF_SELF = 0x02
  47. NTF_MASTER = 0x04
  48. NTF_PROXY = 0x08
  49. NTF_EXT_LEARNED = 0x10
  50. NTF_OFFLOADED = 0x20
  51. NTF_STICKY = 0x40
  52. NTF_ROUTER = 0x80
  53. )
  54. // Extended Neighbor Flags
  55. const (
  56. NTF_EXT_MANAGED = 0x00000001
  57. )
  58. // Ndmsg is for adding, removing or receiving information about a neighbor table entry
  59. type Ndmsg struct {
  60. Family uint8
  61. Index uint32
  62. State uint16
  63. Flags uint8
  64. Type uint8
  65. }
  66. func deserializeNdmsg(b []byte) *Ndmsg {
  67. var dummy Ndmsg
  68. return (*Ndmsg)(unsafe.Pointer(&b[0:unsafe.Sizeof(dummy)][0]))
  69. }
  70. func (msg *Ndmsg) Serialize() []byte {
  71. return (*(*[unsafe.Sizeof(*msg)]byte)(unsafe.Pointer(msg)))[:]
  72. }
  73. func (msg *Ndmsg) Len() int {
  74. return int(unsafe.Sizeof(*msg))
  75. }
  76. // NeighAdd will add an IP to MAC mapping to the ARP table
  77. // Equivalent to: `ip neigh add ....`
  78. func NeighAdd(neigh *Neigh) error {
  79. return pkgHandle.NeighAdd(neigh)
  80. }
  81. // NeighAdd will add an IP to MAC mapping to the ARP table
  82. // Equivalent to: `ip neigh add ....`
  83. func (h *Handle) NeighAdd(neigh *Neigh) error {
  84. return h.neighAdd(neigh, unix.NLM_F_CREATE|unix.NLM_F_EXCL)
  85. }
  86. // NeighSet will add or replace an IP to MAC mapping to the ARP table
  87. // Equivalent to: `ip neigh replace....`
  88. func NeighSet(neigh *Neigh) error {
  89. return pkgHandle.NeighSet(neigh)
  90. }
  91. // NeighSet will add or replace an IP to MAC mapping to the ARP table
  92. // Equivalent to: `ip neigh replace....`
  93. func (h *Handle) NeighSet(neigh *Neigh) error {
  94. return h.neighAdd(neigh, unix.NLM_F_CREATE|unix.NLM_F_REPLACE)
  95. }
  96. // NeighAppend will append an entry to FDB
  97. // Equivalent to: `bridge fdb append...`
  98. func NeighAppend(neigh *Neigh) error {
  99. return pkgHandle.NeighAppend(neigh)
  100. }
  101. // NeighAppend will append an entry to FDB
  102. // Equivalent to: `bridge fdb append...`
  103. func (h *Handle) NeighAppend(neigh *Neigh) error {
  104. return h.neighAdd(neigh, unix.NLM_F_CREATE|unix.NLM_F_APPEND)
  105. }
  106. // NeighAppend will append an entry to FDB
  107. // Equivalent to: `bridge fdb append...`
  108. func neighAdd(neigh *Neigh, mode int) error {
  109. return pkgHandle.neighAdd(neigh, mode)
  110. }
  111. // NeighAppend will append an entry to FDB
  112. // Equivalent to: `bridge fdb append...`
  113. func (h *Handle) neighAdd(neigh *Neigh, mode int) error {
  114. req := h.newNetlinkRequest(unix.RTM_NEWNEIGH, mode|unix.NLM_F_ACK)
  115. return neighHandle(neigh, req)
  116. }
  117. // NeighDel will delete an IP address from a link device.
  118. // Equivalent to: `ip addr del $addr dev $link`
  119. func NeighDel(neigh *Neigh) error {
  120. return pkgHandle.NeighDel(neigh)
  121. }
  122. // NeighDel will delete an IP address from a link device.
  123. // Equivalent to: `ip addr del $addr dev $link`
  124. func (h *Handle) NeighDel(neigh *Neigh) error {
  125. req := h.newNetlinkRequest(unix.RTM_DELNEIGH, unix.NLM_F_ACK)
  126. return neighHandle(neigh, req)
  127. }
  128. func neighHandle(neigh *Neigh, req *nl.NetlinkRequest) error {
  129. var family int
  130. if neigh.Family > 0 {
  131. family = neigh.Family
  132. } else {
  133. family = nl.GetIPFamily(neigh.IP)
  134. }
  135. msg := Ndmsg{
  136. Family: uint8(family),
  137. Index: uint32(neigh.LinkIndex),
  138. State: uint16(neigh.State),
  139. Type: uint8(neigh.Type),
  140. Flags: uint8(neigh.Flags),
  141. }
  142. req.AddData(&msg)
  143. ipData := neigh.IP.To4()
  144. if ipData == nil {
  145. ipData = neigh.IP.To16()
  146. }
  147. dstData := nl.NewRtAttr(NDA_DST, ipData)
  148. req.AddData(dstData)
  149. if neigh.LLIPAddr != nil {
  150. llIPData := nl.NewRtAttr(NDA_LLADDR, neigh.LLIPAddr.To4())
  151. req.AddData(llIPData)
  152. } else if neigh.HardwareAddr != nil {
  153. hwData := nl.NewRtAttr(NDA_LLADDR, []byte(neigh.HardwareAddr))
  154. req.AddData(hwData)
  155. }
  156. if neigh.FlagsExt != 0 {
  157. flagsExtData := nl.NewRtAttr(NDA_FLAGS_EXT, nl.Uint32Attr(uint32(neigh.FlagsExt)))
  158. req.AddData(flagsExtData)
  159. }
  160. if neigh.Vlan != 0 {
  161. vlanData := nl.NewRtAttr(NDA_VLAN, nl.Uint16Attr(uint16(neigh.Vlan)))
  162. req.AddData(vlanData)
  163. }
  164. if neigh.VNI != 0 {
  165. vniData := nl.NewRtAttr(NDA_VNI, nl.Uint32Attr(uint32(neigh.VNI)))
  166. req.AddData(vniData)
  167. }
  168. if neigh.MasterIndex != 0 {
  169. masterData := nl.NewRtAttr(NDA_MASTER, nl.Uint32Attr(uint32(neigh.MasterIndex)))
  170. req.AddData(masterData)
  171. }
  172. _, err := req.Execute(unix.NETLINK_ROUTE, 0)
  173. return err
  174. }
  175. // NeighList returns a list of IP-MAC mappings in the system (ARP table).
  176. // Equivalent to: `ip neighbor show`.
  177. // The list can be filtered by link and ip family.
  178. //
  179. // If the returned error is [ErrDumpInterrupted], results may be inconsistent
  180. // or incomplete.
  181. func NeighList(linkIndex, family int) ([]Neigh, error) {
  182. return pkgHandle.NeighList(linkIndex, family)
  183. }
  184. // NeighProxyList returns a list of neighbor proxies in the system.
  185. // Equivalent to: `ip neighbor show proxy`.
  186. // The list can be filtered by link and ip family.
  187. //
  188. // If the returned error is [ErrDumpInterrupted], results may be inconsistent
  189. // or incomplete.
  190. func NeighProxyList(linkIndex, family int) ([]Neigh, error) {
  191. return pkgHandle.NeighProxyList(linkIndex, family)
  192. }
  193. // NeighList returns a list of IP-MAC mappings in the system (ARP table).
  194. // Equivalent to: `ip neighbor show`.
  195. // The list can be filtered by link and ip family.
  196. //
  197. // If the returned error is [ErrDumpInterrupted], results may be inconsistent
  198. // or incomplete.
  199. func (h *Handle) NeighList(linkIndex, family int) ([]Neigh, error) {
  200. return h.NeighListExecute(Ndmsg{
  201. Family: uint8(family),
  202. Index: uint32(linkIndex),
  203. })
  204. }
  205. // NeighProxyList returns a list of neighbor proxies in the system.
  206. // Equivalent to: `ip neighbor show proxy`.
  207. // The list can be filtered by link, ip family.
  208. //
  209. // If the returned error is [ErrDumpInterrupted], results may be inconsistent
  210. // or incomplete.
  211. func (h *Handle) NeighProxyList(linkIndex, family int) ([]Neigh, error) {
  212. return h.NeighListExecute(Ndmsg{
  213. Family: uint8(family),
  214. Index: uint32(linkIndex),
  215. Flags: NTF_PROXY,
  216. })
  217. }
  218. // NeighListExecute returns a list of neighbour entries filtered by link, ip family, flag and state.
  219. //
  220. // If the returned error is [ErrDumpInterrupted], results may be inconsistent
  221. // or incomplete.
  222. func NeighListExecute(msg Ndmsg) ([]Neigh, error) {
  223. return pkgHandle.NeighListExecute(msg)
  224. }
  225. // NeighListExecute returns a list of neighbour entries filtered by link, ip family, flag and state.
  226. //
  227. // If the returned error is [ErrDumpInterrupted], results may be inconsistent
  228. // or incomplete.
  229. func (h *Handle) NeighListExecute(msg Ndmsg) ([]Neigh, error) {
  230. req := h.newNetlinkRequest(unix.RTM_GETNEIGH, unix.NLM_F_DUMP)
  231. req.AddData(&msg)
  232. msgs, executeErr := req.Execute(unix.NETLINK_ROUTE, unix.RTM_NEWNEIGH)
  233. if executeErr != nil && !errors.Is(executeErr, ErrDumpInterrupted) {
  234. return nil, executeErr
  235. }
  236. var res []Neigh
  237. for _, m := range msgs {
  238. ndm := deserializeNdmsg(m)
  239. if msg.Index != 0 && ndm.Index != msg.Index {
  240. // Ignore messages from other interfaces
  241. continue
  242. }
  243. if msg.Family != 0 && ndm.Family != msg.Family {
  244. continue
  245. }
  246. if msg.State != 0 && ndm.State != msg.State {
  247. continue
  248. }
  249. if msg.Type != 0 && ndm.Type != msg.Type {
  250. continue
  251. }
  252. if msg.Flags != 0 && ndm.Flags != msg.Flags {
  253. continue
  254. }
  255. neigh, err := NeighDeserialize(m)
  256. if err != nil {
  257. continue
  258. }
  259. res = append(res, *neigh)
  260. }
  261. return res, executeErr
  262. }
  263. func NeighDeserialize(m []byte) (*Neigh, error) {
  264. msg := deserializeNdmsg(m)
  265. neigh := Neigh{
  266. LinkIndex: int(msg.Index),
  267. Family: int(msg.Family),
  268. State: int(msg.State),
  269. Type: int(msg.Type),
  270. Flags: int(msg.Flags),
  271. }
  272. attrs, err := nl.ParseRouteAttr(m[msg.Len():])
  273. if err != nil {
  274. return nil, err
  275. }
  276. for _, attr := range attrs {
  277. switch attr.Attr.Type {
  278. case NDA_DST:
  279. neigh.IP = net.IP(attr.Value)
  280. case NDA_LLADDR:
  281. // BUG: Is this a bug in the netlink library?
  282. // #define RTA_LENGTH(len) (RTA_ALIGN(sizeof(struct rtattr)) + (len))
  283. // #define RTA_PAYLOAD(rta) ((int)((rta)->rta_len) - RTA_LENGTH(0))
  284. attrLen := attr.Attr.Len - unix.SizeofRtAttr
  285. if attrLen == 4 {
  286. neigh.LLIPAddr = net.IP(attr.Value)
  287. } else if attrLen == 16 {
  288. // Can be IPv6 or FireWire HWAddr
  289. link, err := LinkByIndex(neigh.LinkIndex)
  290. if err == nil && link.Attrs().EncapType == "tunnel6" {
  291. neigh.IP = net.IP(attr.Value)
  292. } else {
  293. neigh.HardwareAddr = net.HardwareAddr(attr.Value)
  294. }
  295. } else {
  296. neigh.HardwareAddr = net.HardwareAddr(attr.Value)
  297. }
  298. case NDA_FLAGS_EXT:
  299. neigh.FlagsExt = int(native.Uint32(attr.Value[0:4]))
  300. case NDA_VLAN:
  301. neigh.Vlan = int(native.Uint16(attr.Value[0:2]))
  302. case NDA_VNI:
  303. neigh.VNI = int(native.Uint32(attr.Value[0:4]))
  304. case NDA_MASTER:
  305. neigh.MasterIndex = int(native.Uint32(attr.Value[0:4]))
  306. case NDA_CACHEINFO:
  307. neigh.Confirmed = native.Uint32(attr.Value[0:4])
  308. neigh.Used = native.Uint32(attr.Value[4:8])
  309. neigh.Updated = native.Uint32(attr.Value[8:12])
  310. }
  311. }
  312. return &neigh, nil
  313. }
  314. // NeighSubscribe takes a chan down which notifications will be sent
  315. // when neighbors are added or deleted. Close the 'done' chan to stop subscription.
  316. func NeighSubscribe(ch chan<- NeighUpdate, done <-chan struct{}) error {
  317. return neighSubscribeAt(netns.None(), netns.None(), ch, done, nil, false, 0, nil, false)
  318. }
  319. // NeighSubscribeAt works like NeighSubscribe plus it allows the caller
  320. // to choose the network namespace in which to subscribe (ns).
  321. func NeighSubscribeAt(ns netns.NsHandle, ch chan<- NeighUpdate, done <-chan struct{}) error {
  322. return neighSubscribeAt(ns, netns.None(), ch, done, nil, false, 0, nil, false)
  323. }
  324. // NeighSubscribeOptions contains a set of options to use with
  325. // NeighSubscribeWithOptions.
  326. type NeighSubscribeOptions struct {
  327. Namespace *netns.NsHandle
  328. ErrorCallback func(error)
  329. ListExisting bool
  330. // max size is based on value of /proc/sys/net/core/rmem_max
  331. ReceiveBufferSize int
  332. ReceiveBufferForceSize bool
  333. ReceiveTimeout *unix.Timeval
  334. }
  335. // NeighSubscribeWithOptions work like NeighSubscribe but enable to
  336. // provide additional options to modify the behavior. Currently, the
  337. // namespace can be provided as well as an error callback.
  338. //
  339. // When options.ListExisting is true, options.ErrorCallback may be
  340. // called with [ErrDumpInterrupted] to indicate that results from
  341. // the initial dump of links may be inconsistent or incomplete.
  342. func NeighSubscribeWithOptions(ch chan<- NeighUpdate, done <-chan struct{}, options NeighSubscribeOptions) error {
  343. if options.Namespace == nil {
  344. none := netns.None()
  345. options.Namespace = &none
  346. }
  347. return neighSubscribeAt(*options.Namespace, netns.None(), ch, done, options.ErrorCallback, options.ListExisting,
  348. options.ReceiveBufferSize, options.ReceiveTimeout, options.ReceiveBufferForceSize)
  349. }
  350. func neighSubscribeAt(newNs, curNs netns.NsHandle, ch chan<- NeighUpdate, done <-chan struct{}, cberr func(error), listExisting bool,
  351. rcvbuf int, rcvTimeout *unix.Timeval, rcvbufForce bool) error {
  352. s, err := nl.SubscribeAt(newNs, curNs, unix.NETLINK_ROUTE, unix.RTNLGRP_NEIGH)
  353. makeRequest := func(family int) error {
  354. req := pkgHandle.newNetlinkRequest(unix.RTM_GETNEIGH, unix.NLM_F_DUMP)
  355. ndmsg := &Ndmsg{Family: uint8(family)}
  356. req.AddData(ndmsg)
  357. if err := s.Send(req); err != nil {
  358. return err
  359. }
  360. return nil
  361. }
  362. if err != nil {
  363. return err
  364. }
  365. if rcvTimeout != nil {
  366. if err := s.SetReceiveTimeout(rcvTimeout); err != nil {
  367. return err
  368. }
  369. }
  370. if rcvbuf != 0 {
  371. err = s.SetReceiveBufferSize(rcvbuf, rcvbufForce)
  372. if err != nil {
  373. return err
  374. }
  375. }
  376. if done != nil {
  377. go func() {
  378. <-done
  379. s.Close()
  380. }()
  381. }
  382. if listExisting {
  383. if err := makeRequest(unix.AF_UNSPEC); err != nil {
  384. return err
  385. }
  386. // We have to wait for NLMSG_DONE before making AF_BRIDGE request
  387. }
  388. go func() {
  389. defer close(ch)
  390. for {
  391. msgs, from, err := s.Receive()
  392. if err != nil {
  393. if cberr != nil {
  394. cberr(err)
  395. }
  396. return
  397. }
  398. if from.Pid != nl.PidKernel {
  399. if cberr != nil {
  400. cberr(fmt.Errorf("Wrong sender portid %d, expected %d", from.Pid, nl.PidKernel))
  401. }
  402. continue
  403. }
  404. for _, m := range msgs {
  405. if m.Header.Flags&unix.NLM_F_DUMP_INTR != 0 && cberr != nil {
  406. cberr(ErrDumpInterrupted)
  407. }
  408. if m.Header.Type == unix.NLMSG_DONE {
  409. if listExisting {
  410. // This will be called after handling AF_UNSPEC
  411. // list request, we have to wait for NLMSG_DONE
  412. // before making another request
  413. if err := makeRequest(unix.AF_BRIDGE); err != nil {
  414. if cberr != nil {
  415. cberr(err)
  416. }
  417. return
  418. }
  419. listExisting = false
  420. }
  421. continue
  422. }
  423. if m.Header.Type == unix.NLMSG_ERROR {
  424. nError := int32(native.Uint32(m.Data[0:4]))
  425. if nError == 0 {
  426. continue
  427. }
  428. if cberr != nil {
  429. cberr(syscall.Errno(-nError))
  430. }
  431. return
  432. }
  433. neigh, err := NeighDeserialize(m.Data)
  434. if err != nil {
  435. if cberr != nil {
  436. cberr(err)
  437. }
  438. return
  439. }
  440. ch <- NeighUpdate{Type: m.Header.Type, Neigh: *neigh}
  441. }
  442. }
  443. }()
  444. return nil
  445. }