ping.go 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625
  1. // Package probing is a simple but powerful ICMP echo (ping) library.
  2. //
  3. // Here is a very simple example that sends and receives three packets:
  4. //
  5. // pinger, err := probing.NewPinger("www.google.com")
  6. // if err != nil {
  7. // panic(err)
  8. // }
  9. // pinger.Count = 3
  10. // err = pinger.Run() // blocks until finished
  11. // if err != nil {
  12. // panic(err)
  13. // }
  14. // stats := pinger.Statistics() // get send/receive/rtt stats
  15. //
  16. // Here is an example that emulates the traditional UNIX ping command:
  17. //
  18. // pinger, err := probing.NewPinger("www.google.com")
  19. // if err != nil {
  20. // panic(err)
  21. // }
  22. // // Listen for Ctrl-C.
  23. // c := make(chan os.Signal, 1)
  24. // signal.Notify(c, os.Interrupt)
  25. // go func() {
  26. // for _ = range c {
  27. // pinger.Stop()
  28. // }
  29. // }()
  30. // pinger.OnRecv = func(pkt *probing.Packet) {
  31. // fmt.Printf("%d bytes from %s: icmp_seq=%d time=%v\n",
  32. // pkt.Nbytes, pkt.IPAddr, pkt.Seq, pkt.Rtt)
  33. // }
  34. // pinger.OnFinish = func(stats *probing.Statistics) {
  35. // fmt.Printf("\n--- %s ping statistics ---\n", stats.Addr)
  36. // fmt.Printf("%d packets transmitted, %d packets received, %v%% packet loss\n",
  37. // stats.PacketsSent, stats.PacketsRecv, stats.PacketLoss)
  38. // fmt.Printf("round-trip min/avg/max/stddev = %v/%v/%v/%v\n",
  39. // stats.MinRtt, stats.AvgRtt, stats.MaxRtt, stats.StdDevRtt)
  40. // }
  41. // fmt.Printf("PING %s (%s):\n", pinger.Addr(), pinger.IPAddr())
  42. // err = pinger.Run()
  43. // if err != nil {
  44. // panic(err)
  45. // }
  46. //
  47. // It sends ICMP Echo Request packet(s) and waits for an Echo Reply in response.
  48. // If it receives a response, it calls the OnRecv callback. When it's finished,
  49. // it calls the OnFinish callback.
  50. //
  51. // For a full ping example, see "cmd/ping/ping.go".
  52. package probing
  53. import (
  54. "errors"
  55. "math"
  56. "math/rand"
  57. "net"
  58. "sync"
  59. "sync/atomic"
  60. "time"
  61. "trial/ping/probing/icmp"
  62. "git.wecise.com/wecise/common/logger"
  63. "git.wecise.com/wecise/common/matrix/cfg"
  64. "github.com/google/uuid"
  65. "golang.org/x/sync/errgroup"
  66. )
  67. var mcfg = cfg.MConfig()
  68. var receive_buffer_count = mcfg.GetInt("ping.recv.buf.count", 100)
  69. var ttl = mcfg.GetInt("ping.ttl", 64)
  70. var ping_interval = mcfg.GetDuration("ping.interval", 1000*time.Millisecond)
  71. var concurlimit_ping = mcfg.GetInt("concurlimit.ping", 100)
  72. var concurlimit_send = mcfg.GetInt("concurlimit.send", 10)
  73. var concurchan_ping = make(chan struct{}, concurlimit_ping)
  74. var concurchan_send = make(chan struct{}, concurlimit_send)
  75. var lastpingtimemutex sync.Mutex
  76. var lastpingtime = map[string]time.Time{}
  77. var lastsendtimemutex sync.Mutex
  78. var lastsendtime = map[string]time.Time{}
  79. const (
  80. timeSliceLength = 8
  81. trackerLength = len(uuid.UUID{})
  82. )
  83. // New returns a new Pinger struct pointer.
  84. func New(addr string) *Pinger {
  85. r := rand.New(rand.NewSource(getSeed()))
  86. firstUUID := uuid.New()
  87. var firstSequence = map[uuid.UUID]map[int]struct{}{}
  88. firstSequence[firstUUID] = make(map[int]struct{})
  89. return &Pinger{
  90. Count: -1,
  91. Interval: time.Second,
  92. RecordRtts: true,
  93. Size: timeSliceLength + trackerLength,
  94. Timeout: time.Duration(math.MaxInt64),
  95. addr: addr,
  96. done: make(chan interface{}),
  97. id: r.Intn(math.MaxUint16),
  98. trackerUUIDs: []uuid.UUID{firstUUID},
  99. ipaddr: nil,
  100. ipv4: false,
  101. network: "ip",
  102. protocol: "udp",
  103. awaitingSequences: firstSequence,
  104. TTL: 64,
  105. }
  106. }
  107. // NewPinger returns a new Pinger and resolves the address.
  108. func NewPinger(addr string) (*Pinger, error) {
  109. p := New(addr)
  110. return p, p.Resolve()
  111. }
  112. // Pinger represents a packet sender/receiver.
  113. type Pinger struct {
  114. // Interval is the wait time between each packet send. Default is 1s.
  115. Interval time.Duration
  116. // Timeout specifies a timeout before ping exits, regardless of how many
  117. // packets have been received.
  118. Timeout time.Duration
  119. // Count tells pinger to stop after sending (and receiving) Count echo
  120. // packets. If this option is not specified, pinger will operate until
  121. // interrupted.
  122. Count int
  123. // Debug runs in debug mode
  124. Debug bool
  125. // Number of packets sent
  126. PacketsSent int
  127. // Number of packets received
  128. PacketsRecv int
  129. // Number of duplicate packets received
  130. PacketsRecvDuplicates int
  131. // Round trip time statistics
  132. minRtt time.Duration
  133. maxRtt time.Duration
  134. avgRtt time.Duration
  135. stdDevRtt time.Duration
  136. stddevm2 time.Duration
  137. statsMu sync.RWMutex
  138. // If true, keep a record of rtts of all received packets.
  139. // Set to false to avoid memory bloat for long running pings.
  140. RecordRtts bool
  141. // rtts is all of the Rtts
  142. rtts []time.Duration
  143. // OnSetup is called when Pinger has finished setting up the listening socket
  144. OnSetup func()
  145. // OnSend is called when Pinger sends a packet
  146. OnSend func(*Packet)
  147. // OnRecv is called when Pinger receives and processes a packet
  148. OnRecv func(*Packet)
  149. // OnRecv is called when Pinger receives and processes a packet
  150. OnTimeout func(*Packet)
  151. // OnFinish is called when Pinger exits
  152. OnFinish func(*Statistics)
  153. // OnDuplicateRecv is called when a packet is received that has already been received.
  154. OnDuplicateRecv func(*Packet)
  155. // Size of packet being sent
  156. Size int
  157. // Tracker: Used to uniquely identify packets - Deprecated
  158. Tracker uint64
  159. // Source is the source IP address
  160. Source string
  161. // Channel and mutex used to communicate when the Pinger should stop between goroutines.
  162. done chan interface{}
  163. lock sync.Mutex
  164. ipaddr *net.IPAddr
  165. addr string
  166. // trackerUUIDs is the list of UUIDs being used for sending packets.
  167. trackerUUIDs []uuid.UUID
  168. ipv4 bool
  169. id int
  170. sequence_base int
  171. sequence int
  172. // awaitingSequences are in-flight sequence numbers we keep track of to help remove duplicate receipts
  173. awaitingSequences map[uuid.UUID]map[int]struct{}
  174. // network is one of "ip", "ip4", or "ip6".
  175. network string
  176. // protocol is "icmp" or "udp".
  177. protocol string
  178. TTL int
  179. }
  180. // Packet represents a received and processed ICMP echo packet.
  181. type Packet struct {
  182. // Rtt is the round-trip time it took to ping.
  183. Rtt time.Duration
  184. // IPAddr is the address of the host being pinged.
  185. IPAddr *net.IPAddr
  186. // Host is the string address of the host being pinged.
  187. Host string
  188. // NBytes is the number of bytes in the message.
  189. Nbytes int
  190. // Seq is the ICMP sequence number.
  191. Seq int
  192. // TTL is the Time To Live on the packet.
  193. TTL int
  194. // ID is the ICMP identifier.
  195. ID int
  196. }
  197. // Statistics represent the stats of a currently running or finished
  198. // pinger operation.
  199. type Statistics struct {
  200. // PacketsRecv is the number of packets received.
  201. PacketsRecv int
  202. // PacketsSent is the number of packets sent.
  203. PacketsSent int
  204. // PacketsRecvDuplicates is the number of duplicate responses there were to a sent packet.
  205. PacketsRecvDuplicates int
  206. // PacketLoss is the percentage of packets lost.
  207. PacketLoss float64
  208. // IPAddr is the address of the host being pinged.
  209. IPAddr *net.IPAddr
  210. // Addr is the string address of the host being pinged.
  211. Addr string
  212. // Rtts is all of the round-trip times sent via this pinger.
  213. Rtts []time.Duration
  214. // MinRtt is the minimum round-trip time sent via this pinger.
  215. MinRtt time.Duration
  216. // MaxRtt is the maximum round-trip time sent via this pinger.
  217. MaxRtt time.Duration
  218. // AvgRtt is the average round-trip time sent via this pinger.
  219. AvgRtt time.Duration
  220. // StdDevRtt is the standard deviation of the round-trip times sent via
  221. // this pinger.
  222. StdDevRtt time.Duration
  223. }
  224. func (p *Pinger) updateStatistics(pkt *Packet) {
  225. p.statsMu.Lock()
  226. defer p.statsMu.Unlock()
  227. p.PacketsRecv++
  228. if p.RecordRtts {
  229. p.rtts = append(p.rtts, pkt.Rtt)
  230. }
  231. if p.PacketsRecv == 1 || pkt.Rtt < p.minRtt {
  232. p.minRtt = pkt.Rtt
  233. }
  234. if pkt.Rtt > p.maxRtt {
  235. p.maxRtt = pkt.Rtt
  236. }
  237. pktCount := time.Duration(p.PacketsRecv)
  238. // welford's online method for stddev
  239. // https://en.wikipedia.org/wiki/Algorithms_for_calculating_variance#Welford's_online_algorithm
  240. delta := pkt.Rtt - p.avgRtt
  241. p.avgRtt += delta / pktCount
  242. delta2 := pkt.Rtt - p.avgRtt
  243. p.stddevm2 += delta * delta2
  244. p.stdDevRtt = time.Duration(math.Sqrt(float64(p.stddevm2 / pktCount)))
  245. }
  246. // SetIPAddr sets the ip address of the target host.
  247. func (p *Pinger) SetIPAddr(ipaddr *net.IPAddr) {
  248. p.ipv4 = isIPv4(ipaddr.IP)
  249. p.ipaddr = ipaddr
  250. p.addr = ipaddr.String()
  251. }
  252. // IPAddr returns the ip address of the target host.
  253. func (p *Pinger) IPAddr() *net.IPAddr {
  254. return p.ipaddr
  255. }
  256. // Resolve does the DNS lookup for the Pinger address and sets IP protocol.
  257. func (p *Pinger) Resolve() error {
  258. if len(p.addr) == 0 {
  259. return errors.New("addr cannot be empty")
  260. }
  261. ipaddr, err := net.ResolveIPAddr(p.network, p.addr)
  262. if err != nil {
  263. return err
  264. }
  265. p.ipv4 = isIPv4(ipaddr.IP)
  266. p.ipaddr = ipaddr
  267. return nil
  268. }
  269. // SetAddr resolves and sets the ip address of the target host, addr can be a
  270. // DNS name like "www.google.com" or IP like "127.0.0.1".
  271. func (p *Pinger) SetAddr(addr string) error {
  272. oldAddr := p.addr
  273. p.addr = addr
  274. err := p.Resolve()
  275. if err != nil {
  276. p.addr = oldAddr
  277. return err
  278. }
  279. return nil
  280. }
  281. // Addr returns the string ip address of the target host.
  282. func (p *Pinger) Addr() string {
  283. return p.addr
  284. }
  285. // SetNetwork allows configuration of DNS resolution.
  286. // * "ip" will automatically select IPv4 or IPv6.
  287. // * "ip4" will select IPv4.
  288. // * "ip6" will select IPv6.
  289. func (p *Pinger) SetNetwork(n string) {
  290. switch n {
  291. case "ip4":
  292. p.network = "ip4"
  293. case "ip6":
  294. p.network = "ip6"
  295. default:
  296. p.network = "ip"
  297. }
  298. }
  299. // SetPrivileged sets the type of ping pinger will send.
  300. // false means pinger will send an "unprivileged" UDP ping.
  301. // true means pinger will send a "privileged" raw ICMP ping.
  302. // NOTE: setting to true requires that it be run with super-user privileges.
  303. func (p *Pinger) SetPrivileged(privileged bool) {
  304. if privileged {
  305. p.protocol = "icmp"
  306. } else {
  307. p.protocol = "udp"
  308. }
  309. }
  310. // Privileged returns whether pinger is running in privileged mode.
  311. func (p *Pinger) Privileged() bool {
  312. return p.protocol == "icmp"
  313. }
  314. // SetID sets the ICMP identifier.
  315. func (p *Pinger) SetID(id int) {
  316. p.id = id
  317. }
  318. // ID returns the ICMP identifier.
  319. func (p *Pinger) ID() int {
  320. return p.id
  321. }
  322. // Run runs the pinger. This is a blocking function that will exit when it's
  323. // done. If Count or Interval are not specified, it will run continuously until
  324. // it is interrupted.
  325. func (p *Pinger) Run() error {
  326. // sleeptime := time.Duration(0)
  327. // for sleeptime >= 0 {
  328. // time.Sleep(sleeptime)
  329. // lastpingtimemutex.Lock()
  330. // alastpingtime := lastpingtime[p.ipaddr.String()]
  331. // sleeptime = ping_interval_one_host - time.Since(alastpingtime)
  332. // if sleeptime <= 0 {
  333. // lastpingtime[p.ipaddr.String()] = time.Now()
  334. // } else {
  335. // // logger.Error(fmt.Sprint("ping", p.addr, "[", p.ipaddr.String(), "]", "同一地址至少间隔一秒"))
  336. // }
  337. // lastpingtimemutex.Unlock()
  338. // }
  339. // defer func() {
  340. // lastpingtimemutex.Lock()
  341. // lastpingtime[p.ipaddr.String()] = time.Now()
  342. // lastpingtimemutex.Unlock()
  343. // }()
  344. // concurchan_ping <- struct{}{}
  345. // defer func() {
  346. // <-concurchan_ping
  347. // }()
  348. return p.run()
  349. }
  350. func (p *Pinger) run() error {
  351. defer p.finish()
  352. err := MPConn(p.ipv4, p.protocol).Listen()
  353. if err != nil {
  354. return err
  355. }
  356. if handler := p.OnSetup; handler != nil {
  357. handler()
  358. }
  359. var g errgroup.Group
  360. g.Go(func() error {
  361. defer p.Stop()
  362. return p.runLoop()
  363. })
  364. return g.Wait()
  365. }
  366. func (p *Pinger) runLoop() error {
  367. timeout := time.NewTimer(p.Timeout)
  368. interval := time.NewTimer(0)
  369. timeout.Stop()
  370. defer func() {
  371. interval.Stop()
  372. timeout.Stop()
  373. }()
  374. received := make(chan interface{}, 1)
  375. for {
  376. select {
  377. case <-p.done:
  378. return nil
  379. case <-timeout.C:
  380. return nil
  381. case <-interval.C:
  382. if err := p.ping(received); err != nil {
  383. logger.Errorf("sending packet: %s", err)
  384. }
  385. if p.Count > 0 && p.PacketsSent >= p.Count {
  386. timeout.Reset(p.Timeout)
  387. } else {
  388. interval.Reset(p.Interval)
  389. }
  390. case <-received:
  391. if p.Count > 0 && p.PacketsRecv >= p.Count {
  392. return nil
  393. }
  394. }
  395. }
  396. }
  397. func (p *Pinger) ping(received chan interface{}) error {
  398. sleeptime := time.Duration(0)
  399. for sleeptime >= 0 {
  400. time.Sleep(sleeptime)
  401. lastpingtimemutex.Lock()
  402. alastpingtime := lastpingtime[p.ipaddr.String()]
  403. sleeptime = ping_interval - time.Since(alastpingtime)
  404. if sleeptime <= 0 {
  405. lastpingtime[p.ipaddr.String()] = time.Now()
  406. } else {
  407. // logger.Error(fmt.Sprint("ping", p.addr, "[", p.ipaddr.String(), "]", "同一地址至少间隔一秒"))
  408. }
  409. lastpingtimemutex.Unlock()
  410. }
  411. defer func() {
  412. lastpingtimemutex.Lock()
  413. lastpingtime[p.ipaddr.String()] = time.Now()
  414. lastpingtimemutex.Unlock()
  415. }()
  416. pinfo := newPingInfo(p.addr, p.ipaddr, p.Size, p.Timeout)
  417. pinfo.OnSend = func(pkt *icmp.Packet) {
  418. if p.PacketsSent == 0 {
  419. p.sequence_base = pkt.Seq
  420. }
  421. p.PacketsSent++
  422. if p.OnSend != nil {
  423. p.OnSend(&Packet{
  424. Rtt: pkt.Rtt,
  425. IPAddr: pkt.IPAddr,
  426. Host: pinfo.host,
  427. Nbytes: pkt.Nbytes,
  428. Seq: pkt.Seq - p.sequence_base,
  429. TTL: pkt.TTL,
  430. ID: pkt.ID,
  431. })
  432. }
  433. }
  434. pinfo.OnRecv = func(pkt *icmp.Packet) {
  435. inpkt := &Packet{
  436. Rtt: pkt.Rtt,
  437. IPAddr: pkt.IPAddr,
  438. Host: pinfo.host,
  439. Nbytes: pkt.Nbytes,
  440. Seq: pkt.Seq - p.sequence_base,
  441. TTL: pkt.TTL,
  442. ID: pkt.ID,
  443. }
  444. if p.OnRecv != nil {
  445. p.OnRecv(inpkt)
  446. }
  447. p.updateStatistics(inpkt)
  448. received <- nil
  449. }
  450. pinfo.OnRecvDup = func(pkt *icmp.Packet) {
  451. inpkt := &Packet{
  452. Rtt: pkt.Rtt,
  453. IPAddr: pkt.IPAddr,
  454. Host: pinfo.host,
  455. Nbytes: pkt.Nbytes,
  456. Seq: pkt.Seq - p.sequence_base,
  457. TTL: pkt.TTL,
  458. ID: pkt.ID,
  459. }
  460. if p.OnDuplicateRecv != nil {
  461. p.OnDuplicateRecv(inpkt)
  462. }
  463. p.updateStatistics(inpkt)
  464. }
  465. return MPConn(p.ipv4, p.protocol).Ping(pinfo)
  466. }
  467. func (p *Pinger) Stop() {
  468. p.lock.Lock()
  469. defer p.lock.Unlock()
  470. open := true
  471. select {
  472. case _, open = <-p.done:
  473. default:
  474. }
  475. if open {
  476. close(p.done)
  477. }
  478. }
  479. func (p *Pinger) finish() {
  480. handler := p.OnFinish
  481. if handler != nil {
  482. s := p.Statistics()
  483. handler(s)
  484. }
  485. }
  486. // Statistics returns the statistics of the pinger. This can be run while the
  487. // pinger is running or after it is finished. OnFinish calls this function to
  488. // get it's finished statistics.
  489. func (p *Pinger) Statistics() *Statistics {
  490. p.statsMu.RLock()
  491. defer p.statsMu.RUnlock()
  492. sent := p.PacketsSent
  493. loss := float64(sent-p.PacketsRecv) / float64(sent) * 100
  494. s := Statistics{
  495. PacketsSent: sent,
  496. PacketsRecv: p.PacketsRecv,
  497. PacketsRecvDuplicates: p.PacketsRecvDuplicates,
  498. PacketLoss: loss,
  499. Rtts: p.rtts,
  500. Addr: p.addr,
  501. IPAddr: p.ipaddr,
  502. MaxRtt: p.maxRtt,
  503. MinRtt: p.minRtt,
  504. AvgRtt: p.avgRtt,
  505. StdDevRtt: p.stdDevRtt,
  506. }
  507. return &s
  508. }
  509. func bytesToTime(b []byte) time.Time {
  510. var nsec int64
  511. for i := uint8(0); i < 8; i++ {
  512. nsec += int64(b[i]) << ((7 - i) * 8)
  513. }
  514. return time.Unix(nsec/1000000000, nsec%1000000000)
  515. }
  516. func isIPv4(ip net.IP) bool {
  517. return len(ip.To4()) == net.IPv4len
  518. }
  519. func timeToBytes(t time.Time) []byte {
  520. nsec := t.UnixNano()
  521. b := make([]byte, 8)
  522. for i := uint8(0); i < 8; i++ {
  523. b[i] = byte((nsec >> ((7 - i) * 8)) & 0xff)
  524. }
  525. return b
  526. }
  527. var seed = time.Now().UnixNano()
  528. // getSeed returns a goroutine-safe unique seed
  529. func getSeed() int64 {
  530. return atomic.AddInt64(&seed, 1)
  531. }