diff --git a/Cargo.lock b/Cargo.lock index fdcb0e8..571195c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,6 +1,6 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "aho-corasick" diff --git a/maps.py b/maps.py index 885c4af..520ee56 100644 --- a/maps.py +++ b/maps.py @@ -4,7 +4,7 @@ from functools import partial def hex2int(hex): - return int(hex.replace("0x", ""), 16) + return int(hex, 16) def parse_ipv4(rule): @@ -33,8 +33,6 @@ def parse_ipv6(rule): return {k: v} - - filter_idx_map = dict( transport={0: "tcp", 1: "udp"}, network={0: "ipv4", 1: "ipv6", 2: "icmp"}, diff --git a/oryx-tui/src/app.rs b/oryx-tui/src/app.rs index c1c528b..d47f438 100644 --- a/oryx-tui/src/app.rs +++ b/oryx-tui/src/app.rs @@ -12,7 +12,7 @@ use std::{ use crate::{filter::Filter, help::Help}; use crate::{filter::IoChannels, notification::Notification}; -use crate::{packet::AppPacket, section::Section}; +use crate::{packet::NetworkPacket, section::Section}; pub type AppResult = std::result::Result>; @@ -31,6 +31,17 @@ pub struct DataEventHandler { pub sender: kanal::Sender<[u8; RawPacket::LEN]>, pub handler: thread::JoinHandle<()>, } +#[derive(Debug, Clone)] +pub struct Channels { + pub sender: kanal::Sender, + pub receiver: kanal::Receiver, +} +impl Channels { + pub fn new() -> Self { + let (sender, receiver) = kanal::unbounded(); + Self { sender, receiver } + } +} #[derive(Debug)] pub struct App { @@ -38,7 +49,7 @@ pub struct App { pub help: Help, pub filter: Filter, pub start_sniffing: bool, - pub packets: Arc>>, + pub packets: Arc>>, pub notifications: Vec, pub section: Section, pub data_channel_sender: kanal::Sender<[u8; RawPacket::LEN]>, @@ -54,34 +65,37 @@ impl Default for App { impl App { pub fn new() -> Self { - let packets = Arc::new(Mutex::new(Vec::with_capacity(AppPacket::LEN * 1024 * 1024))); + let net_packets: Arc>> = Arc::new(Mutex::new(Vec::with_capacity( + NetworkPacket::LEN * 1024 * 1024, + ))); + let data_channels = Channels::new(); - let (sender, receiver) = kanal::unbounded(); - - let firewall_channels = IoChannels::new(); thread::spawn({ - let packets = packets.clone(); + let net_packets = net_packets.clone(); move || loop { - if let Ok(raw_packet) = receiver.recv() { - let app_packet = AppPacket::from(raw_packet); - let mut packets = packets.lock().unwrap(); - if packets.len() == packets.capacity() { - packets.reserve(1024 * 1024); + if let Ok(raw_packet) = data_channels.receiver.recv() { + let network_packet = NetworkPacket::from(raw_packet); + let mut net_packets = net_packets.lock().unwrap(); + if net_packets.len() == net_packets.capacity() { + net_packets.reserve(1024 * 1024); } - packets.push(app_packet); + + net_packets.push(network_packet); } } }); + let firewall_channels = IoChannels::new(); + Self { running: true, help: Help::new(), filter: Filter::new(firewall_channels.clone()), start_sniffing: false, - packets: packets.clone(), + packets: net_packets.clone(), notifications: Vec::new(), - section: Section::new(packets.clone(), firewall_channels.clone()), - data_channel_sender: sender, + section: Section::new(net_packets.clone(), firewall_channels.clone()), + data_channel_sender: data_channels.sender, is_editing: false, active_popup: None, } diff --git a/oryx-tui/src/export.rs b/oryx-tui/src/export.rs index 78599ca..c03ba33 100644 --- a/oryx-tui/src/export.rs +++ b/oryx-tui/src/export.rs @@ -8,11 +8,11 @@ use crate::{ app::AppResult, packet::{ network::{IpPacket, IpProto}, - AppPacket, + NetworkPacket, }, }; -pub fn export(packets: &[AppPacket]) -> AppResult<()> { +pub fn export(packets: &[NetworkPacket]) -> AppResult<()> { let uid = unsafe { libc::geteuid() }; let oryx_export_dir = dirs::home_dir().unwrap().join("oryx"); @@ -40,7 +40,7 @@ pub fn export(packets: &[AppPacket]) -> AppResult<()> { )?; for packet in packets { match packet { - AppPacket::Arp(p) => { + NetworkPacket::Arp(p) => { writeln!( file, "{:39} {:^11} {:39} {:^11} ARP", @@ -50,7 +50,7 @@ pub fn export(packets: &[AppPacket]) -> AppResult<()> { "-" )?; } - AppPacket::Ip(packet) => match packet { + NetworkPacket::Ip(packet) => match packet { IpPacket::V4(ipv4_packet) => match ipv4_packet.proto { IpProto::Tcp(p) => { writeln!( diff --git a/oryx-tui/src/filter.rs b/oryx-tui/src/filter.rs index a62a717..04f494b 100644 --- a/oryx-tui/src/filter.rs +++ b/oryx-tui/src/filter.rs @@ -26,7 +26,7 @@ use transport::TransportFilter; use tui_big_text::{BigText, PixelSize}; use crate::{ - app::AppResult, + app::{AppResult, Channels}, ebpf::{egress::load_egress, ingress::load_ingress}, event::Event, interface::Interface, @@ -40,25 +40,12 @@ pub enum FilterChannelSignal { Kill, } -#[derive(Debug, Clone)] -pub struct Channels { - pub sender: kanal::Sender, - pub receiver: kanal::Receiver, -} - #[derive(Debug, Clone)] pub struct IoChannels { pub ingress: Channels, pub egress: Channels, } -impl Channels { - pub fn new() -> Self { - let (sender, receiver) = kanal::unbounded(); - Self { sender, receiver } - } -} - impl IoChannels { pub fn new() -> Self { Self { @@ -68,12 +55,6 @@ impl IoChannels { } } -impl Default for Channels { - fn default() -> Self { - Self::new() - } -} - impl Default for IoChannels { fn default() -> Self { Self::new() diff --git a/oryx-tui/src/filter/fuzzy.rs b/oryx-tui/src/filter/fuzzy.rs index 124b45a..a8321b4 100644 --- a/oryx-tui/src/filter/fuzzy.rs +++ b/oryx-tui/src/filter/fuzzy.rs @@ -11,20 +11,20 @@ use ratatui::{ }; use tui_input::Input; -use crate::{app::TICK_RATE, packet::AppPacket}; +use crate::{app::TICK_RATE, packet::NetworkPacket}; #[derive(Debug, Clone, Default)] pub struct Fuzzy { enabled: bool, paused: bool, pub filter: Input, - pub packets: Vec, + pub packets: Vec, pub scroll_state: TableState, pub packet_end_index: usize, } impl Fuzzy { - pub fn new(packets: Arc>>) -> Arc> { + pub fn new(packets: Arc>>) -> Arc> { let fuzzy = Arc::new(Mutex::new(Self::default())); thread::spawn({ @@ -94,21 +94,21 @@ impl Fuzzy { self.scroll_state.select(Some(i)); } - pub fn find(&mut self, packets: &[AppPacket]) { + pub fn find(&mut self, packets: &[NetworkPacket]) { self.packets = packets .iter() .copied() .filter(|p| p.to_string().contains(self.filter.value())) - .collect::>(); + .collect::>(); } - pub fn append(&mut self, packets: &[AppPacket]) { + pub fn append(&mut self, packets: &[NetworkPacket]) { self.packets.append( &mut packets .iter() .copied() .filter(|p| p.to_string().contains(self.filter.value())) - .collect::>(), + .collect::>(), ); } diff --git a/oryx-tui/src/lib.rs b/oryx-tui/src/lib.rs index 7a46ac4..f6f24f0 100644 --- a/oryx-tui/src/lib.rs +++ b/oryx-tui/src/lib.rs @@ -27,3 +27,5 @@ pub mod packet; pub mod section; pub mod dns; + +pub mod pid; diff --git a/oryx-tui/src/packet.rs b/oryx-tui/src/packet.rs index 543469d..29473a0 100644 --- a/oryx-tui/src/packet.rs +++ b/oryx-tui/src/packet.rs @@ -10,17 +10,78 @@ use network_types::ip::IpHdr; use oryx_common::{ProtoHdr, RawPacket}; use transport::{TcpPacket, UdpPacket}; +use crate::pid::IpMap; + #[derive(Debug, Copy, Clone)] -pub enum AppPacket { +pub enum NetworkPacket { Ip(IpPacket), Arp(ArpPacket), } -impl AppPacket { +impl NetworkPacket { pub const LEN: usize = mem::size_of::(); } -impl Display for AppPacket { +#[derive(Debug, Copy, Clone)] +pub struct AppPacket { + pub packet: NetworkPacket, + pub pid: Option, +} + +impl AppPacket { + pub fn from_network_packet( + netpacket: &NetworkPacket, + tcp_map: &IpMap, + udp_map: &IpMap, + ) -> Self { + let pid = match netpacket { + NetworkPacket::Ip(IpPacket::V4(ipv4packet)) => match ipv4packet.proto { + IpProto::Tcp(_) => netpacket.try_get_pid(tcp_map), + IpProto::Udp(_) => netpacket.try_get_pid(udp_map), + _ => None, + }, + _ => None, + }; + Self { + packet: *netpacket, + pid, + } + } +} + +impl NetworkPacket { + fn get_possible_keys(&self) -> Option<[String; 2]> { + match self { + NetworkPacket::Ip(IpPacket::V4(ipv4packet)) => { + let src_ip = ipv4packet.src_ip; + let dst_ip = ipv4packet.dst_ip; + match ipv4packet.proto { + IpProto::Tcp(tcp) => Some([ + format!("{}:{}_{}:{}", src_ip, tcp.src_port, dst_ip, tcp.dst_port), + format!("{}:{}_{}:{}", dst_ip, tcp.dst_port, src_ip, tcp.src_port), + ]), + IpProto::Udp(udp) => Some([ + format!("{}:{}_{}:{}", src_ip, udp.src_port, dst_ip, udp.dst_port), + format!("{}:{}_{}:{}", dst_ip, udp.dst_port, src_ip, udp.src_port), + ]), + _ => None, + } + } + _ => None, + } + } + pub fn try_get_pid(&self, ipmap: &IpMap) -> Option { + if let Some(keys) = self.get_possible_keys() { + for k in keys { + if let Some(conn) = ipmap.map.get(&k) { + return conn.pid; + } + } + } + None + } +} +impl Display for NetworkPacket { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { match self { Self::Arp(packet) => write!(f, "{}", packet), @@ -29,7 +90,7 @@ impl Display for AppPacket { } } -impl From<[u8; RawPacket::LEN]> for AppPacket { +impl From<[u8; RawPacket::LEN]> for NetworkPacket { fn from(value: [u8; RawPacket::LEN]) -> Self { let raw_packet = value.as_ptr() as *const RawPacket; match unsafe { &*raw_packet } { @@ -87,7 +148,7 @@ impl From<[u8; RawPacket::LEN]> for AppPacket { } }; - AppPacket::Ip(IpPacket::V4(Ipv4Packet { + NetworkPacket::Ip(IpPacket::V4(Ipv4Packet { src_ip, dst_ip, ihl: u8::from_be(ipv4_packet.ihl()), @@ -153,7 +214,7 @@ impl From<[u8; RawPacket::LEN]> for AppPacket { } }; - AppPacket::Ip(IpPacket::V6(Ipv6Packet { + NetworkPacket::Ip(IpPacket::V6(Ipv6Packet { traffic_class: ipv6_packet.priority(), flow_label: ipv6_packet.flow_label, payload_length: u16::from_be(ipv6_packet.payload_len), diff --git a/oryx-tui/src/pid.rs b/oryx-tui/src/pid.rs new file mode 100644 index 0000000..3aef5b2 --- /dev/null +++ b/oryx-tui/src/pid.rs @@ -0,0 +1,277 @@ +use std::collections::HashMap; +use std::fs::{self, File}; +use std::io::{Read, Seek}; + +use std::net::IpAddr; +use std::num::ParseIntError; +use std::sync::{Arc, Mutex}; +use std::thread; +use std::time::Duration; + +use itertools::chain; +use log::info; + +use crate::app::AppResult; + +#[derive(Clone, Debug)] +pub struct Connection { + ip_local: IpAddr, + port_local: u16, + ip_remote: IpAddr, + port_remote: u16, + inode: u32, + pub pid: Option, +} +impl Connection { + pub fn key(&self) -> String { + format!( + "{}:{}_{}:{}", + self.ip_local, self.port_local, self.ip_remote, self.port_remote + ) + } + fn try_get_pid(&mut self, inode_pid_map_map: &HashMap) { + self.pid = inode_pid_map_map.get(&self.inode).copied(); + } +} + +impl TryFrom<&Vec<&str>> for Connection { + type Error = Box; + fn try_from(splits: &Vec<&str>) -> Result { + let ip_local: &str = splits[1]; + let mut ip_local_port = ip_local.split(":"); + let ip_local = ip_local_port.next().unwrap(); + let port_local = ip_local_port.next().unwrap(); + + let ip_local = match decode_hex_ipv4(ip_local) { + Ok(ip) => IpAddr::try_from(ip)?, + _ => IpAddr::try_from(decode_hex_ipv6(ip_local)?)?, + }; + + let port_local = decode_hex_port(port_local)?; + + let ip_remote = splits[2]; + let mut ip_remote_port = ip_remote.split(":"); + let ip_remote = ip_remote_port.next().unwrap(); + let port_remote = ip_remote_port.next().unwrap(); + + let ip_remote = match decode_hex_ipv4(ip_remote) { + Ok(ip) => IpAddr::try_from(ip)?, + _ => IpAddr::try_from(decode_hex_ipv6(ip_remote)?)?, + }; + let port_remote = decode_hex_port(port_remote)?; + + let inode = splits[9].parse::().unwrap(); + + Ok(Self { + ip_local, + port_local, + ip_remote, + port_remote, + + inode, + pid: None, + }) + } +} + +#[derive(Clone, Debug)] +pub struct IpMap { + pub map: HashMap, +} + +impl IpMap { + pub fn new() -> Self { + Self { + map: HashMap::new(), + } + } +} + +fn build_inode_map(target_inodes: Vec) -> HashMap { + let mut res = HashMap::::new(); + // Iterate over all directories in /proc (these represent PIDs) + if let Ok(entries) = fs::read_dir("/proc") { + for entry in entries.flatten() { + let pid_str = entry.file_name().to_str().unwrap().to_string(); + if !pid_str.chars().all(char::is_numeric) { + continue; + } + let fd_dir = format!("/proc/{}/fd", pid_str); + if let Ok(fds) = fs::read_dir(&fd_dir) { + for fd in fds.flatten() { + let link_path = fd.path(); + + if let Ok(link_target) = fs::read_link(&link_path) { + // Socket inodes are typically shown as "socket:[inode]" + if let Some(inode_str) = link_target.to_str() { + if inode_str.starts_with("socket:[") && inode_str.ends_with(']') { + if let Ok(inode) = inode_str[8..inode_str.len() - 1].parse::() + { + if target_inodes.contains(&inode) { + res.insert(inode, pid_str.parse::().unwrap()); + } + } + } + } + } + } + } + } + } + + res +} +fn decode_hex_port(hex_str: &str) -> Result { + Ok(u16::from_be_bytes([ + u8::from_str_radix(&hex_str[..2], 16)?, + u8::from_str_radix(&hex_str[2..], 16)?, + ])) +} + +fn decode_hex_ipv4(hex_str: &str) -> AppResult<[u8; 4]> { + let mut bytes = Vec::new(); + // Iterate over the string in chunks of 2 characters + for i in (0..hex_str.len()).step_by(2) { + // Get the current 2-character chunk + let byte_str = &hex_str[i..i + 2]; + + let byte = u8::from_str_radix(byte_str, 16)?; + bytes.push(byte); + } + let mut res: [u8; 4] = bytes.as_slice().try_into()?; + res.reverse(); + Ok(res) +} + +fn decode_hex_ipv6(hex_str: &str) -> AppResult<[u8; 4]> { + let mut bytes = Vec::new(); + // Iterate over the string in chunks of 2 characters + for i in (0..hex_str.len()).step_by(4) { + // Get the current 2-character chunk + let byte_str = &hex_str[i..i + 4]; + + let byte = u8::from_str_radix(byte_str, 16)?; + bytes.push(byte); + } + let mut res: [u8; 4] = bytes.as_slice().try_into()?; + res.reverse(); + Ok(res) +} + +#[derive(Debug)] +pub struct ConnectionsInfo { + sender: kanal::Sender<(IpMap, IpMap)>, +} + +impl ConnectionsInfo { + pub fn get_used_inodes(tcp_map: &IpMap, udp_map: &IpMap) -> Vec { + let mut res = Vec::new(); + + for (_, conn) in tcp_map.map.iter() { + res.push(conn.inode); + } + + for (_, conn) in udp_map.map.iter() { + res.push(conn.inode); + } + res + } + pub fn new(sender: kanal::Sender<(IpMap, IpMap)>) -> Self { + Self { sender } + } + + pub fn spawn(&self) { + let tcp_map: Arc> = Arc::new(Mutex::new(IpMap::new())); + let udp_map: Arc> = Arc::new(Mutex::new(IpMap::new())); + thread::spawn({ + let tcp_map = tcp_map.clone(); + let mut fd_tcp = File::open("/proc/net/tcp").unwrap(); + let mut fd_tcp6 = File::open("/proc/net/tcp6").unwrap(); + move || { + loop { + thread::sleep(Duration::from_millis(250)); + fd_tcp.seek(std::io::SeekFrom::Start(0)).unwrap(); + fd_tcp6.seek(std::io::SeekFrom::Start(0)).unwrap(); + + let mut buffer_tcp = String::new(); + let mut buffer_tcp6 = String::new(); + fd_tcp.read_to_string(&mut buffer_tcp).unwrap(); + fd_tcp6.read_to_string(&mut buffer_tcp6).unwrap(); + + let mut lines_tcp = buffer_tcp.lines(); + let mut lines_tcp6 = buffer_tcp6.lines(); + lines_tcp.next(); //header + lines_tcp6.next(); //header + + let mut map = tcp_map.lock().unwrap(); + for line in chain(lines_tcp, lines_tcp6) { + let splits: Vec<&str> = line.split_whitespace().collect(); + + match Connection::try_from(&splits) { + Ok(conn) => { + map.map.insert(conn.key(), conn); + } + _ => {} //error!("error parsing tcp conn{:#?}", splits)}, + } + } + info!("{:#?}", map); + } + } + }); + + thread::spawn({ + let udp_map = udp_map.clone(); + + move || { + let mut fd_udp = File::open("/proc/net/udp").unwrap(); + + loop { + thread::sleep(Duration::from_millis(250)); + fd_udp.seek(std::io::SeekFrom::Start(0)).unwrap(); + let mut buffer = String::new(); + fd_udp.read_to_string(&mut buffer).unwrap(); + + let mut lines = buffer.lines(); + + lines.next(); //header + let mut map = udp_map.lock().unwrap(); + for line in lines { + let splits: Vec<&str> = line.split_whitespace().collect(); + + match Connection::try_from(&splits) { + Ok(conn) => { + map.map.insert(conn.key(), conn); + } + _ => {} //error!("error parsing udp conn {:#?}", splits), + } + } + } + } + }); + + thread::spawn({ + let tcp_map = tcp_map.clone(); + let udp_map = udp_map.clone(); + let sender = self.sender.clone(); + move || loop { + thread::sleep(Duration::from_millis(500)); + + let mut _udp_map = udp_map.lock().unwrap(); + let mut _tcp_map = tcp_map.lock().unwrap(); + + let inodes = Self::get_used_inodes(&_tcp_map, &_udp_map); + + let inode_pid_map_map = build_inode_map(inodes); + + for (_, conn) in _udp_map.map.iter_mut() { + conn.try_get_pid(&inode_pid_map_map); + } + for (_, conn) in _tcp_map.map.iter_mut() { + conn.try_get_pid(&inode_pid_map_map); + } + + sender.send((_tcp_map.clone(), _udp_map.clone())).ok(); + } + }); + } +} diff --git a/oryx-tui/src/section.rs b/oryx-tui/src/section.rs index e9db88e..b73bd9f 100644 --- a/oryx-tui/src/section.rs +++ b/oryx-tui/src/section.rs @@ -23,7 +23,7 @@ use crate::{ app::{ActivePopup, AppResult}, event::Event, filter::IoChannels, - packet::AppPacket, + packet::NetworkPacket, }; #[derive(Debug, PartialEq)] @@ -45,11 +45,13 @@ pub struct Section { impl Section { pub fn new( - packets: Arc>>, + packets: Arc>>, + firewall_chans: IoChannels, ) -> Self { Self { focused_section: FocusedSection::Inspection, + inspection: Inspection::new(packets.clone()), stats: None, alert: Alert::new(packets.clone()), diff --git a/oryx-tui/src/section/alert.rs b/oryx-tui/src/section/alert.rs index 7263f57..15f4653 100644 --- a/oryx-tui/src/section/alert.rs +++ b/oryx-tui/src/section/alert.rs @@ -9,7 +9,7 @@ use ratatui::{ use std::sync::{atomic::Ordering, Arc, Mutex}; use syn_flood::SynFlood; -use crate::packet::AppPacket; +use crate::packet::NetworkPacket; #[derive(Debug)] pub struct Alert { @@ -19,7 +19,7 @@ pub struct Alert { } impl Alert { - pub fn new(packets: Arc>>) -> Self { + pub fn new(packets: Arc>>) -> Self { Self { syn_flood: SynFlood::new(packets), flash_count: 1, diff --git a/oryx-tui/src/section/alert/syn_flood.rs b/oryx-tui/src/section/alert/syn_flood.rs index c93dfc1..6c85ea3 100644 --- a/oryx-tui/src/section/alert/syn_flood.rs +++ b/oryx-tui/src/section/alert/syn_flood.rs @@ -16,7 +16,7 @@ use ratatui::{ use crate::packet::{ network::{IpPacket, IpProto}, - AppPacket, + NetworkPacket, }; const WIN_SIZE: usize = 100_000; @@ -28,7 +28,7 @@ pub struct SynFlood { } impl SynFlood { - pub fn new(packets: Arc>>) -> Self { + pub fn new(packets: Arc>>) -> Self { let map: Arc>> = Arc::new(Mutex::new(HashMap::new())); let detected = Arc::new(AtomicBool::new(false)); @@ -60,7 +60,7 @@ impl SynFlood { app_packets[start_index..app_packets.len().saturating_sub(1)] .iter() .for_each(|packet| { - if let AppPacket::Ip(ip_packet) = packet { + if let NetworkPacket::Ip(ip_packet) = packet { if let IpPacket::V4(ipv4_packet) = ip_packet { if let IpProto::Tcp(tcp_packet) = ipv4_packet.proto { if tcp_packet.syn == 1 { diff --git a/oryx-tui/src/section/inspection.rs b/oryx-tui/src/section/inspection.rs index 9bbca58..4370ae5 100644 --- a/oryx-tui/src/section/inspection.rs +++ b/oryx-tui/src/section/inspection.rs @@ -1,6 +1,11 @@ -use std::sync::{Arc, Mutex}; +use std::{ + sync::{Arc, Mutex}, + thread, +}; use crossterm::event::{KeyCode, KeyEvent}; + +use log::info; use ratatui::{ layout::{Alignment, Constraint, Direction, Flex, Layout, Margin, Rect}, style::{Style, Stylize}, @@ -14,29 +19,55 @@ use ratatui::{ use tui_input::backend::crossterm::EventHandler; use crate::{ - app::AppResult, + app::{AppResult, Channels}, export, filter::fuzzy::{self, Fuzzy}, notification::{Notification, NotificationLevel}, packet::{ network::{IpPacket, IpProto}, - AppPacket, + AppPacket, NetworkPacket, }, + pid::{self, IpMap}, }; #[derive(Debug)] pub struct Inspection { - pub packets: Arc>>, + pub packets: Arc>>, + pub state: TableState, pub fuzzy: Arc>, pub manuall_scroll: bool, pub packet_end_index: usize, pub packet_window_size: usize, pub packet_index: Option, + tcp_map: Arc>, + udp_map: Arc>, } impl Inspection { - pub fn new(packets: Arc>>) -> Self { + pub fn new(packets: Arc>>) -> Self { + let tcp_map = Arc::new(Mutex::new(IpMap::new())); + let udp_map = Arc::new(Mutex::new(IpMap::new())); + let conn_channels = Channels::new(); + let conn_info = pid::ConnectionsInfo::new(conn_channels.sender.clone()); + conn_info.spawn(); + + thread::spawn({ + let tcp_map = tcp_map.clone(); + let udp_map = udp_map.clone(); + let connection_info_receiver = conn_channels.receiver.clone(); + move || loop { + if let Ok((rcv_tcp_map, rcv_udp_map)) = connection_info_receiver.recv() { + let mut _tcp_map = tcp_map.lock().unwrap(); + *_tcp_map = rcv_tcp_map; + drop(_tcp_map); + let mut _udp_map = udp_map.lock().unwrap(); + *_udp_map = rcv_udp_map; + drop(_udp_map); + } + } + }); + Self { packets: packets.clone(), state: TableState::default(), @@ -45,6 +76,8 @@ impl Inspection { packet_end_index: 0, packet_window_size: 0, packet_index: None, + tcp_map, + udp_map, } } @@ -134,15 +167,15 @@ impl Inspection { } KeyCode::Char('s') => { - let app_packets = self.packets.lock().unwrap(); - if app_packets.is_empty() { + let net_packets = self.packets.lock().unwrap(); + if net_packets.is_empty() { Notification::send( "There is no packets".to_string(), NotificationLevel::Info, event_sender, )?; } else { - match export::export(&app_packets) { + match export::export(&net_packets) { Ok(_) => { Notification::send( "Packets exported to ~/oryx/capture file".to_string(), @@ -179,7 +212,7 @@ impl Inspection { if i > 1 { i - 1 } else if i == 0 && self.packet_end_index > self.packet_window_size { - // shit the window by one + // shift the window by one self.packet_end_index -= 1; 0 } else { @@ -221,7 +254,10 @@ impl Inspection { pub fn render(&mut self, frame: &mut Frame, block: Rect) { let app_packets = self.packets.lock().unwrap(); + let tcp_map = self.tcp_map.lock().unwrap().clone(); + let udp_map = self.udp_map.lock().unwrap().clone(); let mut fuzzy = self.fuzzy.lock().unwrap(); + let fuzzy_packets = fuzzy.clone().packets.clone(); let pattern = fuzzy.clone(); @@ -254,10 +290,11 @@ impl Inspection { }; let widths = [ - Constraint::Min(19), // Source Address - Constraint::Length(11), // Source Port - Constraint::Min(19), // Destination Address - Constraint::Length(16), // Destination Port + Constraint::Min(15), // Source Address + Constraint::Length(12), // Source Port + Constraint::Min(20), // Destination Address + Constraint::Length(17), // Destination Port + Constraint::Length(5), // Pid Constraint::Length(8), // Protocol Constraint::Length(3), // manual scroll sign ]; @@ -327,26 +364,40 @@ impl Inspection { } } }; + // + let packets_to_display = packets_to_display + .iter() + .map(|p| AppPacket::from_network_packet(p, &tcp_map, &udp_map)) + .collect::>(); // Style the packets let packets: Vec = if fuzzy.is_enabled() & !fuzzy.filter.value().is_empty() { packets_to_display .iter() - .map(|app_packet| match app_packet { - AppPacket::Arp(packet) => Row::new(vec![ + .map(|app_packet| match app_packet.packet { + NetworkPacket::Arp(packet) => Row::new(vec![ fuzzy::highlight(pattern, packet.src_mac.to_string()).blue(), Cell::from(Line::from("-").centered()).yellow(), fuzzy::highlight(pattern, packet.dst_mac.to_string()).blue(), Cell::from(Line::from("-").centered()).yellow(), + Cell::from(Line::from("-").centered()).yellow(), fuzzy::highlight(pattern, "ARP".to_string()).cyan(), ]), - AppPacket::Ip(packet) => match packet { + NetworkPacket::Ip(packet) => match packet { IpPacket::V4(ipv4_packet) => match ipv4_packet.proto { IpProto::Tcp(p) => Row::new(vec![ fuzzy::highlight(pattern, ipv4_packet.src_ip.to_string()).blue(), fuzzy::highlight(pattern, p.src_port.to_string()).yellow(), fuzzy::highlight(pattern, ipv4_packet.dst_ip.to_string()).blue(), fuzzy::highlight(pattern, p.dst_port.to_string()).yellow(), + Cell::from( + Line::from(match app_packet.pid { + Some(pid) => pid.to_string(), + None => "-".to_string(), + }) + .centered(), + ) + .yellow(), fuzzy::highlight(pattern, "TCP".to_string()).cyan(), ]), IpProto::Udp(p) => Row::new(vec![ @@ -354,6 +405,14 @@ impl Inspection { fuzzy::highlight(pattern, p.src_port.to_string()).yellow(), fuzzy::highlight(pattern, ipv4_packet.dst_ip.to_string()).blue(), fuzzy::highlight(pattern, p.dst_port.to_string()).yellow(), + Cell::from( + Line::from(match app_packet.pid { + Some(pid) => pid.to_string(), + None => "-".to_string(), + }) + .centered(), + ) + .yellow(), fuzzy::highlight(pattern, "UDP".to_string()).cyan(), ]), IpProto::Icmp(_) => Row::new(vec![ @@ -361,6 +420,7 @@ impl Inspection { Cell::from(Line::from("-").centered()).yellow(), fuzzy::highlight(pattern, ipv4_packet.dst_ip.to_string()).blue(), Cell::from(Line::from("-").centered()).yellow(), + Cell::from(Line::from("-").centered()).yellow(), fuzzy::highlight(pattern, "ICMP".to_string()).cyan(), ]), }, @@ -370,6 +430,7 @@ impl Inspection { fuzzy::highlight(pattern, p.src_port.to_string()).yellow(), fuzzy::highlight(pattern, ipv6_packet.dst_ip.to_string()).blue(), fuzzy::highlight(pattern, p.dst_port.to_string()).yellow(), + Cell::from(Line::from("-").centered()).yellow(), fuzzy::highlight(pattern, "TCP".to_string()).cyan(), ]), IpProto::Udp(p) => Row::new(vec![ @@ -377,6 +438,7 @@ impl Inspection { fuzzy::highlight(pattern, p.src_port.to_string()).yellow(), fuzzy::highlight(pattern, ipv6_packet.dst_ip.to_string()).blue(), fuzzy::highlight(pattern, p.dst_port.to_string()).yellow(), + Cell::from(Line::from("-").centered()).yellow(), fuzzy::highlight(pattern, "UDP".to_string()).cyan(), ]), IpProto::Icmp(_) => Row::new(vec![ @@ -384,6 +446,7 @@ impl Inspection { Cell::from(Line::from("-").centered()).yellow(), fuzzy::highlight(pattern, ipv6_packet.dst_ip.to_string()).blue(), Cell::from(Line::from("-").centered()).yellow(), + Cell::from(Line::from("-").centered()).yellow(), fuzzy::highlight(pattern, "ICMP".to_string()).cyan(), ]), }, @@ -393,8 +456,8 @@ impl Inspection { } else { packets_to_display .iter() - .map(|app_packet| match app_packet { - AppPacket::Arp(packet) => Row::new(vec![ + .map(|app_packet| match app_packet.packet { + NetworkPacket::Arp(packet) => Row::new(vec![ Span::from(packet.src_mac.to_string()) .into_centered_line() .blue(), @@ -403,9 +466,10 @@ impl Inspection { .into_centered_line() .blue(), Span::from("-").into_centered_line().yellow(), + Span::from("-").into_centered_line().yellow(), Span::from("ARP".to_string()).into_centered_line().cyan(), ]), - AppPacket::Ip(packet) => match packet { + NetworkPacket::Ip(packet) => match packet { IpPacket::V4(ipv4_packet) => match ipv4_packet.proto { IpProto::Tcp(p) => Row::new(vec![ Span::from(ipv4_packet.src_ip.to_string()) @@ -420,6 +484,12 @@ impl Inspection { Span::from(p.dst_port.to_string()) .into_centered_line() .yellow(), + Span::from(match app_packet.pid { + Some(pid) => pid.to_string(), + None => "-".to_string(), + }) + .into_centered_line() + .yellow(), Span::from("TCP".to_string()).into_centered_line().cyan(), ]), IpProto::Udp(p) => Row::new(vec![ @@ -435,6 +505,12 @@ impl Inspection { Span::from(p.dst_port.to_string()) .into_centered_line() .yellow(), + Span::from(match app_packet.pid { + Some(pid) => pid.to_string(), + None => "-".to_string(), + }) + .into_centered_line() + .yellow(), Span::from("UDP".to_string()).into_centered_line().cyan(), ]), IpProto::Icmp(_) => Row::new(vec![ @@ -446,6 +522,7 @@ impl Inspection { .into_centered_line() .blue(), Span::from("-").into_centered_line().yellow(), + Span::from("-").into_centered_line().yellow(), Span::from("ICMP".to_string()).into_centered_line().cyan(), ]), }, @@ -463,6 +540,7 @@ impl Inspection { Span::from(p.dst_port.to_string()) .into_centered_line() .yellow(), + Span::from("-").into_centered_line().yellow(), Span::from("TCP".to_string()).into_centered_line().cyan(), ]), IpProto::Udp(p) => Row::new(vec![ @@ -478,6 +556,7 @@ impl Inspection { Span::from(p.dst_port.to_string()) .into_centered_line() .yellow(), + Span::from("-").into_centered_line().yellow(), Span::from("UDP".to_string()).into_centered_line().cyan(), ]), IpProto::Icmp(_) => Row::new(vec![ @@ -489,6 +568,7 @@ impl Inspection { .into_centered_line() .blue(), Span::from("-").into_centered_line().yellow(), + Span::from("-").into_centered_line().yellow(), Span::from("ICMP".to_string()).into_centered_line().cyan(), ]), }, @@ -513,6 +593,7 @@ impl Inspection { Line::from("Source Port").centered(), Line::from("Destination Address").centered(), Line::from("Destination Port").centered(), + Line::from("Pid").centered(), Line::from("Protocol").centered(), { if self.manuall_scroll { @@ -640,7 +721,7 @@ impl Inspection { let fuzzy = self.fuzzy.lock().unwrap(); let packets = self.packets.lock().unwrap(); - let packet = if fuzzy.is_enabled() { + let net_packet = if fuzzy.is_enabled() { fuzzy.packets[self.packet_index.unwrap()] } else { packets[self.packet_index.unwrap()] @@ -657,9 +738,9 @@ impl Inspection { .border_type(BorderType::Thick), block, ); - match packet { - AppPacket::Ip(ip_packet) => ip_packet.render(block, frame), - AppPacket::Arp(arp_packet) => arp_packet.render(block, frame), + match net_packet { + NetworkPacket::Ip(ip_packet) => ip_packet.render(block, frame), + NetworkPacket::Arp(arp_packet) => arp_packet.render(block, frame), }; } } diff --git a/oryx-tui/src/section/stats.rs b/oryx-tui/src/section/stats.rs index 0f813d2..cdbf6c7 100644 --- a/oryx-tui/src/section/stats.rs +++ b/oryx-tui/src/section/stats.rs @@ -20,7 +20,7 @@ use crate::{ interface::NetworkInterface, packet::{ network::{IpPacket, IpProto}, - AppPacket, + NetworkPacket, }, }; @@ -41,7 +41,10 @@ pub struct Stats { } impl Stats { - pub fn new(packets: Arc>>, selected_interface: NetworkInterface) -> Self { + pub fn new( + packets: Arc>>, + selected_interface: NetworkInterface, + ) -> Self { let packet_stats: Arc> = Arc::new(Mutex::new(PacketStats::default())); thread::spawn({ @@ -59,10 +62,10 @@ impl Stats { let mut packet_stats = packet_stats.lock().unwrap(); for packet in packets[last_index..].iter() { match packet { - AppPacket::Arp(_) => { + NetworkPacket::Arp(_) => { packet_stats.link.arp += 1; } - AppPacket::Ip(packet) => match packet { + NetworkPacket::Ip(packet) => match packet { IpPacket::V4(ipv4_packet) => { packet_stats.network.ipv4 += 1;