@@ -26,7 +26,7 @@ use crate::utils::formatted_strings::get_domain_from_r_dns;
2626use crate :: utils:: types:: timestamp:: Timestamp ;
2727use async_channel:: Sender ;
2828use dns_lookup:: lookup_addr;
29- use etherparse:: { EtherType , LaxPacketHeaders } ;
29+ use etherparse:: { EtherType , LaxPacketHeaders , TransportHeader } ;
3030use pcap:: { Address , Packet , PacketHeader } ;
3131use std:: collections:: HashMap ;
3232use std:: net:: IpAddr ;
@@ -59,6 +59,8 @@ pub fn parse_packets(
5959
6060 let mut info_traffic_msg = InfoTraffic :: default ( ) ;
6161
62+ let mut pending_syns: HashMap < ( IpAddr , u16 , IpAddr , u16 ) , Timestamp > = HashMap :: new ( ) ;
63+
6264 let ( lookup_request_tx, lookup_request_rx) = std:: sync:: mpsc:: channel ( ) ;
6365 let ( lookup_result_tx, lookup_result_rx) = std:: sync:: mpsc:: channel ( ) ;
6466 let mut resolutions_state = AddressesResolutionState :: new ( lookup_request_tx, lookup_result_rx) ;
@@ -130,6 +132,10 @@ pub fn parse_packets(
130132 }
131133 Ok ( packet) => {
132134 if let Some ( headers) = get_sniffable_headers ( & packet. data , my_link_type) {
135+ let tcp_flags = match & headers. transport {
136+ Some ( TransportHeader :: Tcp ( tcp) ) => Some ( ( tcp. syn , tcp. ack ) ) ,
137+ _ => None ,
138+ } ;
133139 #[ allow( clippy:: useless_conversion) ]
134140 let secs = i64:: from ( packet. header . ts . tv_sec ) ;
135141 #[ allow( clippy:: useless_conversion) ]
@@ -167,14 +173,35 @@ pub fn parse_packets(
167173 continue ;
168174 } ;
169175
170- // save this packet to PCAP file
176+ let mut latency = None ;
177+ if let Some ( ( true , false ) ) = tcp_flags
178+ && key. protocol == crate :: Protocol :: TCP
179+ && let ( Some ( sport) , Some ( dport) ) = ( key. sport , key. dport )
180+ {
181+ pending_syns. insert (
182+ ( key. source , sport, key. dest , dport) ,
183+ next_packet_timestamp,
184+ ) ;
185+ if pending_syns. len ( ) > 4096 {
186+ pending_syns. clear ( ) ;
187+ }
188+ } else if let Some ( ( true , true ) ) = tcp_flags
189+ && key. protocol == crate :: Protocol :: TCP
190+ && let ( Some ( sport) , Some ( dport) ) = ( key. sport , key. dport )
191+ {
192+ let syn_key = ( key. dest , dport, key. source , sport) ;
193+ if let Some ( syn_ts) = pending_syns. get ( & syn_key) . copied ( ) {
194+ latency = compute_rtt ( syn_ts, next_packet_timestamp) ;
195+ pending_syns. remove ( & syn_key) ;
196+ }
197+ }
198+
171199 if let Some ( file) = savefile. as_mut ( ) {
172200 file. write ( & Packet {
173201 header : & packet. header ,
174202 data : & packet. data ,
175203 } ) ;
176204 }
177- // update the map
178205 let ( traffic_direction, service) = modify_or_insert_in_map (
179206 & mut info_traffic_msg,
180207 & key,
@@ -184,6 +211,7 @@ pub fn parse_packets(
184211 arp_type,
185212 exchanged_bytes,
186213 ip_blacklist,
214+ latency,
187215 ) ;
188216
189217 info_traffic_msg
@@ -553,3 +581,68 @@ struct PacketOwned {
553581 header : PacketHeader ,
554582 data : Box < [ u8 ] > ,
555583}
584+
585+ fn compute_rtt ( syn_ts : Timestamp , synack_ts : Timestamp ) -> Option < Duration > {
586+ let syn_us = syn_ts. to_usecs ( ) ?;
587+ let ack_us = synack_ts. to_usecs ( ) ?;
588+ let diff = ack_us - syn_us;
589+ if diff >= 0 {
590+ Some ( Duration :: from_micros ( diff as u64 ) )
591+ } else {
592+ None
593+ }
594+ }
595+
596+ #[ cfg( test) ]
597+ mod tests {
598+ use super :: * ;
599+
600+ #[ test]
601+ fn test_compute_rtt_basic ( ) {
602+ let syn = Timestamp :: new ( 100 , 0 ) ;
603+ let synack = Timestamp :: new ( 100 , 5000 ) ;
604+ let rtt = compute_rtt ( syn, synack) ;
605+ assert_eq ! ( rtt, Some ( Duration :: from_micros( 5000 ) ) ) ;
606+ }
607+
608+ #[ test]
609+ fn test_compute_rtt_crossing_second ( ) {
610+ let syn = Timestamp :: new ( 100 , 999999 ) ;
611+ let synack = Timestamp :: new ( 101 , 1 ) ;
612+ let rtt = compute_rtt ( syn, synack) ;
613+ assert_eq ! ( rtt, Some ( Duration :: from_micros( 2 ) ) ) ;
614+ }
615+
616+ #[ test]
617+ fn test_compute_rtt_large_gap ( ) {
618+ let syn = Timestamp :: new ( 100 , 0 ) ;
619+ let synack = Timestamp :: new ( 105 , 500000 ) ;
620+ let rtt = compute_rtt ( syn, synack) ;
621+ assert_eq ! ( rtt, Some ( Duration :: from_millis( 5500 ) ) ) ;
622+ }
623+
624+ #[ test]
625+ fn test_compute_rtt_zero ( ) {
626+ let syn = Timestamp :: new ( 100 , 500 ) ;
627+ let synack = Timestamp :: new ( 100 , 500 ) ;
628+ let rtt = compute_rtt ( syn, synack) ;
629+ assert_eq ! ( rtt, Some ( Duration :: from_micros( 0 ) ) ) ;
630+ }
631+
632+ #[ test]
633+ fn test_compute_rtt_negative_returns_none ( ) {
634+ let syn = Timestamp :: new ( 101 , 0 ) ;
635+ let synack = Timestamp :: new ( 100 , 0 ) ;
636+ let rtt = compute_rtt ( syn, synack) ;
637+ assert_eq ! ( rtt, None ) ;
638+ }
639+
640+ #[ test]
641+ fn test_compute_rtt_display_ms ( ) {
642+ let syn = Timestamp :: new ( 0 , 0 ) ;
643+ let synack = Timestamp :: new ( 0 , 25000 ) ;
644+ let rtt = compute_rtt ( syn, synack) . unwrap ( ) ;
645+ let display = format ! ( "{:.1} ms" , rtt. as_secs_f64( ) * 1000.0 ) ;
646+ assert_eq ! ( display, "25.0 ms" ) ;
647+ }
648+ }
0 commit comments