1
1
use std:: {
2
+ io,
2
3
net:: SocketAddr ,
3
4
ops:: Add ,
4
5
time:: { Duration , SystemTime } ,
@@ -16,6 +17,8 @@ use crate::Stats;
16
17
17
18
const TRACK_PINGS_SIZE : usize = 1024 ;
18
19
20
+ /// Pinger struct to send and receive GTPv1-U packets. All fields are private, so you can only
21
+ /// create a new instance using the `new` method.
19
22
#[ derive( Debug ) ]
20
23
pub struct Pinger {
21
24
// UDP socket to send and receive GTPv1-U packets
@@ -30,34 +33,50 @@ pub struct Pinger {
30
33
send_buf : Vec < u8 > ,
31
34
// Internal buffer to store received data
32
35
recv_buf : [ u8 ; 1024 ] ,
33
- // number of sent packets (including lost)
36
+ // Number of sent packets (including lost)
34
37
sent : u64 ,
35
- // number of received packets
38
+ // Number of received packets
36
39
received : u64 ,
37
- // track times needed to send packets for statistics
40
+ // Array to track times needed to send packets for statistics
38
41
send_times : [ f64 ; TRACK_PINGS_SIZE ] ,
39
- // track received packets to detect duplicates
42
+ // Array to track received packets to detect duplicates
40
43
received_packets : [ u64 ; TRACK_PINGS_SIZE ] ,
41
- // number of duplicate packets
44
+ // Number of duplicate packets
42
45
duplicate_packets : u64 ,
43
- // number of refused packets
46
+ // Number of refused packets
44
47
refused_packets : u64 ,
45
- // epoch time of the start of the operation, in milliseconds
48
+ // Epoch time of the start of the operation, in milliseconds
46
49
epoch_ms : u128 ,
47
- // start time of the command for statistics
50
+ // Start time of the command for statistics
48
51
start_time : Instant ,
49
- // last ping time to calculate RTT
52
+ // Last ping time to calculate RTT
50
53
last_ping_time : Instant ,
51
- // last receive time to calculate RTT
54
+ // Last receive time to calculate RTT
52
55
last_receive_time : Instant ,
53
- // interval between pings in milliseconds
54
- interval_ms : Duration ,
55
- // number of pings to send
56
+ // Interval between pings in milliseconds
57
+ interval : Duration ,
58
+ // Time to wait for a response in milliseconds
59
+ timeout : Duration ,
60
+ // Number of pings to send
56
61
count : u64 ,
57
62
}
58
63
59
64
impl Pinger {
60
- pub async fn new ( peer : SocketAddr , count : u64 , interval_ms : u64 ) -> Result < Self > {
65
+ /// Create a new Pinger instance.
66
+ ///
67
+ /// # Arguments
68
+ ///
69
+ /// - `peer`: [`SocketAddr`] of the peer to ping
70
+ /// - `count`: Number of pings to send
71
+ /// - `interval_ms`: Interval between pings in milliseconds
72
+ /// - `timeout_ms`: Time to wait for a response, in milliseconds. 0 means wait (almost)
73
+ /// indefinitely.
74
+ pub async fn new (
75
+ peer : SocketAddr ,
76
+ count : u64 ,
77
+ interval_ms : u64 ,
78
+ timeout_ms : u64 ,
79
+ ) -> Result < Self > {
61
80
let mut packet = Gtpv1Header {
62
81
msgtype : ECHO_REQUEST ,
63
82
sequence_number : Some ( 0 ) ,
@@ -73,8 +92,8 @@ impl Pinger {
73
92
let pinger = Pinger {
74
93
socket : UdpSocket :: bind ( "0.0.0.0:0" ) . await ?,
75
94
peer,
76
- seq : 0 ,
77
95
packet,
96
+ seq : 0 ,
78
97
send_buf : Vec :: with_capacity ( header_size as usize ) ,
79
98
recv_buf : [ 0 ; 1024 ] ,
80
99
sent : 0 ,
@@ -87,13 +106,17 @@ impl Pinger {
87
106
start_time : now,
88
107
last_ping_time : now,
89
108
last_receive_time : now,
90
- interval_ms : Duration :: from_millis ( interval_ms) ,
109
+ interval : Duration :: from_millis ( interval_ms) ,
110
+ timeout : Duration :: from_millis ( if timeout_ms == 0 { u64:: MAX } else { timeout_ms } ) ,
91
111
count,
92
112
} ;
93
113
debug ! ( "Pinger created for {}" , pinger. peer) ;
94
114
Ok ( pinger)
95
115
}
96
116
117
+ /// Send `count` pings to the peer and wait for a response. This method will send a ping, wait
118
+ /// for a response, and then sleep for `interval` milliseconds. If a response is not received
119
+ /// within `timeout` milliseconds, the method will continue to the next ping.
97
120
pub async fn ping ( & mut self ) -> Result < ( ) > {
98
121
debug ! ( "Start pinging for {}" , self . peer) ;
99
122
for _ in 0 ..self . count {
@@ -114,7 +137,7 @@ impl Pinger {
114
137
) ;
115
138
}
116
139
Err ( e) => {
117
- if e. kind ( ) == std :: io:: ErrorKind :: ConnectionRefused {
140
+ if e. kind ( ) == io:: ErrorKind :: ConnectionRefused {
118
141
error ! ( "Connection refused" ) ;
119
142
}
120
143
self . refused_packets += 1 ;
@@ -127,8 +150,12 @@ impl Pinger {
127
150
self . last_ping_time = Instant :: now ( ) ;
128
151
129
152
trace ! ( "Waiting for response" ) ;
153
+ // To implement the timeout, we use tokio::select! macro. This macro allows us to
154
+ // wait for multiple futures to complete, and then execute the branch that completes
155
+ // first. In this case, we wait for the timeout to expire or for a response to be
156
+ // received.
130
157
tokio:: select! {
131
- _ = async { sleep_until( Instant :: now( ) . add( self . interval_ms ) ) . await } => {
158
+ _ = async { sleep_until( Instant :: now( ) . add( self . timeout ) ) . await } => {
132
159
debug!( "Timed out" ) ;
133
160
}
134
161
result = self . socket. recv_from( & mut self . recv_buf) => {
@@ -174,12 +201,12 @@ impl Pinger {
174
201
}
175
202
self . received += 1 ;
176
203
177
- sleep( self . interval_ms ) . await ;
204
+ sleep( self . interval ) . await ;
178
205
} ,
179
206
Err ( _) => {
180
207
error!( "Port closed" ) ;
181
208
self . seq += 1 ;
182
- sleep( self . interval_ms ) . await ;
209
+ sleep( self . interval ) . await ;
183
210
continue ;
184
211
}
185
212
}
@@ -201,6 +228,7 @@ impl Pinger {
201
228
Ok ( ( ) )
202
229
}
203
230
231
+ /// Calculate statistics from the pings sent and received.
204
232
pub fn calculate_stats ( & self ) -> Stats {
205
233
let mut min = 0f64 ;
206
234
let mut max = 0f64 ;
0 commit comments