@@ -5,6 +5,8 @@ use std::{
5
5
net:: { IpAddr , Ipv4Addr , Ipv6Addr , UdpSocket } ,
6
6
slice,
7
7
} ;
8
+ #[ cfg( target_os = "linux" ) ]
9
+ use std:: { mem:: MaybeUninit , time:: Duration } ;
8
10
9
11
use quinn_udp:: { EcnCodepoint , RecvMeta , Transmit , UdpSocketState } ;
10
12
use socket2:: Socket ;
@@ -186,6 +188,72 @@ fn gso() {
186
188
) ;
187
189
}
188
190
191
+ #[ test]
192
+ #[ cfg( target_os = "linux" ) ]
193
+ fn receive_timestamp ( ) {
194
+ let send = UdpSocket :: bind ( ( Ipv6Addr :: LOCALHOST , 0 ) )
195
+ . or_else ( |_| UdpSocket :: bind ( ( Ipv4Addr :: LOCALHOST , 0 ) ) )
196
+ . unwrap ( ) ;
197
+ let recv = UdpSocket :: bind ( ( Ipv6Addr :: LOCALHOST , 0 ) )
198
+ . or_else ( |_| UdpSocket :: bind ( ( Ipv4Addr :: LOCALHOST , 0 ) ) )
199
+ . unwrap ( ) ;
200
+ let dst_addr = recv. local_addr ( ) . unwrap ( ) ;
201
+
202
+ let send_state = UdpSocketState :: new ( ( & send) . into ( ) ) . unwrap ( ) ;
203
+ let recv_state = UdpSocketState :: new ( ( & recv) . into ( ) ) . unwrap ( ) ;
204
+
205
+ recv_state
206
+ . set_recv_timestamping ( ( & recv) . into ( ) , true )
207
+ . expect ( "failed to set sockopt -- unsupported?" ) ;
208
+
209
+ // Reverse non-blocking flag set by `UdpSocketState` to make the test non-racy
210
+ recv. set_nonblocking ( false ) . unwrap ( ) ;
211
+
212
+ let mut buf = [ 0 ; u16:: MAX as usize ] ;
213
+ let mut meta = RecvMeta :: default ( ) ;
214
+
215
+ let mut time_start = MaybeUninit :: < libc:: timespec > :: uninit ( ) ;
216
+ let mut time_end = MaybeUninit :: < libc:: timespec > :: uninit ( ) ;
217
+
218
+ // Use the actual CLOCK_REALTIME clock source in linux, rather than relying on SystemTime
219
+ let errno = unsafe { libc:: clock_gettime ( libc:: CLOCK_REALTIME , time_start. as_mut_ptr ( ) ) } ;
220
+ assert_eq ! ( errno, 0 , "failed to read CLOCK_REALTIME" ) ;
221
+ let time_start = unsafe { time_start. assume_init ( ) } ;
222
+ let time_start = Duration :: new ( time_start. tv_sec as u64 , time_start. tv_nsec as u32 ) ;
223
+
224
+ send_state
225
+ . try_send (
226
+ ( & send) . into ( ) ,
227
+ & Transmit {
228
+ destination : dst_addr,
229
+ ecn : None ,
230
+ contents : b"hello" ,
231
+ segment_size : None ,
232
+ src_ip : None ,
233
+ } ,
234
+ )
235
+ . unwrap ( ) ;
236
+
237
+ recv_state
238
+ . recv (
239
+ ( & recv) . into ( ) ,
240
+ & mut [ IoSliceMut :: new ( & mut buf) ] ,
241
+ slice:: from_mut ( & mut meta) ,
242
+ )
243
+ . unwrap ( ) ;
244
+
245
+ let errno = unsafe { libc:: clock_gettime ( libc:: CLOCK_REALTIME , time_end. as_mut_ptr ( ) ) } ;
246
+ assert_eq ! ( errno, 0 , "failed to read CLOCK_REALTIME" ) ;
247
+ let time_end = unsafe { time_end. assume_init ( ) } ;
248
+ let time_end = Duration :: new ( time_end. tv_sec as u64 , time_end. tv_nsec as u32 ) ;
249
+
250
+ // Note: there's a very slim chance that the clock could jump (via ntp, etc.) and throw off
251
+ // these two checks, but it's still better to validate the timestamp result with that risk
252
+ let timestamp = meta. timestamp . unwrap ( ) ;
253
+ assert ! ( time_start <= timestamp) ;
254
+ assert ! ( timestamp <= time_end) ;
255
+ }
256
+
189
257
fn test_send_recv ( send : & Socket , recv : & Socket , transmit : Transmit ) {
190
258
let send_state = UdpSocketState :: new ( send. into ( ) ) . unwrap ( ) ;
191
259
let recv_state = UdpSocketState :: new ( recv. into ( ) ) . unwrap ( ) ;
0 commit comments