diff options
author | cyqsimon <28627918+cyqsimon@users.noreply.github.com> | 2023-11-01 01:44:27 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-11-01 01:44:27 +0800 |
commit | aef30c9beecad10eff315ee97bfc69e1c4a4f88d (patch) | |
tree | ca1e924ee551078598a3a5e524a6de66f573cf1f | |
parent | 62c0fbf12811ea2e168298e6286f8e0706e2adc7 (diff) |
Log unresolved processes in more detail + general refactor (#318)
* Refactor & reorganisation of `get_proc_name`
* Log both src & dst when we fail to resolve connection to process
- Per recommendation in https://github.com/imsnif/bandwhich/issues/196#issuecomment-1763278674
-rw-r--r-- | src/display/ui_state.rs | 150 | ||||
-rw-r--r-- | src/network/connection.rs | 28 |
2 files changed, 99 insertions, 79 deletions
diff --git a/src/display/ui_state.rs b/src/display/ui_state.rs index e88cde1..d03b8c1 100644 --- a/src/display/ui_state.rs +++ b/src/display/ui_state.rs @@ -1,5 +1,4 @@ use std::{ - cell::RefCell, cmp, collections::{HashMap, HashSet, VecDeque}, hash::Hash, @@ -90,71 +89,10 @@ pub struct UIState { pub remote_addresses_map: HashMap<IpAddr, NetworkData>, pub connections_map: HashMap<Connection, ConnectionData>, /// Used for reducing logging noise. - known_orphan_sockets: RefCell<VecDeque<LocalSocket>>, + known_orphan_sockets: VecDeque<LocalSocket>, } impl UIState { - fn get_proc_name<'a>( - &self, - connections_to_procs: &'a HashMap<LocalSocket, String>, - local_socket: &LocalSocket, - ) -> Option<&'a String> { - let name = connections_to_procs - // direct match - .get(local_socket) - // IPv4-mapped IPv6 addresses - .or_else(|| { - let swapped: IpAddr = match local_socket.ip { - IpAddr::V4(v4) => v4.to_ipv6_mapped().into(), - IpAddr::V6(v6) => v6.to_ipv4_mapped()?.into(), - }; - connections_to_procs.get(&LocalSocket { - ip: swapped, - ..*local_socket - }) - }) - // address unspecified - .or_else(|| { - connections_to_procs.get(&LocalSocket { - ip: Ipv4Addr::UNSPECIFIED.into(), - ..*local_socket - }) - }) - .or_else(|| { - connections_to_procs.get(&LocalSocket { - ip: Ipv6Addr::UNSPECIFIED.into(), - ..*local_socket - }) - }); - - if name.is_none() { - let mut orphans = self.known_orphan_sockets.borrow_mut(); - // only log each orphan connection once - if !orphans.contains(local_socket) { - // newer connections go in the front so that searches are faster - // basically recency bias - orphans.push_front(*local_socket); - orphans.truncate(10_000); // arbitrary maximum backlog - - match connections_to_procs.iter().find( - |(&LocalSocket { port, protocol, .. }, _)| { - port == local_socket.port && protocol == local_socket.protocol - }, - ) { - Some((lookalike, name)) => { - mt_log!( - warn, - r#""{name}" owns a similar looking connection, but its local ip doesn't match."# - ); - mt_log!(warn, "Looking for: {local_socket}; found: {lookalike}"); - } - None => mt_log!(warn, "Cannot determine which process owns {local_socket}."), - }; - } - } - - name - } pub fn update( &mut self, connections_to_procs: HashMap<LocalSocket, String>, @@ -197,18 +135,46 @@ impl UIState { total_bytes_downloaded += connection_info.total_bytes_downloaded; total_bytes_uploaded += connection_info.total_bytes_uploaded; - let data_for_process = if let Some(process_name) = - self.get_proc_name(connections_to_procs, &connection.local_socket) - { - connection_data.process_name = process_name.clone(); - processes - .entry(connection_data.process_name.clone()) - .or_default() - } else { - connection_data.process_name = String::from("<UNKNOWN>"); - processes - .entry(connection_data.process_name.clone()) - .or_default() + let data_for_process = { + let local_socket = connection.local_socket; + let process_name = get_proc_name(connections_to_procs, &local_socket); + + // only log each orphan connection once + if process_name.is_none() && !self.known_orphan_sockets.contains(&local_socket) + { + // newer connections go in the front so that searches are faster + // basically recency bias + self.known_orphan_sockets.push_front(local_socket); + self.known_orphan_sockets.truncate(10_000); // arbitrary maximum backlog + + match connections_to_procs + .iter() + .find(|(&LocalSocket { port, protocol, .. }, _)| { + port == local_socket.port && protocol == local_socket.protocol + }) + .and_then(|(local_conn_lookalike, name)| { + network_utilization + .connections + .keys() + .find(|conn| &conn.local_socket == local_conn_lookalike) + .map(|conn| (conn, name)) + }) { + Some((lookalike, name)) => { + mt_log!( + warn, + r#""{name}" owns a similar looking connection, but its local ip doesn't match."# + ); + mt_log!(warn, "Looking for: {connection:?}; found: {lookalike:?}"); + } + None => { + mt_log!(warn, "Cannot determine which process owns {connection:?}"); + } + }; + } + + let process_display_name = process_name.unwrap_or("<UNKNOWN>").to_owned(); + connection_data.process_name = process_display_name.clone(); + processes.entry(process_display_name).or_default() }; data_for_process.total_bytes_downloaded += connection_info.total_bytes_downloaded; @@ -252,6 +218,40 @@ impl UIState { } } +fn get_proc_name<'a>( + connections_to_procs: &'a HashMap<LocalSocket, String>, + local_socket: &LocalSocket, +) -> Option<&'a str> { + connections_to_procs + // direct match + .get(local_socket) + // IPv4-mapped IPv6 addresses + .or_else(|| { + let swapped: IpAddr = match local_socket.ip { + IpAddr::V4(v4) => v4.to_ipv6_mapped().into(), + IpAddr::V6(v6) => v6.to_ipv4_mapped()?.into(), + }; + connections_to_procs.get(&LocalSocket { + ip: swapped, + ..*local_socket + }) + }) + // address unspecified + .or_else(|| { + connections_to_procs.get(&LocalSocket { + ip: Ipv4Addr::UNSPECIFIED.into(), + ..*local_socket + }) + }) + .or_else(|| { + connections_to_procs.get(&LocalSocket { + ip: Ipv6Addr::UNSPECIFIED.into(), + ..*local_socket + }) + }) + .map(String::as_str) +} + fn merge_bandwidth<K, V>(self_map: &mut HashMap<K, V>, other_map: HashMap<K, V>) where K: Eq + Hash, diff --git a/src/network/connection.rs b/src/network/connection.rs index bbd323b..263ba5a 100644 --- a/src/network/connection.rs +++ b/src/network/connection.rs @@ -30,20 +30,30 @@ impl fmt::Display for Protocol { } } -#[derive(Clone, Ord, PartialOrd, PartialEq, Eq, Hash, Debug, Copy)] +#[derive(Clone, Ord, PartialOrd, PartialEq, Eq, Hash, Copy)] pub struct Socket { pub ip: IpAddr, pub port: u16, } -#[derive(PartialEq, Hash, Eq, Clone, PartialOrd, Ord, Debug, Copy)] +impl fmt::Debug for Socket { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let Socket { ip, port } = self; + match ip { + IpAddr::V4(v4) => write!(f, "{v4}:{port}"), + IpAddr::V6(v6) => write!(f, "[{v6}]:{port}"), + } + } +} + +#[derive(PartialEq, Hash, Eq, Clone, PartialOrd, Ord, Copy)] pub struct LocalSocket { pub ip: IpAddr, pub port: u16, pub protocol: Protocol, } -impl fmt::Display for LocalSocket { +impl fmt::Debug for LocalSocket { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let LocalSocket { ip, port, protocol } = self; match ip { @@ -53,12 +63,22 @@ impl fmt::Display for LocalSocket { } } -#[derive(PartialEq, Hash, Eq, Clone, PartialOrd, Ord, Debug, Copy)] +#[derive(PartialEq, Hash, Eq, Clone, PartialOrd, Ord, Copy)] pub struct Connection { pub remote_socket: Socket, pub local_socket: LocalSocket, } +impl fmt::Debug for Connection { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let Connection { + remote_socket, + local_socket, + } = self; + write!(f, "{local_socket:?} => {remote_socket:?}") + } +} + pub fn display_ip_or_host(ip: IpAddr, ip_to_host: &HashMap<IpAddr, String>) -> String { match ip_to_host.get(&ip) { Some(host) => host.clone(), |