neighbor_linux.go 2.0 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788
  1. //go:build linux
  2. package libbox
  3. import (
  4. "net"
  5. "net/netip"
  6. "slices"
  7. "time"
  8. "github.com/sagernet/sing-box/route"
  9. E "github.com/sagernet/sing/common/exceptions"
  10. "github.com/mdlayher/netlink"
  11. "golang.org/x/sys/unix"
  12. )
  13. func SubscribeNeighborTable(listener NeighborUpdateListener) (*NeighborSubscription, error) {
  14. entries, err := route.ReadNeighborEntries()
  15. if err != nil {
  16. return nil, E.Cause(err, "initial neighbor dump")
  17. }
  18. table := make(map[netip.Addr]net.HardwareAddr)
  19. for _, entry := range entries {
  20. table[entry.Address] = entry.MACAddress
  21. }
  22. listener.UpdateNeighborTable(tableToIterator(table))
  23. connection, err := netlink.Dial(unix.NETLINK_ROUTE, &netlink.Config{
  24. Groups: 1 << (unix.RTNLGRP_NEIGH - 1),
  25. })
  26. if err != nil {
  27. return nil, E.Cause(err, "subscribe neighbor updates")
  28. }
  29. subscription := &NeighborSubscription{
  30. done: make(chan struct{}),
  31. }
  32. go subscription.loop(listener, connection, table)
  33. return subscription, nil
  34. }
  35. func (s *NeighborSubscription) loop(listener NeighborUpdateListener, connection *netlink.Conn, table map[netip.Addr]net.HardwareAddr) {
  36. defer connection.Close()
  37. for {
  38. select {
  39. case <-s.done:
  40. return
  41. default:
  42. }
  43. err := connection.SetReadDeadline(time.Now().Add(3 * time.Second))
  44. if err != nil {
  45. return
  46. }
  47. messages, err := connection.Receive()
  48. if err != nil {
  49. if nerr, ok := err.(net.Error); ok && nerr.Timeout() {
  50. continue
  51. }
  52. select {
  53. case <-s.done:
  54. return
  55. default:
  56. }
  57. continue
  58. }
  59. changed := false
  60. for _, message := range messages {
  61. address, mac, isDelete, ok := route.ParseNeighborMessage(message)
  62. if !ok {
  63. continue
  64. }
  65. if isDelete {
  66. if _, exists := table[address]; exists {
  67. delete(table, address)
  68. changed = true
  69. }
  70. } else {
  71. existing, exists := table[address]
  72. if !exists || !slices.Equal(existing, mac) {
  73. table[address] = mac
  74. changed = true
  75. }
  76. }
  77. }
  78. if changed {
  79. listener.UpdateNeighborTable(tableToIterator(table))
  80. }
  81. }
  82. }