discovery.go 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  1. package discovery
  2. import (
  3. "context"
  4. "encoding/json"
  5. "fmt"
  6. "net"
  7. "strings"
  8. "networktool/internal/config"
  9. "networktool/internal/deviceinfo"
  10. "networktool/internal/logger"
  11. "networktool/internal/model"
  12. )
  13. type Server struct {
  14. cfg config.Config
  15. log *logger.Logger
  16. deviceSvc *deviceinfo.Service
  17. }
  18. func New(cfg config.Config, log *logger.Logger, deviceSvc *deviceinfo.Service) *Server {
  19. return &Server{cfg: cfg, log: log, deviceSvc: deviceSvc}
  20. }
  21. func (s *Server) Run(ctx context.Context) error {
  22. addr, err := net.ResolveUDPAddr("udp4", fmt.Sprintf("%s:%d", s.cfg.UDPHost, s.cfg.UDPPort))
  23. if err != nil {
  24. return err
  25. }
  26. conn, err := net.ListenUDP("udp4", addr)
  27. if err != nil {
  28. return err
  29. }
  30. defer conn.Close()
  31. go func() {
  32. <-ctx.Done()
  33. _ = conn.Close()
  34. }()
  35. s.log.Info("udp discovery listening", "addr", conn.LocalAddr().String())
  36. buf := make([]byte, 2048)
  37. for {
  38. n, remote, err := conn.ReadFromUDP(buf)
  39. if err != nil {
  40. if ctx.Err() != nil {
  41. return nil
  42. }
  43. return err
  44. }
  45. var req model.DiscoverRequest
  46. if err := json.Unmarshal(buf[:n], &req); err != nil || req.MessageType != "discover" {
  47. continue
  48. }
  49. lan2IP, mac := s.maintenanceEndpoint()
  50. if lan2IP == "" {
  51. s.log.Warn("skip discovery response because no 169.254 maintenance address was found")
  52. continue
  53. }
  54. device := s.deviceSvc.Get()
  55. resp := model.DiscoverResponse{
  56. ProtocolVersion: 1,
  57. MessageType: "discover_response",
  58. RequestID: req.RequestID,
  59. DeviceID: device.DeviceID,
  60. Hostname: device.Hostname,
  61. ServerVersion: device.ServerVersion,
  62. MAC: mac,
  63. LAN2IP: lan2IP,
  64. HTTPPort: s.cfg.HTTPPort,
  65. AuthRequired: true,
  66. }
  67. payload, _ := json.Marshal(resp)
  68. _, _ = conn.WriteToUDP(payload, remote)
  69. }
  70. }
  71. func (s *Server) maintenanceEndpoint() (string, string) {
  72. if s.cfg.MaintenanceIP != "" {
  73. mac := findMACByIP(s.cfg.MaintenanceIP)
  74. if mac != "" {
  75. return s.cfg.MaintenanceIP, mac
  76. }
  77. }
  78. return findFirstLinkLocalEndpoint()
  79. }
  80. func findFirstLinkLocalEndpoint() (string, string) {
  81. ifaces, err := net.Interfaces()
  82. if err != nil {
  83. return "", ""
  84. }
  85. for _, iface := range ifaces {
  86. if iface.Flags&net.FlagLoopback != 0 || iface.Flags&net.FlagUp == 0 || len(iface.HardwareAddr) == 0 {
  87. continue
  88. }
  89. addrs, err := iface.Addrs()
  90. if err != nil {
  91. continue
  92. }
  93. for _, addr := range addrs {
  94. current := ipv4FromAddr(addr)
  95. if current == nil || !strings.HasPrefix(current.String(), "169.254.") {
  96. continue
  97. }
  98. return current.String(), iface.HardwareAddr.String()
  99. }
  100. }
  101. return "", ""
  102. }
  103. func findMACByIP(ip string) string {
  104. ifaces, err := net.Interfaces()
  105. if err != nil {
  106. return ""
  107. }
  108. var fallback string
  109. for _, iface := range ifaces {
  110. if iface.Flags&net.FlagLoopback != 0 || len(iface.HardwareAddr) == 0 {
  111. continue
  112. }
  113. addrs, err := iface.Addrs()
  114. if err != nil {
  115. continue
  116. }
  117. for _, addr := range addrs {
  118. current := ipv4FromAddr(addr)
  119. if current == nil {
  120. continue
  121. }
  122. if current.String() == ip {
  123. return iface.HardwareAddr.String()
  124. }
  125. if fallback == "" && strings.HasPrefix(current.String(), "169.254.") {
  126. fallback = iface.HardwareAddr.String()
  127. }
  128. }
  129. }
  130. return fallback
  131. }
  132. func ipv4FromAddr(addr net.Addr) net.IP {
  133. var current net.IP
  134. switch value := addr.(type) {
  135. case *net.IPNet:
  136. current = value.IP
  137. case *net.IPAddr:
  138. current = value.IP
  139. }
  140. if current == nil {
  141. return nil
  142. }
  143. return current.To4()
  144. }