fastping.go 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685
  1. // Package fastping is an ICMP ping library inspired by AnyEvent::FastPing Perl
  2. // module to send ICMP ECHO REQUEST packets quickly. Original Perl module is
  3. // available at
  4. // http://search.cpan.org/~mlehmann/AnyEvent-FastPing-2.01/
  5. //
  6. // It hasn't been fully implemented original functions yet.
  7. //
  8. // Here is an example:
  9. //
  10. // p := fastping.NewPinger()
  11. // ra, err := net.ResolveIPAddr("ip4:icmp", os.Args[1])
  12. // if err != nil {
  13. // fmt.Println(err)
  14. // os.Exit(1)
  15. // }
  16. // p.AddIPAddr(ra)
  17. // p.OnRecv = func(addr *net.IPAddr, rtt time.Duration) {
  18. // fmt.Printf("IP Addr: %s receive, RTT: %v\n", addr.String(), rtt)
  19. // }
  20. // p.OnIdle = func() {
  21. // fmt.Println("finish")
  22. // }
  23. // err = p.Run()
  24. // if err != nil {
  25. // fmt.Println(err)
  26. // }
  27. //
  28. // It sends an ICMP packet and wait a response. If it receives a response,
  29. // it calls "receive" callback. After that, MaxRTT time passed, it calls
  30. // "idle" callback. If you need more example, please see "cmd/ping/ping.go".
  31. //
  32. // This library needs to run as a superuser for sending ICMP packets when
  33. // privileged raw ICMP endpoints is used so in such a case, to run go test
  34. // for the package, please run like a following
  35. //
  36. // sudo go test
  37. //
  38. package fastping
  39. import (
  40. "errors"
  41. "fmt"
  42. "log"
  43. "math/rand"
  44. "net"
  45. "sync"
  46. "syscall"
  47. "time"
  48. "golang.org/x/net/icmp"
  49. "golang.org/x/net/ipv4"
  50. "golang.org/x/net/ipv6"
  51. )
  52. const (
  53. TimeSliceLength = 8
  54. ProtocolICMP = 1
  55. ProtocolIPv6ICMP = 58
  56. )
  57. var (
  58. ipv4Proto = map[string]string{"ip": "ip4:icmp", "udp": "udp4"}
  59. ipv6Proto = map[string]string{"ip": "ip6:ipv6-icmp", "udp": "udp6"}
  60. )
  61. func byteSliceOfSize(n int) []byte {
  62. b := make([]byte, n)
  63. for i := 0; i < len(b); i++ {
  64. b[i] = 1
  65. }
  66. return b
  67. }
  68. func timeToBytes(t time.Time) []byte {
  69. nsec := t.UnixNano()
  70. b := make([]byte, 8)
  71. for i := uint8(0); i < 8; i++ {
  72. b[i] = byte((nsec >> ((7 - i) * 8)) & 0xff)
  73. }
  74. return b
  75. }
  76. func bytesToTime(b []byte) time.Time {
  77. var nsec int64
  78. for i := uint8(0); i < 8; i++ {
  79. nsec += int64(b[i]) << ((7 - i) * 8)
  80. }
  81. return time.Unix(nsec/1000000000, nsec%1000000000)
  82. }
  83. func isIPv4(ip net.IP) bool {
  84. return len(ip.To4()) == net.IPv4len
  85. }
  86. func isIPv6(ip net.IP) bool {
  87. return len(ip) == net.IPv6len
  88. }
  89. func ipv4Payload(b []byte) []byte {
  90. if len(b) < ipv4.HeaderLen {
  91. return b
  92. }
  93. hdrlen := int(b[0]&0x0f) << 2
  94. return b[hdrlen:]
  95. }
  96. type packet struct {
  97. bytes []byte
  98. addr net.Addr
  99. }
  100. type context struct {
  101. stop chan bool
  102. done chan bool
  103. err error
  104. }
  105. func newContext() *context {
  106. return &context{
  107. stop: make(chan bool),
  108. done: make(chan bool),
  109. }
  110. }
  111. // Pinger represents ICMP packet sender/receiver
  112. type Pinger struct {
  113. id int
  114. seq int
  115. // key string is IPAddr.String()
  116. addrs map[string]*net.IPAddr
  117. network string
  118. source string
  119. source6 string
  120. hasIPv4 bool
  121. hasIPv6 bool
  122. ctx *context
  123. mu sync.Mutex
  124. // Size in bytes of the payload to send
  125. Size int
  126. // Number of (nano,milli)seconds of an idle timeout. Once it passed,
  127. // the library calls an idle callback function. It is also used for an
  128. // interval time of RunLoop() method
  129. MaxRTT time.Duration
  130. // OnRecv is called with a response packet's source address and its
  131. // elapsed time when Pinger receives a response packet.
  132. OnRecv func(*net.IPAddr, time.Duration)
  133. // OnIdle is called when MaxRTT time passed
  134. OnIdle func()
  135. // If Debug is true, it prints debug messages to stdout.
  136. Debug bool
  137. }
  138. // NewPinger returns a new Pinger struct pointer
  139. func NewPinger() *Pinger {
  140. rand.Seed(time.Now().UnixNano())
  141. return &Pinger{
  142. id: rand.Intn(0xffff),
  143. seq: rand.Intn(0xffff),
  144. addrs: make(map[string]*net.IPAddr),
  145. network: "ip",
  146. source: "",
  147. source6: "",
  148. hasIPv4: false,
  149. hasIPv6: false,
  150. Size: TimeSliceLength,
  151. MaxRTT: time.Second,
  152. OnRecv: nil,
  153. OnIdle: nil,
  154. Debug: false,
  155. }
  156. }
  157. // Network sets a network endpoints for ICMP ping and returns the previous
  158. // setting. network arg should be "ip" or "udp" string or if others are
  159. // specified, it returns an error. If this function isn't called, Pinger
  160. // uses "ip" as default.
  161. func (p *Pinger) Network(network string) (string, error) {
  162. origNet := p.network
  163. switch network {
  164. case "ip":
  165. fallthrough
  166. case "udp":
  167. p.network = network
  168. default:
  169. return origNet, errors.New(network + " can't be used as ICMP endpoint")
  170. }
  171. return origNet, nil
  172. }
  173. // Source sets ipv4/ipv6 source IP for sending ICMP packets and returns the previous
  174. // setting. Empty value indicates to use system default one (for both ipv4 and ipv6).
  175. func (p *Pinger) Source(source string) (string, error) {
  176. // using ipv4 previous value for new empty one
  177. origSource := p.source
  178. if "" == source {
  179. p.mu.Lock()
  180. p.source = ""
  181. p.source6 = ""
  182. p.mu.Unlock()
  183. return origSource, nil
  184. }
  185. addr := net.ParseIP(source)
  186. if addr == nil {
  187. return origSource, errors.New(source + " is not a valid textual representation of an IPv4/IPv6 address")
  188. }
  189. if isIPv4(addr) {
  190. p.mu.Lock()
  191. p.source = source
  192. p.mu.Unlock()
  193. } else if isIPv6(addr) {
  194. origSource = p.source6
  195. p.mu.Lock()
  196. p.source6 = source
  197. p.mu.Unlock()
  198. } else {
  199. return origSource, errors.New(source + " is not a valid textual representation of an IPv4/IPv6 address")
  200. }
  201. return origSource, nil
  202. }
  203. // AddIP adds an IP address to Pinger. ipaddr arg should be a string like
  204. // "192.0.2.1".
  205. func (p *Pinger) AddIP(ipaddr string) error {
  206. addr := net.ParseIP(ipaddr)
  207. if addr == nil {
  208. return fmt.Errorf("%s is not a valid textual representation of an IP address", ipaddr)
  209. }
  210. p.mu.Lock()
  211. p.addrs[addr.String()] = &net.IPAddr{IP: addr}
  212. if isIPv4(addr) {
  213. p.hasIPv4 = true
  214. } else if isIPv6(addr) {
  215. p.hasIPv6 = true
  216. }
  217. p.mu.Unlock()
  218. return nil
  219. }
  220. // AddIPAddr adds an IP address to Pinger. ip arg should be a net.IPAddr
  221. // pointer.
  222. func (p *Pinger) AddIPAddr(ip *net.IPAddr) {
  223. p.mu.Lock()
  224. p.addrs[ip.String()] = ip
  225. if isIPv4(ip.IP) {
  226. p.hasIPv4 = true
  227. } else if isIPv6(ip.IP) {
  228. p.hasIPv6 = true
  229. }
  230. p.mu.Unlock()
  231. }
  232. // RemoveIP removes an IP address from Pinger. ipaddr arg should be a string
  233. // like "192.0.2.1".
  234. func (p *Pinger) RemoveIP(ipaddr string) error {
  235. addr := net.ParseIP(ipaddr)
  236. if addr == nil {
  237. return fmt.Errorf("%s is not a valid textual representation of an IP address", ipaddr)
  238. }
  239. p.mu.Lock()
  240. delete(p.addrs, addr.String())
  241. p.mu.Unlock()
  242. return nil
  243. }
  244. // RemoveIPAddr removes an IP address from Pinger. ip arg should be a net.IPAddr
  245. // pointer.
  246. func (p *Pinger) RemoveIPAddr(ip *net.IPAddr) {
  247. p.mu.Lock()
  248. delete(p.addrs, ip.String())
  249. p.mu.Unlock()
  250. }
  251. // AddHandler adds event handler to Pinger. event arg should be "receive" or
  252. // "idle" string.
  253. //
  254. // **CAUTION** This function is deprecated. Please use OnRecv and OnIdle field
  255. // of Pinger struct to set following handlers.
  256. //
  257. // "receive" handler should be
  258. //
  259. // func(addr *net.IPAddr, rtt time.Duration)
  260. //
  261. // type function. The handler is called with a response packet's source address
  262. // and its elapsed time when Pinger receives a response packet.
  263. //
  264. // "idle" handler should be
  265. //
  266. // func()
  267. //
  268. // type function. The handler is called when MaxRTT time passed. For more
  269. // detail, please see Run() and RunLoop().
  270. func (p *Pinger) AddHandler(event string, handler interface{}) error {
  271. switch event {
  272. case "receive":
  273. if hdl, ok := handler.(func(*net.IPAddr, time.Duration)); ok {
  274. p.mu.Lock()
  275. p.OnRecv = hdl
  276. p.mu.Unlock()
  277. return nil
  278. }
  279. return errors.New("receive event handler should be `func(*net.IPAddr, time.Duration)`")
  280. case "idle":
  281. if hdl, ok := handler.(func()); ok {
  282. p.mu.Lock()
  283. p.OnIdle = hdl
  284. p.mu.Unlock()
  285. return nil
  286. }
  287. return errors.New("idle event handler should be `func()`")
  288. }
  289. return errors.New("No such event: " + event)
  290. }
  291. // Run invokes a single send/receive procedure. It sends packets to all hosts
  292. // which have already been added by AddIP() etc. and wait those responses. When
  293. // it receives a response, it calls "receive" handler registered by AddHander().
  294. // After MaxRTT seconds, it calls "idle" handler and returns to caller with
  295. // an error value. It means it blocks until MaxRTT seconds passed. For the
  296. // purpose of sending/receiving packets over and over, use RunLoop().
  297. func (p *Pinger) Run() error {
  298. p.mu.Lock()
  299. p.ctx = newContext()
  300. p.mu.Unlock()
  301. p.run(true)
  302. p.mu.Lock()
  303. defer p.mu.Unlock()
  304. return p.ctx.err
  305. }
  306. // RunLoop invokes send/receive procedure repeatedly. It sends packets to all
  307. // hosts which have already been added by AddIP() etc. and wait those responses.
  308. // When it receives a response, it calls "receive" handler registered by
  309. // AddHander(). After MaxRTT seconds, it calls "idle" handler, resend packets
  310. // and wait those response. MaxRTT works as an interval time.
  311. //
  312. // This is a non-blocking method so immediately returns. If you want to monitor
  313. // and stop sending packets, use Done() and Stop() methods. For example,
  314. //
  315. // p.RunLoop()
  316. // ticker := time.NewTicker(time.Millisecond * 250)
  317. // select {
  318. // case <-p.Done():
  319. // if err := p.Err(); err != nil {
  320. // log.Fatalf("Ping failed: %v", err)
  321. // }
  322. // case <-ticker.C:
  323. // break
  324. // }
  325. // ticker.Stop()
  326. // p.Stop()
  327. //
  328. // For more details, please see "cmd/ping/ping.go".
  329. func (p *Pinger) RunLoop() {
  330. p.mu.Lock()
  331. p.ctx = newContext()
  332. p.mu.Unlock()
  333. go p.run(false)
  334. }
  335. // Done returns a channel that is closed when RunLoop() is stopped by an error
  336. // or Stop(). It must be called after RunLoop() call. If not, it causes panic.
  337. func (p *Pinger) Done() <-chan bool {
  338. return p.ctx.done
  339. }
  340. // Stop stops RunLoop(). It must be called after RunLoop(). If not, it causes
  341. // panic.
  342. func (p *Pinger) Stop() {
  343. p.debugln("Stop(): close(p.ctx.stop)")
  344. close(p.ctx.stop)
  345. p.debugln("Stop(): <-p.ctx.done")
  346. <-p.ctx.done
  347. }
  348. // Err returns an error that is set by RunLoop(). It must be called after
  349. // RunLoop(). If not, it causes panic.
  350. func (p *Pinger) Err() error {
  351. p.mu.Lock()
  352. defer p.mu.Unlock()
  353. return p.ctx.err
  354. }
  355. func (p *Pinger) listen(netProto string, source string) *icmp.PacketConn {
  356. conn, err := icmp.ListenPacket(netProto, source)
  357. if err != nil {
  358. p.mu.Lock()
  359. p.ctx.err = err
  360. p.mu.Unlock()
  361. p.debugln("Run(): close(p.ctx.done)")
  362. close(p.ctx.done)
  363. return nil
  364. }
  365. return conn
  366. }
  367. func (p *Pinger) run(once bool) {
  368. p.debugln("Run(): Start")
  369. var conn, conn6 *icmp.PacketConn
  370. if p.hasIPv4 {
  371. if conn = p.listen(ipv4Proto[p.network], p.source); conn == nil {
  372. return
  373. }
  374. defer conn.Close()
  375. }
  376. if p.hasIPv6 {
  377. if conn6 = p.listen(ipv6Proto[p.network], p.source6); conn6 == nil {
  378. return
  379. }
  380. defer conn6.Close()
  381. }
  382. recv := make(chan *packet, 1)
  383. recvCtx := newContext()
  384. wg := new(sync.WaitGroup)
  385. p.debugln("Run(): call recvICMP()")
  386. if conn != nil {
  387. wg.Add(1)
  388. go p.recvICMP(conn, recv, recvCtx, wg)
  389. }
  390. if conn6 != nil {
  391. wg.Add(1)
  392. go p.recvICMP(conn6, recv, recvCtx, wg)
  393. }
  394. p.debugln("Run(): call sendICMP()")
  395. queue, err := p.sendICMP(conn, conn6)
  396. ticker := time.NewTicker(p.MaxRTT)
  397. mainloop:
  398. for {
  399. select {
  400. case <-p.ctx.stop:
  401. p.debugln("Run(): <-p.ctx.stop")
  402. break mainloop
  403. case <-recvCtx.done:
  404. p.debugln("Run(): <-recvCtx.done")
  405. p.mu.Lock()
  406. err = recvCtx.err
  407. p.mu.Unlock()
  408. break mainloop
  409. case <-ticker.C:
  410. p.mu.Lock()
  411. handler := p.OnIdle
  412. p.mu.Unlock()
  413. if handler != nil {
  414. handler()
  415. }
  416. if once || err != nil {
  417. break mainloop
  418. }
  419. p.debugln("Run(): call sendICMP()")
  420. queue, err = p.sendICMP(conn, conn6)
  421. case r := <-recv:
  422. p.debugln("Run(): <-recv")
  423. p.procRecv(r, queue)
  424. }
  425. }
  426. ticker.Stop()
  427. p.debugln("Run(): close(recvCtx.stop)")
  428. close(recvCtx.stop)
  429. p.debugln("Run(): wait recvICMP()")
  430. wg.Wait()
  431. p.mu.Lock()
  432. p.ctx.err = err
  433. p.mu.Unlock()
  434. p.debugln("Run(): close(p.ctx.done)")
  435. close(p.ctx.done)
  436. p.debugln("Run(): End")
  437. }
  438. func (p *Pinger) sendICMP(conn, conn6 *icmp.PacketConn) (map[string]*net.IPAddr, error) {
  439. p.debugln("sendICMP(): Start")
  440. p.mu.Lock()
  441. p.id = rand.Intn(0xffff)
  442. p.seq = rand.Intn(0xffff)
  443. p.mu.Unlock()
  444. queue := make(map[string]*net.IPAddr)
  445. wg := new(sync.WaitGroup)
  446. for key, addr := range p.addrs {
  447. var typ icmp.Type
  448. var cn *icmp.PacketConn
  449. if isIPv4(addr.IP) {
  450. typ = ipv4.ICMPTypeEcho
  451. cn = conn
  452. } else if isIPv6(addr.IP) {
  453. typ = ipv6.ICMPTypeEchoRequest
  454. cn = conn6
  455. } else {
  456. continue
  457. }
  458. if cn == nil {
  459. continue
  460. }
  461. t := timeToBytes(time.Now())
  462. if p.Size-TimeSliceLength != 0 {
  463. t = append(t, byteSliceOfSize(p.Size-TimeSliceLength)...)
  464. }
  465. p.mu.Lock()
  466. bytes, err := (&icmp.Message{
  467. Type: typ, Code: 0,
  468. Body: &icmp.Echo{
  469. ID: p.id, Seq: p.seq,
  470. Data: t,
  471. },
  472. }).Marshal(nil)
  473. p.mu.Unlock()
  474. if err != nil {
  475. wg.Wait()
  476. return queue, err
  477. }
  478. queue[key] = addr
  479. var dst net.Addr = addr
  480. if p.network == "udp" {
  481. dst = &net.UDPAddr{IP: addr.IP, Zone: addr.Zone}
  482. }
  483. p.debugln("sendICMP(): Invoke goroutine")
  484. wg.Add(1)
  485. go func(conn *icmp.PacketConn, ra net.Addr, b []byte) {
  486. for {
  487. if _, err := conn.WriteTo(bytes, ra); err != nil {
  488. if neterr, ok := err.(*net.OpError); ok {
  489. if neterr.Err == syscall.ENOBUFS {
  490. continue
  491. }
  492. }
  493. }
  494. break
  495. }
  496. p.debugln("sendICMP(): WriteTo End")
  497. wg.Done()
  498. }(cn, dst, bytes)
  499. }
  500. wg.Wait()
  501. p.debugln("sendICMP(): End")
  502. return queue, nil
  503. }
  504. func (p *Pinger) recvICMP(conn *icmp.PacketConn, recv chan<- *packet, ctx *context, wg *sync.WaitGroup) {
  505. p.debugln("recvICMP(): Start")
  506. for {
  507. select {
  508. case <-ctx.stop:
  509. p.debugln("recvICMP(): <-ctx.stop")
  510. wg.Done()
  511. p.debugln("recvICMP(): wg.Done()")
  512. return
  513. default:
  514. }
  515. bytes := make([]byte, 512)
  516. conn.SetReadDeadline(time.Now().Add(time.Millisecond * 100))
  517. p.debugln("recvICMP(): ReadFrom Start")
  518. _, ra, err := conn.ReadFrom(bytes)
  519. p.debugln("recvICMP(): ReadFrom End")
  520. if err != nil {
  521. if neterr, ok := err.(*net.OpError); ok {
  522. if neterr.Timeout() {
  523. p.debugln("recvICMP(): Read Timeout")
  524. continue
  525. } else {
  526. p.debugln("recvICMP(): OpError happen", err)
  527. p.mu.Lock()
  528. ctx.err = err
  529. p.mu.Unlock()
  530. p.debugln("recvICMP(): close(ctx.done)")
  531. close(ctx.done)
  532. p.debugln("recvICMP(): wg.Done()")
  533. wg.Done()
  534. return
  535. }
  536. }
  537. }
  538. p.debugln("recvICMP(): p.recv <- packet")
  539. select {
  540. case recv <- &packet{bytes: bytes, addr: ra}:
  541. case <-ctx.stop:
  542. p.debugln("recvICMP(): <-ctx.stop")
  543. wg.Done()
  544. p.debugln("recvICMP(): wg.Done()")
  545. return
  546. }
  547. }
  548. }
  549. func (p *Pinger) procRecv(recv *packet, queue map[string]*net.IPAddr) {
  550. var ipaddr *net.IPAddr
  551. switch adr := recv.addr.(type) {
  552. case *net.IPAddr:
  553. ipaddr = adr
  554. case *net.UDPAddr:
  555. ipaddr = &net.IPAddr{IP: adr.IP, Zone: adr.Zone}
  556. default:
  557. return
  558. }
  559. addr := ipaddr.String()
  560. p.mu.Lock()
  561. if _, ok := p.addrs[addr]; !ok {
  562. p.mu.Unlock()
  563. return
  564. }
  565. p.mu.Unlock()
  566. var bytes []byte
  567. var proto int
  568. if isIPv4(ipaddr.IP) {
  569. if p.network == "ip" {
  570. bytes = ipv4Payload(recv.bytes)
  571. } else {
  572. bytes = recv.bytes
  573. }
  574. proto = ProtocolICMP
  575. } else if isIPv6(ipaddr.IP) {
  576. bytes = recv.bytes
  577. proto = ProtocolIPv6ICMP
  578. } else {
  579. return
  580. }
  581. var m *icmp.Message
  582. var err error
  583. if m, err = icmp.ParseMessage(proto, bytes); err != nil {
  584. return
  585. }
  586. if m.Type != ipv4.ICMPTypeEchoReply && m.Type != ipv6.ICMPTypeEchoReply {
  587. return
  588. }
  589. var rtt time.Duration
  590. switch pkt := m.Body.(type) {
  591. case *icmp.Echo:
  592. p.mu.Lock()
  593. if pkt.ID == p.id && pkt.Seq == p.seq {
  594. rtt = time.Since(bytesToTime(pkt.Data[:TimeSliceLength]))
  595. }
  596. p.mu.Unlock()
  597. default:
  598. return
  599. }
  600. if _, ok := queue[addr]; ok {
  601. delete(queue, addr)
  602. p.mu.Lock()
  603. handler := p.OnRecv
  604. p.mu.Unlock()
  605. if handler != nil {
  606. handler(ipaddr, rtt)
  607. }
  608. }
  609. }
  610. func (p *Pinger) debugln(args ...interface{}) {
  611. p.mu.Lock()
  612. defer p.mu.Unlock()
  613. if p.Debug {
  614. log.Println(args...)
  615. }
  616. }
  617. func (p *Pinger) debugf(format string, args ...interface{}) {
  618. p.mu.Lock()
  619. defer p.mu.Unlock()
  620. if p.Debug {
  621. log.Printf(format, args...)
  622. }
  623. }