@@ -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,48 @@ 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 && ! timeoutTimer .Stop () {
537
+ <- timeoutTimer .C
538
+ }
539
+ skip = false
540
+ first := p .awaitingSequences .peekFirst ()
541
+ if first != nil {
542
+ timeoutTimer .Reset (time .Until (first .time .Add (p .MaxRtt )))
543
+ } else {
544
+ timeoutTimer .Reset (time .Duration (math .MaxInt64 ))
545
+ }
546
+
533
547
select {
534
548
case <- p .done :
535
549
return nil
536
550
551
+ case <- timeoutTimer .C :
552
+ skip = true
553
+ p .awaitingSequences .removeElem (first )
554
+ if p .OnTimeOut != nil {
555
+ inPkt := & Packet {
556
+ IPAddr : p .ipaddr ,
557
+ Addr : p .addr ,
558
+ Rtt : p .MaxRtt ,
559
+ Seq : first .seq ,
560
+ TTL : - 1 ,
561
+ ID : p .id ,
562
+ }
563
+ p .OnTimeOut (inPkt )
564
+ }
537
565
case <- timeout .C :
538
566
return nil
539
567
@@ -680,18 +708,15 @@ func (p *Pinger) getPacketUUID(pkt []byte) (*uuid.UUID, error) {
680
708
if err != nil {
681
709
return nil , fmt .Errorf ("error decoding tracking UUID: %w" , err )
682
710
}
683
-
684
- for _ , item := range p .trackerUUIDs {
685
- if item == packetUUID {
686
- return & packetUUID , nil
687
- }
711
+ if p .awaitingSequences .checkUUIDExist (packetUUID ) {
712
+ return & packetUUID , nil
688
713
}
689
714
return nil , nil
690
715
}
691
716
692
717
// getCurrentTrackerUUID grabs the latest tracker UUID.
693
718
func (p * Pinger ) getCurrentTrackerUUID () uuid.UUID {
694
- return p .trackerUUIDs [ len ( p . trackerUUIDs ) - 1 ]
719
+ return p .awaitingSequences . getCurUuID ()
695
720
}
696
721
697
722
func (p * Pinger ) processPacket (recv * packet ) error {
@@ -744,15 +769,16 @@ func (p *Pinger) processPacket(recv *packet) error {
744
769
inPkt .Rtt = receivedAt .Sub (timestamp )
745
770
inPkt .Seq = pkt .Seq
746
771
// If we've already received this sequence, ignore it.
747
- if _ , inflight := p.awaitingSequences [* pktUUID ][pkt.Seq ]; ! inflight {
772
+ e , inflight := p .awaitingSequences .getElem (* pktUUID , pkt .Seq )
773
+ if ! inflight {
748
774
p .PacketsRecvDuplicates ++
749
775
if p .OnDuplicateRecv != nil {
750
776
p .OnDuplicateRecv (inPkt )
751
777
}
752
778
return nil
753
779
}
754
780
// remove it from the list of sequences we're waiting for so we don't get duplicates.
755
- delete ( p .awaitingSequences [ * pktUUID ], pkt . Seq )
781
+ p .awaitingSequences . removeElem ( e )
756
782
p .updateStatistics (inPkt )
757
783
default :
758
784
// Very bad, not sure how this can happen
@@ -777,7 +803,8 @@ func (p *Pinger) sendICMP(conn packetConn) error {
777
803
if err != nil {
778
804
return fmt .Errorf ("unable to marshal UUID binary: %w" , err )
779
805
}
780
- t := append (timeToBytes (time .Now ()), uuidEncoded ... )
806
+ now := time .Now ()
807
+ t := append (timeToBytes (now ), uuidEncoded ... )
781
808
if remainSize := p .Size - timeSliceLength - trackerLength ; remainSize > 0 {
782
809
t = append (t , bytes .Repeat ([]byte {1 }, remainSize )... )
783
810
}
@@ -829,13 +856,12 @@ func (p *Pinger) sendICMP(conn packetConn) error {
829
856
p .OnSend (outPkt )
830
857
}
831
858
// mark this sequence as in-flight
832
- p.awaitingSequences [ currentUUID ][ p.sequence ] = struct {}{}
859
+ p .awaitingSequences . putElem ( currentUUID , p .sequence , now )
833
860
p .PacketsSent ++
834
861
p .sequence ++
835
862
if p .sequence > 65535 {
836
863
newUUID := uuid .New ()
837
- p .trackerUUIDs = append (p .trackerUUIDs , newUUID )
838
- p .awaitingSequences [newUUID ] = make (map [int ]struct {})
864
+ p .awaitingSequences .newSeqMap (newUUID )
839
865
p .sequence = 0
840
866
}
841
867
break
0 commit comments