@@ -92,24 +92,22 @@ var (
92
92
func New (addr string ) * Pinger {
93
93
r := rand .New (rand .NewSource (getSeed ()))
94
94
firstUUID := uuid .New ()
95
- var firstSequence = map [uuid.UUID ]map [int ]struct {}{}
96
- firstSequence [firstUUID ] = make (map [int ]struct {})
97
95
return & Pinger {
98
96
Count : - 1 ,
99
97
Interval : time .Second ,
100
98
RecordRtts : true ,
101
99
Size : timeSliceLength + trackerLength ,
102
100
Timeout : time .Duration (math .MaxInt64 ),
101
+ MaxRtt : time .Duration (math .MaxInt64 ),
103
102
104
103
addr : addr ,
105
104
done : make (chan interface {}),
106
105
id : r .Intn (math .MaxUint16 ),
107
- trackerUUIDs : []uuid.UUID {firstUUID },
108
106
ipaddr : nil ,
109
107
ipv4 : false ,
110
108
network : "ip" ,
111
109
protocol : "udp" ,
112
- awaitingSequences : firstSequence ,
110
+ awaitingSequences : newSeqMap ( firstUUID ) ,
113
111
TTL : 64 ,
114
112
logger : StdLogger {Logger : log .New (log .Writer (), log .Prefix (), log .Flags ())},
115
113
}
@@ -129,6 +127,9 @@ type Pinger struct {
129
127
// Timeout specifies a timeout before ping exits, regardless of how many
130
128
// packets have been received.
131
129
Timeout time.Duration
130
+ // MaxRtt If no response is received after this time, OnTimeout is called
131
+ // important! This option is not guaranteed. and if we receive the packet that was timeout, the function OnDuplicateRecv will be called
132
+ MaxRtt time.Duration
132
133
133
134
// Count tells pinger to stop after sending (and receiving) Count echo
134
135
// packets. If this option is not specified, pinger will operate until
@@ -183,6 +184,8 @@ type Pinger struct {
183
184
// OnRecvError is called when an error occurs while Pinger attempts to receive a packet
184
185
OnRecvError func (error )
185
186
187
+ // OnTimeOut is called when a packet don't have received after MaxRtt.
188
+ OnTimeOut func (* Packet )
186
189
// Size of packet being sent
187
190
Size int
188
191
@@ -205,14 +208,11 @@ type Pinger struct {
205
208
// df when true sets the do-not-fragment bit in the outer IP or IPv6 header
206
209
df bool
207
210
208
- // trackerUUIDs is the list of UUIDs being used for sending packets.
209
- trackerUUIDs []uuid.UUID
210
-
211
211
ipv4 bool
212
212
id int
213
213
sequence int
214
214
// awaitingSequences are in-flight sequence numbers we keep track of to help remove duplicate receipts
215
- awaitingSequences map [uuid. UUID ] map [ int ] struct {}
215
+ awaitingSequences seqMap
216
216
// network is one of "ip", "ip4", or "ip6".
217
217
network string
218
218
// protocol is "icmp" or "udp".
@@ -520,20 +520,50 @@ func (p *Pinger) runLoop(
520
520
521
521
timeout := time .NewTicker (p .Timeout )
522
522
interval := time .NewTicker (p .Interval )
523
+ timeoutTimer := time .NewTimer (time .Duration (math .MaxInt64 ))
524
+ skip := false
523
525
defer func () {
524
526
interval .Stop ()
525
527
timeout .Stop ()
528
+ timeoutTimer .Stop ()
526
529
}()
527
530
528
531
if err := p .sendICMP (conn ); err != nil {
529
532
return err
530
533
}
531
534
532
535
for {
536
+ if ! skip {
537
+ if ! timeoutTimer .Stop () {
538
+ <- timeoutTimer .C
539
+ }
540
+ }
541
+ skip = false
542
+ first := p .awaitingSequences .peekFirst ()
543
+ if first != nil {
544
+ timeoutTimer .Reset (time .Until (first .time .Add (p .MaxRtt )))
545
+ } else {
546
+ timeoutTimer .Reset (time .Duration (math .MaxInt64 ))
547
+ }
548
+
533
549
select {
534
550
case <- p .done :
535
551
return nil
536
552
553
+ case <- timeoutTimer .C :
554
+ skip = true
555
+ p .awaitingSequences .removeElem (first )
556
+ if p .OnTimeOut != nil {
557
+ inPkt := & Packet {
558
+ IPAddr : p .ipaddr ,
559
+ Addr : p .addr ,
560
+ Rtt : p .MaxRtt ,
561
+ Seq : first .seq ,
562
+ TTL : - 1 ,
563
+ ID : p .id ,
564
+ }
565
+ p .OnTimeOut (inPkt )
566
+ }
537
567
case <- timeout .C :
538
568
return nil
539
569
@@ -681,7 +711,7 @@ func (p *Pinger) getPacketUUID(pkt []byte) (*uuid.UUID, error) {
681
711
return nil , fmt .Errorf ("error decoding tracking UUID: %w" , err )
682
712
}
683
713
684
- for _ , item := range p .trackerUUIDs {
714
+ for _ , item := range p .awaitingSequences . trackerUUIDs {
685
715
if item == packetUUID {
686
716
return & packetUUID , nil
687
717
}
@@ -691,7 +721,7 @@ func (p *Pinger) getPacketUUID(pkt []byte) (*uuid.UUID, error) {
691
721
692
722
// getCurrentTrackerUUID grabs the latest tracker UUID.
693
723
func (p * Pinger ) getCurrentTrackerUUID () uuid.UUID {
694
- return p .trackerUUIDs [len (p .trackerUUIDs )- 1 ]
724
+ return p .awaitingSequences . trackerUUIDs [len (p . awaitingSequences .trackerUUIDs )- 1 ]
695
725
}
696
726
697
727
func (p * Pinger ) processPacket (recv * packet ) error {
@@ -744,15 +774,16 @@ func (p *Pinger) processPacket(recv *packet) error {
744
774
inPkt .Rtt = receivedAt .Sub (timestamp )
745
775
inPkt .Seq = pkt .Seq
746
776
// If we've already received this sequence, ignore it.
747
- if _ , inflight := p.awaitingSequences [* pktUUID ][pkt.Seq ]; ! inflight {
777
+ e , inflight := p .awaitingSequences .getElem (* pktUUID , pkt .Seq )
778
+ if ! inflight {
748
779
p .PacketsRecvDuplicates ++
749
780
if p .OnDuplicateRecv != nil {
750
781
p .OnDuplicateRecv (inPkt )
751
782
}
752
783
return nil
753
784
}
754
785
// remove it from the list of sequences we're waiting for so we don't get duplicates.
755
- delete ( p .awaitingSequences [ * pktUUID ], pkt . Seq )
786
+ p .awaitingSequences . removeElem ( e )
756
787
p .updateStatistics (inPkt )
757
788
default :
758
789
// Very bad, not sure how this can happen
@@ -777,7 +808,8 @@ func (p *Pinger) sendICMP(conn packetConn) error {
777
808
if err != nil {
778
809
return fmt .Errorf ("unable to marshal UUID binary: %w" , err )
779
810
}
780
- t := append (timeToBytes (time .Now ()), uuidEncoded ... )
811
+ now := time .Now ()
812
+ t := append (timeToBytes (now ), uuidEncoded ... )
781
813
if remainSize := p .Size - timeSliceLength - trackerLength ; remainSize > 0 {
782
814
t = append (t , bytes .Repeat ([]byte {1 }, remainSize )... )
783
815
}
@@ -829,13 +861,12 @@ func (p *Pinger) sendICMP(conn packetConn) error {
829
861
p .OnSend (outPkt )
830
862
}
831
863
// mark this sequence as in-flight
832
- p.awaitingSequences [ currentUUID ][ p.sequence ] = struct {}{}
864
+ p .awaitingSequences . putElem ( currentUUID , p .sequence , now )
833
865
p .PacketsSent ++
834
866
p .sequence ++
835
867
if p .sequence > 65535 {
836
868
newUUID := uuid .New ()
837
- p .trackerUUIDs = append (p .trackerUUIDs , newUUID )
838
- p .awaitingSequences [newUUID ] = make (map [int ]struct {})
869
+ p .awaitingSequences .newSeqMap (newUUID )
839
870
p .sequence = 0
840
871
}
841
872
break
0 commit comments