123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896 |
- // Package probing is a simple but powerful ICMP echo (ping) library.
- //
- // Here is a very simple example that sends and receives three packets:
- //
- // pinger, err := probing.NewPinger("www.google.com")
- // if err != nil {
- // panic(err)
- // }
- // pinger.Count = 3
- // err = pinger.Run() // blocks until finished
- // if err != nil {
- // panic(err)
- // }
- // stats := pinger.Statistics() // get send/receive/rtt stats
- //
- // Here is an example that emulates the traditional UNIX ping command:
- //
- // pinger, err := probing.NewPinger("www.google.com")
- // if err != nil {
- // panic(err)
- // }
- // // Listen for Ctrl-C.
- // c := make(chan os.Signal, 1)
- // signal.Notify(c, os.Interrupt)
- // go func() {
- // for _ = range c {
- // pinger.Stop()
- // }
- // }()
- // pinger.OnRecv = func(pkt *probing.Packet) {
- // fmt.Printf("%d bytes from %s: icmp_seq=%d time=%v\n",
- // pkt.Nbytes, pkt.IPAddr, pkt.Seq, pkt.Rtt)
- // }
- // pinger.OnFinish = func(stats *probing.Statistics) {
- // fmt.Printf("\n--- %s ping statistics ---\n", stats.Addr)
- // fmt.Printf("%d packets transmitted, %d packets received, %v%% packet loss\n",
- // stats.PacketsSent, stats.PacketsRecv, stats.PacketLoss)
- // fmt.Printf("round-trip min/avg/max/stddev = %v/%v/%v/%v\n",
- // stats.MinRtt, stats.AvgRtt, stats.MaxRtt, stats.StdDevRtt)
- // }
- // fmt.Printf("PING %s (%s):\n", pinger.Addr(), pinger.IPAddr())
- // err = pinger.Run()
- // if err != nil {
- // panic(err)
- // }
- //
- // It sends ICMP Echo Request packet(s) and waits for an Echo Reply in response.
- // If it receives a response, it calls the OnRecv callback. When it's finished,
- // it calls the OnFinish callback.
- //
- // For a full ping example, see "cmd/ping/ping.go".
- package probing
- import (
- "bytes"
- "errors"
- "fmt"
- "log"
- "math"
- "math/rand"
- "net"
- "sync"
- "sync/atomic"
- "syscall"
- "time"
- "git.wecise.com/wecise/common/matrix/cfg"
- "github.com/google/uuid"
- "golang.org/x/net/icmp"
- "golang.org/x/net/ipv4"
- "golang.org/x/net/ipv6"
- "golang.org/x/sync/errgroup"
- )
- var mcfg = cfg.MConfig()
- var ping_interval_one_host = mcfg.GetDuration("ping.interval.one.host", 10000*time.Millisecond)
- var ping_interval_one_conn = mcfg.GetDuration("ping.interval.one.conn", 200*time.Millisecond)
- var concurlimit_ping = mcfg.GetInt("concurlimit.ping", 100)
- var concurlimit_send = mcfg.GetInt("concurlimit.send", 10)
- var concurchan_ping = make(chan struct{}, concurlimit_ping)
- var concurchan_send = make(chan struct{}, concurlimit_send)
- var lastpingtimemutex sync.Mutex
- var lastpingtime = map[string]time.Time{}
- var lastsendtimemutex sync.Mutex
- var lastsendtime = map[string]time.Time{}
- const (
- timeSliceLength = 8
- trackerLength = len(uuid.UUID{})
- protocolICMP = 1
- protocolIPv6ICMP = 58
- )
- var (
- ipv4Proto = map[string]string{"icmp": "ip4:icmp", "udp": "udp4"}
- ipv6Proto = map[string]string{"icmp": "ip6:ipv6-icmp", "udp": "udp6"}
- )
- // New returns a new Pinger struct pointer.
- func New(addr string) *Pinger {
- r := rand.New(rand.NewSource(getSeed()))
- firstUUID := uuid.New()
- var firstSequence = map[uuid.UUID]map[int]struct{}{}
- firstSequence[firstUUID] = make(map[int]struct{})
- return &Pinger{
- Count: -1,
- Interval: time.Second,
- RecordRtts: true,
- Size: timeSliceLength + trackerLength,
- Timeout: time.Duration(math.MaxInt64),
- addr: addr,
- done: make(chan interface{}),
- id: r.Intn(math.MaxUint16),
- trackerUUIDs: []uuid.UUID{firstUUID},
- ipaddr: nil,
- ipv4: false,
- network: "ip",
- protocol: "udp",
- awaitingSequences: firstSequence,
- TTL: 64,
- logger: StdLogger{Logger: log.New(log.Writer(), log.Prefix(), log.Flags())},
- }
- }
- // NewPinger returns a new Pinger and resolves the address.
- func NewPinger(addr string) (*Pinger, error) {
- p := New(addr)
- return p, p.Resolve()
- }
- // Pinger represents a packet sender/receiver.
- type Pinger struct {
- // Interval is the wait time between each packet send. Default is 1s.
- Interval time.Duration
- // Timeout specifies a timeout before ping exits, regardless of how many
- // packets have been received.
- Timeout time.Duration
- // Count tells pinger to stop after sending (and receiving) Count echo
- // packets. If this option is not specified, pinger will operate until
- // interrupted.
- Count int
- // Debug runs in debug mode
- Debug bool
- // Number of packets sent
- PacketsSent int
- // Number of packets received
- PacketsRecv int
- // Number of duplicate packets received
- PacketsRecvDuplicates int
- // Round trip time statistics
- minRtt time.Duration
- maxRtt time.Duration
- avgRtt time.Duration
- stdDevRtt time.Duration
- stddevm2 time.Duration
- statsMu sync.RWMutex
- // If true, keep a record of rtts of all received packets.
- // Set to false to avoid memory bloat for long running pings.
- RecordRtts bool
- // rtts is all of the Rtts
- rtts []time.Duration
- // OnSetup is called when Pinger has finished setting up the listening socket
- OnSetup func()
- // OnSend is called when Pinger sends a packet
- OnSend func(*Packet)
- // OnRecv is called when Pinger receives and processes a packet
- OnRecv func(*Packet)
- // OnFinish is called when Pinger exits
- OnFinish func(*Statistics)
- // OnDuplicateRecv is called when a packet is received that has already been received.
- OnDuplicateRecv func(*Packet)
- // Size of packet being sent
- Size int
- // Tracker: Used to uniquely identify packets - Deprecated
- Tracker uint64
- // Source is the source IP address
- Source string
- // Channel and mutex used to communicate when the Pinger should stop between goroutines.
- done chan interface{}
- lock sync.Mutex
- ipaddr *net.IPAddr
- addr string
- // trackerUUIDs is the list of UUIDs being used for sending packets.
- trackerUUIDs []uuid.UUID
- ipv4 bool
- id int
- sequence int
- // awaitingSequences are in-flight sequence numbers we keep track of to help remove duplicate receipts
- awaitingSequences map[uuid.UUID]map[int]struct{}
- // network is one of "ip", "ip4", or "ip6".
- network string
- // protocol is "icmp" or "udp".
- protocol string
- logger Logger
- TTL int
- }
- type packet struct {
- bytes []byte
- nbytes int
- ttl int
- }
- // Packet represents a received and processed ICMP echo packet.
- type Packet struct {
- // Rtt is the round-trip time it took to ping.
- Rtt time.Duration
- // IPAddr is the address of the host being pinged.
- IPAddr *net.IPAddr
- // Addr is the string address of the host being pinged.
- Addr string
- // NBytes is the number of bytes in the message.
- Nbytes int
- // Seq is the ICMP sequence number.
- Seq int
- // TTL is the Time To Live on the packet.
- TTL int
- // ID is the ICMP identifier.
- ID int
- }
- // Statistics represent the stats of a currently running or finished
- // pinger operation.
- type Statistics struct {
- // PacketsRecv is the number of packets received.
- PacketsRecv int
- // PacketsSent is the number of packets sent.
- PacketsSent int
- // PacketsRecvDuplicates is the number of duplicate responses there were to a sent packet.
- PacketsRecvDuplicates int
- // PacketLoss is the percentage of packets lost.
- PacketLoss float64
- // IPAddr is the address of the host being pinged.
- IPAddr *net.IPAddr
- // Addr is the string address of the host being pinged.
- Addr string
- // Rtts is all of the round-trip times sent via this pinger.
- Rtts []time.Duration
- // MinRtt is the minimum round-trip time sent via this pinger.
- MinRtt time.Duration
- // MaxRtt is the maximum round-trip time sent via this pinger.
- MaxRtt time.Duration
- // AvgRtt is the average round-trip time sent via this pinger.
- AvgRtt time.Duration
- // StdDevRtt is the standard deviation of the round-trip times sent via
- // this pinger.
- StdDevRtt time.Duration
- }
- func (p *Pinger) updateStatistics(pkt *Packet) {
- p.statsMu.Lock()
- defer p.statsMu.Unlock()
- p.PacketsRecv++
- if p.RecordRtts {
- p.rtts = append(p.rtts, pkt.Rtt)
- }
- if p.PacketsRecv == 1 || pkt.Rtt < p.minRtt {
- p.minRtt = pkt.Rtt
- }
- if pkt.Rtt > p.maxRtt {
- p.maxRtt = pkt.Rtt
- }
- pktCount := time.Duration(p.PacketsRecv)
- // welford's online method for stddev
- // https://en.wikipedia.org/wiki/Algorithms_for_calculating_variance#Welford's_online_algorithm
- delta := pkt.Rtt - p.avgRtt
- p.avgRtt += delta / pktCount
- delta2 := pkt.Rtt - p.avgRtt
- p.stddevm2 += delta * delta2
- p.stdDevRtt = time.Duration(math.Sqrt(float64(p.stddevm2 / pktCount)))
- }
- // SetIPAddr sets the ip address of the target host.
- func (p *Pinger) SetIPAddr(ipaddr *net.IPAddr) {
- p.ipv4 = isIPv4(ipaddr.IP)
- p.ipaddr = ipaddr
- p.addr = ipaddr.String()
- }
- // IPAddr returns the ip address of the target host.
- func (p *Pinger) IPAddr() *net.IPAddr {
- return p.ipaddr
- }
- // Resolve does the DNS lookup for the Pinger address and sets IP protocol.
- func (p *Pinger) Resolve() error {
- if len(p.addr) == 0 {
- return errors.New("addr cannot be empty")
- }
- ipaddr, err := net.ResolveIPAddr(p.network, p.addr)
- if err != nil {
- return err
- }
- p.ipv4 = isIPv4(ipaddr.IP)
- p.ipaddr = ipaddr
- return nil
- }
- // SetAddr resolves and sets the ip address of the target host, addr can be a
- // DNS name like "www.google.com" or IP like "127.0.0.1".
- func (p *Pinger) SetAddr(addr string) error {
- oldAddr := p.addr
- p.addr = addr
- err := p.Resolve()
- if err != nil {
- p.addr = oldAddr
- return err
- }
- return nil
- }
- // Addr returns the string ip address of the target host.
- func (p *Pinger) Addr() string {
- return p.addr
- }
- // SetNetwork allows configuration of DNS resolution.
- // * "ip" will automatically select IPv4 or IPv6.
- // * "ip4" will select IPv4.
- // * "ip6" will select IPv6.
- func (p *Pinger) SetNetwork(n string) {
- switch n {
- case "ip4":
- p.network = "ip4"
- case "ip6":
- p.network = "ip6"
- default:
- p.network = "ip"
- }
- }
- // SetPrivileged sets the type of ping pinger will send.
- // false means pinger will send an "unprivileged" UDP ping.
- // true means pinger will send a "privileged" raw ICMP ping.
- // NOTE: setting to true requires that it be run with super-user privileges.
- func (p *Pinger) SetPrivileged(privileged bool) {
- if privileged {
- p.protocol = "icmp"
- } else {
- p.protocol = "udp"
- }
- }
- // Privileged returns whether pinger is running in privileged mode.
- func (p *Pinger) Privileged() bool {
- return p.protocol == "icmp"
- }
- // SetLogger sets the logger to be used to log events from the pinger.
- func (p *Pinger) SetLogger(logger Logger) {
- p.logger = logger
- }
- // SetID sets the ICMP identifier.
- func (p *Pinger) SetID(id int) {
- p.id = id
- }
- // ID returns the ICMP identifier.
- func (p *Pinger) ID() int {
- return p.id
- }
- // Run runs the pinger. This is a blocking function that will exit when it's
- // done. If Count or Interval are not specified, it will run continuously until
- // it is interrupted.
- func (p *Pinger) Run() error {
- sleeptime := time.Duration(0)
- for sleeptime >= 0 {
- time.Sleep(sleeptime)
- lastpingtimemutex.Lock()
- alastpingtime := lastpingtime[p.ipaddr.String()]
- sleeptime = ping_interval_one_host - time.Since(alastpingtime)
- if sleeptime <= 0 {
- lastpingtime[p.ipaddr.String()] = time.Now()
- } else {
- // logger.Error(fmt.Sprint("ping", p.addr, "[", p.ipaddr.String(), "]", "同一地址至少间隔一秒"))
- }
- lastpingtimemutex.Unlock()
- }
- lastpingtimemutex.Lock()
- for idping[p.id] != nil && idping[p.id] != p {
- p.id = (p.id + 1) % 65536
- }
- idping[p.id] = p
- lastpingtimemutex.Unlock()
- defer func() {
- lastpingtimemutex.Lock()
- lastpingtime[p.ipaddr.String()] = time.Now()
- delete(idping, p.id)
- lastpingtimemutex.Unlock()
- }()
- // concurchan_ping <- struct{}{}
- // defer func() {
- // <-concurchan_ping
- // }()
- var conn packetConn
- var err error
- if p.Size < timeSliceLength+trackerLength {
- return fmt.Errorf("size %d is less than minimum required size %d", p.Size, timeSliceLength+trackerLength)
- }
- if p.ipaddr == nil {
- err = p.Resolve()
- }
- if err != nil {
- return err
- }
- if conn, err = p.listen(); err != nil {
- return err
- }
- defer conn.Close()
- conn.SetTTL(p.TTL)
- return p.run(conn)
- }
- func (p *Pinger) run(conn packetConn) error {
- if err := conn.SetFlagTTL(); err != nil {
- return err
- }
- defer p.finish()
- recv := make(chan *packet, 5)
- defer close(recv)
- if handler := p.OnSetup; handler != nil {
- handler()
- }
- var g errgroup.Group
- g.Go(func() error {
- defer p.Stop()
- return p.recvICMP(conn, recv)
- })
- g.Go(func() error {
- defer p.Stop()
- return p.runLoop(conn, recv)
- })
- return g.Wait()
- }
- func (p *Pinger) runLoop(
- conn packetConn,
- recvCh <-chan *packet,
- ) error {
- logger := p.logger
- if logger == nil {
- logger = NoopLogger{}
- }
- timeout := time.NewTicker(p.Timeout)
- interval := time.NewTicker(p.Interval)
- defer func() {
- interval.Stop()
- timeout.Stop()
- }()
- if err := p.sendICMP(conn); err != nil {
- return err
- }
- for {
- select {
- case <-p.done:
- return nil
- case <-timeout.C:
- return nil
- case r := <-recvCh:
- err := p.processPacket(r)
- if err != nil {
- // FIXME: this logs as FATAL but continues
- logger.Errorf("processing received packet: %s", err)
- }
- case <-interval.C:
- if p.Count > 0 && p.PacketsSent >= p.Count {
- interval.Stop()
- continue
- }
- err := p.sendICMP(conn)
- if err != nil {
- // FIXME: this logs as FATAL but continues
- logger.Errorf("sending packet: %s", err)
- }
- }
- if p.Count > 0 && p.PacketsRecv >= p.Count {
- return nil
- }
- }
- }
- func (p *Pinger) Stop() {
- p.lock.Lock()
- defer p.lock.Unlock()
- open := true
- select {
- case _, open = <-p.done:
- default:
- }
- if open {
- close(p.done)
- }
- }
- func (p *Pinger) finish() {
- handler := p.OnFinish
- if handler != nil {
- s := p.Statistics()
- handler(s)
- }
- }
- // Statistics returns the statistics of the pinger. This can be run while the
- // pinger is running or after it is finished. OnFinish calls this function to
- // get it's finished statistics.
- func (p *Pinger) Statistics() *Statistics {
- p.statsMu.RLock()
- defer p.statsMu.RUnlock()
- sent := p.PacketsSent
- loss := float64(sent-p.PacketsRecv) / float64(sent) * 100
- s := Statistics{
- PacketsSent: sent,
- PacketsRecv: p.PacketsRecv,
- PacketsRecvDuplicates: p.PacketsRecvDuplicates,
- PacketLoss: loss,
- Rtts: p.rtts,
- Addr: p.addr,
- IPAddr: p.ipaddr,
- MaxRtt: p.maxRtt,
- MinRtt: p.minRtt,
- AvgRtt: p.avgRtt,
- StdDevRtt: p.stdDevRtt,
- }
- return &s
- }
- type expBackoff struct {
- baseDelay time.Duration
- maxExp int64
- c int64
- }
- func (b *expBackoff) Get() time.Duration {
- if b.c < b.maxExp {
- b.c++
- }
- return b.baseDelay * time.Duration(rand.Int63n(1<<b.c))
- }
- func newExpBackoff(baseDelay time.Duration, maxExp int64) expBackoff {
- return expBackoff{baseDelay: baseDelay, maxExp: maxExp}
- }
- func (p *Pinger) recvICMP(
- conn packetConn,
- recv chan<- *packet,
- ) error {
- // Start by waiting for 50 µs and increase to a possible maximum of ~ 100 ms.
- expBackoff := newExpBackoff(50*time.Microsecond, 100)
- delay := expBackoff.Get()
- for {
- select {
- case <-p.done:
- return nil
- default:
- bytes := make([]byte, p.getMessageLength())
- if err := conn.SetReadDeadline(time.Now().Add(delay)); err != nil {
- return err
- }
- var n, ttl int
- var err error
- n, ttl, _, err = conn.ReadFrom(bytes)
- if err != nil {
- if neterr, ok := err.(*net.OpError); ok {
- if neterr.Timeout() {
- // Read timeout
- delay = expBackoff.Get()
- continue
- }
- }
- return err
- }
- select {
- case <-p.done:
- return nil
- case recv <- &packet{bytes: bytes, nbytes: n, ttl: ttl}:
- }
- }
- }
- }
- // getPacketUUID scans the tracking slice for matches.
- func (p *Pinger) getPacketUUID(pkt []byte) (*uuid.UUID, error) {
- var packetUUID uuid.UUID
- err := packetUUID.UnmarshalBinary(pkt[timeSliceLength : timeSliceLength+trackerLength])
- if err != nil {
- return nil, fmt.Errorf("error decoding tracking UUID: %w", err)
- }
- for _, item := range p.trackerUUIDs {
- if item == packetUUID {
- return &packetUUID, nil
- }
- }
- return nil, nil
- }
- // getCurrentTrackerUUID grabs the latest tracker UUID.
- func (p *Pinger) getCurrentTrackerUUID() uuid.UUID {
- return p.trackerUUIDs[len(p.trackerUUIDs)-1]
- }
- func (p *Pinger) processPacket(recv *packet) error {
- receivedAt := time.Now()
- var proto int
- if p.ipv4 {
- proto = protocolICMP
- } else {
- proto = protocolIPv6ICMP
- }
- var m *icmp.Message
- var err error
- if m, err = icmp.ParseMessage(proto, recv.bytes); err != nil {
- return fmt.Errorf("error parsing icmp message: %w", err)
- }
- if m.Type != ipv4.ICMPTypeEchoReply && m.Type != ipv6.ICMPTypeEchoReply {
- // Not an echo reply, ignore it
- return nil
- }
- inPkt := &Packet{
- Nbytes: recv.nbytes,
- IPAddr: p.ipaddr,
- Addr: p.addr,
- TTL: recv.ttl,
- ID: p.id,
- }
- switch pkt := m.Body.(type) {
- case *icmp.Echo:
- return p.processEchoReply(pkt, receivedAt, inPkt)
- default:
- // Very bad, not sure how this can happen
- return fmt.Errorf("invalid ICMP echo reply; type: '%T', '%v'", pkt, pkt)
- }
- }
- var idping = map[int]*Pinger{}
- func (p *Pinger) processEchoReply(pkt *icmp.Echo, receivedAt time.Time, inPkt *Packet) error {
- if !p.matchID(pkt.ID) {
- // lastpingtimemutex.Lock()
- // ap := idping[pkt.ID]
- // lastpingtimemutex.Unlock()
- // println(fmt.Sprintf("%#v%s%#v", ap, "\n", p))
- return nil
- }
- if len(pkt.Data) < timeSliceLength+trackerLength {
- return fmt.Errorf("insufficient data received; got: %d %v",
- len(pkt.Data), pkt.Data)
- }
- pktUUID, err := p.getPacketUUID(pkt.Data)
- if err != nil || pktUUID == nil {
- return err
- }
- timestamp := bytesToTime(pkt.Data[:timeSliceLength])
- inPkt.Rtt = receivedAt.Sub(timestamp)
- inPkt.Seq = pkt.Seq
- // If we've already received this sequence, ignore it.
- if _, inflight := p.awaitingSequences[*pktUUID][pkt.Seq]; !inflight {
- p.PacketsRecvDuplicates++
- if p.OnDuplicateRecv != nil {
- p.OnDuplicateRecv(inPkt)
- }
- return nil
- }
- // remove it from the list of sequences we're waiting for so we don't get duplicates.
- delete(p.awaitingSequences[*pktUUID], pkt.Seq)
- p.updateStatistics(inPkt)
- handler := p.OnRecv
- if handler != nil {
- handler(inPkt)
- }
- return nil
- }
- func (p *Pinger) sendICMP(conn packetConn) error {
- concurchan_send <- struct{}{}
- defer func() {
- <-concurchan_send
- lastpingtimemutex.Lock()
- lastpingtime[p.ipaddr.String()] = time.Now()
- lastpingtimemutex.Unlock()
- }()
- // lastsendtimemutex.Lock()
- // alastsendtime := lastsendtime[p.addr]
- // sleeptime := 200*time.Millisecond - time.Since(alastsendtime)
- // lastsendtimemutex.Unlock()
- // if sleeptime > 0 {
- // time.Sleep(sleeptime)
- // }
- // lastsendtimemutex.Lock()
- // lastsendtime[p.addr] = time.Now()
- // lastsendtimemutex.Unlock()
- // defer func() {
- // lastsendtimemutex.Lock()
- // lastsendtime[p.addr] = time.Now()
- // lastsendtimemutex.Unlock()
- // }()
- return p.sendICMPx(conn)
- }
- func (p *Pinger) sendICMPx(conn packetConn) error {
- var dst net.Addr = p.ipaddr
- if p.protocol == "udp" {
- dst = &net.UDPAddr{IP: p.ipaddr.IP, Zone: p.ipaddr.Zone}
- }
- currentUUID := p.getCurrentTrackerUUID()
- uuidEncoded, err := currentUUID.MarshalBinary()
- if err != nil {
- return fmt.Errorf("unable to marshal UUID binary: %w", err)
- }
- t := append(timeToBytes(time.Now()), uuidEncoded...)
- t = append(t, []byte(p.addr)...)
- if remainSize := p.Size - len(t); remainSize > 0 {
- t = append(t, bytes.Repeat([]byte{1}, remainSize)...)
- }
- body := &icmp.Echo{
- ID: p.id,
- Seq: p.sequence,
- Data: t,
- }
- msg := &icmp.Message{
- Type: conn.ICMPRequestType(),
- Code: 0,
- Body: body,
- }
- msgBytes, err := msg.Marshal(nil)
- if err != nil {
- return err
- }
- // fmt.Println("len(body.Data)=", len(body.Data))
- // fmt.Println("len(msgBytes)=", len(msgBytes))
- for {
- if _, err := conn.WriteTo(msgBytes, dst); err != nil {
- if neterr, ok := err.(*net.OpError); ok {
- if neterr.Err == syscall.ENOBUFS {
- fmt.Println("发送失败,重发")
- continue
- }
- }
- return err
- }
- handler := p.OnSend
- if handler != nil {
- outPkt := &Packet{
- Nbytes: len(msgBytes),
- IPAddr: p.ipaddr,
- Addr: p.addr,
- Seq: p.sequence,
- ID: p.id,
- }
- handler(outPkt)
- }
- // mark this sequence as in-flight
- p.awaitingSequences[currentUUID][p.sequence] = struct{}{}
- p.PacketsSent++
- p.sequence++
- if p.sequence > 65535 {
- newUUID := uuid.New()
- p.trackerUUIDs = append(p.trackerUUIDs, newUUID)
- p.awaitingSequences[newUUID] = make(map[int]struct{})
- p.sequence = 0
- }
- break
- }
- return nil
- }
- func (p *Pinger) listen() (packetConn, error) {
- var (
- conn packetConn
- err error
- )
- conn, err = mpconn.listen()
- if err != nil {
- p.Stop()
- return nil, err
- }
- return conn, nil
- }
- func bytesToTime(b []byte) time.Time {
- var nsec int64
- for i := uint8(0); i < 8; i++ {
- nsec += int64(b[i]) << ((7 - i) * 8)
- }
- return time.Unix(nsec/1000000000, nsec%1000000000)
- }
- func isIPv4(ip net.IP) bool {
- return len(ip.To4()) == net.IPv4len
- }
- func timeToBytes(t time.Time) []byte {
- nsec := t.UnixNano()
- b := make([]byte, 8)
- for i := uint8(0); i < 8; i++ {
- b[i] = byte((nsec >> ((7 - i) * 8)) & 0xff)
- }
- return b
- }
- var seed = time.Now().UnixNano()
- // getSeed returns a goroutine-safe unique seed
- func getSeed() int64 {
- return atomic.AddInt64(&seed, 1)
- }
|