arping.go 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  1. // Package arping is a native go library to ping a host per arp datagram, or query a host mac address
  2. //
  3. // The currently supported platforms are: Linux and BSD.
  4. //
  5. //
  6. // The library requires raw socket access. So it must run as root, or with appropriate capabilities under linux:
  7. // `sudo setcap cap_net_raw+ep <BIN>`.
  8. //
  9. //
  10. // Examples:
  11. //
  12. // ping a host:
  13. // ------------
  14. // package main
  15. // import ("fmt"; "github.com/j-keck/arping"; "net")
  16. //
  17. // func main(){
  18. // dstIP := net.ParseIP("192.168.1.1")
  19. // if hwAddr, duration, err := arping.Ping(dstIP); err != nil {
  20. // fmt.Println(err)
  21. // } else {
  22. // fmt.Printf("%s (%s) %d usec\n", dstIP, hwAddr, duration/1000)
  23. // }
  24. // }
  25. //
  26. //
  27. // resolve mac address:
  28. // --------------------
  29. // package main
  30. // import ("fmt"; "github.com/j-keck/arping"; "net")
  31. //
  32. // func main(){
  33. // dstIP := net.ParseIP("192.168.1.1")
  34. // if hwAddr, _, err := arping.Ping(dstIP); err != nil {
  35. // fmt.Println(err)
  36. // } else {
  37. // fmt.Printf("%s is at %s\n", dstIP, hwAddr)
  38. // }
  39. // }
  40. //
  41. //
  42. // check if host is online:
  43. // ------------------------
  44. // package main
  45. // import ("fmt"; "github.com/j-keck/arping"; "net")
  46. //
  47. // func main(){
  48. // dstIP := net.ParseIP("192.168.1.1")
  49. // _, _, err := arping.Ping(dstIP)
  50. // if err == arping.ErrTimeout {
  51. // fmt.Println("offline")
  52. // }else if err != nil {
  53. // fmt.Println(err.Error())
  54. // }else{
  55. // fmt.Println("online")
  56. // }
  57. // }
  58. //
  59. package arping
  60. import (
  61. "errors"
  62. "io/ioutil"
  63. "log"
  64. "net"
  65. "os"
  66. "time"
  67. )
  68. var (
  69. // ErrTimeout error
  70. ErrTimeout = errors.New("timeout")
  71. verboseLog = log.New(ioutil.Discard, "", 0)
  72. timeout = time.Duration(500 * time.Millisecond)
  73. )
  74. // Ping sends an arp ping to 'dstIP'
  75. func Ping(dstIP net.IP) (net.HardwareAddr, time.Duration, error) {
  76. iface, err := findUsableInterfaceForNetwork(dstIP)
  77. if err != nil {
  78. return nil, 0, err
  79. }
  80. return PingOverIface(dstIP, *iface)
  81. }
  82. // PingOverIfaceByName sends an arp ping over interface name 'ifaceName' to 'dstIP'
  83. func PingOverIfaceByName(dstIP net.IP, ifaceName string) (net.HardwareAddr, time.Duration, error) {
  84. iface, err := net.InterfaceByName(ifaceName)
  85. if err != nil {
  86. return nil, 0, err
  87. }
  88. return PingOverIface(dstIP, *iface)
  89. }
  90. // PingOverIface sends an arp ping over interface 'iface' to 'dstIP'
  91. func PingOverIface(dstIP net.IP, iface net.Interface) (net.HardwareAddr, time.Duration, error) {
  92. srcMac := iface.HardwareAddr
  93. srcIP, err := findIPInNetworkFromIface(dstIP, iface)
  94. if err != nil {
  95. return nil, 0, err
  96. }
  97. broadcastMac := []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff}
  98. request := newArpRequest(srcMac, srcIP, broadcastMac, dstIP)
  99. if err := initialize(iface); err != nil {
  100. return nil, 0, err
  101. }
  102. defer deinitialize()
  103. type PingResult struct {
  104. mac net.HardwareAddr
  105. duration time.Duration
  106. err error
  107. }
  108. pingResultChan := make(chan PingResult)
  109. go func() {
  110. // send arp request
  111. verboseLog.Printf("arping '%s' over interface: '%s' with address: '%s'\n", dstIP, iface.Name, srcIP)
  112. if sendTime, err := send(request); err != nil {
  113. pingResultChan <- PingResult{nil, 0, err}
  114. } else {
  115. for {
  116. // receive arp response
  117. response, receiveTime, err := receive()
  118. if err != nil {
  119. pingResultChan <- PingResult{nil, 0, err}
  120. return
  121. }
  122. if response.IsResponseOf(request) {
  123. duration := receiveTime.Sub(sendTime)
  124. verboseLog.Printf("process received arp: srcIP: '%s', srcMac: '%s'\n",
  125. response.SenderIP(), response.SenderMac())
  126. pingResultChan <- PingResult{response.SenderMac(), duration, err}
  127. return
  128. }
  129. verboseLog.Printf("ignore received arp: srcIP: '%s', srcMac: '%s'\n",
  130. response.SenderIP(), response.SenderMac())
  131. }
  132. }
  133. }()
  134. select {
  135. case pingResult := <-pingResultChan:
  136. return pingResult.mac, pingResult.duration, pingResult.err
  137. case <-time.After(timeout):
  138. return nil, 0, ErrTimeout
  139. }
  140. }
  141. // EnableVerboseLog enables verbose logging on stdout
  142. func EnableVerboseLog() {
  143. verboseLog = log.New(os.Stdout, "", 0)
  144. }
  145. // SetTimeout sets ping timeout
  146. func SetTimeout(t time.Duration) {
  147. timeout = t
  148. }