@@ -5,6 +5,8 @@ use std::{
55 net:: { IpAddr , Ipv4Addr , Ipv6Addr , UdpSocket } ,
66 slice,
77} ;
8+ #[ cfg( target_os = "linux" ) ]
9+ use std:: { mem:: MaybeUninit , time:: Duration } ;
810
911use quinn_udp:: { EcnCodepoint , RecvMeta , Transmit , UdpSocketState } ;
1012use socket2:: Socket ;
@@ -186,6 +188,72 @@ fn gso() {
186188 ) ;
187189}
188190
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+
189257fn test_send_recv ( send : & Socket , recv : & Socket , transmit : Transmit ) {
190258 let send_state = UdpSocketState :: new ( send. into ( ) ) . unwrap ( ) ;
191259 let recv_state = UdpSocketState :: new ( recv. into ( ) ) . unwrap ( ) ;
0 commit comments