diff --git a/Cargo.lock b/Cargo.lock index 183fe81..bd96721 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,18 +2,6 @@ # It is not intended for manual editing. version = 3 -[[package]] -name = "ahash" -version = "0.8.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" -dependencies = [ - "cfg-if", - "once_cell", - "version_check", - "zerocopy", -] - [[package]] name = "aho-corasick" version = "1.1.3" @@ -98,30 +86,30 @@ checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" [[package]] name = "aya" -version = "0.12.0" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90eea657cc8028447cbda5068f4e10c4fadba0131624f4f7dd1a9c46ffc8d81f" +checksum = "7127cbe933572dfabb7a87d2c740f5b720c3e19b4d2b8c99a262ffa35c8761ff" dependencies = [ "assert_matches", "aya-obj", "bitflags", "bytes", - "lazy_static", "libc", "log", "object", + "once_cell", "thiserror", ] [[package]] name = "aya-obj" -version = "0.1.0" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c02024a307161cf3d1f052161958fd13b1a33e3e038083e58082c0700fdab85" +checksum = "56e133d505de14d5948a312060b8b12752c22a965632cc57da12f5db653de4c0" dependencies = [ "bytes", "core-error", - "hashbrown 0.14.5", + "hashbrown", "log", "object", "thiserror", @@ -238,6 +226,15 @@ dependencies = [ "version_check", ] +[[package]] +name = "crc32fast" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" +dependencies = [ + "cfg-if", +] + [[package]] name = "crossterm" version = "0.28.1" @@ -442,16 +439,6 @@ dependencies = [ "wasi", ] -[[package]] -name = "hashbrown" -version = "0.14.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" -dependencies = [ - "ahash", - "allocator-api2", -] - [[package]] name = "hashbrown" version = "0.15.0" @@ -487,6 +474,16 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" +[[package]] +name = "indexmap" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" +dependencies = [ + "equivalent", + "hashbrown", +] + [[package]] name = "instability" version = "0.3.2" @@ -528,12 +525,6 @@ dependencies = [ "lock_api", ] -[[package]] -name = "lazy_static" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" - [[package]] name = "libc" version = "0.2.159" @@ -588,7 +579,7 @@ version = "0.12.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "234cf4f4a04dc1f57e24b96cc0cd600cf2af460d4161ac5ecdd0af8e1f3b2a38" dependencies = [ - "hashbrown 0.15.0", + "hashbrown", ] [[package]] @@ -627,10 +618,13 @@ checksum = "e82e9f64c09f56aa7c80c3fa087997bd99a913f91d9c74d36cf5fd75dd5773e6" [[package]] name = "object" -version = "0.32.2" +version = "0.36.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" +checksum = "aedf0a2d09c573ed1d8d85b30c119153926a2b36dce0ab28322c09a117a4683e" dependencies = [ + "crc32fast", + "hashbrown", + "indexmap", "memchr", ] @@ -1188,23 +1182,3 @@ dependencies = [ "anyhow", "clap", ] - -[[package]] -name = "zerocopy" -version = "0.7.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" -dependencies = [ - "zerocopy-derive", -] - -[[package]] -name = "zerocopy-derive" -version = "0.7.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] diff --git a/oryx-tui/Cargo.toml b/oryx-tui/Cargo.toml index 8466043..68513af 100644 --- a/oryx-tui/Cargo.toml +++ b/oryx-tui/Cargo.toml @@ -14,7 +14,7 @@ ratatui = "0.28" tui-big-text = "0.6" tui-input = "0.10" libc = "0.2" -aya = "0.12" +aya = "0.13" oryx-common = { path = "../oryx-common" } mio = { version = "1", features = ["os-poll", "os-ext"] } itertools = "0.13" diff --git a/oryx-tui/src/app.rs b/oryx-tui/src/app.rs index cd47f60..ac3818d 100644 --- a/oryx-tui/src/app.rs +++ b/oryx-tui/src/app.rs @@ -119,6 +119,7 @@ impl App { frame, section_block, &self.filter.interface.selected_interface.name, + self.active_popup.as_ref(), ); self.filter.render_on_sniffing(frame, settings_block); diff --git a/oryx-tui/src/ebpf.rs b/oryx-tui/src/ebpf.rs index 652a017..60147f1 100644 --- a/oryx-tui/src/ebpf.rs +++ b/oryx-tui/src/ebpf.rs @@ -11,7 +11,7 @@ use aya::{ include_bytes_aligned, maps::{ring_buf::RingBufItem, Array, HashMap, MapData, RingBuf}, programs::{tc, SchedClassifier, TcAttachType}, - Bpf, + Ebpf, }; use oryx_common::{protocols::Protocol, RawPacket, MAX_RULES_PORT}; @@ -23,15 +23,13 @@ use crate::{ }; use mio::{event::Source, unix::SourceFd, Events, Interest, Poll, Registry, Token}; -pub struct Ebpf; - pub struct RingBuffer<'a> { buffer: RingBuf<&'a mut MapData>, } impl<'a> RingBuffer<'a> { - fn new(bpf: &'a mut Bpf) -> Self { - let buffer = RingBuf::try_from(bpf.map_mut("DATA").unwrap()).unwrap(); + fn new(ebpf: &'a mut Ebpf) -> Self { + let buffer = RingBuf::try_from(ebpf.map_mut("DATA").unwrap()).unwrap(); Self { buffer } } @@ -201,384 +199,380 @@ fn update_ipv6_blocklist( } } -impl Ebpf { - pub fn load_ingress( - iface: String, - notification_sender: kanal::Sender, - data_sender: kanal::Sender<[u8; RawPacket::LEN]>, - filter_channel_receiver: kanal::Receiver, - firewall_ingress_receiver: kanal::Receiver, - terminate: Arc, - ) { - thread::spawn({ - let iface = iface.to_owned(); - let notification_sender = notification_sender.clone(); - - move || { - let rlim = libc::rlimit { - rlim_cur: libc::RLIM_INFINITY, - rlim_max: libc::RLIM_INFINITY, - }; - - unsafe { libc::setrlimit(libc::RLIMIT_MEMLOCK, &rlim) }; - - #[cfg(debug_assertions)] - let mut bpf = match Bpf::load(include_bytes_aligned!( - "../../target/bpfel-unknown-none/debug/oryx" - )) { - Ok(v) => v, - Err(e) => { - Notification::send( - format!("Failed to load the ingress eBPF bytecode\n {}", e), - NotificationLevel::Error, - notification_sender, - ) - .unwrap(); - return; - } - }; - - #[cfg(not(debug_assertions))] - let mut bpf = match Bpf::load(include_bytes_aligned!( - "../../target/bpfel-unknown-none/release/oryx" - )) { - Ok(v) => v, - Err(e) => { - Notification::send( - format!("Failed to load the ingress eBPF bytecode\n {}", e), - NotificationLevel::Error, - notification_sender, - ) - .unwrap(); - return; - } - }; - - let _ = tc::qdisc_add_clsact(&iface); - - let program: &mut SchedClassifier = - bpf.program_mut("oryx").unwrap().try_into().unwrap(); - - if let Err(e) = program.load() { +pub fn load_ingress( + iface: String, + notification_sender: kanal::Sender, + data_sender: kanal::Sender<[u8; RawPacket::LEN]>, + filter_channel_receiver: kanal::Receiver, + firewall_ingress_receiver: kanal::Receiver, + terminate: Arc, +) { + thread::spawn({ + let iface = iface.to_owned(); + let notification_sender = notification_sender.clone(); + + move || { + let rlim = libc::rlimit { + rlim_cur: libc::RLIM_INFINITY, + rlim_max: libc::RLIM_INFINITY, + }; + + unsafe { libc::setrlimit(libc::RLIMIT_MEMLOCK, &rlim) }; + + #[cfg(debug_assertions)] + let mut bpf = match Ebpf::load(include_bytes_aligned!( + "../../target/bpfel-unknown-none/debug/oryx" + )) { + Ok(v) => v, + Err(e) => { Notification::send( - format!( - "Failed to load the ingress eBPF program to the kernel\n{}", - e - ), + format!("Failed to load the ingress eBPF bytecode\n {}", e), NotificationLevel::Error, notification_sender, ) .unwrap(); return; - }; - - if let Err(e) = program.attach(&iface, TcAttachType::Ingress) { + } + }; + + #[cfg(not(debug_assertions))] + let mut bpf = match Bpf::load(include_bytes_aligned!( + "../../target/bpfel-unknown-none/release/oryx" + )) { + Ok(v) => v, + Err(e) => { Notification::send( - format!( - "Failed to attach the ingress eBPF program to the interface\n{}", - e - ), + format!("Failed to load the ingress eBPF bytecode\n {}", e), NotificationLevel::Error, notification_sender, ) .unwrap(); return; - }; - - let mut poll = Poll::new().unwrap(); - let mut events = Events::with_capacity(128); - - //filter-ebpf interface - let mut transport_filters: Array<_, u32> = - Array::try_from(bpf.take_map("TRANSPORT_FILTERS").unwrap()).unwrap(); - - let mut network_filters: Array<_, u32> = - Array::try_from(bpf.take_map("NETWORK_FILTERS").unwrap()).unwrap(); - - let mut link_filters: Array<_, u32> = - Array::try_from(bpf.take_map("LINK_FILTERS").unwrap()).unwrap(); - - // firewall-ebpf interface - let mut ipv4_firewall: HashMap<_, u32, [u16; MAX_RULES_PORT]> = - HashMap::try_from(bpf.take_map("BLOCKLIST_IPV4").unwrap()).unwrap(); - - let mut ipv6_firewall: HashMap<_, u128, [u16; MAX_RULES_PORT]> = - HashMap::try_from(bpf.take_map("BLOCKLIST_IPV6").unwrap()).unwrap(); - - thread::spawn(move || loop { - if let Ok(signal) = firewall_ingress_receiver.recv() { - match signal { - FirewallSignal::Rule(rule) => match rule.ip { - IpAddr::V4(addr) => update_ipv4_blocklist( - &mut ipv4_firewall, - addr, - rule.port, - rule.enabled, - ), - - IpAddr::V6(addr) => update_ipv6_blocklist( - &mut ipv6_firewall, - addr, - rule.port, - rule.enabled, - ), - }, - FirewallSignal::Kill => { - break; - } + } + }; + + let _ = tc::qdisc_add_clsact(&iface); + + let program: &mut SchedClassifier = + bpf.program_mut("oryx").unwrap().try_into().unwrap(); + + if let Err(e) = program.load() { + Notification::send( + format!( + "Failed to load the ingress eBPF program to the kernel\n{}", + e + ), + NotificationLevel::Error, + notification_sender, + ) + .unwrap(); + return; + }; + + if let Err(e) = program.attach(&iface, TcAttachType::Ingress) { + Notification::send( + format!( + "Failed to attach the ingress eBPF program to the interface\n{}", + e + ), + NotificationLevel::Error, + notification_sender, + ) + .unwrap(); + return; + }; + + let mut poll = Poll::new().unwrap(); + let mut events = Events::with_capacity(128); + + //filter-ebpf interface + let mut transport_filters: Array<_, u32> = + Array::try_from(bpf.take_map("TRANSPORT_FILTERS").unwrap()).unwrap(); + + let mut network_filters: Array<_, u32> = + Array::try_from(bpf.take_map("NETWORK_FILTERS").unwrap()).unwrap(); + + let mut link_filters: Array<_, u32> = + Array::try_from(bpf.take_map("LINK_FILTERS").unwrap()).unwrap(); + + // firewall-ebpf interface + let mut ipv4_firewall: HashMap<_, u32, [u16; MAX_RULES_PORT]> = + HashMap::try_from(bpf.take_map("BLOCKLIST_IPV4").unwrap()).unwrap(); + + let mut ipv6_firewall: HashMap<_, u128, [u16; MAX_RULES_PORT]> = + HashMap::try_from(bpf.take_map("BLOCKLIST_IPV6").unwrap()).unwrap(); + + thread::spawn(move || loop { + if let Ok(signal) = firewall_ingress_receiver.recv() { + match signal { + FirewallSignal::Rule(rule) => match rule.ip { + IpAddr::V4(addr) => update_ipv4_blocklist( + &mut ipv4_firewall, + addr, + rule.port, + rule.enabled, + ), + + IpAddr::V6(addr) => update_ipv6_blocklist( + &mut ipv6_firewall, + addr, + rule.port, + rule.enabled, + ), + }, + FirewallSignal::Kill => { + break; } } - }); - - thread::spawn(move || loop { - if let Ok(signal) = filter_channel_receiver.recv() { - match signal { - FilterChannelSignal::Update((filter, flag)) => match filter { - Protocol::Transport(p) => { - let _ = transport_filters.set(p as u32, flag as u32, 0); - } - Protocol::Network(p) => { - let _ = network_filters.set(p as u32, flag as u32, 0); - } - Protocol::Link(p) => { - let _ = link_filters.set(p as u32, flag as u32, 0); - } - }, - FilterChannelSignal::Kill => { - break; + } + }); + + thread::spawn(move || loop { + if let Ok(signal) = filter_channel_receiver.recv() { + match signal { + FilterChannelSignal::Update((filter, flag)) => match filter { + Protocol::Transport(p) => { + let _ = transport_filters.set(p as u32, flag as u32, 0); + } + Protocol::Network(p) => { + let _ = network_filters.set(p as u32, flag as u32, 0); + } + Protocol::Link(p) => { + let _ = link_filters.set(p as u32, flag as u32, 0); } + }, + FilterChannelSignal::Kill => { + break; } } - }); + } + }); - let mut ring_buf = RingBuffer::new(&mut bpf); + let mut ring_buf = RingBuffer::new(&mut bpf); - poll.registry() - .register( - &mut SourceFd(&ring_buf.buffer.as_raw_fd()), - Token(0), - Interest::READABLE, - ) - .unwrap(); + poll.registry() + .register( + &mut SourceFd(&ring_buf.buffer.as_raw_fd()), + Token(0), + Interest::READABLE, + ) + .unwrap(); - loop { - poll.poll(&mut events, Some(Duration::from_millis(100))) - .unwrap(); + loop { + poll.poll(&mut events, Some(Duration::from_millis(100))) + .unwrap(); + if terminate.load(std::sync::atomic::Ordering::Relaxed) { + break; + } + for event in &events { if terminate.load(std::sync::atomic::Ordering::Relaxed) { break; } - for event in &events { + if event.token() == Token(0) && event.is_readable() { if terminate.load(std::sync::atomic::Ordering::Relaxed) { break; } - if event.token() == Token(0) && event.is_readable() { + while let Some(item) = ring_buf.next() { if terminate.load(std::sync::atomic::Ordering::Relaxed) { break; } - while let Some(item) = ring_buf.next() { - if terminate.load(std::sync::atomic::Ordering::Relaxed) { - break; - } - let packet: [u8; RawPacket::LEN] = - item.to_owned().try_into().unwrap(); - data_sender.send(packet).ok(); - } + let packet: [u8; RawPacket::LEN] = item.to_owned().try_into().unwrap(); + data_sender.send(packet).ok(); } } } - - let _ = poll - .registry() - .deregister(&mut SourceFd(&ring_buf.buffer.as_raw_fd())); } - }); - } - pub fn load_egress( - iface: String, - notification_sender: kanal::Sender, - data_sender: kanal::Sender<[u8; RawPacket::LEN]>, - filter_channel_receiver: kanal::Receiver, - firewall_egress_receiver: kanal::Receiver, - terminate: Arc, - ) { - thread::spawn({ - let iface = iface.to_owned(); - let notification_sender = notification_sender.clone(); - - move || { - let rlim = libc::rlimit { - rlim_cur: libc::RLIM_INFINITY, - rlim_max: libc::RLIM_INFINITY, - }; - - unsafe { libc::setrlimit(libc::RLIMIT_MEMLOCK, &rlim) }; - - #[cfg(debug_assertions)] - let mut bpf = match Bpf::load(include_bytes_aligned!( - "../../target/bpfel-unknown-none/debug/oryx" - )) { - Ok(v) => v, - Err(e) => { - Notification::send( - format!("Fail to load the egress eBPF bytecode\n {}", e), - NotificationLevel::Error, - notification_sender, - ) - .unwrap(); - return; - } - }; - - #[cfg(not(debug_assertions))] - let mut bpf = match Bpf::load(include_bytes_aligned!( - "../../target/bpfel-unknown-none/release/oryx" - )) { - Ok(v) => v, - Err(e) => { - Notification::send( - format!("Failed to load the egress eBPF bytecode\n {}", e), - NotificationLevel::Error, - notification_sender, - ) - .unwrap(); - return; - } - }; - - let _ = tc::qdisc_add_clsact(&iface); - let program: &mut SchedClassifier = - bpf.program_mut("oryx").unwrap().try_into().unwrap(); + let _ = poll + .registry() + .deregister(&mut SourceFd(&ring_buf.buffer.as_raw_fd())); + } + }); +} - if let Err(e) = program.load() { +pub fn load_egress( + iface: String, + notification_sender: kanal::Sender, + data_sender: kanal::Sender<[u8; RawPacket::LEN]>, + filter_channel_receiver: kanal::Receiver, + firewall_egress_receiver: kanal::Receiver, + terminate: Arc, +) { + thread::spawn({ + let iface = iface.to_owned(); + let notification_sender = notification_sender.clone(); + + move || { + let rlim = libc::rlimit { + rlim_cur: libc::RLIM_INFINITY, + rlim_max: libc::RLIM_INFINITY, + }; + + unsafe { libc::setrlimit(libc::RLIMIT_MEMLOCK, &rlim) }; + + #[cfg(debug_assertions)] + let mut bpf = match Ebpf::load(include_bytes_aligned!( + "../../target/bpfel-unknown-none/debug/oryx" + )) { + Ok(v) => v, + Err(e) => { Notification::send( - format!("Fail to load the egress eBPF program to the kernel\n{}", e), + format!("Fail to load the egress eBPF bytecode\n {}", e), NotificationLevel::Error, notification_sender, ) .unwrap(); return; - }; - - if let Err(e) = program.attach(&iface, TcAttachType::Egress) { + } + }; + + #[cfg(not(debug_assertions))] + let mut bpf = match Bpf::load(include_bytes_aligned!( + "../../target/bpfel-unknown-none/release/oryx" + )) { + Ok(v) => v, + Err(e) => { Notification::send( - format!( - "Failed to attach the egress eBPF program to the interface\n{}", - e - ), + format!("Failed to load the egress eBPF bytecode\n {}", e), NotificationLevel::Error, notification_sender, ) .unwrap(); return; - }; - - let mut poll = Poll::new().unwrap(); - let mut events = Events::with_capacity(128); - - //filter-ebpf interface - let mut transport_filters: Array<_, u32> = - Array::try_from(bpf.take_map("TRANSPORT_FILTERS").unwrap()).unwrap(); - - let mut network_filters: Array<_, u32> = - Array::try_from(bpf.take_map("NETWORK_FILTERS").unwrap()).unwrap(); - - let mut link_filters: Array<_, u32> = - Array::try_from(bpf.take_map("LINK_FILTERS").unwrap()).unwrap(); - - // firewall-ebpf interface - let mut ipv4_firewall: HashMap<_, u32, [u16; MAX_RULES_PORT]> = - HashMap::try_from(bpf.take_map("BLOCKLIST_IPV4").unwrap()).unwrap(); - - let mut ipv6_firewall: HashMap<_, u128, [u16; MAX_RULES_PORT]> = - HashMap::try_from(bpf.take_map("BLOCKLIST_IPV6").unwrap()).unwrap(); - - thread::spawn(move || loop { - if let Ok(signal) = firewall_egress_receiver.recv() { - match signal { - FirewallSignal::Rule(rule) => match rule.ip { - IpAddr::V4(addr) => update_ipv4_blocklist( - &mut ipv4_firewall, - addr, - rule.port, - rule.enabled, - ), - - IpAddr::V6(addr) => update_ipv6_blocklist( - &mut ipv6_firewall, - addr, - rule.port, - rule.enabled, - ), - }, - FirewallSignal::Kill => { - break; - } + } + }; + + let _ = tc::qdisc_add_clsact(&iface); + let program: &mut SchedClassifier = + bpf.program_mut("oryx").unwrap().try_into().unwrap(); + + if let Err(e) = program.load() { + Notification::send( + format!("Fail to load the egress eBPF program to the kernel\n{}", e), + NotificationLevel::Error, + notification_sender, + ) + .unwrap(); + return; + }; + + if let Err(e) = program.attach(&iface, TcAttachType::Egress) { + Notification::send( + format!( + "Failed to attach the egress eBPF program to the interface\n{}", + e + ), + NotificationLevel::Error, + notification_sender, + ) + .unwrap(); + return; + }; + + let mut poll = Poll::new().unwrap(); + let mut events = Events::with_capacity(128); + + //filter-ebpf interface + let mut transport_filters: Array<_, u32> = + Array::try_from(bpf.take_map("TRANSPORT_FILTERS").unwrap()).unwrap(); + + let mut network_filters: Array<_, u32> = + Array::try_from(bpf.take_map("NETWORK_FILTERS").unwrap()).unwrap(); + + let mut link_filters: Array<_, u32> = + Array::try_from(bpf.take_map("LINK_FILTERS").unwrap()).unwrap(); + + // firewall-ebpf interface + let mut ipv4_firewall: HashMap<_, u32, [u16; MAX_RULES_PORT]> = + HashMap::try_from(bpf.take_map("BLOCKLIST_IPV4").unwrap()).unwrap(); + + let mut ipv6_firewall: HashMap<_, u128, [u16; MAX_RULES_PORT]> = + HashMap::try_from(bpf.take_map("BLOCKLIST_IPV6").unwrap()).unwrap(); + + thread::spawn(move || loop { + if let Ok(signal) = firewall_egress_receiver.recv() { + match signal { + FirewallSignal::Rule(rule) => match rule.ip { + IpAddr::V4(addr) => update_ipv4_blocklist( + &mut ipv4_firewall, + addr, + rule.port, + rule.enabled, + ), + + IpAddr::V6(addr) => update_ipv6_blocklist( + &mut ipv6_firewall, + addr, + rule.port, + rule.enabled, + ), + }, + FirewallSignal::Kill => { + break; } } - }); - - thread::spawn(move || loop { - if let Ok(signal) = filter_channel_receiver.recv() { - match signal { - FilterChannelSignal::Update((filter, flag)) => match filter { - Protocol::Transport(p) => { - let _ = transport_filters.set(p as u32, flag as u32, 0); - } - Protocol::Network(p) => { - let _ = network_filters.set(p as u32, flag as u32, 0); - } - Protocol::Link(p) => { - let _ = link_filters.set(p as u32, flag as u32, 0); - } - }, - FilterChannelSignal::Kill => { - break; + } + }); + + thread::spawn(move || loop { + if let Ok(signal) = filter_channel_receiver.recv() { + match signal { + FilterChannelSignal::Update((filter, flag)) => match filter { + Protocol::Transport(p) => { + let _ = transport_filters.set(p as u32, flag as u32, 0); } + Protocol::Network(p) => { + let _ = network_filters.set(p as u32, flag as u32, 0); + } + Protocol::Link(p) => { + let _ = link_filters.set(p as u32, flag as u32, 0); + } + }, + FilterChannelSignal::Kill => { + break; } } - }); + } + }); - let mut ring_buf = RingBuffer::new(&mut bpf); + let mut ring_buf = RingBuffer::new(&mut bpf); - poll.registry() - .register( - &mut SourceFd(&ring_buf.buffer.as_raw_fd()), - Token(0), - Interest::READABLE, - ) - .unwrap(); + poll.registry() + .register( + &mut SourceFd(&ring_buf.buffer.as_raw_fd()), + Token(0), + Interest::READABLE, + ) + .unwrap(); - loop { - poll.poll(&mut events, Some(Duration::from_millis(100))) - .unwrap(); + loop { + poll.poll(&mut events, Some(Duration::from_millis(100))) + .unwrap(); + if terminate.load(std::sync::atomic::Ordering::Relaxed) { + break; + } + for event in &events { if terminate.load(std::sync::atomic::Ordering::Relaxed) { break; } - for event in &events { + if event.token() == Token(0) && event.is_readable() { if terminate.load(std::sync::atomic::Ordering::Relaxed) { break; } - if event.token() == Token(0) && event.is_readable() { + while let Some(item) = ring_buf.next() { if terminate.load(std::sync::atomic::Ordering::Relaxed) { break; } - while let Some(item) = ring_buf.next() { - if terminate.load(std::sync::atomic::Ordering::Relaxed) { - break; - } - let packet: [u8; RawPacket::LEN] = - item.to_owned().try_into().unwrap(); - data_sender.send(packet).ok(); - } + let packet: [u8; RawPacket::LEN] = item.to_owned().try_into().unwrap(); + data_sender.send(packet).ok(); } } } - - let _ = poll - .registry() - .deregister(&mut SourceFd(&ring_buf.buffer.as_raw_fd())); } - }); - } + + let _ = poll + .registry() + .deregister(&mut SourceFd(&ring_buf.buffer.as_raw_fd())); + } + }); } diff --git a/oryx-tui/src/filter.rs b/oryx-tui/src/filter.rs index cba3f3b..7eb057e 100644 --- a/oryx-tui/src/filter.rs +++ b/oryx-tui/src/filter.rs @@ -18,9 +18,9 @@ use oryx_common::{ RawPacket, }; use ratatui::{ - layout::{Alignment, Constraint, Direction, Flex, Layout, Rect}, + layout::{Alignment, Constraint, Direction, Flex, Layout, Margin, Rect}, style::{Style, Stylize}, - text::{Line, Span}, + text::{Line, Span, Text}, widgets::{Block, BorderType, Borders, Clear, Padding, Row, Table, TableState}, Frame, }; @@ -28,7 +28,10 @@ use transport::TransportFilter; use tui_big_text::{BigText, PixelSize}; use crate::{ - app::AppResult, ebpf::Ebpf, event::Event, interface::Interface, + app::AppResult, + ebpf::{load_egress, load_ingress}, + event::Event, + interface::Interface, section::firewall::FirewallSignal, }; @@ -146,7 +149,7 @@ impl Filter { .applied_direction .contains(&TrafficDirection::Ingress) { - Ebpf::load_ingress( + load_ingress( iface.clone(), notification_sender.clone(), data_sender.clone(), @@ -161,7 +164,7 @@ impl Filter { .applied_direction .contains(&TrafficDirection::Egress) { - Ebpf::load_egress( + load_egress( iface, notification_sender, data_sender, @@ -336,7 +339,7 @@ impl Filter { let iface = self.interface.selected_interface.name.clone(); - Ebpf::load_egress( + load_egress( iface, notification_sender.clone(), data_sender.clone(), @@ -378,7 +381,7 @@ impl Filter { self.traffic_direction .terminate_ingress .store(false, std::sync::atomic::Ordering::Relaxed); - Ebpf::load_ingress( + load_ingress( iface, notification_sender.clone(), data_sender.clone(), @@ -570,6 +573,16 @@ impl Filter { } pub fn render_on_setup(&mut self, frame: &mut Frame) { + let (filters_block, help_block) = { + let chunks = Layout::default() + .direction(Direction::Vertical) + .constraints([Constraint::Fill(1), Constraint::Length(3)]) + .flex(ratatui::layout::Flex::SpaceBetween) + .split(frame.area()); + + (chunks[0], chunks[1]) + }; + let ( interface_block, transport_filter_block, @@ -590,7 +603,7 @@ impl Filter { ]) .margin(1) .flex(Flex::SpaceAround) - .split(frame.area()); + .split(filters_block); ( chunks[0], chunks[1], chunks[2], chunks[3], chunks[4], chunks[5], ) @@ -638,6 +651,36 @@ impl Filter { .build(); frame.render_widget(start, start_block); + + let help = Text::from(vec![ + Line::from(""), + Line::from(vec![ + Span::from("k,").bold(), + Span::from(" Up").bold(), + Span::from(" | ").bold(), + Span::from("j,").bold(), + Span::from(" Down").bold(), + Span::from(" | ").bold(), + Span::from("󱁐 ").bold(), + Span::from(" Toggle Select").bold(), + Span::from(" | ").bold(), + Span::from("󱞦 ").bold(), + Span::from(" Apply").bold(), + Span::from(" | ").bold(), + Span::from(" ").bold(), + Span::from(" Nav").bold(), + ]), + ]) + .blue() + .centered(); + + frame.render_widget( + help, + help_block.inner(Margin { + horizontal: 1, + vertical: 0, + }), + ); } pub fn render_on_sniffing(&mut self, frame: &mut Frame, block: Rect) { @@ -762,7 +805,7 @@ impl Filter { .direction(Direction::Horizontal) .constraints([ Constraint::Fill(1), - Constraint::Length(60), + Constraint::Max(82), Constraint::Fill(1), ]) .flex(ratatui::layout::Flex::SpaceBetween) @@ -778,6 +821,7 @@ impl Filter { let chunks = Layout::default() .direction(Direction::Vertical) .constraints([ + Constraint::Length(1), Constraint::Length(NB_TRANSPORT_PROTOCOL + 4), Constraint::Length(NB_NETWORK_PROTOCOL + 4), Constraint::Length(NB_LINK_PROTOCOL + 4), @@ -787,10 +831,11 @@ impl Filter { .margin(1) .flex(Flex::SpaceBetween) .split(block); - (chunks[0], chunks[1], chunks[2], chunks[3], chunks[4]) + (chunks[1], chunks[2], chunks[3], chunks[4], chunks[5]) }; frame.render_widget(Clear, block); + frame.render_widget( Block::new() .borders(Borders::all()) diff --git a/oryx-tui/src/section.rs b/oryx-tui/src/section.rs index bec9732..7be8994 100644 --- a/oryx-tui/src/section.rs +++ b/oryx-tui/src/section.rs @@ -11,15 +11,19 @@ use firewall::{Firewall, FirewallSignal}; use inspection::Inspection; use ratatui::{ - layout::{Alignment, Rect}, + layout::{Alignment, Constraint, Direction, Layout, Margin, Rect}, style::{Color, Style, Stylize}, - text::{Line, Span}, + text::{Line, Span, Text}, widgets::{Block, BorderType, Borders, Padding}, Frame, }; use stats::Stats; -use crate::{app::AppResult, event::Event, packet::AppPacket}; +use crate::{ + app::{ActivePopup, AppResult}, + event::Event, + packet::AppPacket, +}; #[derive(Debug, PartialEq)] pub enum FocusedSection { @@ -89,6 +93,119 @@ impl Section { } } + fn render_footer_help( + &self, + frame: &mut Frame, + block: Rect, + active_popup: Option<&ActivePopup>, + ) { + let message = { + match active_popup { + Some(ActivePopup::UpdateFilters) => Line::from(vec![ + Span::from("k,").bold(), + Span::from(" Up").bold(), + Span::from(" | ").bold(), + Span::from("j,").bold(), + Span::from(" Down").bold(), + Span::from(" | ").bold(), + Span::from("󱁐 ").bold(), + Span::from(" Toggle Select").bold(), + Span::from(" | ").bold(), + Span::from("󱊷 ").bold(), + Span::from(": Discard").bold(), + Span::from(" | ").bold(), + Span::from("󱞦 ").bold(), + Span::from(" Apply").bold(), + Span::from(" | ").bold(), + Span::from(" ").bold(), + Span::from(" Nav").bold(), + ]), + Some(ActivePopup::NewFirewallRule) => Line::from(vec![ + Span::from("j,k,,").bold(), + Span::from(": Toggle Direction").bold(), + Span::from(" | ").bold(), + Span::from("󱊷 ").bold(), + Span::from(": Discard").bold(), + Span::from(" | ").bold(), + Span::from("󱞦 ").bold(), + Span::from(": Save").bold(), + Span::from(" | ").bold(), + Span::from(" ").bold(), + Span::from(": Naviguate").bold(), + ]), + Some(ActivePopup::PacketInfos) | Some(ActivePopup::Help) => Line::from(vec![ + Span::from("󱊷 ").bold(), + Span::from(": Discard Popup").bold(), + ]), + _ => match self.focused_section { + FocusedSection::Inspection => Line::from(vec![ + Span::from("k,").bold(), + Span::from(" Up").bold(), + Span::from(" | ").bold(), + Span::from("j,").bold(), + Span::from(" Down").bold(), + Span::from(" | ").bold(), + Span::from("/").bold(), + Span::from(" Search").bold(), + Span::from(" | ").bold(), + Span::from("i").bold(), + Span::from(" Infos").bold(), + Span::from(" | ").bold(), + Span::from("f").bold(), + Span::from(" Filters").bold(), + Span::from(" | ").bold(), + Span::from("󱊷 ").bold(), + Span::from(": Discard").bold(), + Span::from(" | ").bold(), + Span::from(" ").bold(), + Span::from(" Nav").bold(), + ]), + FocusedSection::Firewall => Line::from(vec![ + Span::from("k,").bold(), + Span::from(" Up").bold(), + Span::from(" | ").bold(), + Span::from("j,").bold(), + Span::from(" Down").bold(), + Span::from(" | ").bold(), + Span::from("n").bold(), + Span::from(" New").bold(), + Span::from(" | ").bold(), + Span::from("d").bold(), + Span::from(" Delete").bold(), + Span::from(" | ").bold(), + Span::from("e").bold(), + Span::from(" Edit").bold(), + Span::from(" | ").bold(), + Span::from("󱁐 ").bold(), + Span::from(" Toggle").bold(), + Span::from(" | ").bold(), + Span::from("f").bold(), + Span::from(" Filters").bold(), + Span::from(" | ").bold(), + Span::from(" ").bold(), + Span::from(" Nav").bold(), + ]), + _ => Line::from(vec![ + Span::from("f").bold(), + Span::from(" Filters").bold(), + Span::from(" | ").bold(), + Span::from(" ").bold(), + Span::from(" Nav").bold(), + ]), + }, + } + }; + + let help = Text::from(vec![Line::from(""), message]).blue().centered(); + frame.render_widget( + help, + block.inner(Margin { + horizontal: 1, + vertical: 0, + }), + ); + } + pub fn render_header(&mut self, frame: &mut Frame, block: Rect) { frame.render_widget( Block::default() @@ -109,13 +226,31 @@ impl Section { block, ); } - pub fn render(&mut self, frame: &mut Frame, block: Rect, network_interace: &str) { - self.render_header(frame, block); + pub fn render( + &mut self, + frame: &mut Frame, + block: Rect, + network_interace: &str, + active_popup: Option<&ActivePopup>, + ) { + let (section_block, help_block) = { + let chunks = Layout::default() + .direction(Direction::Vertical) + .constraints([Constraint::Fill(1), Constraint::Length(3)]) + .flex(ratatui::layout::Flex::SpaceBetween) + .split(block); + + (chunks[0], chunks[1]) + }; + + self.render_header(frame, section_block); + self.render_footer_help(frame, help_block, active_popup); + match self.focused_section { - FocusedSection::Inspection => self.inspection.render(frame, block), - FocusedSection::Stats => self.stats.render(frame, block, network_interace), - FocusedSection::Alerts => self.alert.render(frame, block), - FocusedSection::Firewall => self.firewall.render(frame, block), + FocusedSection::Inspection => self.inspection.render(frame, section_block), + FocusedSection::Stats => self.stats.render(frame, section_block, network_interace), + FocusedSection::Alerts => self.alert.render(frame, section_block), + FocusedSection::Firewall => self.firewall.render(frame, section_block), } } diff --git a/oryx-tui/src/section/firewall.rs b/oryx-tui/src/section/firewall.rs index f6b3b43..02317ef 100644 --- a/oryx-tui/src/section/firewall.rs +++ b/oryx-tui/src/section/firewall.rs @@ -253,6 +253,7 @@ impl UserInput { .block( Block::default() .title(" Firewall Rule ") + .bold() .title_alignment(ratatui::layout::Alignment::Center) .borders(Borders::all()) .border_type(ratatui::widgets::BorderType::Thick) @@ -535,6 +536,10 @@ impl Firewall { } KeyCode::Char('j') | KeyCode::Down => { + if self.rules.is_empty() { + return Ok(()); + } + let i = match self.state.selected() { Some(i) => { if i < self.rules.len() - 1 { @@ -550,6 +555,9 @@ impl Firewall { } KeyCode::Char('k') | KeyCode::Up => { + if self.rules.is_empty() { + return Ok(()); + } let i = match self.state.selected() { Some(i) => { if i > 1 {