Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

support ipv6 for dns lookup #33

Merged
merged 3 commits into from
Oct 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
perf.data
flamegraph.svg
log-file
debug/
target/
Expand Down
31 changes: 4 additions & 27 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions Release.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
## v0.5 - TBA

### Added

- stats: top 10 websites support ipv6

## v0.4 - 2024-10-13

### Added
Expand Down
1 change: 0 additions & 1 deletion oryx-tui/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ oryx-common = { path = "../oryx-common" }
mio = { version = "1", features = ["os-poll", "os-ext"] }
itertools = "0.13"
dirs = "5"
dns-lookup = "2"
kanal = "0.1.0-pre8"
mimalloc = "0.1"
clap = { version = "4", features = ["derive", "cargo"] }
Expand Down
80 changes: 80 additions & 0 deletions oryx-tui/src/dns.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
use libc::{c_char, getnameinfo, sockaddr_in, sockaddr_in6, socklen_t, NI_MAXHOST, NI_NAMEREQD};
use std::ffi::CStr;
use std::mem;
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};

use crate::app::AppResult;

pub fn get_hostname(ip: &IpAddr) -> AppResult<String> {
match ip {
IpAddr::V4(v) => get_hostname_v4(v),
IpAddr::V6(v) => get_hostname_v6(v),
}
}

fn get_hostname_v4(ip: &Ipv4Addr) -> AppResult<String> {
let sockaddr = sockaddr_in {
sin_family: libc::AF_INET as u16,
sin_port: 0,
sin_addr: libc::in_addr {
s_addr: ip.to_bits(),
},
sin_zero: [0; 8],
};

let mut host: [c_char; NI_MAXHOST as usize] = [0; NI_MAXHOST as usize];

let result = unsafe {
getnameinfo(
&sockaddr as *const _ as *const _,
mem::size_of::<sockaddr_in>() as socklen_t,
host.as_mut_ptr(),
NI_MAXHOST,
std::ptr::null_mut(),
0,
NI_NAMEREQD,
)
};

if result != 0 {
return Err("Failed to get hostname".into());
}

let host_str = unsafe { CStr::from_ptr(host.as_ptr()).to_string_lossy().into_owned() };

Ok(host_str)
}

fn get_hostname_v6(ip: &Ipv6Addr) -> AppResult<String> {
let sockaddr = sockaddr_in6 {
sin6_family: libc::AF_INET6 as u16,
sin6_port: 0,
sin6_addr: libc::in6_addr {
s6_addr: ip.octets(),
},
sin6_flowinfo: 0,
sin6_scope_id: 0,
};

let mut host: [c_char; NI_MAXHOST as usize] = [0; NI_MAXHOST as usize];

let result = unsafe {
getnameinfo(
&sockaddr as *const _ as *const _,
mem::size_of::<sockaddr_in6>() as socklen_t,
host.as_mut_ptr(),
NI_MAXHOST,
std::ptr::null_mut(),
0,
NI_NAMEREQD,
)
};

if result != 0 {
return Err("Failed to get hostname".into());
}

let host_str = unsafe { CStr::from_ptr(host.as_ptr()).to_string_lossy().into_owned() };

Ok(host_str)
}
6 changes: 5 additions & 1 deletion oryx-tui/src/handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use crate::{
app::{ActivePopup, App, AppResult},
event::Event,
filter::FocusedBlock,
section::FocusedSection,
section::{stats::Stats, FocusedSection},
};
use ratatui::crossterm::event::{KeyCode, KeyEvent, KeyModifiers};

Expand All @@ -18,6 +18,10 @@ pub fn handle_key_events(
match key_event.code {
KeyCode::Enter => {
if app.filter.focused_block == FocusedBlock::Apply {
app.section.stats = Some(Stats::new(
app.packets.clone(),
app.filter.interface.selected_interface.clone(),
));
app.filter
.start(event_sender.clone(), app.data_channel_sender.clone())?;

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 @@ -25,3 +25,5 @@ pub mod bandwidth;
pub mod packet;

pub mod section;

pub mod dns;
10 changes: 7 additions & 3 deletions oryx-tui/src/section.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ pub enum FocusedSection {
pub struct Section {
pub focused_section: FocusedSection,
pub inspection: Inspection,
pub stats: Stats,
pub stats: Option<Stats>,
pub alert: Alert,
pub firewall: Firewall,
}
Expand All @@ -51,7 +51,7 @@ impl Section {
Self {
focused_section: FocusedSection::Inspection,
inspection: Inspection::new(packets.clone()),
stats: Stats::new(packets.clone()),
stats: None,
alert: Alert::new(packets.clone()),
firewall: Firewall::new(firewall_chans.ingress.sender, firewall_chans.egress.sender),
}
Expand Down Expand Up @@ -254,7 +254,11 @@ impl Section {

match self.focused_section {
FocusedSection::Inspection => self.inspection.render(frame, section_block),
FocusedSection::Stats => self.stats.render(frame, section_block, network_interace),
FocusedSection::Stats => {
if let Some(stats) = &self.stats {
stats.render(frame, section_block, network_interace)
}
}
FocusedSection::Alerts => self.alert.render(frame, section_block),
FocusedSection::Firewall => self.firewall.render(frame, section_block),
}
Expand Down
57 changes: 42 additions & 15 deletions oryx-tui/src/section/stats.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
use dns_lookup::lookup_addr;
use std::{
collections::HashMap,
net::{IpAddr, Ipv4Addr},
net::IpAddr,
sync::{Arc, Mutex},
thread,
time::Duration,
Expand All @@ -17,6 +16,8 @@ use ratatui::{

use crate::{
bandwidth::Bandwidth,
dns::get_hostname,
interface::NetworkInterface,
packet::{
network::{IpPacket, IpProto},
AppPacket,
Expand All @@ -30,7 +31,7 @@ pub struct PacketStats {
pub network: NetworkStats,
pub transport: TransportStats,
pub link: LinkStats,
pub addresses: HashMap<Ipv4Addr, (Option<String>, usize)>,
pub addresses: HashMap<IpAddr, (Option<String>, usize)>,
}

#[derive(Debug)]
Expand All @@ -40,7 +41,7 @@ pub struct Stats {
}

impl Stats {
pub fn new(packets: Arc<Mutex<Vec<AppPacket>>>) -> Self {
pub fn new(packets: Arc<Mutex<Vec<AppPacket>>>, selected_interface: NetworkInterface) -> Self {
let packet_stats: Arc<Mutex<PacketStats>> = Arc::new(Mutex::new(PacketStats::default()));

thread::spawn({
Expand All @@ -67,20 +68,22 @@ impl Stats {
if !ipv4_packet.dst_ip.is_private()
&& !ipv4_packet.dst_ip.is_loopback()
{
if let Some((_, counts)) =
packet_stats.addresses.get_mut(&ipv4_packet.dst_ip)
if let Some((_, counts)) = packet_stats
.addresses
.get_mut(&IpAddr::V4(ipv4_packet.dst_ip))
{
*counts += 1;
} else if let Ok(host) =
lookup_addr(&IpAddr::V4(ipv4_packet.dst_ip))
get_hostname(&IpAddr::V4(ipv4_packet.dst_ip))
{
packet_stats
.addresses
.insert(ipv4_packet.dst_ip, (Some(host), 1));
packet_stats.addresses.insert(
IpAddr::V4(ipv4_packet.dst_ip),
(Some(host), 1),
);
} else {
packet_stats
.addresses
.insert(ipv4_packet.dst_ip, (None, 1));
.insert(IpAddr::V4(ipv4_packet.dst_ip), (None, 1));
}
}

Expand All @@ -98,6 +101,30 @@ impl Stats {
}
IpPacket::V6(ipv6_packet) => {
packet_stats.network.ipv6 += 1;

if !selected_interface
.addresses
.contains(&IpAddr::V6(ipv6_packet.dst_ip))
{
if let Some((_, counts)) = packet_stats
.addresses
.get_mut(&IpAddr::V6(ipv6_packet.dst_ip))
{
*counts += 1;
} else if let Ok(host) =
get_hostname(&IpAddr::V6(ipv6_packet.dst_ip))
{
packet_stats.addresses.insert(
IpAddr::V6(ipv6_packet.dst_ip),
(Some(host), 1),
);
} else {
packet_stats
.addresses
.insert(IpAddr::V6(ipv6_packet.dst_ip), (None, 1));
}
}

match ipv6_packet.proto {
IpProto::Tcp(_) => {
packet_stats.transport.tcp += 1;
Expand Down Expand Up @@ -128,9 +155,9 @@ impl Stats {
}
pub fn get_top_10(
&self,
addresses: HashMap<Ipv4Addr, (Option<String>, usize)>,
) -> Vec<(Ipv4Addr, (Option<String>, usize))> {
let mut items: Vec<(Ipv4Addr, (Option<String>, usize))> = addresses.into_iter().collect();
addresses: HashMap<IpAddr, (Option<String>, usize)>,
) -> Vec<(IpAddr, (Option<String>, usize))> {
let mut items: Vec<(IpAddr, (Option<String>, usize))> = addresses.into_iter().collect();
items.sort_by(|a, b| b.1 .1.cmp(&a.1 .1));
items.into_iter().take(10).collect()
}
Expand Down Expand Up @@ -302,7 +329,7 @@ impl Stats {
.title_alignment(Alignment::Center)
.padding(Padding::horizontal(1))
.padding(Padding::right(3))
.title_bottom("Top visited websites (Ipv4 only)"),
.title_bottom("Top visited websites"),
);

frame.render_widget(addresses_chart, address_block);
Expand Down