Skip to content

Commit

Permalink
show pid for tcp and udp
Browse files Browse the repository at this point in the history
  • Loading branch information
pythops committed Nov 16, 2024
1 parent 8c2686a commit d1da41b
Show file tree
Hide file tree
Showing 6 changed files with 352 additions and 1 deletion.
16 changes: 15 additions & 1 deletion oryx-tui/src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@ use crate::{
filter::Filter,
help::Help,
packet::{direction::TrafficDirection, NetworkPacket},
pid,
};

use crate::{filter::IoChannels, notification::Notification};
use crate::{packet::AppPacket, section::Section};

Expand Down Expand Up @@ -66,17 +68,29 @@ impl App {
thread::spawn({
let packets = packets.clone();
move || loop {
let pid_map = pid::ConnectionMap::new();
if let Ok((raw_packet, direction)) = receiver.recv() {
let network_packet = NetworkPacket::from(raw_packet);

let pid = {
if direction == TrafficDirection::Egress {
pid::get_pid(network_packet, &pid_map)
} else {
None
}
};

let app_packet = AppPacket {
packet: network_packet,
pid: None,
pid,
direction,
};

let mut packets = packets.lock().unwrap();
if packets.len() == packets.capacity() {
packets.reserve(1024 * 1024);
}

packets.push(app_packet);
}
}
Expand Down
2 changes: 2 additions & 0 deletions oryx-tui/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,5 @@ pub mod packet;
pub mod section;

pub mod dns;

pub mod pid;
104 changes: 104 additions & 0 deletions oryx-tui/src/pid.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
use std::hash::{DefaultHasher, Hash, Hasher};
use std::net::IpAddr;
use std::num::ParseIntError;

pub mod tcp;
pub mod udp;

use tcp::TcpConnectionMap;
use udp::UdpConnectionMap;

use crate::app::AppResult;
use crate::packet::network::{IpPacket, IpProto};
use crate::packet::NetworkPacket;

pub fn get_pid(packet: NetworkPacket, map: &ConnectionMap) -> Option<usize> {
match packet {
NetworkPacket::Ip(ip_packet) => match ip_packet {
IpPacket::V4(ipv4_packet) => match ipv4_packet.proto {
IpProto::Tcp(tcp_packet) => {
let connection = Connection {
ip_local: IpAddr::V4(ipv4_packet.src_ip),
port_local: Some(tcp_packet.src_port),
ip_remote: IpAddr::V4(ipv4_packet.dst_ip),
port_remote: Some(tcp_packet.dst_port),
};
return map.tcp.map.get(&connection.calculate_hash()).copied();
}

IpProto::Udp(udp_packet) => {
let connection = Connection {
ip_local: IpAddr::V4(ipv4_packet.src_ip),
port_local: Some(udp_packet.src_port),
ip_remote: IpAddr::V4(ipv4_packet.dst_ip),
port_remote: Some(udp_packet.dst_port),
};
return map.udp.map.get(&connection.calculate_hash()).copied();
}
_ => {}
},
_ => {}
},
_ => {}
};
None
}

fn decode_hex_ipv4(hex_str: &str) -> AppResult<[u8; 4]> {
let mut bytes = Vec::new();
for i in (0..hex_str.len()).step_by(2) {
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_port(hex_str: &str) -> Result<u16, ParseIntError> {
Ok(u16::from_be_bytes([
u8::from_str_radix(&hex_str[..2], 16)?,
u8::from_str_radix(&hex_str[2..], 16)?,
]))
}

#[derive(Clone, Debug)]
pub struct ConnectionMap {
tcp: TcpConnectionMap,
udp: UdpConnectionMap,
}

impl ConnectionMap {
pub fn new() -> Self {
Self {
tcp: TcpConnectionMap::new(),
udp: UdpConnectionMap::new(),
}
}
}

#[derive(Clone, Debug)]
pub struct Connection {
ip_local: IpAddr,
port_local: Option<u16>,
ip_remote: IpAddr,
port_remote: Option<u16>,
}

impl Hash for Connection {
fn hash<H: Hasher>(&self, state: &mut H) {
self.ip_local.hash(state);
self.port_local.hash(state);
self.ip_remote.hash(state);
self.port_remote.hash(state);
}
}

impl Connection {
pub fn calculate_hash(&self) -> u64 {
let mut s = DefaultHasher::new();
self.hash(&mut s);
s.finish()
}
}
99 changes: 99 additions & 0 deletions oryx-tui/src/pid/tcp.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
use std::{
collections::HashMap,
fs::{self, File},
io::Read,
net::{IpAddr, Ipv4Addr},
};

use super::{decode_hex_ipv4, decode_hex_port, Connection};

#[derive(Clone, Debug)]
pub struct TcpConnectionMap {
pub map: HashMap<u64, usize>,
}

impl TcpConnectionMap {
fn inode_map() -> HashMap<usize, u64> {
let mut map = HashMap::new();
let mut file = File::open("/proc/net/tcp").unwrap();
let mut buffer = String::new();
file.read_to_string(&mut buffer).unwrap();

let mut lines_tcp = buffer.lines();
lines_tcp.next();

for line in lines_tcp {
let splits: Vec<&str> = line.split_whitespace().collect();
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 = decode_hex_ipv4(ip_local).unwrap();
let ip_local = IpAddr::V4(Ipv4Addr::from(ip_local));

let port_local = decode_hex_port(port_local).unwrap();

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 = decode_hex_ipv4(ip_remote).unwrap();
let ip_remote = IpAddr::V4(Ipv4Addr::from(ip_remote));

let port_remote = decode_hex_port(port_remote).unwrap();

let inode = splits[9].parse::<usize>().unwrap();

let connection = Connection {
ip_local,
port_local: Some(port_local),
ip_remote,
port_remote: Some(port_remote),
};

map.insert(inode, connection.calculate_hash());
}
map
}

pub fn new() -> Self {
let mut map: HashMap<u64, usize> = HashMap::new();

let inode_map = Self::inode_map();

if let Ok(entries) = fs::read_dir("/proc") {
for entry in entries.flatten() {
let pid_str = entry.file_name();
let pid_str = pid_str.to_str().unwrap();
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::<usize>()
{
if let Some(connection_hash) = inode_map.get(&inode) {
let pid = pid_str.parse::<usize>().unwrap();
map.insert(*connection_hash, pid);
}
}
}
}
}
}
}
}
}
Self { map }
}
}
98 changes: 98 additions & 0 deletions oryx-tui/src/pid/udp.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
use std::{
collections::HashMap,
fs::{self, File},
io::Read,
net::{IpAddr, Ipv4Addr},
};

use super::{decode_hex_ipv4, decode_hex_port, Connection};

#[derive(Clone, Debug)]
pub struct UdpConnectionMap {
pub map: HashMap<u64, usize>,
}

impl UdpConnectionMap {
fn inode_map() -> HashMap<usize, u64> {
let mut map = HashMap::new();
let mut file = File::open("/proc/net/udp").unwrap();
let mut buffer = String::new();
file.read_to_string(&mut buffer).unwrap();

let mut lines_tcp = buffer.lines();
lines_tcp.next();

for line in lines_tcp {
let splits: Vec<&str> = line.split_whitespace().collect();
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 = decode_hex_ipv4(ip_local).unwrap();
let ip_local = IpAddr::V4(Ipv4Addr::from(ip_local));

let port_local = decode_hex_port(port_local).unwrap();

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 = decode_hex_ipv4(ip_remote).unwrap();
let ip_remote = IpAddr::V4(Ipv4Addr::from(ip_remote));

let port_remote = decode_hex_port(port_remote).unwrap();

let inode = splits[9].parse::<usize>().unwrap();

let connection = Connection {
ip_local,
port_local: Some(port_local),
ip_remote,
port_remote: Some(port_remote),
};

map.insert(inode, connection.calculate_hash());
}
map
}

pub fn new() -> Self {
let mut map: HashMap<u64, usize> = HashMap::new();

let inode_map = Self::inode_map();

if let Ok(entries) = fs::read_dir("/proc") {
for entry in entries.flatten() {
let pid_str = entry.file_name();
let pid_str = pid_str.to_str().unwrap();
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) {
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::<usize>()
{
if let Some(connection_hash) = inode_map.get(&inode) {
let pid = pid_str.parse::<usize>().unwrap();
map.insert(*connection_hash, pid);
}
}
}
}
}
}
}
}
}
Self { map }
}
}
Loading

0 comments on commit d1da41b

Please sign in to comment.