Skip to content

Commit 5e92641

Browse files
committed
Refactor packet tracking.
Signed-off-by: SuperQ <superq@gmail.com>
1 parent 20aa09d commit 5e92641

3 files changed

Lines changed: 148 additions & 49 deletions

File tree

packet_tracking.go

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
package probing
2+
3+
import (
4+
"sync"
5+
"time"
6+
7+
"github.com/google/uuid"
8+
)
9+
10+
type PacketTracker struct {
11+
currentUUID uuid.UUID
12+
packets map[uuid.UUID]PacketSequence
13+
sequence int
14+
nextSequence int
15+
timeout time.Duration
16+
timeoutCh chan *inFlightPacket
17+
18+
mutex sync.RWMutex
19+
}
20+
21+
type PacketSequence struct {
22+
packets map[int]inFlightPacket
23+
}
24+
25+
func (ps PacketSequence) NewInflightPacket(sequence int) {
26+
ps.packets[sequence] = inFlightPacket{}
27+
}
28+
29+
func (ps PacketSequence) GetPacket(sequence int) (inFlightPacket, bool) {
30+
packet, ok := ps.packets[sequence]
31+
return packet, ok
32+
}
33+
34+
func (ps PacketSequence) RemovePacket(sequence int) {
35+
delete(ps.packets, sequence)
36+
}
37+
38+
type inFlightPacket struct {
39+
timeoutTimer *time.Timer
40+
}
41+
42+
func newPacketTracker(t time.Duration) *PacketTracker {
43+
firstUUID := uuid.New()
44+
var firstSequence = map[uuid.UUID]map[int]struct{}{}
45+
firstSequence[firstUUID] = make(map[int]struct{})
46+
47+
return &PacketTracker{
48+
packets: map[uuid.UUID]PacketSequence{},
49+
sequence: 0,
50+
timeout: t,
51+
}
52+
}
53+
54+
func (t *PacketTracker) AddPacket() int {
55+
t.mutex.Lock()
56+
defer t.mutex.Unlock()
57+
58+
if t.nextSequence > 65535 {
59+
newUUID := uuid.New()
60+
t.packets[newUUID] = PacketSequence{}
61+
t.currentUUID = newUUID
62+
t.nextSequence = 0
63+
}
64+
65+
t.sequence = t.nextSequence
66+
t.packets[t.currentUUID].NewInflightPacket(t.sequence)
67+
// if t.timeout > 0 {
68+
// t.packets[t.currentUUID][t.sequence].timeoutTimer = time.Timer(t.timeout)
69+
// }
70+
t.nextSequence++
71+
return t.sequence
72+
}
73+
74+
// DeletePacket removes a packet from the tracker.
75+
func (t *PacketTracker) DeletePacket(u uuid.UUID, seq int) {
76+
t.mutex.Lock()
77+
defer t.mutex.Unlock()
78+
79+
if t.hasPacket(u, seq) {
80+
// if _, ok := t.packets[u].GetPacket(seq) ; ok != nil {
81+
// t.packets[u][seq].timeoutTimer.Stop()
82+
// }
83+
t.packets[u].RemovePacket(seq)
84+
}
85+
}
86+
87+
func (t *PacketTracker) hasPacket(u uuid.UUID, seq int) bool {
88+
inflight, ok := t.packets[u]
89+
if ok == false {
90+
return ok
91+
}
92+
_, ok = inflight.GetPacket(seq)
93+
return ok
94+
}
95+
96+
// HasPacket checks the tracker to see if it's currently tracking a packet.
97+
func (t *PacketTracker) HasPacket(u uuid.UUID, seq int) bool {
98+
t.mutex.RLock()
99+
defer t.mutex.Unlock()
100+
101+
return t.hasPacket(u, seq)
102+
}
103+
104+
func (t *PacketTracker) HasUUID(u uuid.UUID) bool {
105+
_, hasUUID := t.packets[u]
106+
return hasUUID
107+
}
108+
109+
func (t *PacketTracker) CurrentUUID() uuid.UUID {
110+
// t.mutex.RLock()
111+
// defer t.mutex.Unlock()
112+
113+
return t.currentUUID
114+
}

ping.go

Lines changed: 24 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -86,27 +86,22 @@ var (
8686
// New returns a new Pinger struct pointer.
8787
func New(addr string) *Pinger {
8888
r := rand.New(rand.NewSource(getSeed()))
89-
firstUUID := uuid.New()
90-
var firstSequence = map[uuid.UUID]map[int]struct{}{}
91-
firstSequence[firstUUID] = make(map[int]struct{})
9289
return &Pinger{
9390
Count: -1,
9491
Interval: time.Second,
9592
RecordRtts: true,
9693
Size: timeSliceLength + trackerLength,
9794
Timeout: time.Duration(math.MaxInt64),
9895

99-
addr: addr,
100-
done: make(chan interface{}),
101-
id: r.Intn(math.MaxUint16),
102-
trackerUUIDs: []uuid.UUID{firstUUID},
103-
ipaddr: nil,
104-
ipv4: false,
105-
network: "ip",
106-
protocol: "udp",
107-
awaitingSequences: firstSequence,
108-
TTL: 64,
109-
logger: StdLogger{Logger: log.New(log.Writer(), log.Prefix(), log.Flags())},
96+
addr: addr,
97+
done: make(chan interface{}),
98+
id: r.Intn(math.MaxUint16),
99+
ipaddr: nil,
100+
ipv4: false,
101+
network: "ip",
102+
protocol: "udp",
103+
TTL: 64,
104+
logger: StdLogger{Logger: log.New(log.Writer(), log.Prefix(), log.Flags())},
110105
}
111106
}
112107

@@ -142,6 +137,9 @@ type Pinger struct {
142137
// Number of duplicate packets received
143138
PacketsRecvDuplicates int
144139

140+
// Per-packet timeout
141+
PacketTimeout time.Duration
142+
145143
// Round trip time statistics
146144
minRtt time.Duration
147145
maxRtt time.Duration
@@ -188,14 +186,11 @@ type Pinger struct {
188186
ipaddr *net.IPAddr
189187
addr string
190188

191-
// trackerUUIDs is the list of UUIDs being used for sending packets.
192-
trackerUUIDs []uuid.UUID
193-
194189
ipv4 bool
195190
id int
196191
sequence int
197-
// awaitingSequences are in-flight sequence numbers we keep track of to help remove duplicate receipts
198-
awaitingSequences map[uuid.UUID]map[int]struct{}
192+
// tracker is a PacketTrackrer of UUIDs and sequence numbers.
193+
tracker *PacketTracker
199194
// network is one of "ip", "ip4", or "ip6".
200195
network string
201196
// protocol is "icmp" or "udp".
@@ -412,6 +407,9 @@ func (p *Pinger) Run() error {
412407
if err != nil {
413408
return err
414409
}
410+
411+
p.tracker = newPacketTracker(p.PacketTimeout)
412+
415413
if conn, err = p.listen(); err != nil {
416414
return err
417415
}
@@ -614,19 +612,12 @@ func (p *Pinger) getPacketUUID(pkt []byte) (*uuid.UUID, error) {
614612
return nil, fmt.Errorf("error decoding tracking UUID: %w", err)
615613
}
616614

617-
for _, item := range p.trackerUUIDs {
618-
if item == packetUUID {
619-
return &packetUUID, nil
620-
}
615+
if p.tracker.HasUUID(packetUUID) {
616+
return &packetUUID, nil
621617
}
622618
return nil, nil
623619
}
624620

625-
// getCurrentTrackerUUID grabs the latest tracker UUID.
626-
func (p *Pinger) getCurrentTrackerUUID() uuid.UUID {
627-
return p.trackerUUIDs[len(p.trackerUUIDs)-1]
628-
}
629-
630621
func (p *Pinger) processPacket(recv *packet) error {
631622
receivedAt := time.Now()
632623
var proto int
@@ -675,15 +666,15 @@ func (p *Pinger) processPacket(recv *packet) error {
675666
inPkt.Rtt = receivedAt.Sub(timestamp)
676667
inPkt.Seq = pkt.Seq
677668
// If we've already received this sequence, ignore it.
678-
if _, inflight := p.awaitingSequences[*pktUUID][pkt.Seq]; !inflight {
669+
if !p.tracker.HasPacket(*pktUUID, pkt.Seq) {
679670
p.PacketsRecvDuplicates++
680671
if p.OnDuplicateRecv != nil {
681672
p.OnDuplicateRecv(inPkt)
682673
}
683674
return nil
684675
}
685-
// remove it from the list of sequences we're waiting for so we don't get duplicates.
686-
delete(p.awaitingSequences[*pktUUID], pkt.Seq)
676+
// Remove it from the list of sequences we're waiting for so we don't get duplicates.
677+
p.tracker.DeletePacket(*pktUUID, pkt.Seq)
687678
p.updateStatistics(inPkt)
688679
default:
689680
// Very bad, not sure how this can happen
@@ -704,7 +695,7 @@ func (p *Pinger) sendICMP(conn packetConn) error {
704695
dst = &net.UDPAddr{IP: p.ipaddr.IP, Zone: p.ipaddr.Zone}
705696
}
706697

707-
currentUUID := p.getCurrentTrackerUUID()
698+
currentUUID := p.tracker.CurrentUUID()
708699
uuidEncoded, err := currentUUID.MarshalBinary()
709700
if err != nil {
710701
return fmt.Errorf("unable to marshal UUID binary: %w", err)
@@ -752,15 +743,8 @@ func (p *Pinger) sendICMP(conn packetConn) error {
752743
handler(outPkt)
753744
}
754745
// mark this sequence as in-flight
755-
p.awaitingSequences[currentUUID][p.sequence] = struct{}{}
746+
p.sequence = p.tracker.AddPacket()
756747
p.PacketsSent++
757-
p.sequence++
758-
if p.sequence > 65535 {
759-
newUUID := uuid.New()
760-
p.trackerUUIDs = append(p.trackerUUIDs, newUUID)
761-
p.awaitingSequences[newUUID] = make(map[int]struct{})
762-
p.sequence = 0
763-
}
764748
break
765749
}
766750

ping_test.go

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ func TestProcessPacket(t *testing.T) {
2222
shouldBe1++
2323
}
2424

25-
currentUUID := pinger.getCurrentTrackerUUID()
25+
currentUUID := pinger.tracker.CurrentUUID()
2626
uuidEncoded, err := currentUUID.MarshalBinary()
2727
if err != nil {
2828
t.Fatalf("unable to marshal UUID binary: %s", err)
@@ -37,7 +37,7 @@ func TestProcessPacket(t *testing.T) {
3737
Seq: pinger.sequence,
3838
Data: data,
3939
}
40-
pinger.awaitingSequences[currentUUID][pinger.sequence] = struct{}{}
40+
pinger.tracker.AddPacket()
4141

4242
msg := &icmp.Message{
4343
Type: ipv4.ICMPTypeEchoReply,
@@ -66,7 +66,7 @@ func TestProcessPacket_IgnoreNonEchoReplies(t *testing.T) {
6666
shouldBe0++
6767
}
6868

69-
currentUUID, err := pinger.getCurrentTrackerUUID().MarshalBinary()
69+
currentUUID, err := pinger.tracker.CurrentUUID().MarshalBinary()
7070
if err != nil {
7171
t.Fatalf("unable to marshal UUID binary: %s", err)
7272
}
@@ -109,7 +109,7 @@ func TestProcessPacket_IDMismatch(t *testing.T) {
109109
shouldBe0++
110110
}
111111

112-
currentUUID, err := pinger.getCurrentTrackerUUID().MarshalBinary()
112+
currentUUID, err := pinger.tracker.CurrentUUID().MarshalBinary()
113113
if err != nil {
114114
t.Fatalf("unable to marshal UUID binary: %s", err)
115115
}
@@ -189,7 +189,7 @@ func TestProcessPacket_LargePacket(t *testing.T) {
189189
pinger := makeTestPinger()
190190
pinger.Size = 4096
191191

192-
currentUUID, err := pinger.getCurrentTrackerUUID().MarshalBinary()
192+
currentUUID, err := pinger.tracker.CurrentUUID().MarshalBinary()
193193
if err != nil {
194194
t.Fatalf("unable to marshal UUID binary: %s", err)
195195
}
@@ -484,6 +484,7 @@ func makeTestPinger() *Pinger {
484484
pinger.protocol = "icmp"
485485
pinger.id = 123
486486
pinger.Size = 0
487+
pinger.tracker = newPacketTracker(time.Second * 5)
487488

488489
return pinger
489490
}
@@ -542,7 +543,7 @@ func BenchmarkProcessPacket(b *testing.B) {
542543
pinger.protocol = "ip4:icmp"
543544
pinger.id = 123
544545

545-
currentUUID, err := pinger.getCurrentTrackerUUID().MarshalBinary()
546+
currentUUID, err := pinger.tracker.CurrentUUID().MarshalBinary()
546547
if err != nil {
547548
b.Fatalf("unable to marshal UUID binary: %s", err)
548549
}
@@ -591,7 +592,7 @@ func TestProcessPacket_IgnoresDuplicateSequence(t *testing.T) {
591592
dups++
592593
}
593594

594-
currentUUID := pinger.getCurrentTrackerUUID()
595+
currentUUID := pinger.tracker.CurrentUUID()
595596
uuidEncoded, err := currentUUID.MarshalBinary()
596597
if err != nil {
597598
t.Fatalf("unable to marshal UUID binary: %s", err)
@@ -606,8 +607,8 @@ func TestProcessPacket_IgnoresDuplicateSequence(t *testing.T) {
606607
Seq: 0,
607608
Data: data,
608609
}
609-
// register the sequence as sent
610-
pinger.awaitingSequences[currentUUID][0] = struct{}{}
610+
// Register the sequence as sent.
611+
pinger.tracker.AddPacket()
611612

612613
msg := &icmp.Message{
613614
Type: ipv4.ICMPTypeEchoReply,

0 commit comments

Comments
 (0)