@@ -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,7 @@ pub fn parse_packets(
130132 }
131133 Ok ( packet) => {
132134 if let Some ( headers) = get_sniffable_headers ( & packet. data , my_link_type) {
135+ let headers_clone = headers. clone ( ) ;
133136 #[ allow( clippy:: useless_conversion) ]
134137 let secs = i64:: from ( packet. header . ts . tv_sec ) ;
135138 #[ allow( clippy:: useless_conversion) ]
@@ -167,14 +170,32 @@ pub fn parse_packets(
167170 continue ;
168171 } ;
169172
170- // save this packet to PCAP file
173+ let mut latency = None ;
174+ if key. protocol == crate :: Protocol :: TCP {
175+ if let ( Some ( sport) , Some ( dport) ) = ( key. sport , key. dport ) {
176+ if let Some ( TransportHeader :: Tcp ( tcp) ) = headers_clone. transport {
177+ if tcp. syn && !tcp. ack {
178+ pending_syns. insert (
179+ ( key. source , sport, key. dest , dport) ,
180+ next_packet_timestamp,
181+ ) ;
182+ } else if tcp. syn && tcp. ack {
183+ let syn_key = ( key. dest , dport, key. source , sport) ;
184+ if let Some ( syn_ts) = pending_syns. get ( & syn_key) . copied ( ) {
185+ latency = compute_rtt ( syn_ts, next_packet_timestamp) ;
186+ pending_syns. remove ( & syn_key) ;
187+ }
188+ }
189+ }
190+ }
191+ }
192+
171193 if let Some ( file) = savefile. as_mut ( ) {
172194 file. write ( & Packet {
173195 header : & packet. header ,
174196 data : & packet. data ,
175197 } ) ;
176198 }
177- // update the map
178199 let ( traffic_direction, service) = modify_or_insert_in_map (
179200 & mut info_traffic_msg,
180201 & key,
@@ -184,6 +205,7 @@ pub fn parse_packets(
184205 arp_type,
185206 exchanged_bytes,
186207 ip_blacklist,
208+ latency,
187209 ) ;
188210
189211 info_traffic_msg
@@ -553,3 +575,68 @@ struct PacketOwned {
553575 header : PacketHeader ,
554576 data : Box < [ u8 ] > ,
555577}
578+
579+ fn compute_rtt ( syn_ts : Timestamp , synack_ts : Timestamp ) -> Option < Duration > {
580+ let syn_us = syn_ts. to_usecs ( ) ?;
581+ let ack_us = synack_ts. to_usecs ( ) ?;
582+ let diff = ack_us - syn_us;
583+ if diff >= 0 {
584+ Some ( Duration :: from_micros ( diff as u64 ) )
585+ } else {
586+ None
587+ }
588+ }
589+
590+ #[ cfg( test) ]
591+ mod tests {
592+ use super :: * ;
593+
594+ #[ test]
595+ fn test_compute_rtt_basic ( ) {
596+ let syn = Timestamp :: new ( 100 , 0 ) ;
597+ let synack = Timestamp :: new ( 100 , 5000 ) ;
598+ let rtt = compute_rtt ( syn, synack) ;
599+ assert_eq ! ( rtt, Some ( Duration :: from_micros( 5000 ) ) ) ;
600+ }
601+
602+ #[ test]
603+ fn test_compute_rtt_crossing_second ( ) {
604+ let syn = Timestamp :: new ( 100 , 999999 ) ;
605+ let synack = Timestamp :: new ( 101 , 1 ) ;
606+ let rtt = compute_rtt ( syn, synack) ;
607+ assert_eq ! ( rtt, Some ( Duration :: from_micros( 2 ) ) ) ;
608+ }
609+
610+ #[ test]
611+ fn test_compute_rtt_large_gap ( ) {
612+ let syn = Timestamp :: new ( 100 , 0 ) ;
613+ let synack = Timestamp :: new ( 105 , 500000 ) ;
614+ let rtt = compute_rtt ( syn, synack) ;
615+ assert_eq ! ( rtt, Some ( Duration :: from_millis( 5500 ) ) ) ;
616+ }
617+
618+ #[ test]
619+ fn test_compute_rtt_zero ( ) {
620+ let syn = Timestamp :: new ( 100 , 500 ) ;
621+ let synack = Timestamp :: new ( 100 , 500 ) ;
622+ let rtt = compute_rtt ( syn, synack) ;
623+ assert_eq ! ( rtt, Some ( Duration :: from_micros( 0 ) ) ) ;
624+ }
625+
626+ #[ test]
627+ fn test_compute_rtt_negative_returns_none ( ) {
628+ let syn = Timestamp :: new ( 101 , 0 ) ;
629+ let synack = Timestamp :: new ( 100 , 0 ) ;
630+ let rtt = compute_rtt ( syn, synack) ;
631+ assert_eq ! ( rtt, None ) ;
632+ }
633+
634+ #[ test]
635+ fn test_compute_rtt_display_ms ( ) {
636+ let syn = Timestamp :: new ( 0 , 0 ) ;
637+ let synack = Timestamp :: new ( 0 , 25000 ) ;
638+ let rtt = compute_rtt ( syn, synack) . unwrap ( ) ;
639+ let display = format ! ( "{:.1} ms" , rtt. as_secs_f64( ) * 1000.0 ) ;
640+ assert_eq ! ( display, "25.0 ms" ) ;
641+ }
642+ }
0 commit comments