Skip to content

Commit 5ffecbf

Browse files
committed
support ipv6 for dns lookup (#33)
1 parent 2f6603e commit 5ffecbf

File tree

9 files changed

+148
-47
lines changed

9 files changed

+148
-47
lines changed

Diff for: .gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
perf.data
2+
flamegraph.svg
13
log-file
24
debug/
35
target/

Diff for: Cargo.lock

+4-27
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Diff for: Release.md

+6
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
## v0.5 - TBA
2+
3+
### Added
4+
5+
- stats: top 10 websites support ipv6
6+
17
## v0.4 - 2024-10-13
28

39
### Added

Diff for: oryx-tui/Cargo.toml

-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ oryx-common = { path = "../oryx-common" }
1919
mio = { version = "1", features = ["os-poll", "os-ext"] }
2020
itertools = "0.13"
2121
dirs = "5"
22-
dns-lookup = "2"
2322
kanal = "0.1.0-pre8"
2423
mimalloc = "0.1"
2524
clap = { version = "4", features = ["derive", "cargo"] }

Diff for: oryx-tui/src/dns.rs

+80
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
use libc::{c_char, getnameinfo, sockaddr_in, sockaddr_in6, socklen_t, NI_MAXHOST, NI_NAMEREQD};
2+
use std::ffi::CStr;
3+
use std::mem;
4+
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
5+
6+
use crate::app::AppResult;
7+
8+
pub fn get_hostname(ip: &IpAddr) -> AppResult<String> {
9+
match ip {
10+
IpAddr::V4(v) => get_hostname_v4(v),
11+
IpAddr::V6(v) => get_hostname_v6(v),
12+
}
13+
}
14+
15+
fn get_hostname_v4(ip: &Ipv4Addr) -> AppResult<String> {
16+
let sockaddr = sockaddr_in {
17+
sin_family: libc::AF_INET as u16,
18+
sin_port: 0,
19+
sin_addr: libc::in_addr {
20+
s_addr: ip.to_bits(),
21+
},
22+
sin_zero: [0; 8],
23+
};
24+
25+
let mut host: [c_char; NI_MAXHOST as usize] = [0; NI_MAXHOST as usize];
26+
27+
let result = unsafe {
28+
getnameinfo(
29+
&sockaddr as *const _ as *const _,
30+
mem::size_of::<sockaddr_in>() as socklen_t,
31+
host.as_mut_ptr(),
32+
NI_MAXHOST,
33+
std::ptr::null_mut(),
34+
0,
35+
NI_NAMEREQD,
36+
)
37+
};
38+
39+
if result != 0 {
40+
return Err("Failed to get hostname".into());
41+
}
42+
43+
let host_str = unsafe { CStr::from_ptr(host.as_ptr()).to_string_lossy().into_owned() };
44+
45+
Ok(host_str)
46+
}
47+
48+
fn get_hostname_v6(ip: &Ipv6Addr) -> AppResult<String> {
49+
let sockaddr = sockaddr_in6 {
50+
sin6_family: libc::AF_INET6 as u16,
51+
sin6_port: 0,
52+
sin6_addr: libc::in6_addr {
53+
s6_addr: ip.octets(),
54+
},
55+
sin6_flowinfo: 0,
56+
sin6_scope_id: 0,
57+
};
58+
59+
let mut host: [c_char; NI_MAXHOST as usize] = [0; NI_MAXHOST as usize];
60+
61+
let result = unsafe {
62+
getnameinfo(
63+
&sockaddr as *const _ as *const _,
64+
mem::size_of::<sockaddr_in6>() as socklen_t,
65+
host.as_mut_ptr(),
66+
NI_MAXHOST,
67+
std::ptr::null_mut(),
68+
0,
69+
NI_NAMEREQD,
70+
)
71+
};
72+
73+
if result != 0 {
74+
return Err("Failed to get hostname".into());
75+
}
76+
77+
let host_str = unsafe { CStr::from_ptr(host.as_ptr()).to_string_lossy().into_owned() };
78+
79+
Ok(host_str)
80+
}

Diff for: oryx-tui/src/handler.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use crate::{
44
app::{ActivePopup, App, AppResult},
55
event::Event,
66
filter::FocusedBlock,
7-
section::FocusedSection,
7+
section::{stats::Stats, FocusedSection},
88
};
99
use ratatui::crossterm::event::{KeyCode, KeyEvent, KeyModifiers};
1010

@@ -18,6 +18,10 @@ pub fn handle_key_events(
1818
match key_event.code {
1919
KeyCode::Enter => {
2020
if app.filter.focused_block == FocusedBlock::Apply {
21+
app.section.stats = Some(Stats::new(
22+
app.packets.clone(),
23+
app.filter.interface.selected_interface.clone(),
24+
));
2125
app.filter
2226
.start(event_sender.clone(), app.data_channel_sender.clone())?;
2327

Diff for: oryx-tui/src/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -25,3 +25,5 @@ pub mod bandwidth;
2525
pub mod packet;
2626

2727
pub mod section;
28+
29+
pub mod dns;

Diff for: oryx-tui/src/section.rs

+7-3
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ pub enum FocusedSection {
3838
pub struct Section {
3939
pub focused_section: FocusedSection,
4040
pub inspection: Inspection,
41-
pub stats: Stats,
41+
pub stats: Option<Stats>,
4242
pub alert: Alert,
4343
pub firewall: Firewall,
4444
}
@@ -51,7 +51,7 @@ impl Section {
5151
Self {
5252
focused_section: FocusedSection::Inspection,
5353
inspection: Inspection::new(packets.clone()),
54-
stats: Stats::new(packets.clone()),
54+
stats: None,
5555
alert: Alert::new(packets.clone()),
5656
firewall: Firewall::new(firewall_chans.ingress.sender, firewall_chans.egress.sender),
5757
}
@@ -254,7 +254,11 @@ impl Section {
254254

255255
match self.focused_section {
256256
FocusedSection::Inspection => self.inspection.render(frame, section_block),
257-
FocusedSection::Stats => self.stats.render(frame, section_block, network_interace),
257+
FocusedSection::Stats => {
258+
if let Some(stats) = &self.stats {
259+
stats.render(frame, section_block, network_interace)
260+
}
261+
}
258262
FocusedSection::Alerts => self.alert.render(frame, section_block),
259263
FocusedSection::Firewall => self.firewall.render(frame, section_block),
260264
}

Diff for: oryx-tui/src/section/stats.rs

+42-15
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
1-
use dns_lookup::lookup_addr;
21
use std::{
32
collections::HashMap,
4-
net::{IpAddr, Ipv4Addr},
3+
net::IpAddr,
54
sync::{Arc, Mutex},
65
thread,
76
time::Duration,
@@ -17,6 +16,8 @@ use ratatui::{
1716

1817
use crate::{
1918
bandwidth::Bandwidth,
19+
dns::get_hostname,
20+
interface::NetworkInterface,
2021
packet::{
2122
network::{IpPacket, IpProto},
2223
AppPacket,
@@ -30,7 +31,7 @@ pub struct PacketStats {
3031
pub network: NetworkStats,
3132
pub transport: TransportStats,
3233
pub link: LinkStats,
33-
pub addresses: HashMap<Ipv4Addr, (Option<String>, usize)>,
34+
pub addresses: HashMap<IpAddr, (Option<String>, usize)>,
3435
}
3536

3637
#[derive(Debug)]
@@ -40,7 +41,7 @@ pub struct Stats {
4041
}
4142

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

4647
thread::spawn({
@@ -67,20 +68,22 @@ impl Stats {
6768
if !ipv4_packet.dst_ip.is_private()
6869
&& !ipv4_packet.dst_ip.is_loopback()
6970
{
70-
if let Some((_, counts)) =
71-
packet_stats.addresses.get_mut(&ipv4_packet.dst_ip)
71+
if let Some((_, counts)) = packet_stats
72+
.addresses
73+
.get_mut(&IpAddr::V4(ipv4_packet.dst_ip))
7274
{
7375
*counts += 1;
7476
} else if let Ok(host) =
75-
lookup_addr(&IpAddr::V4(ipv4_packet.dst_ip))
77+
get_hostname(&IpAddr::V4(ipv4_packet.dst_ip))
7678
{
77-
packet_stats
78-
.addresses
79-
.insert(ipv4_packet.dst_ip, (Some(host), 1));
79+
packet_stats.addresses.insert(
80+
IpAddr::V4(ipv4_packet.dst_ip),
81+
(Some(host), 1),
82+
);
8083
} else {
8184
packet_stats
8285
.addresses
83-
.insert(ipv4_packet.dst_ip, (None, 1));
86+
.insert(IpAddr::V4(ipv4_packet.dst_ip), (None, 1));
8487
}
8588
}
8689

@@ -98,6 +101,30 @@ impl Stats {
98101
}
99102
IpPacket::V6(ipv6_packet) => {
100103
packet_stats.network.ipv6 += 1;
104+
105+
if !selected_interface
106+
.addresses
107+
.contains(&IpAddr::V6(ipv6_packet.dst_ip))
108+
{
109+
if let Some((_, counts)) = packet_stats
110+
.addresses
111+
.get_mut(&IpAddr::V6(ipv6_packet.dst_ip))
112+
{
113+
*counts += 1;
114+
} else if let Ok(host) =
115+
get_hostname(&IpAddr::V6(ipv6_packet.dst_ip))
116+
{
117+
packet_stats.addresses.insert(
118+
IpAddr::V6(ipv6_packet.dst_ip),
119+
(Some(host), 1),
120+
);
121+
} else {
122+
packet_stats
123+
.addresses
124+
.insert(IpAddr::V6(ipv6_packet.dst_ip), (None, 1));
125+
}
126+
}
127+
101128
match ipv6_packet.proto {
102129
IpProto::Tcp(_) => {
103130
packet_stats.transport.tcp += 1;
@@ -128,9 +155,9 @@ impl Stats {
128155
}
129156
pub fn get_top_10(
130157
&self,
131-
addresses: HashMap<Ipv4Addr, (Option<String>, usize)>,
132-
) -> Vec<(Ipv4Addr, (Option<String>, usize))> {
133-
let mut items: Vec<(Ipv4Addr, (Option<String>, usize))> = addresses.into_iter().collect();
158+
addresses: HashMap<IpAddr, (Option<String>, usize)>,
159+
) -> Vec<(IpAddr, (Option<String>, usize))> {
160+
let mut items: Vec<(IpAddr, (Option<String>, usize))> = addresses.into_iter().collect();
134161
items.sort_by(|a, b| b.1 .1.cmp(&a.1 .1));
135162
items.into_iter().take(10).collect()
136163
}
@@ -302,7 +329,7 @@ impl Stats {
302329
.title_alignment(Alignment::Center)
303330
.padding(Padding::horizontal(1))
304331
.padding(Padding::right(3))
305-
.title_bottom("Top visited websites (Ipv4 only)"),
332+
.title_bottom("Top visited websites"),
306333
);
307334

308335
frame.render_widget(addresses_chart, address_block);

0 commit comments

Comments
 (0)