From 850f0f32d82c7157aa3eb284319959391f3f031e Mon Sep 17 00:00:00 2001 From: adrien gaultier Date: Wed, 2 Oct 2024 21:04:27 +0200 Subject: [PATCH 01/41] shite --- oryx-tui/src/ebpf.rs | 2 + oryx-tui/src/filter.rs | 83 ++++++--- oryx-tui/src/handler.rs | 7 +- oryx-tui/src/section.rs | 135 +++++++------- oryx-tui/src/section/firewall.rs | 294 +++++++++++++++++++++++++++++++ 5 files changed, 428 insertions(+), 93 deletions(-) create mode 100644 oryx-tui/src/section/firewall.rs diff --git a/oryx-tui/src/ebpf.rs b/oryx-tui/src/ebpf.rs index edf08c8..a7d1e38 100644 --- a/oryx-tui/src/ebpf.rs +++ b/oryx-tui/src/ebpf.rs @@ -67,6 +67,7 @@ impl Ebpf { notification_sender: kanal::Sender, data_sender: kanal::Sender<[u8; RawPacket::LEN]>, filter_channel_receiver: kanal::Receiver<(Protocol, bool)>, + firewall_channel_receiver: kanal::Receiver<(Protocol, bool)>, terminate: Arc, ) { thread::spawn({ @@ -220,6 +221,7 @@ impl Ebpf { notification_sender: kanal::Sender, data_sender: kanal::Sender<[u8; RawPacket::LEN]>, filter_channel_receiver: kanal::Receiver<(Protocol, bool)>, + firewall_channel_receiver: kanal::Receiver<(Protocol, bool)>, terminate: Arc, ) { thread::spawn({ diff --git a/oryx-tui/src/filter.rs b/oryx-tui/src/filter.rs index d6c9ea6..b8fd9aa 100644 --- a/oryx-tui/src/filter.rs +++ b/oryx-tui/src/filter.rs @@ -30,19 +30,40 @@ use tui_big_text::{BigText, PixelSize}; use crate::{app::AppResult, ebpf::Ebpf, event::Event, interface::Interface}; #[derive(Debug, Clone)] -pub struct FilterChannel { +pub struct Channels { pub sender: kanal::Sender<(Protocol, bool)>, pub receiver: kanal::Receiver<(Protocol, bool)>, } -impl FilterChannel { +#[derive(Debug, Clone)] +pub struct IoChans { + pub ingress: Channels, + pub egress: Channels, +} + +impl Channels { pub fn new() -> Self { let (sender, receiver) = kanal::unbounded(); Self { sender, receiver } } } -impl Default for FilterChannel { +impl IoChans { + pub fn new() -> Self { + Self { + ingress: Channels::new(), + egress: Channels::new(), + } + } +} + +impl Default for Channels { + fn default() -> Self { + Self::new() + } +} + +impl Default for IoChans { fn default() -> Self { Self::new() } @@ -65,8 +86,8 @@ pub struct Filter { pub transport: TransportFilter, pub link: LinkFilter, pub traffic_direction: TrafficDirectionFilter, - pub ingress_channel: FilterChannel, - pub egress_channel: FilterChannel, + pub filter_chans: IoChans, + pub firewall_chans: IoChans, pub focused_block: FocusedBlock, } @@ -84,8 +105,8 @@ impl Filter { transport: TransportFilter::new(), link: LinkFilter::new(), traffic_direction: TrafficDirectionFilter::new(), - ingress_channel: FilterChannel::new(), - egress_channel: FilterChannel::new(), + filter_chans: IoChans::new(), + firewall_chans: IoChans::new(), focused_block: FocusedBlock::Interface, } } @@ -113,7 +134,8 @@ impl Filter { iface.clone(), notification_sender.clone(), data_sender.clone(), - self.ingress_channel.receiver.clone(), + self.filter_chans.ingress.receiver.clone(), + self.firewall_chans.ingress.receiver.clone(), self.traffic_direction.terminate_ingress.clone(), ); } @@ -127,7 +149,8 @@ impl Filter { iface, notification_sender, data_sender, - self.egress_channel.receiver.clone(), + self.filter_chans.egress.receiver.clone(), + self.firewall_chans.egress.receiver.clone(), self.traffic_direction.terminate_egress.clone(), ); } @@ -151,17 +174,21 @@ impl Filter { pub fn sync(&mut self) -> AppResult<()> { for protocol in TransportProtocol::all().iter() { if self.transport.applied_protocols.contains(protocol) { - self.ingress_channel + self.filter_chans + .ingress .sender .send((Protocol::Transport(*protocol), false))?; - self.egress_channel + self.filter_chans + .egress .sender .send((Protocol::Transport(*protocol), false))?; } else { - self.ingress_channel + self.filter_chans + .ingress .sender .send((Protocol::Transport(*protocol), true))?; - self.egress_channel + self.filter_chans + .egress .sender .send((Protocol::Transport(*protocol), true))?; } @@ -169,17 +196,21 @@ impl Filter { for protocol in NetworkProtocol::all().iter() { if self.network.applied_protocols.contains(protocol) { - self.ingress_channel + self.filter_chans + .ingress .sender .send((Protocol::Network(*protocol), false))?; - self.egress_channel + self.filter_chans + .egress .sender .send((Protocol::Network(*protocol), false))?; } else { - self.ingress_channel + self.filter_chans + .ingress .sender .send((Protocol::Network(*protocol), true))?; - self.egress_channel + self.filter_chans + .egress .sender .send((Protocol::Network(*protocol), true))?; } @@ -187,17 +218,21 @@ impl Filter { for protocol in LinkProtocol::all().iter() { if self.link.applied_protocols.contains(protocol) { - self.ingress_channel + self.filter_chans + .ingress .sender .send((Protocol::Link(*protocol), false))?; - self.egress_channel + self.filter_chans + .egress .sender .send((Protocol::Link(*protocol), false))?; } else { - self.ingress_channel + self.filter_chans + .ingress .sender .send((Protocol::Link(*protocol), true))?; - self.egress_channel + self.filter_chans + .egress .sender .send((Protocol::Link(*protocol), true))?; } @@ -244,7 +279,8 @@ impl Filter { iface, notification_sender.clone(), data_sender.clone(), - self.egress_channel.receiver.clone(), + self.filter_chans.egress.receiver.clone(), + self.firewall_chans.egress.receiver.clone(), self.traffic_direction.terminate_egress.clone(), ); } @@ -280,7 +316,8 @@ impl Filter { iface, notification_sender.clone(), data_sender.clone(), - self.ingress_channel.receiver.clone(), + self.filter_chans.ingress.receiver.clone(), + self.firewall_chans.ingress.receiver.clone(), self.traffic_direction.terminate_ingress.clone(), ); } diff --git a/oryx-tui/src/handler.rs b/oryx-tui/src/handler.rs index 6ffa71d..aeafcf9 100644 --- a/oryx-tui/src/handler.rs +++ b/oryx-tui/src/handler.rs @@ -81,8 +81,9 @@ pub fn handle_key_events( } if app.is_editing { - if key_event.code == KeyCode::Esc { - app.is_editing = false + match key_event.code { + KeyCode::Esc | KeyCode::Enter => app.is_editing = false, + _ => {} } app.section.handle_keys(key_event); @@ -121,7 +122,7 @@ pub fn handle_key_events( } } - KeyCode::Char('/') => { + KeyCode::Char('/') | KeyCode::Char('n') => { app.is_editing = true; app.section.handle_keys(key_event); } diff --git a/oryx-tui/src/section.rs b/oryx-tui/src/section.rs index 3cc132a..0f30efc 100644 --- a/oryx-tui/src/section.rs +++ b/oryx-tui/src/section.rs @@ -1,4 +1,5 @@ pub mod alert; +pub mod firewall; pub mod inspection; pub mod stats; @@ -6,6 +7,7 @@ use std::sync::{Arc, Mutex}; use alert::Alert; use crossterm::event::{KeyCode, KeyEvent}; +use firewall::Firewall; use inspection::Inspection; use ratatui::{ @@ -24,6 +26,7 @@ pub enum FocusedSection { Inspection, Stats, Alerts, + Firewall, } #[derive(Debug)] @@ -32,6 +35,7 @@ pub struct Section { pub inspection: Inspection, pub stats: Stats, pub alert: Alert, + pub firewall: Firewall, } impl Section { @@ -41,93 +45,90 @@ impl Section { inspection: Inspection::new(packets.clone()), stats: Stats::new(packets.clone()), alert: Alert::new(packets.clone()), + firewall: Firewall::new(), } } - - pub fn render(&mut self, frame: &mut Frame, block: Rect, network_interace: &str) { - match self.focused_section { + fn title_span(&self, header_section: FocusedSection) -> Span { + let is_focused = self.focused_section == header_section; + match header_section { FocusedSection::Inspection => { - frame.render_widget( - Block::default() - .title({ - Line::from(vec![ - Span::styled( - " Inspection ", - Style::default().bg(Color::Green).fg(Color::White).bold(), - ), - Span::from(" Stats ").fg(Color::DarkGray), - self.alert.title_span(false), - ]) - }) - .title_alignment(Alignment::Left) - .padding(Padding::top(1)) - .borders(Borders::ALL) - .style(Style::default()) - .border_type(BorderType::default()) - .border_style(Style::default().green()), - block, - ); - self.inspection.render(frame, block); + if is_focused { + Span::styled( + " Inspection ", + Style::default().bg(Color::Green).fg(Color::White).bold(), + ) + } else { + Span::from(" Inspection ").fg(Color::DarkGray) + } } FocusedSection::Stats => { - frame.render_widget( - Block::default() - .title({ - Line::from(vec![ - Span::from(" Inspection ").fg(Color::DarkGray), - Span::styled( - " Stats ", - Style::default().bg(Color::Green).fg(Color::White).bold(), - ), - self.alert.title_span(false), - ]) - }) - .title_alignment(Alignment::Left) - .padding(Padding::top(1)) - .borders(Borders::ALL) - .style(Style::default()) - .border_type(BorderType::default()) - .border_style(Style::default().green()), - block, - ); - self.stats.render(frame, block, network_interace) + if is_focused { + Span::styled( + " Stats ", + Style::default().bg(Color::Green).fg(Color::White).bold(), + ) + } else { + Span::from(" Stats ").fg(Color::DarkGray) + } } - FocusedSection::Alerts => { - frame.render_widget( - Block::default() - .title({ - Line::from(vec![ - Span::from(" Inspection ").fg(Color::DarkGray), - Span::from(" Stats ").fg(Color::DarkGray), - self.alert.title_span(true), - ]) - }) - .title_alignment(Alignment::Left) - .padding(Padding::top(1)) - .borders(Borders::ALL) - .style(Style::default()) - .border_type(BorderType::default()) - .border_style(Style::default().green()), - block, - ); - - self.alert.render(frame, block); + FocusedSection::Alerts => self.alert.title_span(is_focused), + FocusedSection::Firewall => { + if is_focused { + Span::styled( + " Firewall ", + Style::default().bg(Color::Green).fg(Color::White).bold(), + ) + } else { + Span::from(" Firewall ").fg(Color::DarkGray) + } } } } + pub fn render_header(&mut self, frame: &mut Frame, block: Rect) { + frame.render_widget( + Block::default() + .title({ + Line::from(vec![ + self.title_span(FocusedSection::Inspection), + self.title_span(FocusedSection::Stats), + self.title_span(FocusedSection::Alerts), + self.title_span(FocusedSection::Firewall), + ]) + }) + .title_alignment(Alignment::Left) + .padding(Padding::top(1)) + .borders(Borders::ALL) + .style(Style::default()) + .border_type(BorderType::default()) + .border_style(Style::default().green()), + block, + ); + } + pub fn render(&mut self, frame: &mut Frame, block: Rect, network_interace: &str) { + self.render_header(frame, block); + 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.alert.render(frame, block), + } + } + pub fn handle_keys(&mut self, key_event: KeyEvent) { match key_event.code { KeyCode::Tab => match self.focused_section { FocusedSection::Inspection => self.focused_section = FocusedSection::Stats, FocusedSection::Stats => self.focused_section = FocusedSection::Alerts, - FocusedSection::Alerts => self.focused_section = FocusedSection::Inspection, + FocusedSection::Alerts => self.focused_section = FocusedSection::Firewall, + FocusedSection::Firewall => self.focused_section = FocusedSection::Inspection, }, KeyCode::BackTab => match self.focused_section { - FocusedSection::Inspection => self.focused_section = FocusedSection::Alerts, + FocusedSection::Inspection => self.focused_section = FocusedSection::Firewall, FocusedSection::Stats => self.focused_section = FocusedSection::Inspection, FocusedSection::Alerts => self.focused_section = FocusedSection::Stats, + FocusedSection::Firewall => self.focused_section = FocusedSection::Alerts, }, _ => { diff --git a/oryx-tui/src/section/firewall.rs b/oryx-tui/src/section/firewall.rs new file mode 100644 index 0000000..e035ca4 --- /dev/null +++ b/oryx-tui/src/section/firewall.rs @@ -0,0 +1,294 @@ +use crossterm::event::{KeyCode, KeyEvent}; +use ratatui::{ + layout::{Alignment, Constraint, Flex, Rect}, + style::{Style, Stylize}, + text::Line, + widgets::{Block, Borders, Padding, Row, Table}, + Frame, +}; +use std::net::IpAddr; +use std::str::FromStr; +use tui_input::{backend::crossterm::EventHandler, Input}; + +#[derive(Debug, Clone, Default)] +pub struct FirewallRule { + name: String, + enabled: bool, + ip: Option, + port: Option, +} +impl FirewallRule { + pub fn new() -> Self { + Self { + name: "".to_string(), + enabled: false, + ip: None, + port: None, + } + } + + pub fn update(&mut self, inputs: Inputs) { + match inputs.focus { + FocusedInput::Name => self.name = inputs.name.value().into(), + FocusedInput::Ip => { + let ip = IpAddr::from_str(inputs.ip.value()); + match ip { + Ok(ipaddr) => self.ip = Some(ipaddr), + _ => {} //TODO: error notif + } + } + FocusedInput::Port => { + let p = String::from(inputs.port).parse::(); + match p { + Ok(port) => self.port = Some(port), + _ => {} //TODO: error notif + } + } + } + } +} + +#[derive(Debug, Clone)] +pub struct Firewall { + rules: Vec, + is_editing: bool, + focused_rule: Option, + inputs: Inputs, +} +#[derive(Debug, Clone)] +pub enum FocusedInput { + Name, + Ip, + Port, +} + +#[derive(Debug, Clone)] +struct Inputs { + pub name: Input, + pub ip: Input, + pub port: Input, + pub focus: FocusedInput, +} + +impl Inputs { + pub fn new() -> Self { + Self { + name: Input::new("".to_string()), + ip: Input::new("".to_string()), + port: Input::new("".to_string()), + focus: FocusedInput::Name, + } + } + pub fn reset(&mut self) { + self.name.reset(); + self.ip.reset(); + self.port.reset(); + } + + pub fn handle_event(&mut self, event: &crossterm::event::Event) { + let _ = match self.focus { + FocusedInput::Name => self.name.handle_event(event), + FocusedInput::Ip => self.ip.handle_event(event), + FocusedInput::Port => self.port.handle_event(event), + }; + } + pub fn render(&mut self, frame: &mut Frame, block: Rect) { + let edited_value = match self.focus { + FocusedInput::Name => self.name.value(), + FocusedInput::Ip => self.ip.value(), + FocusedInput::Port => self.port.value(), + }; + + Paragraph::new(format!("> {}", edited_value)) + .alignment(Alignment::Left) + .style(Style::default().white()) + .block( + Block::new() + .borders(Borders::TOP) + .title(" Search  ") + .padding(Padding::horizontal(1)) + .title_style({ Style::default().bold().yellow() }), + ); + + frame.render_widget(fuzzy, fuzzy_block); + } +} + +impl From for Inputs { + fn from(rule: FirewallRule) -> Self { + Self { + name: Input::new(rule.name), + ip: Input::new(match rule.ip { + Some(ip) => ip.to_string(), + None => "".to_string(), + }), + port: Input::new(match rule.port { + Some(port) => port.to_string(), + None => "".to_string(), + }), + focus: FocusedInput::Name, + } + } +} + +impl Firewall { + pub fn new() -> Self { + Self { + rules: Vec::new(), + is_editing: false, + focused_rule: None, + inputs: Inputs::new(), + } + } + + pub fn add_rule(&mut self, rule: FirewallRule) { + if self.rules.iter().any(|r| r.name == rule.name) { + return; + } + self.rules.push(rule); + } + pub fn remove_rule(&mut self, rule: &FirewallRule) { + self.rules.retain(|r| r.name != rule.name); + } + pub fn handle_keys(&mut self, key_event: KeyEvent) { + if self.is_editing { + match key_event.code { + KeyCode::Esc => { + self.is_editing = false; + self.inputs.reset() + } + KeyCode::Enter => { + self.is_editing = false; + self.focused_rule + .as_mut() + .unwrap() + .update(self.inputs.clone()); + self.inputs.reset(); + } + _ => { + self.inputs + .handle_event(&crossterm::event::Event::Key(key_event)); + } + } + } else { + match key_event.code { + KeyCode::Char('j') | KeyCode::Down => {} + KeyCode::Char('k') | KeyCode::Up => {} + KeyCode::Char('n') => { + self.is_editing = true; + self.add_rule(FirewallRule::new()); + } + _ => {} + } + } + } + pub fn render(&self, frame: &mut Frame, block: Rect) { + let widths = [ + Constraint::Min(30), + Constraint::Min(20), + Constraint::Length(10), + Constraint::Length(10), + ]; + + let rows = self.rules.iter().map(|rule| { + if self.is_editing && self.focused_rule.as_ref().unwrap().name == rule.name { + Row::new(vec![ + Line::from(rule.name.clone()).centered().bold(), + Line::from({ + if let Some(ip) = rule.ip { + ip.to_string() + } else { + "-".to_string() + } + }) + .centered() + .bold(), + Line::from({ + if let Some(port) = rule.port { + port.to_string() + } else { + "-".to_string() + } + }) + .centered(), + Line::from(rule.enabled.to_string()).centered(), + ]) + } else { + Row::new(vec![ + Line::from(rule.name.clone()).centered().bold(), + Line::from({ + if let Some(ip) = rule.ip { + ip.to_string() + } else { + "-".to_string() + } + }) + .centered() + .bold(), + Line::from({ + if let Some(port) = rule.port { + port.to_string() + } else { + "-".to_string() + } + }) + .centered(), + Line::from(rule.enabled.to_string()).centered(), + ]) + } + }); + let table = Table::new(rows, widths) + .column_spacing(2) + .flex(Flex::SpaceBetween) + .header( + Row::new(vec![ + Line::from("Name").centered(), + Line::from("IP Address").centered(), + Line::from("Port").centered(), + Line::from("Enabled?").centered(), + ]) + .style(Style::new().bold()) + .bottom_margin(1), + ) + .block( + Block::new() + .title(" Firewall Rules ") + .borders(Borders::all()) + .border_style(Style::new().yellow()) + .title_alignment(Alignment::Center) + .padding(Padding::uniform(2)), + ); + + frame.render_widget(table, block); + } +} + +// Paragraph::new(format!("> {}", fuzzy.filter.value())) +// .alignment(Alignment::Left) +// .style(Style::default().white()) +// .block( +// Block::new() +// .borders(Borders::TOP) +// .title(" Search  ") +// .padding(Padding::horizontal(1)) +// .title_style({ +// if fuzzy.is_paused() { +// Style::default().bold().yellow() +// } else { +// Style::default().bold().green() +// } +// }) +// .border_type({ +// if fuzzy.is_paused() { +// BorderType::default() +// } else { +// BorderType::Thick +// } +// }) +// .border_style({ +// if fuzzy.is_paused() { +// Style::default().yellow() +// } else { +// Style::default().green() +// } +// }), From 92b76c9378e386537014a5d735749a02fa1409bc Mon Sep 17 00:00:00 2001 From: Badr Date: Sat, 5 Oct 2024 10:42:01 +0200 Subject: [PATCH 02/41] enhance firewall rendering --- oryx-tui/src/app.rs | 1 + oryx-tui/src/handler.rs | 59 ++-- oryx-tui/src/section.rs | 9 +- oryx-tui/src/section/firewall.rs | 473 +++++++++++++++++-------------- oryx-tui/src/ui.rs | 1 + 5 files changed, 316 insertions(+), 227 deletions(-) diff --git a/oryx-tui/src/app.rs b/oryx-tui/src/app.rs index ae1cc1c..82e9eee 100644 --- a/oryx-tui/src/app.rs +++ b/oryx-tui/src/app.rs @@ -22,6 +22,7 @@ pub enum ActivePopup { Help, UpdateFilters, PacketInfos, + NewFirewallRule, } #[derive(Debug)] diff --git a/oryx-tui/src/handler.rs b/oryx-tui/src/handler.rs index aeafcf9..daf9145 100644 --- a/oryx-tui/src/handler.rs +++ b/oryx-tui/src/handler.rs @@ -6,6 +6,7 @@ use crate::{ export::export, filter::FocusedBlock, notification::{Notification, NotificationLevel}, + section::{FocusedSection, Section}, }; use ratatui::crossterm::event::{KeyCode, KeyEvent, KeyModifiers}; @@ -56,25 +57,41 @@ pub fn handle_key_events( match key_event.code { KeyCode::Esc => { app.active_popup = None; - if popup == ActivePopup::UpdateFilters { - app.filter.handle_key_events(key_event, true); + match popup { + ActivePopup::UpdateFilters => { + app.filter.handle_key_events(key_event, true); + } + ActivePopup::NewFirewallRule => { + app.section.firewall.handle_keys(key_event); + app.is_editing = false; + } + _ => {} } } - KeyCode::Enter => { - if popup == ActivePopup::UpdateFilters - && app.filter.focused_block == FocusedBlock::Apply - { - app.filter - .update(sender.clone(), app.data_channel_sender.clone())?; + KeyCode::Enter => match popup { + ActivePopup::UpdateFilters => { + if app.filter.focused_block == FocusedBlock::Apply { + app.filter + .update(sender.clone(), app.data_channel_sender.clone())?; - app.active_popup = None; + app.active_popup = None; + } } - } - _ => { - if popup == ActivePopup::UpdateFilters { + ActivePopup::NewFirewallRule => { + app.section.firewall.handle_keys(key_event); + } + _ => {} + }, + + _ => match popup { + ActivePopup::UpdateFilters => { app.filter.handle_key_events(key_event, true); } - } + ActivePopup::NewFirewallRule => { + app.section.firewall.handle_keys(key_event); + } + _ => {} + }, } return Ok(()); @@ -122,9 +139,19 @@ pub fn handle_key_events( } } - KeyCode::Char('/') | KeyCode::Char('n') => { - app.is_editing = true; - app.section.handle_keys(key_event); + KeyCode::Char('/') => { + if app.section.focused_section == FocusedSection::Inspection { + app.is_editing = true; + app.section.handle_keys(key_event); + } + } + + KeyCode::Char('n') => { + if app.section.focused_section == FocusedSection::Firewall { + app.is_editing = true; + app.section.handle_keys(key_event); + app.active_popup = Some(ActivePopup::NewFirewallRule); + } } KeyCode::Char('i') => { diff --git a/oryx-tui/src/section.rs b/oryx-tui/src/section.rs index 0f30efc..82373e7 100644 --- a/oryx-tui/src/section.rs +++ b/oryx-tui/src/section.rs @@ -31,7 +31,7 @@ pub enum FocusedSection { #[derive(Debug)] pub struct Section { - focused_section: FocusedSection, + pub focused_section: FocusedSection, pub inspection: Inspection, pub stats: Stats, pub alert: Alert, @@ -111,7 +111,7 @@ impl 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.alert.render(frame, block), + FocusedSection::Firewall => self.firewall.render(frame, block), } } @@ -135,6 +135,11 @@ impl Section { if self.focused_section == FocusedSection::Inspection { self.inspection.handle_keys(key_event); } + match self.focused_section { + FocusedSection::Inspection => self.inspection.handle_keys(key_event), + FocusedSection::Firewall => self.firewall.handle_keys(key_event), + _ => {} + } } } } diff --git a/oryx-tui/src/section/firewall.rs b/oryx-tui/src/section/firewall.rs index e035ca4..161bd9e 100644 --- a/oryx-tui/src/section/firewall.rs +++ b/oryx-tui/src/section/firewall.rs @@ -1,61 +1,23 @@ -use crossterm::event::{KeyCode, KeyEvent}; +use crossterm::event::{Event, KeyCode, KeyEvent}; use ratatui::{ - layout::{Alignment, Constraint, Flex, Rect}, - style::{Style, Stylize}, - text::Line, - widgets::{Block, Borders, Padding, Row, Table}, + layout::{Constraint, Direction, Flex, Layout, Margin, Rect}, + style::{Color, Style, Stylize}, + text::{Line, Text}, + widgets::{Block, Borders, Cell, Clear, HighlightSpacing, Padding, Row, Table, TableState}, Frame, }; -use std::net::IpAddr; -use std::str::FromStr; +use std::{net::IpAddr, str::FromStr}; use tui_input::{backend::crossterm::EventHandler, Input}; -#[derive(Debug, Clone, Default)] +#[derive(Debug, Clone)] pub struct FirewallRule { name: String, enabled: bool, - ip: Option, - port: Option, + ip: IpAddr, + port: u16, } -impl FirewallRule { - pub fn new() -> Self { - Self { - name: "".to_string(), - enabled: false, - ip: None, - port: None, - } - } - pub fn update(&mut self, inputs: Inputs) { - match inputs.focus { - FocusedInput::Name => self.name = inputs.name.value().into(), - FocusedInput::Ip => { - let ip = IpAddr::from_str(inputs.ip.value()); - match ip { - Ok(ipaddr) => self.ip = Some(ipaddr), - _ => {} //TODO: error notif - } - } - FocusedInput::Port => { - let p = String::from(inputs.port).parse::(); - match p { - Ok(port) => self.port = Some(port), - _ => {} //TODO: error notif - } - } - } - } -} - -#[derive(Debug, Clone)] -pub struct Firewall { - rules: Vec, - is_editing: bool, - focused_rule: Option, - inputs: Inputs, -} -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub enum FocusedInput { Name, Ip, @@ -63,232 +25,325 @@ pub enum FocusedInput { } #[derive(Debug, Clone)] -struct Inputs { - pub name: Input, - pub ip: Input, - pub port: Input, - pub focus: FocusedInput, +struct UserInput { + pub name: UserInputField, + pub ip: UserInputField, + pub port: UserInputField, + focus_input: FocusedInput, } -impl Inputs { +#[derive(Debug, Clone, Default)] +struct UserInputField { + field: Input, + error: String, +} + +impl UserInput { pub fn new() -> Self { Self { - name: Input::new("".to_string()), - ip: Input::new("".to_string()), - port: Input::new("".to_string()), - focus: FocusedInput::Name, + name: UserInputField::default(), + ip: UserInputField::default(), + port: UserInputField::default(), + focus_input: FocusedInput::Name, } } - pub fn reset(&mut self) { - self.name.reset(); - self.ip.reset(); - self.port.reset(); + + fn validate_name(&mut self) { + self.name.error.clear(); + if self.name.field.value().is_empty() { + self.name.error = "Required field.".to_string(); + } + } + + fn validate_ip(&mut self) { + self.ip.error.clear(); + if self.ip.field.value().is_empty() { + self.ip.error = "Required field.".to_string(); + } else if IpAddr::from_str(self.ip.field.value()).is_err() { + self.ip.error = "Invalid IP Address.".to_string(); + } + } + + fn validate_port(&mut self) { + self.port.error.clear(); + if self.port.field.value().is_empty() { + self.port.error = "Required field.".to_string(); + } else if u16::from_str(self.port.field.value()).is_err() { + self.port.error = "Invalid Port number.".to_string(); + } } - pub fn handle_event(&mut self, event: &crossterm::event::Event) { - let _ = match self.focus { - FocusedInput::Name => self.name.handle_event(event), - FocusedInput::Ip => self.ip.handle_event(event), - FocusedInput::Port => self.port.handle_event(event), - }; + fn validate(&mut self) { + self.validate_name(); + self.validate_ip(); + self.validate_port(); } - pub fn render(&mut self, frame: &mut Frame, block: Rect) { - let edited_value = match self.focus { - FocusedInput::Name => self.name.value(), - FocusedInput::Ip => self.ip.value(), - FocusedInput::Port => self.port.value(), - }; - - Paragraph::new(format!("> {}", edited_value)) - .alignment(Alignment::Left) - .style(Style::default().white()) + + pub fn render(&mut self, frame: &mut Frame) { + let layout = Layout::default() + .direction(Direction::Vertical) + .constraints([ + Constraint::Fill(1), + Constraint::Length(9), + Constraint::Fill(1), + ]) + .flex(ratatui::layout::Flex::SpaceBetween) + .split(frame.area()); + + let block = Layout::default() + .direction(Direction::Horizontal) + .constraints([ + Constraint::Fill(1), + Constraint::Max(80), + Constraint::Fill(1), + ]) + .flex(ratatui::layout::Flex::SpaceBetween) + .split(layout[1])[1]; + + let rows = [ + Row::new(vec![ + Cell::from(self.name.field.to_string()) + .bg({ + if self.focus_input == FocusedInput::Name { + Color::Gray + } else { + Color::DarkGray + } + }) + .fg(Color::White), + Cell::from(self.ip.field.to_string()) + .bg({ + if self.focus_input == FocusedInput::Ip { + Color::Gray + } else { + Color::DarkGray + } + }) + .fg(Color::White), + Cell::from(self.port.field.to_string()) + .bg({ + if self.focus_input == FocusedInput::Port { + Color::Gray + } else { + Color::DarkGray + } + }) + .fg(Color::White), + ]), + Row::new(vec![Cell::new(""), Cell::new(""), Cell::new("")]), + Row::new(vec![ + Cell::from(self.name.clone().error).red(), + Cell::from(self.ip.clone().error).red(), + Cell::from(self.port.clone().error).red(), + ]), + ]; + + let widths = [ + Constraint::Percentage(33), + Constraint::Percentage(33), + Constraint::Percentage(33), + ]; + + let table = Table::new(rows, widths) + .header( + Row::new(vec![ + Line::from("Name").centered(), + Line::from("IP").centered(), + Line::from("Port").centered(), + ]) + .style(Style::new().bold()) + .bottom_margin(1), + ) + .column_spacing(2) + .flex(Flex::SpaceBetween) + .highlight_spacing(HighlightSpacing::Always) .block( - Block::new() - .borders(Borders::TOP) - .title(" Search  ") - .padding(Padding::horizontal(1)) - .title_style({ Style::default().bold().yellow() }), + Block::default() + .title(" New Firewall Rule ") + .title_alignment(ratatui::layout::Alignment::Center) + .borders(Borders::all()) + .border_type(ratatui::widgets::BorderType::Thick) + .border_style(Style::default().green()) + .padding(Padding::uniform(1)), ); - frame.render_widget(fuzzy, fuzzy_block); + frame.render_widget(Clear, block); + frame.render_widget(table, block); } } -impl From for Inputs { - fn from(rule: FirewallRule) -> Self { - Self { - name: Input::new(rule.name), - ip: Input::new(match rule.ip { - Some(ip) => ip.to_string(), - None => "".to_string(), - }), - port: Input::new(match rule.port { - Some(port) => port.to_string(), - None => "".to_string(), - }), - focus: FocusedInput::Name, - } - } +#[derive(Debug, Clone, Default)] +pub struct Firewall { + rules: Vec, + state: TableState, + user_input: Option, } impl Firewall { pub fn new() -> Self { Self { rules: Vec::new(), - is_editing: false, - focused_rule: None, - inputs: Inputs::new(), + state: TableState::default(), + user_input: None, } } - pub fn add_rule(&mut self, rule: FirewallRule) { - if self.rules.iter().any(|r| r.name == rule.name) { - return; - } - self.rules.push(rule); + pub fn add_rule(&mut self) { + self.user_input = Some(UserInput::new()); } + pub fn remove_rule(&mut self, rule: &FirewallRule) { self.rules.retain(|r| r.name != rule.name); } + pub fn handle_keys(&mut self, key_event: KeyEvent) { - if self.is_editing { + if let Some(user_input) = &mut self.user_input { match key_event.code { KeyCode::Esc => { - self.is_editing = false; - self.inputs.reset() + self.user_input = None; } + KeyCode::Enter => { - self.is_editing = false; - self.focused_rule - .as_mut() - .unwrap() - .update(self.inputs.clone()); - self.inputs.reset(); + if let Some(user_input) = &mut self.user_input { + user_input.validate(); + } } - _ => { - self.inputs - .handle_event(&crossterm::event::Event::Key(key_event)); + + KeyCode::Tab => { + if let Some(user_input) = &mut self.user_input { + match user_input.focus_input { + FocusedInput::Name => user_input.focus_input = FocusedInput::Ip, + FocusedInput::Ip => user_input.focus_input = FocusedInput::Port, + FocusedInput::Port => user_input.focus_input = FocusedInput::Name, + } + } } + + _ => match user_input.focus_input { + FocusedInput::Name => { + user_input.name.field.handle_event(&Event::Key(key_event)); + } + FocusedInput::Ip => { + user_input.ip.field.handle_event(&Event::Key(key_event)); + } + FocusedInput::Port => { + user_input.port.field.handle_event(&Event::Key(key_event)); + } + }, } } else { match key_event.code { - KeyCode::Char('j') | KeyCode::Down => {} - KeyCode::Char('k') | KeyCode::Up => {} KeyCode::Char('n') => { - self.is_editing = true; - self.add_rule(FirewallRule::new()); + self.add_rule(); + } + + KeyCode::Char('j') | KeyCode::Down => { + let i = match self.state.selected() { + Some(i) => { + if i < self.rules.len() - 1 { + i + 1 + } else { + i + } + } + None => 0, + }; + + self.state.select(Some(i)); + } + + KeyCode::Char('k') | KeyCode::Up => { + let i = match self.state.selected() { + Some(i) => { + if i > 1 { + i - 1 + } else { + 0 + } + } + None => 0, + }; + + self.state.select(Some(i)); } _ => {} } } } + pub fn render(&self, frame: &mut Frame, block: Rect) { + if self.rules.is_empty() { + let text_block = Layout::default() + .direction(Direction::Vertical) + .constraints([ + Constraint::Fill(1), + Constraint::Length(3), + Constraint::Fill(1), + ]) + .flex(ratatui::layout::Flex::SpaceBetween) + .margin(2) + .split(block)[1]; + + let text = Text::from("No Rules").bold().centered(); + frame.render_widget(text, text_block); + return; + } + let widths = [ - Constraint::Min(30), - Constraint::Min(20), + Constraint::Max(30), + Constraint::Max(20), Constraint::Length(10), Constraint::Length(10), ]; let rows = self.rules.iter().map(|rule| { - if self.is_editing && self.focused_rule.as_ref().unwrap().name == rule.name { - Row::new(vec![ - Line::from(rule.name.clone()).centered().bold(), - Line::from({ - if let Some(ip) = rule.ip { - ip.to_string() - } else { - "-".to_string() - } - }) + Row::new(vec![ + Line::from(rule.name.clone()).centered().bold(), + Line::from(rule.ip.to_string()).centered().centered().bold(), + Line::from(rule.port.to_string()) .centered() - .bold(), - Line::from({ - if let Some(port) = rule.port { - port.to_string() - } else { - "-".to_string() - } - }) - .centered(), - Line::from(rule.enabled.to_string()).centered(), - ]) - } else { - Row::new(vec![ - Line::from(rule.name.clone()).centered().bold(), - Line::from({ - if let Some(ip) = rule.ip { - ip.to_string() - } else { - "-".to_string() - } - }) .centered() .bold(), - Line::from({ - if let Some(port) = rule.port { - port.to_string() - } else { - "-".to_string() - } - }) - .centered(), - Line::from(rule.enabled.to_string()).centered(), - ]) - } + Line::from({ + if rule.enabled { + "Enabled".to_string() + } else { + "Disabled".to_string() + } + }) + .centered() + .centered() + .bold(), + ]) }); + let table = Table::new(rows, widths) .column_spacing(2) - .flex(Flex::SpaceBetween) + .flex(Flex::SpaceAround) + .highlight_style(Style::default().bg(Color::DarkGray)) .header( Row::new(vec![ Line::from("Name").centered(), - Line::from("IP Address").centered(), + Line::from("IP").centered(), Line::from("Port").centered(), - Line::from("Enabled?").centered(), + Line::from("Status").centered(), ]) .style(Style::new().bold()) .bottom_margin(1), - ) - .block( - Block::new() - .title(" Firewall Rules ") - .borders(Borders::all()) - .border_style(Style::new().yellow()) - .title_alignment(Alignment::Center) - .padding(Padding::uniform(2)), ); - frame.render_widget(table, block); + frame.render_widget( + table, + block.inner(Margin { + horizontal: 2, + vertical: 2, + }), + ); } -} -// Paragraph::new(format!("> {}", fuzzy.filter.value())) -// .alignment(Alignment::Left) -// .style(Style::default().white()) -// .block( -// Block::new() -// .borders(Borders::TOP) -// .title(" Search  ") -// .padding(Padding::horizontal(1)) -// .title_style({ -// if fuzzy.is_paused() { -// Style::default().bold().yellow() -// } else { -// Style::default().bold().green() -// } -// }) -// .border_type({ -// if fuzzy.is_paused() { -// BorderType::default() -// } else { -// BorderType::Thick -// } -// }) -// .border_style({ -// if fuzzy.is_paused() { -// Style::default().yellow() -// } else { -// Style::default().green() -// } -// }), + pub fn render_new_rule_popup(&self, frame: &mut Frame) { + if let Some(user_input) = &mut self.user_input.clone() { + user_input.render(frame); + } + } +} diff --git a/oryx-tui/src/ui.rs b/oryx-tui/src/ui.rs index d30b9ca..1453a73 100644 --- a/oryx-tui/src/ui.rs +++ b/oryx-tui/src/ui.rs @@ -10,6 +10,7 @@ pub fn render(app: &mut App, frame: &mut Frame) { ActivePopup::Help => app.help.render(frame), ActivePopup::PacketInfos => app.section.inspection.render_packet_infos_popup(frame), ActivePopup::UpdateFilters => app.filter.render_update_popup(frame), + ActivePopup::NewFirewallRule => app.section.firewall.render_new_rule_popup(frame), } } for (index, notification) in app.notifications.iter().enumerate() { From 85724e21161c6f8cd37935117799811c97e37a04 Mon Sep 17 00:00:00 2001 From: Badr Date: Sat, 5 Oct 2024 12:21:31 +0200 Subject: [PATCH 03/41] handle Enter for new rule popup --- oryx-tui/src/handler.rs | 18 +++++---- oryx-tui/src/section.rs | 7 ++-- oryx-tui/src/section/firewall.rs | 68 +++++++++++++++++++++++++------- 3 files changed, 67 insertions(+), 26 deletions(-) diff --git a/oryx-tui/src/handler.rs b/oryx-tui/src/handler.rs index daf9145..dfcdeca 100644 --- a/oryx-tui/src/handler.rs +++ b/oryx-tui/src/handler.rs @@ -6,7 +6,7 @@ use crate::{ export::export, filter::FocusedBlock, notification::{Notification, NotificationLevel}, - section::{FocusedSection, Section}, + section::FocusedSection, }; use ratatui::crossterm::event::{KeyCode, KeyEvent, KeyModifiers}; @@ -62,7 +62,7 @@ pub fn handle_key_events( app.filter.handle_key_events(key_event, true); } ActivePopup::NewFirewallRule => { - app.section.firewall.handle_keys(key_event); + app.section.firewall.handle_keys(key_event)?; app.is_editing = false; } _ => {} @@ -78,7 +78,9 @@ pub fn handle_key_events( } } ActivePopup::NewFirewallRule => { - app.section.firewall.handle_keys(key_event); + if app.section.firewall.handle_keys(key_event).is_ok() { + app.active_popup = None; + } } _ => {} }, @@ -88,7 +90,7 @@ pub fn handle_key_events( app.filter.handle_key_events(key_event, true); } ActivePopup::NewFirewallRule => { - app.section.firewall.handle_keys(key_event); + app.section.firewall.handle_keys(key_event)?; } _ => {} }, @@ -103,7 +105,7 @@ pub fn handle_key_events( _ => {} } - app.section.handle_keys(key_event); + app.section.handle_keys(key_event)?; return Ok(()); } @@ -142,14 +144,14 @@ pub fn handle_key_events( KeyCode::Char('/') => { if app.section.focused_section == FocusedSection::Inspection { app.is_editing = true; - app.section.handle_keys(key_event); + app.section.handle_keys(key_event)?; } } KeyCode::Char('n') => { if app.section.focused_section == FocusedSection::Firewall { app.is_editing = true; - app.section.handle_keys(key_event); + app.section.handle_keys(key_event)?; app.active_popup = Some(ActivePopup::NewFirewallRule); } } @@ -184,7 +186,7 @@ pub fn handle_key_events( } } _ => { - app.section.handle_keys(key_event); + app.section.handle_keys(key_event)?; } } diff --git a/oryx-tui/src/section.rs b/oryx-tui/src/section.rs index 82373e7..dc97ca2 100644 --- a/oryx-tui/src/section.rs +++ b/oryx-tui/src/section.rs @@ -19,7 +19,7 @@ use ratatui::{ }; use stats::Stats; -use crate::packet::AppPacket; +use crate::{app::AppResult, packet::AppPacket}; #[derive(Debug, PartialEq)] pub enum FocusedSection { @@ -115,7 +115,7 @@ impl Section { } } - pub fn handle_keys(&mut self, key_event: KeyEvent) { + pub fn handle_keys(&mut self, key_event: KeyEvent) -> AppResult<()> { match key_event.code { KeyCode::Tab => match self.focused_section { FocusedSection::Inspection => self.focused_section = FocusedSection::Stats, @@ -137,10 +137,11 @@ impl Section { } match self.focused_section { FocusedSection::Inspection => self.inspection.handle_keys(key_event), - FocusedSection::Firewall => self.firewall.handle_keys(key_event), + FocusedSection::Firewall => self.firewall.handle_keys(key_event)?, _ => {} } } } + Ok(()) } } diff --git a/oryx-tui/src/section/firewall.rs b/oryx-tui/src/section/firewall.rs index 161bd9e..35d823a 100644 --- a/oryx-tui/src/section/firewall.rs +++ b/oryx-tui/src/section/firewall.rs @@ -9,6 +9,8 @@ use ratatui::{ use std::{net::IpAddr, str::FromStr}; use tui_input::{backend::crossterm::EventHandler, Input}; +use crate::app::AppResult; + #[derive(Debug, Clone)] pub struct FirewallRule { name: String, @@ -35,7 +37,7 @@ struct UserInput { #[derive(Debug, Clone, Default)] struct UserInputField { field: Input, - error: String, + error: Option, } impl UserInput { @@ -49,34 +51,39 @@ impl UserInput { } fn validate_name(&mut self) { - self.name.error.clear(); + self.name.error = None; if self.name.field.value().is_empty() { - self.name.error = "Required field.".to_string(); + self.name.error = Some("Required field.".to_string()); } } fn validate_ip(&mut self) { - self.ip.error.clear(); + self.ip.error = None; if self.ip.field.value().is_empty() { - self.ip.error = "Required field.".to_string(); + self.ip.error = Some("Required field.".to_string()); } else if IpAddr::from_str(self.ip.field.value()).is_err() { - self.ip.error = "Invalid IP Address.".to_string(); + self.ip.error = Some("Invalid IP Address.".to_string()); } } fn validate_port(&mut self) { - self.port.error.clear(); + self.port.error = None; if self.port.field.value().is_empty() { - self.port.error = "Required field.".to_string(); + self.port.error = Some("Required field.".to_string()); } else if u16::from_str(self.port.field.value()).is_err() { - self.port.error = "Invalid Port number.".to_string(); + self.port.error = Some("Invalid Port number.".to_string()); } } - fn validate(&mut self) { + fn validate(&mut self) -> AppResult<()> { self.validate_name(); self.validate_ip(); self.validate_port(); + + if self.name.error.is_some() || self.ip.error.is_some() || self.port.error.is_some() { + return Err("Valdidation Error".into()); + } + Ok(()) } pub fn render(&mut self, frame: &mut Frame) { @@ -132,9 +139,30 @@ impl UserInput { ]), Row::new(vec![Cell::new(""), Cell::new(""), Cell::new("")]), Row::new(vec![ - Cell::from(self.name.clone().error).red(), - Cell::from(self.ip.clone().error).red(), - Cell::from(self.port.clone().error).red(), + Cell::from({ + if let Some(error) = &self.name.error { + error.to_string() + } else { + String::new() + } + }) + .red(), + Cell::from({ + if let Some(error) = &self.ip.error { + error.to_string() + } else { + String::new() + } + }) + .red(), + Cell::from({ + if let Some(error) = &self.port.error { + error.to_string() + } else { + String::new() + } + }) + .red(), ]), ]; @@ -196,7 +224,7 @@ impl Firewall { self.rules.retain(|r| r.name != rule.name); } - pub fn handle_keys(&mut self, key_event: KeyEvent) { + pub fn handle_keys(&mut self, key_event: KeyEvent) -> AppResult<()> { if let Some(user_input) = &mut self.user_input { match key_event.code { KeyCode::Esc => { @@ -205,7 +233,15 @@ impl Firewall { KeyCode::Enter => { if let Some(user_input) = &mut self.user_input { - user_input.validate(); + user_input.validate()?; + + let rule = FirewallRule { + name: user_input.name.field.value().to_lowercase(), + ip: IpAddr::from_str(user_input.ip.field.value()).unwrap(), + port: u16::from_str(user_input.port.field.value()).unwrap(), + enabled: false, + }; + self.rules.push(rule); } } @@ -269,6 +305,8 @@ impl Firewall { _ => {} } } + + Ok(()) } pub fn render(&self, frame: &mut Frame, block: Rect) { From 7984692cf5432d4881268d3ee11c9669e08f1efa Mon Sep 17 00:00:00 2001 From: Badr Date: Sat, 5 Oct 2024 12:37:21 +0200 Subject: [PATCH 04/41] change status of a rule + delete a rule --- oryx-tui/src/handler.rs | 1 + oryx-tui/src/section/firewall.rs | 24 +++++++++++++++++++++--- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/oryx-tui/src/handler.rs b/oryx-tui/src/handler.rs index dfcdeca..bbacc0b 100644 --- a/oryx-tui/src/handler.rs +++ b/oryx-tui/src/handler.rs @@ -80,6 +80,7 @@ pub fn handle_key_events( ActivePopup::NewFirewallRule => { if app.section.firewall.handle_keys(key_event).is_ok() { app.active_popup = None; + app.is_editing = false; } } _ => {} diff --git a/oryx-tui/src/section/firewall.rs b/oryx-tui/src/section/firewall.rs index 35d823a..490bc8a 100644 --- a/oryx-tui/src/section/firewall.rs +++ b/oryx-tui/src/section/firewall.rs @@ -242,6 +242,7 @@ impl Firewall { enabled: false, }; self.rules.push(rule); + self.user_input = None; } } @@ -273,6 +274,18 @@ impl Firewall { self.add_rule(); } + KeyCode::Char(' ') => { + if let Some(index) = self.state.selected() { + self.rules[index].enabled = !self.rules[index].enabled; + } + } + + KeyCode::Char('d') => { + if let Some(index) = self.state.selected() { + self.rules.remove(index); + } + } + KeyCode::Char('j') | KeyCode::Down => { let i = match self.state.selected() { Some(i) => { @@ -309,7 +322,7 @@ impl Firewall { Ok(()) } - pub fn render(&self, frame: &mut Frame, block: Rect) { + pub fn render(&mut self, frame: &mut Frame, block: Rect) { if self.rules.is_empty() { let text_block = Layout::default() .direction(Direction::Vertical) @@ -355,9 +368,13 @@ impl Firewall { ]) }); + if self.state.selected().is_none() && !self.rules.is_empty() { + self.state.select(Some(0)); + } + let table = Table::new(rows, widths) .column_spacing(2) - .flex(Flex::SpaceAround) + .flex(Flex::SpaceBetween) .highlight_style(Style::default().bg(Color::DarkGray)) .header( Row::new(vec![ @@ -370,12 +387,13 @@ impl Firewall { .bottom_margin(1), ); - frame.render_widget( + frame.render_stateful_widget( table, block.inner(Margin { horizontal: 2, vertical: 2, }), + &mut self.state, ); } From 6a07b86245f2121ec01affee785b09a9e1f9c33d Mon Sep 17 00:00:00 2001 From: Badr Date: Sat, 5 Oct 2024 13:20:32 +0200 Subject: [PATCH 05/41] add icons --- oryx-tui/src/section.rs | 12 ++++++------ oryx-tui/src/section/alert.rs | 12 ++++++------ oryx-tui/src/section/firewall.rs | 8 ++++---- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/oryx-tui/src/section.rs b/oryx-tui/src/section.rs index dc97ca2..47060e9 100644 --- a/oryx-tui/src/section.rs +++ b/oryx-tui/src/section.rs @@ -54,32 +54,32 @@ impl Section { FocusedSection::Inspection => { if is_focused { Span::styled( - " Inspection ", + " Inspection 󰏖 ", Style::default().bg(Color::Green).fg(Color::White).bold(), ) } else { - Span::from(" Inspection ").fg(Color::DarkGray) + Span::from(" Inspection 󰏖 ").fg(Color::DarkGray) } } FocusedSection::Stats => { if is_focused { Span::styled( - " Stats ", + " Stats 󱕍 ", Style::default().bg(Color::Green).fg(Color::White).bold(), ) } else { - Span::from(" Stats ").fg(Color::DarkGray) + Span::from(" Stats 󱕍 ").fg(Color::DarkGray) } } FocusedSection::Alerts => self.alert.title_span(is_focused), FocusedSection::Firewall => { if is_focused { Span::styled( - " Firewall ", + " Firewall 󰞀 ", Style::default().bg(Color::Green).fg(Color::White).bold(), ) } else { - Span::from(" Firewall ").fg(Color::DarkGray) + Span::from(" Firewall 󰞀 ").fg(Color::DarkGray) } } } diff --git a/oryx-tui/src/section/alert.rs b/oryx-tui/src/section/alert.rs index e3c5fef..7263f57 100644 --- a/oryx-tui/src/section/alert.rs +++ b/oryx-tui/src/section/alert.rs @@ -80,24 +80,24 @@ impl Alert { if is_focused { if self.detected { if self.flash_count % 12 == 0 { - Span::from(" Alert 󰐼 ").fg(Color::White).bg(Color::Red) + Span::from(" Alert 󰐼 ").fg(Color::White).bg(Color::Red) } else { - Span::from(" Alert 󰐼 ").bg(Color::Red) + Span::from(" Alert 󰐼 ").bg(Color::Red) } } else { Span::styled( - " Alert ", + " Alert 󰀦 ", Style::default().bg(Color::Green).fg(Color::White).bold(), ) } } else if self.detected { if self.flash_count % 12 == 0 { - Span::from(" Alert 󰐼 ").fg(Color::White).bg(Color::Red) + Span::from(" Alert 󰐼 ").fg(Color::White).bg(Color::Red) } else { - Span::from(" Alert 󰐼 ").fg(Color::Red) + Span::from(" Alert 󰐼 ").fg(Color::Red) } } else { - Span::from(" Alert ").fg(Color::DarkGray) + Span::from(" Alert 󰀦 ").fg(Color::DarkGray) } } } diff --git a/oryx-tui/src/section/firewall.rs b/oryx-tui/src/section/firewall.rs index 490bc8a..867ab92 100644 --- a/oryx-tui/src/section/firewall.rs +++ b/oryx-tui/src/section/firewall.rs @@ -378,10 +378,10 @@ impl Firewall { .highlight_style(Style::default().bg(Color::DarkGray)) .header( Row::new(vec![ - Line::from("Name").centered(), - Line::from("IP").centered(), - Line::from("Port").centered(), - Line::from("Status").centered(), + Line::from("Name").centered().blue(), + Line::from("IP").centered().blue(), + Line::from("Port").centered().blue(), + Line::from("Status").centered().blue(), ]) .style(Style::new().bold()) .bottom_margin(1), From 5c7aec61154faa3e9705d46ca33d4e9a4c85b598 Mon Sep 17 00:00:00 2001 From: Badr Date: Sat, 5 Oct 2024 14:13:21 +0200 Subject: [PATCH 06/41] update Constraint val --- oryx-tui/src/section/firewall.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/oryx-tui/src/section/firewall.rs b/oryx-tui/src/section/firewall.rs index 867ab92..574a836 100644 --- a/oryx-tui/src/section/firewall.rs +++ b/oryx-tui/src/section/firewall.rs @@ -344,7 +344,7 @@ impl Firewall { Constraint::Max(30), Constraint::Max(20), Constraint::Length(10), - Constraint::Length(10), + Constraint::Length(14), ]; let rows = self.rules.iter().map(|rule| { From 4678af215244874f6134fe07bebdf1d6f255defc Mon Sep 17 00:00:00 2001 From: Badr Date: Sat, 5 Oct 2024 14:14:53 +0200 Subject: [PATCH 07/41] fix linting --- oryx-tui/src/ebpf.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/oryx-tui/src/ebpf.rs b/oryx-tui/src/ebpf.rs index a7d1e38..11bf66a 100644 --- a/oryx-tui/src/ebpf.rs +++ b/oryx-tui/src/ebpf.rs @@ -67,7 +67,7 @@ impl Ebpf { notification_sender: kanal::Sender, data_sender: kanal::Sender<[u8; RawPacket::LEN]>, filter_channel_receiver: kanal::Receiver<(Protocol, bool)>, - firewall_channel_receiver: kanal::Receiver<(Protocol, bool)>, + _firewall_channel_receiver: kanal::Receiver<(Protocol, bool)>, terminate: Arc, ) { thread::spawn({ @@ -221,7 +221,7 @@ impl Ebpf { notification_sender: kanal::Sender, data_sender: kanal::Sender<[u8; RawPacket::LEN]>, filter_channel_receiver: kanal::Receiver<(Protocol, bool)>, - firewall_channel_receiver: kanal::Receiver<(Protocol, bool)>, + _firewall_channel_receiver: kanal::Receiver<(Protocol, bool)>, terminate: Arc, ) { thread::spawn({ From 5015cc7659324fee5c10d86cce0fff82b5b064fc Mon Sep 17 00:00:00 2001 From: Badr Date: Sat, 5 Oct 2024 18:51:40 +0200 Subject: [PATCH 08/41] change fg color --- oryx-tui/src/section/firewall.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/oryx-tui/src/section/firewall.rs b/oryx-tui/src/section/firewall.rs index 574a836..35f5fe5 100644 --- a/oryx-tui/src/section/firewall.rs +++ b/oryx-tui/src/section/firewall.rs @@ -117,7 +117,7 @@ impl UserInput { Color::DarkGray } }) - .fg(Color::White), + .fg(Color::Black), Cell::from(self.ip.field.to_string()) .bg({ if self.focus_input == FocusedInput::Ip { @@ -126,7 +126,7 @@ impl UserInput { Color::DarkGray } }) - .fg(Color::White), + .fg(Color::Black), Cell::from(self.port.field.to_string()) .bg({ if self.focus_input == FocusedInput::Port { @@ -135,7 +135,7 @@ impl UserInput { Color::DarkGray } }) - .fg(Color::White), + .fg(Color::Black), ]), Row::new(vec![Cell::new(""), Cell::new(""), Cell::new("")]), Row::new(vec![ From d4914c8606bb6a04372da6b6daa0513128534ce7 Mon Sep 17 00:00:00 2001 From: Badr Date: Sat, 5 Oct 2024 20:43:41 +0200 Subject: [PATCH 09/41] remove duplicated handle keys for inspection --- oryx-tui/src/section.rs | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/oryx-tui/src/section.rs b/oryx-tui/src/section.rs index 47060e9..79f5aa7 100644 --- a/oryx-tui/src/section.rs +++ b/oryx-tui/src/section.rs @@ -131,16 +131,11 @@ impl Section { FocusedSection::Firewall => self.focused_section = FocusedSection::Alerts, }, - _ => { - if self.focused_section == FocusedSection::Inspection { - self.inspection.handle_keys(key_event); - } - match self.focused_section { - FocusedSection::Inspection => self.inspection.handle_keys(key_event), - FocusedSection::Firewall => self.firewall.handle_keys(key_event)?, - _ => {} - } - } + _ => match self.focused_section { + FocusedSection::Inspection => self.inspection.handle_keys(key_event), + FocusedSection::Firewall => self.firewall.handle_keys(key_event)?, + _ => {} + }, } Ok(()) } From dd854638dde59da0402afe6fda6bf2a2917d76f9 Mon Sep 17 00:00:00 2001 From: Badr Date: Sat, 5 Oct 2024 20:48:25 +0200 Subject: [PATCH 10/41] update setting layout --- oryx-tui/src/app.rs | 2 +- oryx-tui/src/filter.rs | 10 +++++++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/oryx-tui/src/app.rs b/oryx-tui/src/app.rs index 82e9eee..aef2f18 100644 --- a/oryx-tui/src/app.rs +++ b/oryx-tui/src/app.rs @@ -93,7 +93,7 @@ impl App { let (settings_block, section_block) = { let chunks = Layout::default() .direction(Direction::Vertical) - .constraints([Constraint::Length(8), Constraint::Fill(1)]) + .constraints([Constraint::Length(6), Constraint::Fill(1)]) .split(frame.area()); (chunks[0], chunks[1]) }; diff --git a/oryx-tui/src/filter.rs b/oryx-tui/src/filter.rs index b8fd9aa..bc7f626 100644 --- a/oryx-tui/src/filter.rs +++ b/oryx-tui/src/filter.rs @@ -578,10 +578,14 @@ impl Filter { let (filter_summury_block, interface_block) = { let chunks = Layout::default() .direction(Direction::Horizontal) - .constraints([Constraint::Percentage(50), Constraint::Percentage(50)]) - .margin(1) + .constraints([ + Constraint::Percentage(50), + Constraint::Length(1), + Constraint::Percentage(50), + ]) .split(block); - (chunks[0], chunks[1]) + + (chunks[0], chunks[2]) }; self.interface.render_on_sniffing(frame, interface_block); From 8faecd715e86bf0698bc25e347141b1d158f32df Mon Sep 17 00:00:00 2001 From: Badr Date: Sat, 5 Oct 2024 20:49:51 +0200 Subject: [PATCH 11/41] add vert margin for inspection section --- oryx-tui/src/app.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/oryx-tui/src/app.rs b/oryx-tui/src/app.rs index aef2f18..6caf277 100644 --- a/oryx-tui/src/app.rs +++ b/oryx-tui/src/app.rs @@ -93,9 +93,13 @@ impl App { let (settings_block, section_block) = { let chunks = Layout::default() .direction(Direction::Vertical) - .constraints([Constraint::Length(6), Constraint::Fill(1)]) + .constraints([ + Constraint::Length(6), + Constraint::Length(1), + Constraint::Fill(1), + ]) .split(frame.area()); - (chunks[0], chunks[1]) + (chunks[0], chunks[2]) }; self.section.render( From 988887a8f2a755ec52c5e36974107521546560ff Mon Sep 17 00:00:00 2001 From: Badr Date: Sat, 5 Oct 2024 22:25:10 +0200 Subject: [PATCH 12/41] edit firewall rules --- Cargo.lock | 10 ++++++ oryx-tui/Cargo.toml | 1 + oryx-tui/src/app.rs | 7 ++-- oryx-tui/src/ebpf.rs | 3 +- oryx-tui/src/filter.rs | 18 +++++----- oryx-tui/src/handler.rs | 2 +- oryx-tui/src/section.rs | 9 +++-- oryx-tui/src/section/firewall.rs | 62 ++++++++++++++++++++++++++------ 8 files changed, 84 insertions(+), 28 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f44dfbd..0753626 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -611,6 +611,7 @@ dependencies = [ "ratatui", "tui-big-text", "tui-input", + "uuid", ] [[package]] @@ -906,6 +907,15 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" +[[package]] +name = "uuid" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81dfa00651efa65069b0b6b651f4aaa31ba9e3c3ce0137aaad053604ee7e0314" +dependencies = [ + "getrandom", +] + [[package]] name = "version_check" version = "0.9.5" diff --git a/oryx-tui/Cargo.toml b/oryx-tui/Cargo.toml index 0c97533..3960aa3 100644 --- a/oryx-tui/Cargo.toml +++ b/oryx-tui/Cargo.toml @@ -24,6 +24,7 @@ kanal = "0.1.0-pre8" mimalloc = "0.1" clap = { version = "4", features = ["derive", "cargo"] } network-types = "0.0.7" +uuid = { version = "1", default-features = false, features = ["v4"] } [[bin]] name = "oryx" diff --git a/oryx-tui/src/app.rs b/oryx-tui/src/app.rs index 6caf277..5f93699 100644 --- a/oryx-tui/src/app.rs +++ b/oryx-tui/src/app.rs @@ -56,6 +56,9 @@ impl App { let packets = Arc::new(Mutex::new(Vec::with_capacity(AppPacket::LEN * 1024 * 1024))); let (sender, receiver) = kanal::unbounded(); + + let (firewall_ingress_sender, firewall_egress_receiver) = kanal::unbounded(); + thread::spawn({ let packets = packets.clone(); move || loop { @@ -73,11 +76,11 @@ impl App { Self { running: true, help: Help::new(), - filter: Filter::new(), + filter: Filter::new(firewall_egress_receiver), start_sniffing: false, packets: packets.clone(), notifications: Vec::new(), - section: Section::new(packets.clone()), + section: Section::new(packets.clone(), firewall_ingress_sender), data_channel_sender: sender, is_editing: false, active_popup: None, diff --git a/oryx-tui/src/ebpf.rs b/oryx-tui/src/ebpf.rs index 11bf66a..a77b8d2 100644 --- a/oryx-tui/src/ebpf.rs +++ b/oryx-tui/src/ebpf.rs @@ -17,6 +17,7 @@ use oryx_common::{protocols::Protocol, RawPacket}; use crate::{ event::Event, notification::{Notification, NotificationLevel}, + section::firewall::FirewallRule, }; use mio::{event::Source, unix::SourceFd, Events, Interest, Poll, Registry, Token}; @@ -67,7 +68,7 @@ impl Ebpf { notification_sender: kanal::Sender, data_sender: kanal::Sender<[u8; RawPacket::LEN]>, filter_channel_receiver: kanal::Receiver<(Protocol, bool)>, - _firewall_channel_receiver: kanal::Receiver<(Protocol, bool)>, + _firewall_ingress_receiver: kanal::Receiver, terminate: Arc, ) { thread::spawn({ diff --git a/oryx-tui/src/filter.rs b/oryx-tui/src/filter.rs index bc7f626..764a37a 100644 --- a/oryx-tui/src/filter.rs +++ b/oryx-tui/src/filter.rs @@ -27,7 +27,9 @@ use ratatui::{ use transport::TransportFilter; use tui_big_text::{BigText, PixelSize}; -use crate::{app::AppResult, ebpf::Ebpf, event::Event, interface::Interface}; +use crate::{ + app::AppResult, ebpf::Ebpf, event::Event, interface::Interface, section::firewall::FirewallRule, +}; #[derive(Debug, Clone)] pub struct Channels { @@ -89,16 +91,11 @@ pub struct Filter { pub filter_chans: IoChans, pub firewall_chans: IoChans, pub focused_block: FocusedBlock, -} - -impl Default for Filter { - fn default() -> Self { - Self::new() - } + pub firewall_ingress_receiver: kanal::Receiver, } impl Filter { - pub fn new() -> Self { + pub fn new(firewall_ingress_receiver: kanal::Receiver) -> Self { Self { interface: Interface::new(), network: NetworkFilter::new(), @@ -108,6 +105,7 @@ impl Filter { filter_chans: IoChans::new(), firewall_chans: IoChans::new(), focused_block: FocusedBlock::Interface, + firewall_ingress_receiver, } } @@ -135,7 +133,7 @@ impl Filter { notification_sender.clone(), data_sender.clone(), self.filter_chans.ingress.receiver.clone(), - self.firewall_chans.ingress.receiver.clone(), + self.firewall_ingress_receiver.clone(), self.traffic_direction.terminate_ingress.clone(), ); } @@ -317,7 +315,7 @@ impl Filter { notification_sender.clone(), data_sender.clone(), self.filter_chans.ingress.receiver.clone(), - self.firewall_chans.ingress.receiver.clone(), + self.firewall_ingress_receiver.clone(), self.traffic_direction.terminate_ingress.clone(), ); } diff --git a/oryx-tui/src/handler.rs b/oryx-tui/src/handler.rs index bbacc0b..6ed37eb 100644 --- a/oryx-tui/src/handler.rs +++ b/oryx-tui/src/handler.rs @@ -149,7 +149,7 @@ pub fn handle_key_events( } } - KeyCode::Char('n') => { + KeyCode::Char('n') | KeyCode::Char('e') => { if app.section.focused_section == FocusedSection::Firewall { app.is_editing = true; app.section.handle_keys(key_event)?; diff --git a/oryx-tui/src/section.rs b/oryx-tui/src/section.rs index 79f5aa7..e0fa47e 100644 --- a/oryx-tui/src/section.rs +++ b/oryx-tui/src/section.rs @@ -7,7 +7,7 @@ use std::sync::{Arc, Mutex}; use alert::Alert; use crossterm::event::{KeyCode, KeyEvent}; -use firewall::Firewall; +use firewall::{Firewall, FirewallRule}; use inspection::Inspection; use ratatui::{ @@ -39,13 +39,16 @@ pub struct Section { } impl Section { - pub fn new(packets: Arc>>) -> Self { + pub fn new( + packets: Arc>>, + firewall_ingress_sender: kanal::Sender, + ) -> Self { Self { focused_section: FocusedSection::Inspection, inspection: Inspection::new(packets.clone()), stats: Stats::new(packets.clone()), alert: Alert::new(packets.clone()), - firewall: Firewall::new(), + firewall: Firewall::new(firewall_ingress_sender), } } fn title_span(&self, header_section: FocusedSection) -> Span { diff --git a/oryx-tui/src/section/firewall.rs b/oryx-tui/src/section/firewall.rs index 35f5fe5..a1d3ce4 100644 --- a/oryx-tui/src/section/firewall.rs +++ b/oryx-tui/src/section/firewall.rs @@ -8,11 +8,13 @@ use ratatui::{ }; use std::{net::IpAddr, str::FromStr}; use tui_input::{backend::crossterm::EventHandler, Input}; +use uuid; use crate::app::AppResult; #[derive(Debug, Clone)] pub struct FirewallRule { + id: uuid::Uuid, name: String, enabled: bool, ip: IpAddr, @@ -28,6 +30,7 @@ pub enum FocusedInput { #[derive(Debug, Clone)] struct UserInput { + id: Option, pub name: UserInputField, pub ip: UserInputField, pub port: UserInputField, @@ -43,6 +46,7 @@ struct UserInputField { impl UserInput { pub fn new() -> Self { Self { + id: None, name: UserInputField::default(), ip: UserInputField::default(), port: UserInputField::default(), @@ -187,7 +191,7 @@ impl UserInput { .highlight_spacing(HighlightSpacing::Always) .block( Block::default() - .title(" New Firewall Rule ") + .title(" Firewall Rule ") .title_alignment(ratatui::layout::Alignment::Center) .borders(Borders::all()) .border_type(ratatui::widgets::BorderType::Thick) @@ -200,19 +204,21 @@ impl UserInput { } } -#[derive(Debug, Clone, Default)] +#[derive(Debug, Clone)] pub struct Firewall { rules: Vec, state: TableState, user_input: Option, + ingress_sender: kanal::Sender, } impl Firewall { - pub fn new() -> Self { + pub fn new(ingress_sender: kanal::Sender) -> Self { Self { rules: Vec::new(), state: TableState::default(), user_input: None, + ingress_sender, } } @@ -235,14 +241,22 @@ impl Firewall { if let Some(user_input) = &mut self.user_input { user_input.validate()?; - let rule = FirewallRule { - name: user_input.name.field.value().to_lowercase(), - ip: IpAddr::from_str(user_input.ip.field.value()).unwrap(), - port: u16::from_str(user_input.port.field.value()).unwrap(), - enabled: false, - }; - self.rules.push(rule); - self.user_input = None; + if let Some(id) = user_input.id { + let rule = self.rules.iter_mut().find(|rule| rule.id == id).unwrap(); + rule.name = user_input.name.field.to_string(); + rule.ip = IpAddr::from_str(user_input.ip.field.value()).unwrap(); + rule.port = u16::from_str(user_input.port.field.value()).unwrap(); + } else { + let rule = FirewallRule { + id: uuid::Uuid::new_v4(), + name: user_input.name.field.value().to_lowercase(), + ip: IpAddr::from_str(user_input.ip.field.value()).unwrap(), + port: u16::from_str(user_input.port.field.value()).unwrap(), + enabled: false, + }; + self.rules.push(rule); + self.user_input = None; + } } } @@ -277,6 +291,32 @@ impl Firewall { KeyCode::Char(' ') => { if let Some(index) = self.state.selected() { self.rules[index].enabled = !self.rules[index].enabled; + self.ingress_sender.send(self.rules[index].clone())? + } + } + + KeyCode::Char('e') => { + if let Some(index) = self.state.selected() { + let rule = self.rules[index].clone(); + + let user_input = UserInput { + id: Some(rule.id), + name: UserInputField { + field: Input::from(rule.name), + error: None, + }, + ip: UserInputField { + field: Input::from(rule.ip.to_string()), + error: None, + }, + port: UserInputField { + field: Input::from(rule.port.to_string()), + error: None, + }, + focus_input: FocusedInput::Name, + }; + + self.user_input = Some(user_input); } } From d0d42bc5c8aac0e7861e20c3c644f14b93533bbb Mon Sep 17 00:00:00 2001 From: adrien gaultier Date: Sat, 5 Oct 2024 23:12:53 +0200 Subject: [PATCH 13/41] little fixes --- oryx-tui/src/app.rs | 4 +-- oryx-tui/src/section/firewall.rs | 45 +++++++++++++++++--------------- 2 files changed, 26 insertions(+), 23 deletions(-) diff --git a/oryx-tui/src/app.rs b/oryx-tui/src/app.rs index 5f93699..9c82fb3 100644 --- a/oryx-tui/src/app.rs +++ b/oryx-tui/src/app.rs @@ -57,7 +57,7 @@ impl App { let (sender, receiver) = kanal::unbounded(); - let (firewall_ingress_sender, firewall_egress_receiver) = kanal::unbounded(); + let (firewall_ingress_sender, firewall_ingress_receiver) = kanal::unbounded(); thread::spawn({ let packets = packets.clone(); @@ -76,7 +76,7 @@ impl App { Self { running: true, help: Help::new(), - filter: Filter::new(firewall_egress_receiver), + filter: Filter::new(firewall_ingress_receiver), start_sniffing: false, packets: packets.clone(), notifications: Vec::new(), diff --git a/oryx-tui/src/section/firewall.rs b/oryx-tui/src/section/firewall.rs index a1d3ce4..40455ed 100644 --- a/oryx-tui/src/section/firewall.rs +++ b/oryx-tui/src/section/firewall.rs @@ -204,6 +204,27 @@ impl UserInput { } } +impl From for UserInput { + fn from(rule: FirewallRule) -> Self { + Self { + id: Some(rule.id), + name: UserInputField { + field: Input::from(rule.name), + error: None, + }, + ip: UserInputField { + field: Input::from(rule.ip.to_string()), + error: None, + }, + port: UserInputField { + field: Input::from(rule.port.to_string()), + error: None, + }, + focus_input: FocusedInput::Name, + } + } +} + #[derive(Debug, Clone)] pub struct Firewall { rules: Vec, @@ -249,14 +270,14 @@ impl Firewall { } else { let rule = FirewallRule { id: uuid::Uuid::new_v4(), - name: user_input.name.field.value().to_lowercase(), + name: user_input.name.field.to_string(), ip: IpAddr::from_str(user_input.ip.field.value()).unwrap(), port: u16::from_str(user_input.port.field.value()).unwrap(), enabled: false, }; self.rules.push(rule); - self.user_input = None; } + self.user_input = None; } } @@ -298,25 +319,7 @@ impl Firewall { KeyCode::Char('e') => { if let Some(index) = self.state.selected() { let rule = self.rules[index].clone(); - - let user_input = UserInput { - id: Some(rule.id), - name: UserInputField { - field: Input::from(rule.name), - error: None, - }, - ip: UserInputField { - field: Input::from(rule.ip.to_string()), - error: None, - }, - port: UserInputField { - field: Input::from(rule.port.to_string()), - error: None, - }, - focus_input: FocusedInput::Name, - }; - - self.user_input = Some(user_input); + self.user_input = Some(rule.into()); } } From 6e9b208150099504059dbac0ca1bc443cb21da62 Mon Sep 17 00:00:00 2001 From: adrien gaultier Date: Sun, 6 Oct 2024 21:17:09 +0200 Subject: [PATCH 14/41] updates --- oryx-common/src/lib.rs | 13 ++++- oryx-ebpf/src/main.rs | 90 ++++++++++++++++++++++++-------- oryx-tui/src/ebpf.rs | 40 +++++++++++++- oryx-tui/src/section/firewall.rs | 12 +++-- 4 files changed, 128 insertions(+), 27 deletions(-) diff --git a/oryx-common/src/lib.rs b/oryx-common/src/lib.rs index 32483fb..c6ffc7c 100644 --- a/oryx-common/src/lib.rs +++ b/oryx-common/src/lib.rs @@ -1,6 +1,6 @@ #![no_std] -use core::mem; +use core::mem::{self, transmute}; use network_types::{arp::ArpHdr, icmp::IcmpHdr, ip::IpHdr, tcp::TcpHdr, udp::UdpHdr}; @@ -24,3 +24,14 @@ pub enum ProtoHdr { impl RawPacket { pub const LEN: usize = mem::size_of::(); } + +pub fn to_u128(x: [u16; 8]) -> u128 { + // (u128::from(x[0]) << 96) + // | (u128::from(x[1]) << 64) + // | (u128::from(x[2]) << 32) + // | u128::from(x[3]) + // } + + let addr16 = x.map(|x| x.to_be()); + u128::from_be_bytes(unsafe { transmute::<_, [u8; 16]>(addr16) }) +} diff --git a/oryx-ebpf/src/main.rs b/oryx-ebpf/src/main.rs index f124f3d..be369d5 100644 --- a/oryx-ebpf/src/main.rs +++ b/oryx-ebpf/src/main.rs @@ -2,9 +2,9 @@ #![no_main] use aya_ebpf::{ - bindings::TC_ACT_PIPE, + bindings::{TC_ACT_PIPE, TC_ACT_SHOT}, macros::{classifier, map}, - maps::{Array, RingBuf}, + maps::{Array, HashMap, RingBuf}, programs::TcContext, }; use core::mem; @@ -18,7 +18,7 @@ use network_types::{ }; use oryx_common::{ protocols::{LinkProtocol, NetworkProtocol, Protocol, TransportProtocol}, - ProtoHdr, RawPacket, + to_u128, ProtoHdr, RawPacket, }; #[map] @@ -33,6 +33,15 @@ static TRANSPORT_FILTERS: Array = Array::with_max_entries(8, 0); #[map] static LINK_FILTERS: Array = Array::with_max_entries(8, 0); +#[map] +static BLOCKLIST_IPV6_INGRESS: HashMap = HashMap::::with_max_entries(128, 0); +#[map] +static BLOCKLIST_IPV6_EGRESS: HashMap = HashMap::::with_max_entries(128, 0); +#[map] +static BLOCKLIST_IPV4_INGRESS: HashMap = HashMap::::with_max_entries(128, 0); +#[map] +static BLOCKLIST_IPV4_EGRESS: HashMap = HashMap::::with_max_entries(128, 0); + #[classifier] pub fn oryx(ctx: TcContext) -> i32 { match process(ctx) { @@ -89,26 +98,47 @@ fn process(ctx: TcContext) -> Result { match ethhdr.ether_type { EtherType::Ipv4 => { - if filter_packet(Protocol::Network(NetworkProtocol::Ipv4)) { - return Ok(TC_ACT_PIPE); - } let header: Ipv4Hdr = ctx.load(EthHdr::LEN).map_err(|_| ())?; + let src_addr = header.src_addr; + let dst_addr = header.dst_addr; + match header.proto { IpProto::Tcp => { - if filter_packet(Protocol::Transport(TransportProtocol::TCP)) { - return Ok(TC_ACT_PIPE); - } let tcphdr: *const TcpHdr = ptr_at(&ctx, EthHdr::LEN + Ipv4Hdr::LEN)?; + let src_port = u16::from_be(unsafe { (*tcphdr).source }); + let dst_port = u16::from_be(unsafe { (*tcphdr).dest }); + + if unsafe { BLOCKLIST_IPV4_INGRESS.get(&src_addr) } == Some(&src_port) + || unsafe { BLOCKLIST_IPV4_EGRESS.get(&dst_addr) } == Some(&dst_port) + { + return Ok(TC_ACT_SHOT); //DROP PACKET + } + + if filter_packet(Protocol::Network(NetworkProtocol::Ipv4)) + || filter_packet(Protocol::Transport(TransportProtocol::TCP)) + { + return Ok(TC_ACT_PIPE); //DONT FWD PACKET TO TUI + } submit(RawPacket::Ip( IpHdr::V4(header), ProtoHdr::Tcp(unsafe { *tcphdr }), )); } IpProto::Udp => { - if filter_packet(Protocol::Transport(TransportProtocol::UDP)) { - return Ok(TC_ACT_PIPE); - } let udphdr: *const UdpHdr = ptr_at(&ctx, EthHdr::LEN + Ipv4Hdr::LEN)?; + let src_port = u16::from_be(unsafe { (*udphdr).source }); + let dst_port = u16::from_be(unsafe { (*udphdr).dest }); + + if unsafe { BLOCKLIST_IPV4_INGRESS.get(&src_addr) } == Some(&src_port) + || unsafe { BLOCKLIST_IPV4_EGRESS.get(&dst_addr) } == Some(&dst_port) + { + return Ok(TC_ACT_SHOT); //DROP PACKET + } + if filter_packet(Protocol::Network(NetworkProtocol::Ipv4)) + || filter_packet(Protocol::Transport(TransportProtocol::UDP)) + { + return Ok(TC_ACT_PIPE); //DONT FWD PACKET TO TUI + } submit(RawPacket::Ip( IpHdr::V4(header), ProtoHdr::Udp(unsafe { *udphdr }), @@ -128,26 +158,44 @@ fn process(ctx: TcContext) -> Result { } } EtherType::Ipv6 => { - if filter_packet(Protocol::Network(NetworkProtocol::Ipv6)) { - return Ok(TC_ACT_PIPE); - } let header: Ipv6Hdr = ctx.load(EthHdr::LEN).map_err(|_| ())?; + let src_addr = to_u128(unsafe { header.src_addr.in6_u.u6_addr16 }); + let dst_addr = to_u128(unsafe { header.dst_addr.in6_u.u6_addr16 }); + match header.next_hdr { IpProto::Tcp => { - if filter_packet(Protocol::Transport(TransportProtocol::TCP)) { - return Ok(TC_ACT_PIPE); - } let tcphdr: *const TcpHdr = ptr_at(&ctx, EthHdr::LEN + Ipv6Hdr::LEN)?; + let src_port = u16::from_be(unsafe { (*tcphdr).source }); + let dst_port = u16::from_be(unsafe { (*tcphdr).dest }); + if unsafe { BLOCKLIST_IPV6_INGRESS.get(&src_addr) } == Some(&src_port) + || unsafe { BLOCKLIST_IPV6_EGRESS.get(&dst_addr) } == Some(&dst_port) + { + return Ok(TC_ACT_SHOT); //DROP PACKET + } + if filter_packet(Protocol::Network(NetworkProtocol::Ipv6)) + || filter_packet(Protocol::Transport(TransportProtocol::TCP)) + { + return Ok(TC_ACT_PIPE); //DONT FWD PACKET TO TUI + } submit(RawPacket::Ip( IpHdr::V6(header), ProtoHdr::Tcp(unsafe { *tcphdr }), )); } IpProto::Udp => { - if filter_packet(Protocol::Transport(TransportProtocol::UDP)) { - return Ok(TC_ACT_PIPE); - } let udphdr: *const UdpHdr = ptr_at(&ctx, EthHdr::LEN + Ipv6Hdr::LEN)?; + let src_port = u16::from_be(unsafe { (*udphdr).source }); + let dst_port = u16::from_be(unsafe { (*udphdr).dest }); + if unsafe { BLOCKLIST_IPV6_INGRESS.get(&src_addr.into()) } == Some(&src_port) + || unsafe { BLOCKLIST_IPV6_EGRESS.get(&dst_addr.into()) } == Some(&dst_port) + { + return Ok(TC_ACT_SHOT); //DROP PACKET + } + if filter_packet(Protocol::Network(NetworkProtocol::Ipv6)) + || filter_packet(Protocol::Transport(TransportProtocol::UDP)) + { + return Ok(TC_ACT_PIPE); //DONT FWD PACKET TO TUI + } submit(RawPacket::Ip( IpHdr::V6(header), ProtoHdr::Udp(unsafe { *udphdr }), diff --git a/oryx-tui/src/ebpf.rs b/oryx-tui/src/ebpf.rs index a77b8d2..3960108 100644 --- a/oryx-tui/src/ebpf.rs +++ b/oryx-tui/src/ebpf.rs @@ -1,5 +1,6 @@ use std::{ io, + net::IpAddr, os::fd::AsRawFd, sync::{atomic::AtomicBool, Arc}, thread::{self, spawn}, @@ -8,7 +9,7 @@ use std::{ use aya::{ include_bytes_aligned, - maps::{ring_buf::RingBufItem, Array, MapData, RingBuf}, + maps::{ring_buf::RingBufItem, Array, HashMap, MapData, RingBuf}, programs::{tc, SchedClassifier, TcAttachType}, Bpf, }; @@ -68,7 +69,7 @@ impl Ebpf { notification_sender: kanal::Sender, data_sender: kanal::Sender<[u8; RawPacket::LEN]>, filter_channel_receiver: kanal::Receiver<(Protocol, bool)>, - _firewall_ingress_receiver: kanal::Receiver, + firewall_ingress_receiver: kanal::Receiver, terminate: Arc, ) { thread::spawn({ @@ -149,6 +150,7 @@ impl Ebpf { 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(); @@ -157,8 +159,42 @@ impl Ebpf { 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> = + HashMap::try_from(bpf.take_map("BLOCKLIST_IPV4_INGRESS").unwrap()).unwrap(); + let mut ipv6_firewall: HashMap<_, u128, u16> = + HashMap::try_from(bpf.take_map("BLOCKLIST_IPV6_INGRESS").unwrap()).unwrap(); spawn(move || loop { + if let Ok(rule) = firewall_ingress_receiver.recv() { + match rule.enabled { + true => match rule.ip { + IpAddr::V4(addr) => { + println!("{}", rule); + ipv4_firewall.insert(u32::from(addr), rule.port, 0).unwrap(); + } + IpAddr::V6(addr) => { + let _ = ipv6_firewall.insert( + u128::from_be_bytes(addr.octets()), + rule.port, + 0, + ); + } + }, + + false => match rule.ip { + IpAddr::V4(addr) => { + let _ = ipv4_firewall.remove(&u32::from(addr)); + } + + IpAddr::V6(addr) => { + let _ = + ipv6_firewall.remove(&u128::from_be_bytes(addr.octets())); + } + }, + } + } + if let Ok((filter, flag)) = filter_channel_receiver.recv() { match filter { Protocol::Transport(p) => { diff --git a/oryx-tui/src/section/firewall.rs b/oryx-tui/src/section/firewall.rs index 40455ed..a87bf3c 100644 --- a/oryx-tui/src/section/firewall.rs +++ b/oryx-tui/src/section/firewall.rs @@ -1,3 +1,4 @@ +use core::fmt::Display; use crossterm::event::{Event, KeyCode, KeyEvent}; use ratatui::{ layout::{Constraint, Direction, Flex, Layout, Margin, Rect}, @@ -16,11 +17,16 @@ use crate::app::AppResult; pub struct FirewallRule { id: uuid::Uuid, name: String, - enabled: bool, - ip: IpAddr, - port: u16, + pub enabled: bool, + pub ip: IpAddr, + pub port: u16, } +impl Display for FirewallRule { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{} {}", self.ip, self.port) + } +} #[derive(Debug, Clone, PartialEq)] pub enum FocusedInput { Name, From 3db3bea9f3f0c53cfecd05f86f2a7f40dcbeee71 Mon Sep 17 00:00:00 2001 From: Badr Date: Mon, 7 Oct 2024 00:55:26 +0200 Subject: [PATCH 15/41] sync filters at start --- oryx-tui/src/filter.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/oryx-tui/src/filter.rs b/oryx-tui/src/filter.rs index 764a37a..b42774d 100644 --- a/oryx-tui/src/filter.rs +++ b/oryx-tui/src/filter.rs @@ -118,7 +118,7 @@ impl Filter { &mut self, notification_sender: kanal::Sender, data_sender: kanal::Sender<[u8; RawPacket::LEN]>, - ) { + ) -> AppResult<()> { let iface = self.interface.selected_interface.name.clone(); self.apply(); @@ -152,6 +152,10 @@ impl Filter { self.traffic_direction.terminate_egress.clone(), ); } + + self.sync()?; + + Ok(()) } pub fn trigger(&mut self) { From f3db11e7cc4ed48282f3374ba2f973095f784457 Mon Sep 17 00:00:00 2001 From: Badr Date: Mon, 7 Oct 2024 00:55:49 +0200 Subject: [PATCH 16/41] fix firewall --- oryx-common/src/lib.rs | 13 +------------ oryx-ebpf/src/main.rs | 32 ++++++++++---------------------- oryx-tui/src/ebpf.rs | 9 +++++---- oryx-tui/src/handler.rs | 2 +- 4 files changed, 17 insertions(+), 39 deletions(-) diff --git a/oryx-common/src/lib.rs b/oryx-common/src/lib.rs index c6ffc7c..32483fb 100644 --- a/oryx-common/src/lib.rs +++ b/oryx-common/src/lib.rs @@ -1,6 +1,6 @@ #![no_std] -use core::mem::{self, transmute}; +use core::mem; use network_types::{arp::ArpHdr, icmp::IcmpHdr, ip::IpHdr, tcp::TcpHdr, udp::UdpHdr}; @@ -24,14 +24,3 @@ pub enum ProtoHdr { impl RawPacket { pub const LEN: usize = mem::size_of::(); } - -pub fn to_u128(x: [u16; 8]) -> u128 { - // (u128::from(x[0]) << 96) - // | (u128::from(x[1]) << 64) - // | (u128::from(x[2]) << 32) - // | u128::from(x[3]) - // } - - let addr16 = x.map(|x| x.to_be()); - u128::from_be_bytes(unsafe { transmute::<_, [u8; 16]>(addr16) }) -} diff --git a/oryx-ebpf/src/main.rs b/oryx-ebpf/src/main.rs index be369d5..a2e1837 100644 --- a/oryx-ebpf/src/main.rs +++ b/oryx-ebpf/src/main.rs @@ -18,7 +18,7 @@ use network_types::{ }; use oryx_common::{ protocols::{LinkProtocol, NetworkProtocol, Protocol, TransportProtocol}, - to_u128, ProtoHdr, RawPacket, + ProtoHdr, RawPacket, }; #[map] @@ -99,8 +99,8 @@ fn process(ctx: TcContext) -> Result { match ethhdr.ether_type { EtherType::Ipv4 => { let header: Ipv4Hdr = ctx.load(EthHdr::LEN).map_err(|_| ())?; - let src_addr = header.src_addr; - let dst_addr = header.dst_addr; + let src_addr = u32::from_be(header.src_addr); + let dst_addr = u32::from_be(header.dst_addr); match header.proto { IpProto::Tcp => { @@ -111,7 +111,7 @@ fn process(ctx: TcContext) -> Result { if unsafe { BLOCKLIST_IPV4_INGRESS.get(&src_addr) } == Some(&src_port) || unsafe { BLOCKLIST_IPV4_EGRESS.get(&dst_addr) } == Some(&dst_port) { - return Ok(TC_ACT_SHOT); //DROP PACKET + return Ok(TC_ACT_SHOT); } if filter_packet(Protocol::Network(NetworkProtocol::Ipv4)) @@ -132,13 +132,15 @@ fn process(ctx: TcContext) -> Result { if unsafe { BLOCKLIST_IPV4_INGRESS.get(&src_addr) } == Some(&src_port) || unsafe { BLOCKLIST_IPV4_EGRESS.get(&dst_addr) } == Some(&dst_port) { - return Ok(TC_ACT_SHOT); //DROP PACKET + return Ok(TC_ACT_SHOT); } + if filter_packet(Protocol::Network(NetworkProtocol::Ipv4)) || filter_packet(Protocol::Transport(TransportProtocol::UDP)) { - return Ok(TC_ACT_PIPE); //DONT FWD PACKET TO TUI + return Ok(TC_ACT_PIPE); } + submit(RawPacket::Ip( IpHdr::V4(header), ProtoHdr::Udp(unsafe { *udphdr }), @@ -159,19 +161,11 @@ fn process(ctx: TcContext) -> Result { } EtherType::Ipv6 => { let header: Ipv6Hdr = ctx.load(EthHdr::LEN).map_err(|_| ())?; - let src_addr = to_u128(unsafe { header.src_addr.in6_u.u6_addr16 }); - let dst_addr = to_u128(unsafe { header.dst_addr.in6_u.u6_addr16 }); match header.next_hdr { IpProto::Tcp => { let tcphdr: *const TcpHdr = ptr_at(&ctx, EthHdr::LEN + Ipv6Hdr::LEN)?; - let src_port = u16::from_be(unsafe { (*tcphdr).source }); - let dst_port = u16::from_be(unsafe { (*tcphdr).dest }); - if unsafe { BLOCKLIST_IPV6_INGRESS.get(&src_addr) } == Some(&src_port) - || unsafe { BLOCKLIST_IPV6_EGRESS.get(&dst_addr) } == Some(&dst_port) - { - return Ok(TC_ACT_SHOT); //DROP PACKET - } + if filter_packet(Protocol::Network(NetworkProtocol::Ipv6)) || filter_packet(Protocol::Transport(TransportProtocol::TCP)) { @@ -184,13 +178,7 @@ fn process(ctx: TcContext) -> Result { } IpProto::Udp => { let udphdr: *const UdpHdr = ptr_at(&ctx, EthHdr::LEN + Ipv6Hdr::LEN)?; - let src_port = u16::from_be(unsafe { (*udphdr).source }); - let dst_port = u16::from_be(unsafe { (*udphdr).dest }); - if unsafe { BLOCKLIST_IPV6_INGRESS.get(&src_addr.into()) } == Some(&src_port) - || unsafe { BLOCKLIST_IPV6_EGRESS.get(&dst_addr.into()) } == Some(&dst_port) - { - return Ok(TC_ACT_SHOT); //DROP PACKET - } + if filter_packet(Protocol::Network(NetworkProtocol::Ipv6)) || filter_packet(Protocol::Transport(TransportProtocol::UDP)) { diff --git a/oryx-tui/src/ebpf.rs b/oryx-tui/src/ebpf.rs index 3960108..217e1c3 100644 --- a/oryx-tui/src/ebpf.rs +++ b/oryx-tui/src/ebpf.rs @@ -165,13 +165,12 @@ impl Ebpf { let mut ipv6_firewall: HashMap<_, u128, u16> = HashMap::try_from(bpf.take_map("BLOCKLIST_IPV6_INGRESS").unwrap()).unwrap(); - spawn(move || loop { + thread::spawn(move || loop { if let Ok(rule) = firewall_ingress_receiver.recv() { match rule.enabled { true => match rule.ip { IpAddr::V4(addr) => { - println!("{}", rule); - ipv4_firewall.insert(u32::from(addr), rule.port, 0).unwrap(); + ipv4_firewall.insert(addr.to_bits(), rule.port, 0).unwrap(); } IpAddr::V6(addr) => { let _ = ipv6_firewall.insert( @@ -184,7 +183,7 @@ impl Ebpf { false => match rule.ip { IpAddr::V4(addr) => { - let _ = ipv4_firewall.remove(&u32::from(addr)); + ipv4_firewall.remove(&addr.to_bits()).unwrap(); } IpAddr::V6(addr) => { @@ -194,7 +193,9 @@ impl Ebpf { }, } } + }); + thread::spawn(move || loop { if let Ok((filter, flag)) = filter_channel_receiver.recv() { match filter { Protocol::Transport(p) => { diff --git a/oryx-tui/src/handler.rs b/oryx-tui/src/handler.rs index 6ed37eb..07b9f22 100644 --- a/oryx-tui/src/handler.rs +++ b/oryx-tui/src/handler.rs @@ -21,7 +21,7 @@ pub fn handle_key_events( KeyCode::Enter => { if app.filter.focused_block == FocusedBlock::Apply { app.filter - .start(sender.clone(), app.data_channel_sender.clone()); + .start(sender.clone(), app.data_channel_sender.clone())?; app.start_sniffing = true; } From 73ad77aad6ef2a34a4c7763ec2708ca835457251 Mon Sep 17 00:00:00 2001 From: adrien gaultier Date: Mon, 7 Oct 2024 12:42:16 +0200 Subject: [PATCH 17/41] lol --- oryx-ebpf/src/main.rs | 42 +++++++++++--- oryx-tui/src/ebpf.rs | 124 +++++++++++++++++++++++++++++++++--------- 2 files changed, 131 insertions(+), 35 deletions(-) diff --git a/oryx-ebpf/src/main.rs b/oryx-ebpf/src/main.rs index a2e1837..6afd83c 100644 --- a/oryx-ebpf/src/main.rs +++ b/oryx-ebpf/src/main.rs @@ -34,13 +34,17 @@ static TRANSPORT_FILTERS: Array = Array::with_max_entries(8, 0); static LINK_FILTERS: Array = Array::with_max_entries(8, 0); #[map] -static BLOCKLIST_IPV6_INGRESS: HashMap = HashMap::::with_max_entries(128, 0); +static BLOCKLIST_IPV6_INGRESS: HashMap = + HashMap::::with_max_entries(128, 0); #[map] -static BLOCKLIST_IPV6_EGRESS: HashMap = HashMap::::with_max_entries(128, 0); +static BLOCKLIST_IPV6_EGRESS: HashMap = + HashMap::::with_max_entries(128, 0); #[map] -static BLOCKLIST_IPV4_INGRESS: HashMap = HashMap::::with_max_entries(128, 0); +static BLOCKLIST_IPV4_INGRESS: HashMap = + HashMap::::with_max_entries(128, 0); #[map] -static BLOCKLIST_IPV4_EGRESS: HashMap = HashMap::::with_max_entries(128, 0); +static BLOCKLIST_IPV4_EGRESS: HashMap = + HashMap::::with_max_entries(128, 0); #[classifier] pub fn oryx(ctx: TcContext) -> i32 { @@ -70,6 +74,28 @@ fn ptr_at(ctx: &TcContext, offset: usize) -> Result<*const T, ()> { Ok((start + offset) as *const T) } +#[inline] +fn filter_for_ipv4_address( + addr: u32, + port: u16, + blocked_ports_map: &HashMap, +) -> bool { + if let Some(blocked_ports) = unsafe { blocked_ports_map.get(&addr) } { + for (idx, blocked_port) in blocked_ports.iter().enumerate() { + if *blocked_port == 0 { + if idx == 0 { + return true; + } else { + break; + } + } else if *blocked_port == port { + return true; + } + } + } + false +} + #[inline] fn filter_packet(protocol: Protocol) -> bool { match protocol { @@ -108,8 +134,8 @@ fn process(ctx: TcContext) -> Result { let src_port = u16::from_be(unsafe { (*tcphdr).source }); let dst_port = u16::from_be(unsafe { (*tcphdr).dest }); - if unsafe { BLOCKLIST_IPV4_INGRESS.get(&src_addr) } == Some(&src_port) - || unsafe { BLOCKLIST_IPV4_EGRESS.get(&dst_addr) } == Some(&dst_port) + if filter_for_ipv4_address(src_addr, src_port, &BLOCKLIST_IPV4_INGRESS) + || filter_for_ipv4_address(dst_addr, dst_port, &BLOCKLIST_IPV4_EGRESS) { return Ok(TC_ACT_SHOT); } @@ -129,8 +155,8 @@ fn process(ctx: TcContext) -> Result { let src_port = u16::from_be(unsafe { (*udphdr).source }); let dst_port = u16::from_be(unsafe { (*udphdr).dest }); - if unsafe { BLOCKLIST_IPV4_INGRESS.get(&src_addr) } == Some(&src_port) - || unsafe { BLOCKLIST_IPV4_EGRESS.get(&dst_addr) } == Some(&dst_port) + if filter_for_ipv4_address(src_addr, src_port, &BLOCKLIST_IPV4_INGRESS) + || filter_for_ipv4_address(dst_addr, dst_port, &BLOCKLIST_IPV4_EGRESS) { return Ok(TC_ACT_SHOT); } diff --git a/oryx-tui/src/ebpf.rs b/oryx-tui/src/ebpf.rs index 217e1c3..bd1fba1 100644 --- a/oryx-tui/src/ebpf.rs +++ b/oryx-tui/src/ebpf.rs @@ -1,6 +1,6 @@ use std::{ io, - net::IpAddr, + net::{IpAddr, Ipv4Addr, Ipv6Addr}, os::fd::AsRawFd, sync::{atomic::AtomicBool, Arc}, thread::{self, spawn}, @@ -63,6 +63,86 @@ impl Source for RingBuffer<'_> { } } +fn update_ipv4_blocklist( + ipv4_firewall: &mut HashMap, + addr: Ipv4Addr, + port: u16, + enabled: bool, +) { + if let Ok(mut blocked_ports) = ipv4_firewall.get(&addr.to_bits(), 0) { + if enabled { + // add port to blocklist + if let Some(first_zero) = blocked_ports.iter().enumerate().find(|&x| *x.1 == 0) { + blocked_ports[first_zero.0] = port; + dbg!("UPSERTING"); + dbg!(blocked_ports[0], blocked_ports[1]); + ipv4_firewall + .insert(addr.to_bits(), blocked_ports, 0) + .unwrap(); + } else { + todo!(); // list is full + } + } else { + // remove port from blocklist + if let Some(matching_port) = blocked_ports.iter().enumerate().find(|&x| *x.1 == port) { + blocked_ports[matching_port.0] = 0; + dbg!("REMOVING"); + dbg!(blocked_ports[0], blocked_ports[1]); + ipv4_firewall + .insert(addr.to_bits(), blocked_ports, 0) + .unwrap(); + } + } + } else { + // shouldn't be disabling if blocklist is empty + assert!(enabled); + //create new blocklist with port as first element + let mut blocked_ports: [u16; 32] = [0; 32]; + blocked_ports[0] = port; + ipv4_firewall + .insert(addr.to_bits(), blocked_ports, 0) + .unwrap(); + } +} + +fn update_ipv6_blocklist( + ipv6_firewall: &mut HashMap, + addr: Ipv6Addr, + port: u16, + enabled: bool, +) { + if let Ok(mut blocked_ports) = ipv6_firewall.get(&addr.to_bits(), 0) { + if enabled { + // add port to blocklist + if let Some(first_zero) = blocked_ports.iter().enumerate().find(|&x| *x.1 == 0) { + blocked_ports[first_zero.0] = port; + ipv6_firewall + .insert(addr.to_bits(), blocked_ports, 0) + .unwrap(); + } else { + todo!(); // list is full + } + } else { + // remove port from blocklist + if let Some(matching_port) = blocked_ports.iter().enumerate().find(|&x| *x.1 == port) { + blocked_ports[matching_port.0] = 0; + ipv6_firewall + .insert(addr.to_bits(), blocked_ports, 0) + .unwrap(); + } + } + } else { + // shouldn't be disabling if blocklist is empty + assert!(enabled); + //create new blocklist with port as first element + let mut blocked_ports: [u16; 32] = [0; 32]; + blocked_ports[0] = port; + ipv6_firewall + .insert(addr.to_bits(), blocked_ports, 0) + .unwrap(); + } +} + impl Ebpf { pub fn load_ingress( iface: String, @@ -160,37 +240,27 @@ impl Ebpf { 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> = + let mut ipv4_firewall: HashMap<_, u32, [u16; 32]> = HashMap::try_from(bpf.take_map("BLOCKLIST_IPV4_INGRESS").unwrap()).unwrap(); - let mut ipv6_firewall: HashMap<_, u128, u16> = + let mut ipv6_firewall: HashMap<_, u128, [u16; 32]> = HashMap::try_from(bpf.take_map("BLOCKLIST_IPV6_INGRESS").unwrap()).unwrap(); thread::spawn(move || loop { if let Ok(rule) = firewall_ingress_receiver.recv() { - match rule.enabled { - true => match rule.ip { - IpAddr::V4(addr) => { - ipv4_firewall.insert(addr.to_bits(), rule.port, 0).unwrap(); - } - IpAddr::V6(addr) => { - let _ = ipv6_firewall.insert( - u128::from_be_bytes(addr.octets()), - rule.port, - 0, - ); - } - }, - - false => match rule.ip { - IpAddr::V4(addr) => { - ipv4_firewall.remove(&addr.to_bits()).unwrap(); - } - - IpAddr::V6(addr) => { - let _ = - ipv6_firewall.remove(&u128::from_be_bytes(addr.octets())); - } - }, + 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, + ), } } }); From 48a40786a265341151480648a239e654ecd4485f Mon Sep 17 00:00:00 2001 From: adrien gaultier Date: Mon, 7 Oct 2024 15:49:37 +0200 Subject: [PATCH 18/41] =?UTF-8?q?on=20tient=20qqc=20l=C3=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- oryx-tui/src/ebpf.rs | 26 +++++++++++++++++++------- oryx-tui/src/section/firewall.rs | 10 ++++++++++ 2 files changed, 29 insertions(+), 7 deletions(-) diff --git a/oryx-tui/src/ebpf.rs b/oryx-tui/src/ebpf.rs index bd1fba1..2cecd60 100644 --- a/oryx-tui/src/ebpf.rs +++ b/oryx-tui/src/ebpf.rs @@ -74,8 +74,8 @@ fn update_ipv4_blocklist( // add port to blocklist if let Some(first_zero) = blocked_ports.iter().enumerate().find(|&x| *x.1 == 0) { blocked_ports[first_zero.0] = port; - dbg!("UPSERTING"); - dbg!(blocked_ports[0], blocked_ports[1]); + // dbg!("UPSERTING"); + // dbg!(blocked_ports[0], blocked_ports[1]); ipv4_firewall .insert(addr.to_bits(), blocked_ports, 0) .unwrap(); @@ -84,10 +84,22 @@ fn update_ipv4_blocklist( } } else { // remove port from blocklist - if let Some(matching_port) = blocked_ports.iter().enumerate().find(|&x| *x.1 == port) { - blocked_ports[matching_port.0] = 0; - dbg!("REMOVING"); - dbg!(blocked_ports[0], blocked_ports[1]); + // on veut rebuild une blocklist avec les ports restants non nuls + // par example là [8888,0,80,0,..] + // hashmap = key:[0,0,0] + // => [8888,80,0 ....] + let non_null_ports = blocked_ports + .into_iter() + .filter(|p| (*p != 0 && *p != port)) + .collect::>(); + let mut blocked_ports = [0; 32]; + for (idx, p) in non_null_ports.iter().enumerate() { + blocked_ports[idx] = *p; + } + if blocked_ports.iter().sum::() == 0 { + //now block_list is empty, we need to delete key + ipv4_firewall.remove(&addr.to_bits()).unwrap(); + } else { ipv4_firewall .insert(addr.to_bits(), blocked_ports, 0) .unwrap(); @@ -96,7 +108,7 @@ fn update_ipv4_blocklist( } else { // shouldn't be disabling if blocklist is empty assert!(enabled); - //create new blocklist with port as first element + // create new blocklist with port as first element let mut blocked_ports: [u16; 32] = [0; 32]; blocked_ports[0] = port; ipv4_firewall diff --git a/oryx-tui/src/section/firewall.rs b/oryx-tui/src/section/firewall.rs index a87bf3c..2958dc7 100644 --- a/oryx-tui/src/section/firewall.rs +++ b/oryx-tui/src/section/firewall.rs @@ -270,6 +270,14 @@ impl Firewall { if let Some(id) = user_input.id { let rule = self.rules.iter_mut().find(|rule| rule.id == id).unwrap(); + + if rule.enabled { + // set disable notification on previous rule definition + rule.enabled = false; + self.ingress_sender.send(rule.clone())?; + } + + // update rule with user input rule.name = user_input.name.field.to_string(); rule.ip = IpAddr::from_str(user_input.ip.field.value()).unwrap(); rule.port = u16::from_str(user_input.port.field.value()).unwrap(); @@ -331,6 +339,8 @@ impl Firewall { KeyCode::Char('d') => { if let Some(index) = self.state.selected() { + self.rules[index].enabled = false; + self.ingress_sender.send(self.rules[index].clone())?; self.rules.remove(index); } } From a30f6fca3b08c404b87ace4816e156e1789cb8a0 Mon Sep 17 00:00:00 2001 From: adrien gaultier Date: Mon, 7 Oct 2024 16:33:38 +0200 Subject: [PATCH 19/41] add all ports support denoted as "*" --- oryx-tui/src/ebpf.rs | 169 ++++++++++++++++++++----------- oryx-tui/src/section/firewall.rs | 37 ++++++- 2 files changed, 143 insertions(+), 63 deletions(-) diff --git a/oryx-tui/src/ebpf.rs b/oryx-tui/src/ebpf.rs index 2cecd60..4a55c30 100644 --- a/oryx-tui/src/ebpf.rs +++ b/oryx-tui/src/ebpf.rs @@ -18,7 +18,7 @@ use oryx_common::{protocols::Protocol, RawPacket}; use crate::{ event::Event, notification::{Notification, NotificationLevel}, - section::firewall::FirewallRule, + section::firewall::{BlockedPort, FirewallRule}, }; use mio::{event::Source, unix::SourceFd, Events, Interest, Poll, Registry, Token}; @@ -66,51 +66,70 @@ impl Source for RingBuffer<'_> { fn update_ipv4_blocklist( ipv4_firewall: &mut HashMap, addr: Ipv4Addr, - port: u16, + port: BlockedPort, enabled: bool, ) { + // hashmap entry exists if let Ok(mut blocked_ports) = ipv4_firewall.get(&addr.to_bits(), 0) { - if enabled { - // add port to blocklist - if let Some(first_zero) = blocked_ports.iter().enumerate().find(|&x| *x.1 == 0) { - blocked_ports[first_zero.0] = port; - // dbg!("UPSERTING"); - // dbg!(blocked_ports[0], blocked_ports[1]); - ipv4_firewall - .insert(addr.to_bits(), blocked_ports, 0) - .unwrap(); - } else { - todo!(); // list is full - } - } else { - // remove port from blocklist - // on veut rebuild une blocklist avec les ports restants non nuls - // par example là [8888,0,80,0,..] - // hashmap = key:[0,0,0] - // => [8888,80,0 ....] - let non_null_ports = blocked_ports - .into_iter() - .filter(|p| (*p != 0 && *p != port)) - .collect::>(); - let mut blocked_ports = [0; 32]; - for (idx, p) in non_null_ports.iter().enumerate() { - blocked_ports[idx] = *p; + match port { + // single port update + BlockedPort::Single(port) => { + if enabled { + // add port to blocklist + if let Some(first_zero) = blocked_ports.iter().enumerate().find(|&x| *x.1 == 0) + { + blocked_ports[first_zero.0] = port; + // dbg!("UPSERTING"); + // dbg!(blocked_ports[0], blocked_ports[1]); + ipv4_firewall + .insert(addr.to_bits(), blocked_ports, 0) + .unwrap(); + } else { + todo!(); // list is full + } + } else { + // remove port from blocklist + // eg: remove port 53 [8888,53,80,0,..] => [8888,0,80,0,..] => [8888,80,0 ....] + let non_null_ports = blocked_ports + .into_iter() + .filter(|p| (*p != 0 && *p != port)) + .collect::>(); + let mut blocked_ports = [0; 32]; + for (idx, p) in non_null_ports.iter().enumerate() { + blocked_ports[idx] = *p; + } + if blocked_ports.iter().sum::() == 0 { + //if block_list is now empty, we need to delete key + ipv4_firewall.remove(&addr.to_bits()).unwrap(); + } else { + ipv4_firewall + .insert(addr.to_bits(), blocked_ports, 0) + .unwrap(); + } + } } - if blocked_ports.iter().sum::() == 0 { - //now block_list is empty, we need to delete key - ipv4_firewall.remove(&addr.to_bits()).unwrap(); - } else { - ipv4_firewall - .insert(addr.to_bits(), blocked_ports, 0) - .unwrap(); + BlockedPort::All => { + if enabled { + ipv4_firewall.insert(addr.to_bits(), [0; 32], 0).unwrap(); + } else { + ipv4_firewall.remove(&addr.to_bits()).unwrap(); + } } } - } else { + } + // no hashmap entry, create new blocklist + else { // shouldn't be disabling if blocklist is empty assert!(enabled); - // create new blocklist with port as first element + let mut blocked_ports: [u16; 32] = [0; 32]; - blocked_ports[0] = port; + match port { + BlockedPort::Single(port) => { + blocked_ports[0] = port; + } + BlockedPort::All => {} + } + ipv4_firewall .insert(addr.to_bits(), blocked_ports, 0) .unwrap(); @@ -120,41 +139,75 @@ fn update_ipv4_blocklist( fn update_ipv6_blocklist( ipv6_firewall: &mut HashMap, addr: Ipv6Addr, - port: u16, + port: BlockedPort, enabled: bool, ) { + // hashmap entry exists if let Ok(mut blocked_ports) = ipv6_firewall.get(&addr.to_bits(), 0) { - if enabled { - // add port to blocklist - if let Some(first_zero) = blocked_ports.iter().enumerate().find(|&x| *x.1 == 0) { - blocked_ports[first_zero.0] = port; - ipv6_firewall - .insert(addr.to_bits(), blocked_ports, 0) - .unwrap(); - } else { - todo!(); // list is full + match port { + // single port update + BlockedPort::Single(port) => { + if enabled { + // add port to blocklist + if let Some(first_zero) = blocked_ports.iter().enumerate().find(|&x| *x.1 == 0) + { + blocked_ports[first_zero.0] = port; + // dbg!("UPSERTING"); + // dbg!(blocked_ports[0], blocked_ports[1]); + ipv6_firewall + .insert(addr.to_bits(), blocked_ports, 0) + .unwrap(); + } else { + todo!(); // list is full + } + } else { + // remove port from blocklist + // eg: remove port 53 [8888,53,80,0,..] => [8888,0,80,0,..] => [8888,80,0 ....] + let non_null_ports = blocked_ports + .into_iter() + .filter(|p| (*p != 0 && *p != port)) + .collect::>(); + let mut blocked_ports = [0; 32]; + for (idx, p) in non_null_ports.iter().enumerate() { + blocked_ports[idx] = *p; + } + if blocked_ports.iter().sum::() == 0 { + //if block_list is now empty, we need to delete key + ipv6_firewall.remove(&addr.to_bits()).unwrap(); + } else { + ipv6_firewall + .insert(addr.to_bits(), blocked_ports, 0) + .unwrap(); + } + } } - } else { - // remove port from blocklist - if let Some(matching_port) = blocked_ports.iter().enumerate().find(|&x| *x.1 == port) { - blocked_ports[matching_port.0] = 0; - ipv6_firewall - .insert(addr.to_bits(), blocked_ports, 0) - .unwrap(); + BlockedPort::All => { + if enabled { + ipv6_firewall.insert(addr.to_bits(), [0; 32], 0).unwrap(); + } else { + ipv6_firewall.remove(&addr.to_bits()).unwrap(); + } } } - } else { + } + // no hashmap entry, create new blocklist + else { // shouldn't be disabling if blocklist is empty assert!(enabled); - //create new blocklist with port as first element + let mut blocked_ports: [u16; 32] = [0; 32]; - blocked_ports[0] = port; + match port { + BlockedPort::Single(port) => { + blocked_ports[0] = port; + } + BlockedPort::All => {} + } + ipv6_firewall .insert(addr.to_bits(), blocked_ports, 0) .unwrap(); } } - impl Ebpf { pub fn load_ingress( iface: String, diff --git a/oryx-tui/src/section/firewall.rs b/oryx-tui/src/section/firewall.rs index 2958dc7..35175b0 100644 --- a/oryx-tui/src/section/firewall.rs +++ b/oryx-tui/src/section/firewall.rs @@ -7,7 +7,7 @@ use ratatui::{ widgets::{Block, Borders, Cell, Clear, HighlightSpacing, Padding, Row, Table, TableState}, Frame, }; -use std::{net::IpAddr, str::FromStr}; +use std::{net::IpAddr, num::ParseIntError, str::FromStr}; use tui_input::{backend::crossterm::EventHandler, Input}; use uuid; @@ -19,7 +19,33 @@ pub struct FirewallRule { name: String, pub enabled: bool, pub ip: IpAddr, - pub port: u16, + pub port: BlockedPort, +} + +#[derive(Debug, Clone)] +pub enum BlockedPort { + Single(u16), + All, +} + +impl Display for BlockedPort { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + BlockedPort::Single(p) => write!(f, "{}", p), + BlockedPort::All => write!(f, "*"), + } + } +} + +impl FromStr for BlockedPort { + type Err = ParseIntError; + fn from_str(s: &str) -> Result { + if s == "*" { + return Ok(BlockedPort::All); + } else { + return Ok(BlockedPort::Single(u16::from_str(s)?)); + } + } } impl Display for FirewallRule { @@ -80,7 +106,7 @@ impl UserInput { self.port.error = None; if self.port.field.value().is_empty() { self.port.error = Some("Required field.".to_string()); - } else if u16::from_str(self.port.field.value()).is_err() { + } else if BlockedPort::from_str(self.port.field.value()).is_err() { self.port.error = Some("Invalid Port number.".to_string()); } } @@ -280,13 +306,14 @@ impl Firewall { // update rule with user input rule.name = user_input.name.field.to_string(); rule.ip = IpAddr::from_str(user_input.ip.field.value()).unwrap(); - rule.port = u16::from_str(user_input.port.field.value()).unwrap(); + rule.port = + BlockedPort::from_str(user_input.port.field.value()).unwrap(); } else { let rule = FirewallRule { id: uuid::Uuid::new_v4(), name: user_input.name.field.to_string(), ip: IpAddr::from_str(user_input.ip.field.value()).unwrap(), - port: u16::from_str(user_input.port.field.value()).unwrap(), + port: BlockedPort::from_str(user_input.port.field.value()).unwrap(), enabled: false, }; self.rules.push(rule); From 3faee02fe1b3e3adc0278df7374b81166483c405 Mon Sep 17 00:00:00 2001 From: Badr Date: Mon, 7 Oct 2024 18:48:38 +0200 Subject: [PATCH 20/41] do not edit enabled rule --- oryx-tui/src/handler.rs | 23 ++++++++++++++++------- oryx-tui/src/notification.rs | 2 +- oryx-tui/src/section.rs | 12 +++++++++--- oryx-tui/src/section/firewall.rs | 30 +++++++++++++++++------------- 4 files changed, 43 insertions(+), 24 deletions(-) diff --git a/oryx-tui/src/handler.rs b/oryx-tui/src/handler.rs index 07b9f22..86bad30 100644 --- a/oryx-tui/src/handler.rs +++ b/oryx-tui/src/handler.rs @@ -62,7 +62,9 @@ pub fn handle_key_events( app.filter.handle_key_events(key_event, true); } ActivePopup::NewFirewallRule => { - app.section.firewall.handle_keys(key_event)?; + app.section + .firewall + .handle_keys(key_event, sender.clone())?; app.is_editing = false; } _ => {} @@ -78,7 +80,12 @@ pub fn handle_key_events( } } ActivePopup::NewFirewallRule => { - if app.section.firewall.handle_keys(key_event).is_ok() { + if app + .section + .firewall + .handle_keys(key_event, sender.clone()) + .is_ok() + { app.active_popup = None; app.is_editing = false; } @@ -91,7 +98,9 @@ pub fn handle_key_events( app.filter.handle_key_events(key_event, true); } ActivePopup::NewFirewallRule => { - app.section.firewall.handle_keys(key_event)?; + app.section + .firewall + .handle_keys(key_event, sender.clone())?; } _ => {} }, @@ -106,7 +115,7 @@ pub fn handle_key_events( _ => {} } - app.section.handle_keys(key_event)?; + app.section.handle_keys(key_event, sender.clone())?; return Ok(()); } @@ -145,14 +154,14 @@ pub fn handle_key_events( KeyCode::Char('/') => { if app.section.focused_section == FocusedSection::Inspection { app.is_editing = true; - app.section.handle_keys(key_event)?; + app.section.handle_keys(key_event, sender.clone())?; } } KeyCode::Char('n') | KeyCode::Char('e') => { if app.section.focused_section == FocusedSection::Firewall { app.is_editing = true; - app.section.handle_keys(key_event)?; + app.section.handle_keys(key_event, sender)?; app.active_popup = Some(ActivePopup::NewFirewallRule); } } @@ -187,7 +196,7 @@ pub fn handle_key_events( } } _ => { - app.section.handle_keys(key_event)?; + app.section.handle_keys(key_event, sender.clone())?; } } diff --git a/oryx-tui/src/notification.rs b/oryx-tui/src/notification.rs index 643b31f..68772ea 100644 --- a/oryx-tui/src/notification.rs +++ b/oryx-tui/src/notification.rs @@ -69,7 +69,7 @@ impl Notification { let notif = Notification { message: message.to_string(), level, - ttl: 500, + ttl: 75, }; sender.send(Event::Notification(notif))?; diff --git a/oryx-tui/src/section.rs b/oryx-tui/src/section.rs index e0fa47e..0b20077 100644 --- a/oryx-tui/src/section.rs +++ b/oryx-tui/src/section.rs @@ -19,7 +19,7 @@ use ratatui::{ }; use stats::Stats; -use crate::{app::AppResult, packet::AppPacket}; +use crate::{app::AppResult, event::Event, packet::AppPacket}; #[derive(Debug, PartialEq)] pub enum FocusedSection { @@ -118,7 +118,11 @@ impl Section { } } - pub fn handle_keys(&mut self, key_event: KeyEvent) -> AppResult<()> { + pub fn handle_keys( + &mut self, + key_event: KeyEvent, + notification_sender: kanal::Sender, + ) -> AppResult<()> { match key_event.code { KeyCode::Tab => match self.focused_section { FocusedSection::Inspection => self.focused_section = FocusedSection::Stats, @@ -136,7 +140,9 @@ impl Section { _ => match self.focused_section { FocusedSection::Inspection => self.inspection.handle_keys(key_event), - FocusedSection::Firewall => self.firewall.handle_keys(key_event)?, + FocusedSection::Firewall => self + .firewall + .handle_keys(key_event, notification_sender.clone())?, _ => {} }, } diff --git a/oryx-tui/src/section/firewall.rs b/oryx-tui/src/section/firewall.rs index 35175b0..feeca87 100644 --- a/oryx-tui/src/section/firewall.rs +++ b/oryx-tui/src/section/firewall.rs @@ -11,7 +11,7 @@ use std::{net::IpAddr, num::ParseIntError, str::FromStr}; use tui_input::{backend::crossterm::EventHandler, Input}; use uuid; -use crate::app::AppResult; +use crate::{app::AppResult, notification::Notification}; #[derive(Debug, Clone)] pub struct FirewallRule { @@ -41,9 +41,9 @@ impl FromStr for BlockedPort { type Err = ParseIntError; fn from_str(s: &str) -> Result { if s == "*" { - return Ok(BlockedPort::All); + Ok(BlockedPort::All) } else { - return Ok(BlockedPort::Single(u16::from_str(s)?)); + Ok(BlockedPort::Single(u16::from_str(s)?)) } } } @@ -283,7 +283,11 @@ impl Firewall { self.rules.retain(|r| r.name != rule.name); } - pub fn handle_keys(&mut self, key_event: KeyEvent) -> AppResult<()> { + pub fn handle_keys( + &mut self, + key_event: KeyEvent, + sender: kanal::Sender, + ) -> AppResult<()> { if let Some(user_input) = &mut self.user_input { match key_event.code { KeyCode::Esc => { @@ -296,14 +300,6 @@ impl Firewall { if let Some(id) = user_input.id { let rule = self.rules.iter_mut().find(|rule| rule.id == id).unwrap(); - - if rule.enabled { - // set disable notification on previous rule definition - rule.enabled = false; - self.ingress_sender.send(rule.clone())?; - } - - // update rule with user input rule.name = user_input.name.field.to_string(); rule.ip = IpAddr::from_str(user_input.ip.field.value()).unwrap(); rule.port = @@ -360,7 +356,15 @@ impl Firewall { KeyCode::Char('e') => { if let Some(index) = self.state.selected() { let rule = self.rules[index].clone(); - self.user_input = Some(rule.into()); + if rule.enabled { + Notification::send( + "Can not edit enabled rule", + crate::notification::NotificationLevel::Warning, + sender.clone(), + )?; + } else { + self.user_input = Some(rule.into()); + } } } From 534609b6aea32732542de8de51e5a09eabe5a8bf Mon Sep 17 00:00:00 2001 From: Badr Date: Mon, 7 Oct 2024 19:09:30 +0200 Subject: [PATCH 21/41] handle duplicate rules --- oryx-tui/src/section/firewall.rs | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/oryx-tui/src/section/firewall.rs b/oryx-tui/src/section/firewall.rs index feeca87..0dba263 100644 --- a/oryx-tui/src/section/firewall.rs +++ b/oryx-tui/src/section/firewall.rs @@ -22,7 +22,7 @@ pub struct FirewallRule { pub port: BlockedPort, } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub enum BlockedPort { Single(u16), All, @@ -305,6 +305,24 @@ impl Firewall { rule.port = BlockedPort::from_str(user_input.port.field.value()).unwrap(); } else { + if let Some(exiting_rule_with_same_ip) = + self.rules.iter().find(|rule| { + rule.ip + == IpAddr::from_str(user_input.ip.field.value()).unwrap() + }) + { + let new_port = + BlockedPort::from_str(user_input.port.field.value()).unwrap(); + if exiting_rule_with_same_ip.port == new_port { + Notification::send( + "Duplicate Rule", + crate::notification::NotificationLevel::Warning, + sender.clone(), + )?; + return Err("Rule validation error".into()); + } + } + let rule = FirewallRule { id: uuid::Uuid::new_v4(), name: user_input.name.field.to_string(), From 8b19b0780d13bb3dc26cecf67311a2bb6b3bde5d Mon Sep 17 00:00:00 2001 From: Badr Date: Mon, 7 Oct 2024 19:22:12 +0200 Subject: [PATCH 22/41] handle more cases of duplicate rules --- oryx-tui/src/section/firewall.rs | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/oryx-tui/src/section/firewall.rs b/oryx-tui/src/section/firewall.rs index 0dba263..3b149db 100644 --- a/oryx-tui/src/section/firewall.rs +++ b/oryx-tui/src/section/firewall.rs @@ -313,6 +313,7 @@ impl Firewall { { let new_port = BlockedPort::from_str(user_input.port.field.value()).unwrap(); + if exiting_rule_with_same_ip.port == new_port { Notification::send( "Duplicate Rule", @@ -321,6 +322,28 @@ impl Firewall { )?; return Err("Rule validation error".into()); } + + match exiting_rule_with_same_ip.port { + BlockedPort::Single(_) => { + if new_port == BlockedPort::All { + Notification::send( + "Duplicate Rule", + crate::notification::NotificationLevel::Warning, + sender.clone(), + )?; + return Err("Rule validation error".into()); + } + } + + BlockedPort::All => { + Notification::send( + "Duplicate Rule", + crate::notification::NotificationLevel::Warning, + sender.clone(), + )?; + return Err("Rule validation error".into()); + } + } } let rule = FirewallRule { From a2ce3f0235ab8c034b5583379e0909a563db956f Mon Sep 17 00:00:00 2001 From: Badr Date: Mon, 7 Oct 2024 20:56:27 +0200 Subject: [PATCH 23/41] fix crash when deleting disabled rule --- oryx-tui/src/ebpf.rs | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/oryx-tui/src/ebpf.rs b/oryx-tui/src/ebpf.rs index 4a55c30..fe2b286 100644 --- a/oryx-tui/src/ebpf.rs +++ b/oryx-tui/src/ebpf.rs @@ -116,12 +116,7 @@ fn update_ipv4_blocklist( } } } - } - // no hashmap entry, create new blocklist - else { - // shouldn't be disabling if blocklist is empty - assert!(enabled); - + } else if enabled { let mut blocked_ports: [u16; 32] = [0; 32]; match port { BlockedPort::Single(port) => { @@ -189,12 +184,7 @@ fn update_ipv6_blocklist( } } } - } - // no hashmap entry, create new blocklist - else { - // shouldn't be disabling if blocklist is empty - assert!(enabled); - + } else if enabled { let mut blocked_ports: [u16; 32] = [0; 32]; match port { BlockedPort::Single(port) => { From dfeb8ace871aa2e29ed8fc528e12f0a6023a88c5 Mon Sep 17 00:00:00 2001 From: Badr Date: Mon, 7 Oct 2024 22:47:56 +0200 Subject: [PATCH 24/41] handle duplicates when editing --- oryx-tui/src/ebpf.rs | 71 ++++++++++++++-------------- oryx-tui/src/section/firewall.rs | 79 +++++++++++++++----------------- 2 files changed, 73 insertions(+), 77 deletions(-) diff --git a/oryx-tui/src/ebpf.rs b/oryx-tui/src/ebpf.rs index fe2b286..e9c47ee 100644 --- a/oryx-tui/src/ebpf.rs +++ b/oryx-tui/src/ebpf.rs @@ -67,39 +67,38 @@ fn update_ipv4_blocklist( ipv4_firewall: &mut HashMap, addr: Ipv4Addr, port: BlockedPort, - enabled: bool, + to_insert: bool, ) { - // hashmap entry exists if let Ok(mut blocked_ports) = ipv4_firewall.get(&addr.to_bits(), 0) { match port { - // single port update BlockedPort::Single(port) => { - if enabled { - // add port to blocklist - if let Some(first_zero) = blocked_ports.iter().enumerate().find(|&x| *x.1 == 0) + if to_insert { + if let Some((first_zero_index, _)) = blocked_ports + .iter() + .enumerate() + .find(|(_, &value)| value == 0) { - blocked_ports[first_zero.0] = port; - // dbg!("UPSERTING"); - // dbg!(blocked_ports[0], blocked_ports[1]); + blocked_ports[first_zero_index] = port; ipv4_firewall .insert(addr.to_bits(), blocked_ports, 0) .unwrap(); } else { - todo!(); // list is full + //TODO: + unreachable!(); // list is full } } else { - // remove port from blocklist - // eg: remove port 53 [8888,53,80,0,..] => [8888,0,80,0,..] => [8888,80,0 ....] - let non_null_ports = blocked_ports + let not_null_ports = blocked_ports .into_iter() .filter(|p| (*p != 0 && *p != port)) .collect::>(); + let mut blocked_ports = [0; 32]; - for (idx, p) in non_null_ports.iter().enumerate() { + + for (idx, p) in not_null_ports.iter().enumerate() { blocked_ports[idx] = *p; } - if blocked_ports.iter().sum::() == 0 { - //if block_list is now empty, we need to delete key + + if blocked_ports.iter().all(|&port| port == 0) { ipv4_firewall.remove(&addr.to_bits()).unwrap(); } else { ipv4_firewall @@ -109,14 +108,14 @@ fn update_ipv4_blocklist( } } BlockedPort::All => { - if enabled { + if to_insert { ipv4_firewall.insert(addr.to_bits(), [0; 32], 0).unwrap(); } else { ipv4_firewall.remove(&addr.to_bits()).unwrap(); } } } - } else if enabled { + } else if to_insert { let mut blocked_ports: [u16; 32] = [0; 32]; match port { BlockedPort::Single(port) => { @@ -135,39 +134,38 @@ fn update_ipv6_blocklist( ipv6_firewall: &mut HashMap, addr: Ipv6Addr, port: BlockedPort, - enabled: bool, + to_insert: bool, ) { - // hashmap entry exists if let Ok(mut blocked_ports) = ipv6_firewall.get(&addr.to_bits(), 0) { match port { - // single port update BlockedPort::Single(port) => { - if enabled { - // add port to blocklist - if let Some(first_zero) = blocked_ports.iter().enumerate().find(|&x| *x.1 == 0) + if to_insert { + if let Some((first_zero_index, _)) = blocked_ports + .iter() + .enumerate() + .find(|(_, &value)| value == 0) { - blocked_ports[first_zero.0] = port; - // dbg!("UPSERTING"); - // dbg!(blocked_ports[0], blocked_ports[1]); + blocked_ports[first_zero_index] = port; ipv6_firewall .insert(addr.to_bits(), blocked_ports, 0) .unwrap(); } else { - todo!(); // list is full + //TODO: + unreachable!(); // list is full } } else { - // remove port from blocklist - // eg: remove port 53 [8888,53,80,0,..] => [8888,0,80,0,..] => [8888,80,0 ....] - let non_null_ports = blocked_ports + let not_null_ports = blocked_ports .into_iter() .filter(|p| (*p != 0 && *p != port)) .collect::>(); + let mut blocked_ports = [0; 32]; - for (idx, p) in non_null_ports.iter().enumerate() { + + for (idx, p) in not_null_ports.iter().enumerate() { blocked_ports[idx] = *p; } - if blocked_ports.iter().sum::() == 0 { - //if block_list is now empty, we need to delete key + + if blocked_ports.iter().all(|&port| port == 0) { ipv6_firewall.remove(&addr.to_bits()).unwrap(); } else { ipv6_firewall @@ -177,14 +175,14 @@ fn update_ipv6_blocklist( } } BlockedPort::All => { - if enabled { + if to_insert { ipv6_firewall.insert(addr.to_bits(), [0; 32], 0).unwrap(); } else { ipv6_firewall.remove(&addr.to_bits()).unwrap(); } } } - } else if enabled { + } else if to_insert { let mut blocked_ports: [u16; 32] = [0; 32]; match port { BlockedPort::Single(port) => { @@ -198,6 +196,7 @@ fn update_ipv6_blocklist( .unwrap(); } } + impl Ebpf { pub fn load_ingress( iface: String, diff --git a/oryx-tui/src/section/firewall.rs b/oryx-tui/src/section/firewall.rs index 3b149db..2e7dbab 100644 --- a/oryx-tui/src/section/firewall.rs +++ b/oryx-tui/src/section/firewall.rs @@ -279,6 +279,33 @@ impl Firewall { self.user_input = Some(UserInput::new()); } + fn validate_duplicate_rules(rules: &[FirewallRule], user_input: &UserInput) -> AppResult<()> { + if let Some(exiting_rule_with_same_ip) = rules + .iter() + .find(|rule| rule.ip == IpAddr::from_str(user_input.ip.field.value()).unwrap()) + { + let new_port = BlockedPort::from_str(user_input.port.field.value()).unwrap(); + + if exiting_rule_with_same_ip.port == new_port { + return Err("Rule validation error".into()); + } + + match exiting_rule_with_same_ip.port { + BlockedPort::Single(_) => { + if new_port == BlockedPort::All { + return Err("Rule validation error".into()); + } + } + + BlockedPort::All => { + return Err("Rule validation error".into()); + } + } + } + + Ok(()) + } + pub fn remove_rule(&mut self, rule: &FirewallRule) { self.rules.retain(|r| r.name != rule.name); } @@ -298,54 +325,24 @@ impl Firewall { if let Some(user_input) = &mut self.user_input { user_input.validate()?; + if let Err(e) = Firewall::validate_duplicate_rules(&self.rules, user_input) + { + Notification::send( + "Duplicate Rule", + crate::notification::NotificationLevel::Warning, + sender.clone(), + )?; + return Err(e); + } + if let Some(id) = user_input.id { let rule = self.rules.iter_mut().find(|rule| rule.id == id).unwrap(); + rule.name = user_input.name.field.to_string(); rule.ip = IpAddr::from_str(user_input.ip.field.value()).unwrap(); rule.port = BlockedPort::from_str(user_input.port.field.value()).unwrap(); } else { - if let Some(exiting_rule_with_same_ip) = - self.rules.iter().find(|rule| { - rule.ip - == IpAddr::from_str(user_input.ip.field.value()).unwrap() - }) - { - let new_port = - BlockedPort::from_str(user_input.port.field.value()).unwrap(); - - if exiting_rule_with_same_ip.port == new_port { - Notification::send( - "Duplicate Rule", - crate::notification::NotificationLevel::Warning, - sender.clone(), - )?; - return Err("Rule validation error".into()); - } - - match exiting_rule_with_same_ip.port { - BlockedPort::Single(_) => { - if new_port == BlockedPort::All { - Notification::send( - "Duplicate Rule", - crate::notification::NotificationLevel::Warning, - sender.clone(), - )?; - return Err("Rule validation error".into()); - } - } - - BlockedPort::All => { - Notification::send( - "Duplicate Rule", - crate::notification::NotificationLevel::Warning, - sender.clone(), - )?; - return Err("Rule validation error".into()); - } - } - } - let rule = FirewallRule { id: uuid::Uuid::new_v4(), name: user_input.name.field.to_string(), From 81829c61718478759313604de0b806fd61433b2c Mon Sep 17 00:00:00 2001 From: adrien gaultier Date: Tue, 8 Oct 2024 10:20:05 +0200 Subject: [PATCH 25/41] fix validate_duplicate_rules --- oryx-tui/src/section/firewall.rs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/oryx-tui/src/section/firewall.rs b/oryx-tui/src/section/firewall.rs index 2e7dbab..25fccbc 100644 --- a/oryx-tui/src/section/firewall.rs +++ b/oryx-tui/src/section/firewall.rs @@ -280,10 +280,13 @@ impl Firewall { } fn validate_duplicate_rules(rules: &[FirewallRule], user_input: &UserInput) -> AppResult<()> { - if let Some(exiting_rule_with_same_ip) = rules - .iter() - .find(|rule| rule.ip == IpAddr::from_str(user_input.ip.field.value()).unwrap()) - { + if let Some(exiting_rule_with_same_ip) = rules.iter().find(|rule| { + rule.ip == IpAddr::from_str(user_input.ip.field.value()).unwrap() + && match user_input.id { + Some(uuid) => rule.id != uuid, + None => true, + } + }) { let new_port = BlockedPort::from_str(user_input.port.field.value()).unwrap(); if exiting_rule_with_same_ip.port == new_port { From 567cfcbd2bf6fbcd4dc7ddb0ae636cbf586faf38 Mon Sep 17 00:00:00 2001 From: adrien gaultier Date: Tue, 8 Oct 2024 10:38:46 +0200 Subject: [PATCH 26/41] handle egress(WIP) --- oryx-tui/src/app.rs | 9 +++++++-- oryx-tui/src/ebpf.rs | 32 +++++++++++++++++++++++++++++--- oryx-tui/src/filter.rs | 11 ++++++++--- oryx-tui/src/section.rs | 3 ++- oryx-tui/src/section/firewall.rs | 11 +++++++++-- 5 files changed, 55 insertions(+), 11 deletions(-) diff --git a/oryx-tui/src/app.rs b/oryx-tui/src/app.rs index 9c82fb3..7425ce2 100644 --- a/oryx-tui/src/app.rs +++ b/oryx-tui/src/app.rs @@ -58,6 +58,7 @@ impl App { let (sender, receiver) = kanal::unbounded(); let (firewall_ingress_sender, firewall_ingress_receiver) = kanal::unbounded(); + let (firewall_egress_sender, firewall_egress_receiver) = kanal::unbounded(); thread::spawn({ let packets = packets.clone(); @@ -76,11 +77,15 @@ impl App { Self { running: true, help: Help::new(), - filter: Filter::new(firewall_ingress_receiver), + filter: Filter::new(firewall_ingress_receiver, firewall_egress_receiver), start_sniffing: false, packets: packets.clone(), notifications: Vec::new(), - section: Section::new(packets.clone(), firewall_ingress_sender), + section: Section::new( + packets.clone(), + firewall_ingress_sender, + firewall_egress_sender, + ), data_channel_sender: sender, is_editing: false, active_popup: None, diff --git a/oryx-tui/src/ebpf.rs b/oryx-tui/src/ebpf.rs index e9c47ee..9c77d91 100644 --- a/oryx-tui/src/ebpf.rs +++ b/oryx-tui/src/ebpf.rs @@ -3,7 +3,7 @@ use std::{ net::{IpAddr, Ipv4Addr, Ipv6Addr}, os::fd::AsRawFd, sync::{atomic::AtomicBool, Arc}, - thread::{self, spawn}, + thread, time::Duration, }; @@ -383,7 +383,7 @@ impl Ebpf { notification_sender: kanal::Sender, data_sender: kanal::Sender<[u8; RawPacket::LEN]>, filter_channel_receiver: kanal::Receiver<(Protocol, bool)>, - _firewall_channel_receiver: kanal::Receiver<(Protocol, bool)>, + firewall_egress_receiver: kanal::Receiver, terminate: Arc, ) { thread::spawn({ @@ -460,6 +460,7 @@ impl Ebpf { 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(); @@ -469,7 +470,32 @@ impl Ebpf { let mut link_filters: Array<_, u32> = Array::try_from(bpf.take_map("LINK_FILTERS").unwrap()).unwrap(); - spawn(move || loop { + // firewall-ebpf interface + let mut ipv4_firewall: HashMap<_, u32, [u16; 32]> = + HashMap::try_from(bpf.take_map("BLOCKLIST_IPV4_EGRESS").unwrap()).unwrap(); + let mut ipv6_firewall: HashMap<_, u128, [u16; 32]> = + HashMap::try_from(bpf.take_map("BLOCKLIST_IPV6_EGRESS").unwrap()).unwrap(); + thread::spawn(move || loop { + if let Ok(rule) = firewall_egress_receiver.recv() { + 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, + ), + } + } + }); + + thread::spawn(move || loop { if let Ok((filter, flag)) = filter_channel_receiver.recv() { match filter { Protocol::Transport(p) => { diff --git a/oryx-tui/src/filter.rs b/oryx-tui/src/filter.rs index b42774d..932fa11 100644 --- a/oryx-tui/src/filter.rs +++ b/oryx-tui/src/filter.rs @@ -92,10 +92,14 @@ pub struct Filter { pub firewall_chans: IoChans, pub focused_block: FocusedBlock, pub firewall_ingress_receiver: kanal::Receiver, + pub firewall_egress_receiver: kanal::Receiver, } impl Filter { - pub fn new(firewall_ingress_receiver: kanal::Receiver) -> Self { + pub fn new( + firewall_ingress_receiver: kanal::Receiver, + firewall_egress_receiver: kanal::Receiver, + ) -> Self { Self { interface: Interface::new(), network: NetworkFilter::new(), @@ -106,6 +110,7 @@ impl Filter { firewall_chans: IoChans::new(), focused_block: FocusedBlock::Interface, firewall_ingress_receiver, + firewall_egress_receiver, } } @@ -148,7 +153,7 @@ impl Filter { notification_sender, data_sender, self.filter_chans.egress.receiver.clone(), - self.firewall_chans.egress.receiver.clone(), + self.firewall_egress_receiver.clone(), self.traffic_direction.terminate_egress.clone(), ); } @@ -282,7 +287,7 @@ impl Filter { notification_sender.clone(), data_sender.clone(), self.filter_chans.egress.receiver.clone(), - self.firewall_chans.egress.receiver.clone(), + self.firewall_egress_receiver.clone(), self.traffic_direction.terminate_egress.clone(), ); } diff --git a/oryx-tui/src/section.rs b/oryx-tui/src/section.rs index 0b20077..110fc49 100644 --- a/oryx-tui/src/section.rs +++ b/oryx-tui/src/section.rs @@ -42,13 +42,14 @@ impl Section { pub fn new( packets: Arc>>, firewall_ingress_sender: kanal::Sender, + firewall_egress_sender: kanal::Sender, ) -> Self { Self { focused_section: FocusedSection::Inspection, inspection: Inspection::new(packets.clone()), stats: Stats::new(packets.clone()), alert: Alert::new(packets.clone()), - firewall: Firewall::new(firewall_ingress_sender), + firewall: Firewall::new(firewall_ingress_sender, firewall_egress_sender), } } fn title_span(&self, header_section: FocusedSection) -> Span { diff --git a/oryx-tui/src/section/firewall.rs b/oryx-tui/src/section/firewall.rs index 25fccbc..2915a13 100644 --- a/oryx-tui/src/section/firewall.rs +++ b/oryx-tui/src/section/firewall.rs @@ -263,15 +263,20 @@ pub struct Firewall { state: TableState, user_input: Option, ingress_sender: kanal::Sender, + egress_sender: kanal::Sender, } impl Firewall { - pub fn new(ingress_sender: kanal::Sender) -> Self { + pub fn new( + ingress_sender: kanal::Sender, + egress_sender: kanal::Sender, + ) -> Self { Self { rules: Vec::new(), state: TableState::default(), user_input: None, ingress_sender, + egress_sender, } } @@ -390,7 +395,8 @@ impl Firewall { KeyCode::Char(' ') => { if let Some(index) = self.state.selected() { self.rules[index].enabled = !self.rules[index].enabled; - self.ingress_sender.send(self.rules[index].clone())? + self.ingress_sender.send(self.rules[index].clone())?; + self.egress_sender.send(self.rules[index].clone())? } } @@ -413,6 +419,7 @@ impl Firewall { if let Some(index) = self.state.selected() { self.rules[index].enabled = false; self.ingress_sender.send(self.rules[index].clone())?; + self.egress_sender.send(self.rules[index].clone())?; self.rules.remove(index); } } From e96112b9e5c2757fa5ed8d2c96328de6a2793092 Mon Sep 17 00:00:00 2001 From: Badr Date: Tue, 8 Oct 2024 18:20:38 +0200 Subject: [PATCH 27/41] add direction --- oryx-tui/src/section/firewall.rs | 101 ++++++++++++++++++++++++------- 1 file changed, 79 insertions(+), 22 deletions(-) diff --git a/oryx-tui/src/section/firewall.rs b/oryx-tui/src/section/firewall.rs index 2915a13..c9a13c8 100644 --- a/oryx-tui/src/section/firewall.rs +++ b/oryx-tui/src/section/firewall.rs @@ -11,7 +11,7 @@ use std::{net::IpAddr, num::ParseIntError, str::FromStr}; use tui_input::{backend::crossterm::EventHandler, Input}; use uuid; -use crate::{app::AppResult, notification::Notification}; +use crate::{app::AppResult, filter::direction::TrafficDirection, notification::Notification}; #[derive(Debug, Clone)] pub struct FirewallRule { @@ -20,6 +20,7 @@ pub struct FirewallRule { pub enabled: bool, pub ip: IpAddr, pub port: BlockedPort, + direction: TrafficDirection, } #[derive(Debug, Clone, PartialEq)] @@ -48,6 +49,7 @@ impl FromStr for BlockedPort { } } +// TODO: Add direction impl Display for FirewallRule { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{} {}", self.ip, self.port) @@ -58,14 +60,16 @@ pub enum FocusedInput { Name, Ip, Port, + Direction, } #[derive(Debug, Clone)] struct UserInput { id: Option, - pub name: UserInputField, - pub ip: UserInputField, - pub port: UserInputField, + name: UserInputField, + ip: UserInputField, + port: UserInputField, + direction: TrafficDirection, focus_input: FocusedInput, } @@ -82,6 +86,7 @@ impl UserInput { name: UserInputField::default(), ip: UserInputField::default(), port: UserInputField::default(), + direction: TrafficDirection::Ingress, focus_input: FocusedInput::Name, } } @@ -137,7 +142,7 @@ impl UserInput { .direction(Direction::Horizontal) .constraints([ Constraint::Fill(1), - Constraint::Max(80), + Constraint::Percentage(80), Constraint::Fill(1), ]) .flex(ratatui::layout::Flex::SpaceBetween) @@ -172,8 +177,22 @@ impl UserInput { } }) .fg(Color::Black), + Cell::from(self.direction.to_string()) + .bg({ + if self.focus_input == FocusedInput::Direction { + Color::Gray + } else { + Color::DarkGray + } + }) + .fg(Color::Black), + ]), + Row::new(vec![ + Cell::new(""), + Cell::new(""), + Cell::new(""), + Cell::new(""), ]), - Row::new(vec![Cell::new(""), Cell::new(""), Cell::new("")]), Row::new(vec![ Cell::from({ if let Some(error) = &self.name.error { @@ -199,13 +218,15 @@ impl UserInput { } }) .red(), + Cell::new(""), ]), ]; let widths = [ - Constraint::Percentage(33), - Constraint::Percentage(33), - Constraint::Percentage(33), + Constraint::Percentage(25), + Constraint::Percentage(25), + Constraint::Percentage(25), + Constraint::Percentage(25), ]; let table = Table::new(rows, widths) @@ -214,6 +235,7 @@ impl UserInput { Line::from("Name").centered(), Line::from("IP").centered(), Line::from("Port").centered(), + Line::from("Direction").centered(), ]) .style(Style::new().bold()) .bottom_margin(1), @@ -252,6 +274,7 @@ impl From for UserInput { field: Input::from(rule.port.to_string()), error: None, }, + direction: rule.direction, focus_input: FocusedInput::Name, } } @@ -318,6 +341,9 @@ impl Firewall { self.rules.retain(|r| r.name != rule.name); } + pub fn remove_ingress_rules(&mut self) {} + pub fn remove_egress_rules(&mut self) {} + pub fn handle_keys( &mut self, key_event: KeyEvent, @@ -350,12 +376,14 @@ impl Firewall { rule.ip = IpAddr::from_str(user_input.ip.field.value()).unwrap(); rule.port = BlockedPort::from_str(user_input.port.field.value()).unwrap(); + rule.direction = user_input.direction; } else { let rule = FirewallRule { id: uuid::Uuid::new_v4(), name: user_input.name.field.to_string(), ip: IpAddr::from_str(user_input.ip.field.value()).unwrap(), port: BlockedPort::from_str(user_input.port.field.value()).unwrap(), + direction: user_input.direction, enabled: false, }; self.rules.push(rule); @@ -369,7 +397,8 @@ impl Firewall { match user_input.focus_input { FocusedInput::Name => user_input.focus_input = FocusedInput::Ip, FocusedInput::Ip => user_input.focus_input = FocusedInput::Port, - FocusedInput::Port => user_input.focus_input = FocusedInput::Name, + FocusedInput::Port => user_input.focus_input = FocusedInput::Direction, + FocusedInput::Direction => user_input.focus_input = FocusedInput::Name, } } } @@ -384,6 +413,15 @@ impl Firewall { FocusedInput::Port => { user_input.port.field.handle_event(&Event::Key(key_event)); } + FocusedInput::Direction => match key_event.code { + KeyCode::Char('j') | KeyCode::Down => { + user_input.direction = TrafficDirection::Ingress; + } + KeyCode::Char('k') | KeyCode::Up => { + user_input.direction = TrafficDirection::Egress; + } + _ => {} + }, }, } } else { @@ -394,9 +432,15 @@ impl Firewall { KeyCode::Char(' ') => { if let Some(index) = self.state.selected() { - self.rules[index].enabled = !self.rules[index].enabled; - self.ingress_sender.send(self.rules[index].clone())?; - self.egress_sender.send(self.rules[index].clone())? + let rule = &mut self.rules[index]; + rule.enabled = !rule.enabled; + + match rule.direction { + TrafficDirection::Ingress => { + self.ingress_sender.send(rule.clone())?; + } + TrafficDirection::Egress => self.egress_sender.send(rule.clone())?, + } } } @@ -417,9 +461,16 @@ impl Firewall { KeyCode::Char('d') => { if let Some(index) = self.state.selected() { - self.rules[index].enabled = false; - self.ingress_sender.send(self.rules[index].clone())?; - self.egress_sender.send(self.rules[index].clone())?; + let rule = &mut self.rules[index]; + + rule.enabled = false; + match rule.direction { + TrafficDirection::Ingress => { + self.ingress_sender.send(rule.clone())?; + } + TrafficDirection::Egress => self.egress_sender.send(rule.clone())?, + } + self.rules.remove(index); } } @@ -483,16 +534,22 @@ impl Firewall { Constraint::Max(20), Constraint::Length(10), Constraint::Length(14), + Constraint::Length(14), ]; let rows = self.rules.iter().map(|rule| { Row::new(vec![ Line::from(rule.name.clone()).centered().bold(), - Line::from(rule.ip.to_string()).centered().centered().bold(), - Line::from(rule.port.to_string()) - .centered() - .centered() - .bold(), + Line::from(rule.ip.to_string()).centered().bold(), + Line::from(rule.port.to_string()).centered().bold(), + Line::from({ + match rule.direction { + TrafficDirection::Ingress => String::from("Ingress 󰁅 "), + TrafficDirection::Egress => String::from("Egress  "), + } + }) + .centered() + .bold(), Line::from({ if rule.enabled { "Enabled".to_string() @@ -501,7 +558,6 @@ impl Firewall { } }) .centered() - .centered() .bold(), ]) }); @@ -519,6 +575,7 @@ impl Firewall { Line::from("Name").centered().blue(), Line::from("IP").centered().blue(), Line::from("Port").centered().blue(), + Line::from("Direction").centered().blue(), Line::from("Status").centered().blue(), ]) .style(Style::new().bold()) From d216eef43880eeb19afdce9e14792294d4194a95 Mon Sep 17 00:00:00 2001 From: Badr Date: Tue, 8 Oct 2024 22:01:41 +0200 Subject: [PATCH 28/41] handle rules with direction lifetimes --- oryx-tui/src/filter/direction.rs | 8 ++++ oryx-tui/src/handler.rs | 17 ++++++++ oryx-tui/src/section/firewall.rs | 69 ++++++++++++++++++++++++-------- 3 files changed, 78 insertions(+), 16 deletions(-) diff --git a/oryx-tui/src/filter/direction.rs b/oryx-tui/src/filter/direction.rs index 95104ce..0d27105 100644 --- a/oryx-tui/src/filter/direction.rs +++ b/oryx-tui/src/filter/direction.rs @@ -82,6 +82,14 @@ impl TrafficDirectionFilter { self.selected_direction.clear(); } + pub fn is_ingress_loaded(&self) -> bool { + self.applied_direction.contains(&TrafficDirection::Ingress) + } + + pub fn is_egress_loaded(&self) -> bool { + self.applied_direction.contains(&TrafficDirection::Egress) + } + pub fn render(&mut self, frame: &mut Frame, block: Rect, is_focused: bool) { let layout = Layout::default() .direction(Direction::Horizontal) diff --git a/oryx-tui/src/handler.rs b/oryx-tui/src/handler.rs index 86bad30..0c042c3 100644 --- a/oryx-tui/src/handler.rs +++ b/oryx-tui/src/handler.rs @@ -75,6 +75,13 @@ pub fn handle_key_events( if app.filter.focused_block == FocusedBlock::Apply { app.filter .update(sender.clone(), app.data_channel_sender.clone())?; + if !app.filter.traffic_direction.is_ingress_loaded() { + app.section.firewall.disable_ingress_rules(); + } + + if !app.filter.traffic_direction.is_egress_loaded() { + app.section.firewall.disable_egress_rules(); + } app.active_popup = None; } @@ -172,6 +179,16 @@ pub fn handle_key_events( } } + KeyCode::Char(' ') => { + if app.section.focused_section == FocusedSection::Firewall { + app.section.firewall.load_rule( + sender.clone(), + app.filter.traffic_direction.is_ingress_loaded(), + app.filter.traffic_direction.is_egress_loaded(), + )?; + } + } + KeyCode::Char('s') => { let app_packets = app.packets.lock().unwrap(); if app_packets.is_empty() { diff --git a/oryx-tui/src/section/firewall.rs b/oryx-tui/src/section/firewall.rs index c9a13c8..7891ef9 100644 --- a/oryx-tui/src/section/firewall.rs +++ b/oryx-tui/src/section/firewall.rs @@ -341,8 +341,59 @@ impl Firewall { self.rules.retain(|r| r.name != rule.name); } - pub fn remove_ingress_rules(&mut self) {} - pub fn remove_egress_rules(&mut self) {} + pub fn disable_ingress_rules(&mut self) { + self.rules.iter_mut().for_each(|rule| { + if rule.enabled && rule.direction == TrafficDirection::Ingress { + rule.enabled = false; + } + }); + } + pub fn disable_egress_rules(&mut self) { + self.rules.iter_mut().for_each(|rule| { + if rule.enabled && rule.direction == TrafficDirection::Egress { + rule.enabled = false; + } + }); + } + + pub fn load_rule( + &mut self, + sender: kanal::Sender, + is_ingress_loaded: bool, + is_egress_loaded: bool, + ) -> AppResult<()> { + if let Some(index) = self.state.selected() { + let rule = &mut self.rules[index]; + + match rule.direction { + TrafficDirection::Ingress => { + if is_ingress_loaded { + rule.enabled = !rule.enabled; + self.ingress_sender.send(rule.clone())?; + } else { + Notification::send( + "Ingress is not loaded.", + crate::notification::NotificationLevel::Warning, + sender.clone(), + )?; + } + } + TrafficDirection::Egress => { + if is_egress_loaded { + rule.enabled = !rule.enabled; + self.egress_sender.send(rule.clone())?; + } else { + Notification::send( + "Egress is not loaded.", + crate::notification::NotificationLevel::Warning, + sender.clone(), + )?; + } + } + } + } + Ok(()) + } pub fn handle_keys( &mut self, @@ -430,20 +481,6 @@ impl Firewall { self.add_rule(); } - KeyCode::Char(' ') => { - if let Some(index) = self.state.selected() { - let rule = &mut self.rules[index]; - rule.enabled = !rule.enabled; - - match rule.direction { - TrafficDirection::Ingress => { - self.ingress_sender.send(rule.clone())?; - } - TrafficDirection::Egress => self.egress_sender.send(rule.clone())?, - } - } - } - KeyCode::Char('e') => { if let Some(index) = self.state.selected() { let rule = self.rules[index].clone(); From 222501358ed78d88e43610023ae1654f191b0b38 Mon Sep 17 00:00:00 2001 From: Badr Date: Tue, 8 Oct 2024 22:41:23 +0200 Subject: [PATCH 29/41] update docs --- Cargo.lock | 103 ++++++++++++++++++++++++++----------------- Readme.md | 17 ++++++- oryx-tui/src/help.rs | 39 +++++++++------- 3 files changed, 100 insertions(+), 59 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0753626..6ff68b8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -83,9 +83,9 @@ checksum = "9b34d609dfbaf33d6889b2b7106d3ca345eacad44200913df5ba02bfd31d2ba9" [[package]] name = "autocfg" -version = "1.3.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" [[package]] name = "aya" @@ -112,7 +112,7 @@ checksum = "2c02024a307161cf3d1f052161958fd13b1a33e3e038083e58082c0700fdab85" dependencies = [ "bytes", "core-error", - "hashbrown", + "hashbrown 0.14.5", "log", "object", "thiserror", @@ -126,9 +126,9 @@ checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" [[package]] name = "bytes" -version = "1.7.1" +version = "1.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8318a53db07bb3f8dca91a600466bdb3f2eaadeedfdbcf02e1accbad9271ba50" +checksum = "428d9aa8fbc0670b7b8d6030a7fadd0f86151cae55e4dbbece15f3780a3dfaf3" [[package]] name = "cassowary" @@ -147,9 +147,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.1.20" +version = "1.1.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45bcde016d64c21da4be18b655631e5ab6d3107607e71a73a9f53eb48aae23fb" +checksum = "2e80e3b6a3ab07840e1cae9b0666a63970dc28e8ed5ffbcdacbfc760c281bfc1" dependencies = [ "shlex", ] @@ -162,9 +162,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clap" -version = "4.5.17" +version = "4.5.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e5a21b8495e732f1b3c364c9949b201ca7bae518c502c80256c96ad79eaf6ac" +checksum = "b97f376d85a664d5837dbae44bf546e6477a679ff6610010f17276f686d867e8" dependencies = [ "clap_builder", "clap_derive", @@ -172,9 +172,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.17" +version = "4.5.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8cf2dd12af7a047ad9d6da2b6b249759a22a7abc0f474c1dae1777afa4b21a73" +checksum = "19bc80abd44e4bed93ca373a0704ccbd1b710dc5749406201bb018272808dc54" dependencies = [ "anstream", "anstyle", @@ -184,9 +184,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.13" +version = "4.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "501d359d5f3dcaf6ecdeee48833ae73ec6e42723a1e52419c79abf9507eec0a0" +checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab" dependencies = [ "heck", "proc-macro2", @@ -291,18 +291,18 @@ dependencies = [ [[package]] name = "derive_builder" -version = "0.20.1" +version = "0.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd33f37ee6a119146a1781d3356a7c26028f83d779b2e04ecd45fdc75c76877b" +checksum = "507dfb09ea8b7fa618fcf76e953f4f5e192547945816d5358edffe39f6f94947" dependencies = [ "derive_builder_macro", ] [[package]] name = "derive_builder_core" -version = "0.20.1" +version = "0.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7431fa049613920234f22c47fdc33e6cf3ee83067091ea4277a3f8c4587aae38" +checksum = "2d5bcf7b024d6835cfb3d473887cd966994907effbe9227e8c8219824d06c4e8" dependencies = [ "darling", "proc-macro2", @@ -312,9 +312,9 @@ dependencies = [ [[package]] name = "derive_builder_macro" -version = "0.20.1" +version = "0.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4abae7035bf79b9877b779505d8cf3749285b80c43941eda66604841889451dc" +checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c" dependencies = [ "derive_builder_core", "syn", @@ -359,6 +359,12 @@ version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + [[package]] name = "errno" version = "0.3.9" @@ -375,6 +381,12 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foldhash" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f81ec6369c545a7d40e4589b5597581fa1c441fe1cce96dd1de43159910a36a2" + [[package]] name = "font8x8" version = "0.3.1" @@ -383,9 +395,9 @@ checksum = "875488b8711a968268c7cf5d139578713097ca4635a76044e8fe8eedf831d07e" [[package]] name = "futures-core" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" [[package]] name = "getrandom" @@ -408,6 +420,17 @@ dependencies = [ "allocator-api2", ] +[[package]] +name = "hashbrown" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb" +dependencies = [ + "allocator-api2", + "equivalent", + "foldhash", +] + [[package]] name = "heck" version = "0.5.0" @@ -475,9 +498,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" -version = "0.2.158" +version = "0.2.159" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439" +checksum = "561d97a539a36e26a9a5fad1ea11a3039a67714694aaa379433e580854bc3dc5" [[package]] name = "libmimalloc-sys" @@ -523,11 +546,11 @@ checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" [[package]] name = "lru" -version = "0.12.4" +version = "0.12.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37ee39891760e7d94734f6f63fedc29a2e4a152f836120753a72503f09fcf904" +checksum = "234cf4f4a04dc1f57e24b96cc0cd600cf2af460d4161ac5ecdd0af8e1f3b2a38" dependencies = [ - "hashbrown", + "hashbrown 0.15.0", ] [[package]] @@ -575,9 +598,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.19.0" +version = "1.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" [[package]] name = "option-ext" @@ -645,9 +668,9 @@ checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" [[package]] name = "proc-macro2" -version = "1.0.86" +version = "1.0.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +checksum = "b3e4daa0dcf6feba26f985457cdf104d4b4256fc5a09547140f3631bb076b19a" dependencies = [ "unicode-ident", ] @@ -684,9 +707,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.4" +version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0884ad60e090bf1345b93da0a5de8923c93884cd03f40dfcfddd3b4bee661853" +checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f" dependencies = [ "bitflags", ] @@ -821,9 +844,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.77" +version = "2.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed" +checksum = "89132cd0bf050864e1d38dc3bbc07a0eb8e7530af26344d3d2bbbef83499f590" dependencies = [ "proc-macro2", "quote", @@ -832,18 +855,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.63" +version = "1.0.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724" +checksum = "d50af8abc119fb8bb6dbabcfa89656f46f84aa0ac7688088608076ad2b459a84" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.63" +version = "1.0.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" +checksum = "08904e7672f5eb876eaaf87e0ce17857500934f4981c4a0ab2b4aa98baac7fc3" dependencies = [ "proc-macro2", "quote", @@ -897,9 +920,9 @@ dependencies = [ [[package]] name = "unicode-width" -version = "0.1.13" +version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d" +checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" [[package]] name = "utf8parse" diff --git a/Readme.md b/Readme.md index 4e0fcf0..d7cfe05 100644 --- a/Readme.md +++ b/Readme.md @@ -10,6 +10,7 @@ - Real-time traffic inspection and visualization. - Comprehensive Traffic Statistics. +- Firewall functionalities. - Fuzzy search. ## 💡 Prerequisites @@ -103,14 +104,26 @@ sudo oryx `f`: Update the applied filters. -`i`: Show more infos about the selected packet. - `ctrl + r`: Reset the app. `ctrl + s`: Export the capture to `~/oryx/capture` file. +### Inspection Section + +`i`: Show more infos about the selected packet. + `/`: Start fuzzy search. +### Firewall Section + +`Space`: Toggle firewall rules status. + +`n` : Add new firewall rule. + +`e`: Edit a firewall rule. + +`Enter`: Create or Save a firewall rule. + ## ⚖️ License GPLv3 diff --git a/oryx-tui/src/help.rs b/oryx-tui/src/help.rs index cd867ef..3685a8d 100644 --- a/oryx-tui/src/help.rs +++ b/oryx-tui/src/help.rs @@ -25,35 +25,40 @@ impl Help { state, keys: vec![ ( - Cell::from("Esc").bold().yellow(), + Cell::from("Esc").bold(), "Dismiss different pop-ups and modes", ), ( - Cell::from("Tab or Shift+Tab").bold().yellow(), + Cell::from("Tab or Shift+Tab").bold(), "Switch between different sections", ), - (Cell::from("j or Down").bold().yellow(), "Scroll down"), - (Cell::from("k or Up").bold().yellow(), "Scroll up"), - (Cell::from("?").bold().yellow(), "Show help"), - (Cell::from("q or ctrl+c").bold().yellow(), "Quit"), - (Cell::from("ctrl + r").bold().yellow(), "Reset the app"), + (Cell::from("j or Down").bold(), "Scroll down"), + (Cell::from("k or Up").bold(), "Scroll up"), + (Cell::from("?").bold(), "Show help"), + (Cell::from("q or ctrl+c").bold(), "Quit"), ( - Cell::from("Space").bold().yellow(), + Cell::from("Space").bold(), "Select/Deselect interface or filter", ), - (Cell::from("/").bold().yellow(), "Start fuzzy finding"), + (Cell::from("f").bold(), "Update the applied filters"), + (Cell::from("ctrl + r").bold(), "Reset the app"), ( - Cell::from("f").bold().yellow(), - "Update the applied filters", + Cell::from("ctrl + s").bold(), + "Export the capture to ~/oryx/capture file", ), + (Cell::from(""), ""), + (Cell::from("## Inspection").bold().yellow(), ""), ( - Cell::from("i").bold().yellow(), + Cell::from("i").bold(), "Show more infos about the selected packet", ), - ( - Cell::from("ctrl + s").bold().yellow(), - "Export the capture to ~/oryx/capture file", - ), + (Cell::from("/").bold(), "Start fuzzy finding"), + (Cell::from(""), ""), + (Cell::from("## Firewall").bold().yellow(), ""), + (Cell::from("n").bold(), "Add new firewall rule"), + (Cell::from("e").bold(), "Edit a firewall rule"), + (Cell::from("Space").bold(), "Toggle firewall rule status"), + (Cell::from("Enter").bold(), "Create or Save a firewall rule"), ], } } @@ -92,7 +97,7 @@ impl Help { .direction(Direction::Vertical) .constraints([ Constraint::Fill(1), - Constraint::Length(18), + Constraint::Length(26), Constraint::Fill(1), ]) .flex(ratatui::layout::Flex::SpaceBetween) From a0374351af222b782ee0e6d40d100b3ea1123a6b Mon Sep 17 00:00:00 2001 From: Badr Date: Tue, 8 Oct 2024 22:44:32 +0200 Subject: [PATCH 30/41] update Readme --- Readme.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Readme.md b/Readme.md index d7cfe05..6a806ca 100644 --- a/Readme.md +++ b/Readme.md @@ -108,13 +108,13 @@ sudo oryx `ctrl + s`: Export the capture to `~/oryx/capture` file. -### Inspection Section +#### Inspection Section `i`: Show more infos about the selected packet. `/`: Start fuzzy search. -### Firewall Section +#### Firewall Section `Space`: Toggle firewall rules status. From 47d99f4c575b5b8248723c17b32988a0d9f55aa2 Mon Sep 17 00:00:00 2001 From: Badr Date: Wed, 9 Oct 2024 12:09:54 +0200 Subject: [PATCH 31/41] fix firewall reload --- .gitignore | 1 + Cargo.lock | 69 ++++++++++++++++++++++++++++ Justfile | 4 ++ oryx-tui/Cargo.toml | 2 + oryx-tui/src/app.rs | 7 ++- oryx-tui/src/ebpf.rs | 79 ++++++++++++++++++-------------- oryx-tui/src/filter.rs | 19 ++++++-- oryx-tui/src/main.rs | 1 + oryx-tui/src/section.rs | 6 +-- oryx-tui/src/section/firewall.rs | 27 +++++++---- 10 files changed, 163 insertions(+), 52 deletions(-) diff --git a/.gitignore b/.gitignore index b798b63..e6f5aea 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +log-file debug/ target/ **/*.rs.bk diff --git a/Cargo.lock b/Cargo.lock index 6ff68b8..183fe81 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -14,6 +14,15 @@ dependencies = [ "zerocopy", ] +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + [[package]] name = "allocator-api2" version = "0.2.18" @@ -359,6 +368,29 @@ version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" +[[package]] +name = "env_filter" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f2c92ceda6ceec50f43169f9ee8424fe2db276791afde7b2cd8bc084cb376ab" +dependencies = [ + "log", + "regex", +] + +[[package]] +name = "env_logger" +version = "0.11.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13fa619b91fb2381732789fc5de83b45675e882f66623b7d8cb4f643017018d" +dependencies = [ + "anstream", + "anstyle", + "env_filter", + "humantime", + "log", +] + [[package]] name = "equivalent" version = "1.0.1" @@ -443,6 +475,12 @@ version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + [[package]] name = "ident_case" version = "1.0.1" @@ -624,9 +662,11 @@ dependencies = [ "crossterm", "dirs", "dns-lookup", + "env_logger", "itertools", "kanal", "libc", + "log", "mimalloc", "mio", "network-types", @@ -725,6 +765,35 @@ dependencies = [ "thiserror", ] +[[package]] +name = "regex" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38200e5ee88914975b69f657f0801b6f6dccafd44fd9326302a4aaeecfacb1d8" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" + [[package]] name = "rustix" version = "0.38.37" diff --git a/Justfile b/Justfile index 0c8de83..f808523 100644 --- a/Justfile +++ b/Justfile @@ -15,6 +15,10 @@ show interface: sudo tc filter show dev $interface egress # Run oryx debug +run-debug: + echo "" > log-file + RUST_LOG=info cargo xtask run 2> log-file + run: cargo xtask run diff --git a/oryx-tui/Cargo.toml b/oryx-tui/Cargo.toml index 3960aa3..8466043 100644 --- a/oryx-tui/Cargo.toml +++ b/oryx-tui/Cargo.toml @@ -25,6 +25,8 @@ mimalloc = "0.1" clap = { version = "4", features = ["derive", "cargo"] } network-types = "0.0.7" uuid = { version = "1", default-features = false, features = ["v4"] } +log = "0.4" +env_logger = "0.11" [[bin]] name = "oryx" diff --git a/oryx-tui/src/app.rs b/oryx-tui/src/app.rs index 7425ce2..cd47f60 100644 --- a/oryx-tui/src/app.rs +++ b/oryx-tui/src/app.rs @@ -77,7 +77,12 @@ impl App { Self { running: true, help: Help::new(), - filter: Filter::new(firewall_ingress_receiver, firewall_egress_receiver), + filter: Filter::new( + firewall_ingress_sender.clone(), + firewall_ingress_receiver, + firewall_egress_sender.clone(), + firewall_egress_receiver, + ), start_sniffing: false, packets: packets.clone(), notifications: Vec::new(), diff --git a/oryx-tui/src/ebpf.rs b/oryx-tui/src/ebpf.rs index 9c77d91..2d5cbfa 100644 --- a/oryx-tui/src/ebpf.rs +++ b/oryx-tui/src/ebpf.rs @@ -18,7 +18,7 @@ use oryx_common::{protocols::Protocol, RawPacket}; use crate::{ event::Event, notification::{Notification, NotificationLevel}, - section::firewall::{BlockedPort, FirewallRule}, + section::firewall::{BlockedPort, FirewallSignal}, }; use mio::{event::Source, unix::SourceFd, Events, Interest, Poll, Registry, Token}; @@ -83,8 +83,7 @@ fn update_ipv4_blocklist( .insert(addr.to_bits(), blocked_ports, 0) .unwrap(); } else { - //TODO: - unreachable!(); // list is full + unreachable!(); } } else { let not_null_ports = blocked_ports @@ -203,7 +202,7 @@ impl Ebpf { notification_sender: kanal::Sender, data_sender: kanal::Sender<[u8; RawPacket::LEN]>, filter_channel_receiver: kanal::Receiver<(Protocol, bool)>, - firewall_ingress_receiver: kanal::Receiver, + firewall_ingress_receiver: kanal::Receiver, terminate: Arc, ) { thread::spawn({ @@ -300,21 +299,26 @@ impl Ebpf { HashMap::try_from(bpf.take_map("BLOCKLIST_IPV6_INGRESS").unwrap()).unwrap(); thread::spawn(move || loop { - if let Ok(rule) = firewall_ingress_receiver.recv() { - 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, - ), + 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; + } } } }); @@ -383,7 +387,7 @@ impl Ebpf { notification_sender: kanal::Sender, data_sender: kanal::Sender<[u8; RawPacket::LEN]>, filter_channel_receiver: kanal::Receiver<(Protocol, bool)>, - firewall_egress_receiver: kanal::Receiver, + firewall_egress_receiver: kanal::Receiver, terminate: Arc, ) { thread::spawn({ @@ -476,21 +480,26 @@ impl Ebpf { let mut ipv6_firewall: HashMap<_, u128, [u16; 32]> = HashMap::try_from(bpf.take_map("BLOCKLIST_IPV6_EGRESS").unwrap()).unwrap(); thread::spawn(move || loop { - if let Ok(rule) = firewall_egress_receiver.recv() { - 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, - ), + 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; + } } } }); diff --git a/oryx-tui/src/filter.rs b/oryx-tui/src/filter.rs index 932fa11..8c44e01 100644 --- a/oryx-tui/src/filter.rs +++ b/oryx-tui/src/filter.rs @@ -28,7 +28,8 @@ use transport::TransportFilter; use tui_big_text::{BigText, PixelSize}; use crate::{ - app::AppResult, ebpf::Ebpf, event::Event, interface::Interface, section::firewall::FirewallRule, + app::AppResult, ebpf::Ebpf, event::Event, interface::Interface, + section::firewall::FirewallSignal, }; #[derive(Debug, Clone)] @@ -91,14 +92,18 @@ pub struct Filter { pub filter_chans: IoChans, pub firewall_chans: IoChans, pub focused_block: FocusedBlock, - pub firewall_ingress_receiver: kanal::Receiver, - pub firewall_egress_receiver: kanal::Receiver, + pub firewall_ingress_sender: kanal::Sender, + pub firewall_ingress_receiver: kanal::Receiver, + pub firewall_egress_sender: kanal::Sender, + pub firewall_egress_receiver: kanal::Receiver, } impl Filter { pub fn new( - firewall_ingress_receiver: kanal::Receiver, - firewall_egress_receiver: kanal::Receiver, + firewall_ingress_sender: kanal::Sender, + firewall_ingress_receiver: kanal::Receiver, + firewall_egress_sender: kanal::Sender, + firewall_egress_receiver: kanal::Receiver, ) -> Self { Self { interface: Interface::new(), @@ -109,7 +114,9 @@ impl Filter { filter_chans: IoChans::new(), firewall_chans: IoChans::new(), focused_block: FocusedBlock::Interface, + firewall_ingress_sender, firewall_ingress_receiver, + firewall_egress_sender, firewall_egress_receiver, } } @@ -263,6 +270,7 @@ impl Filter { .selected_direction .contains(&TrafficDirection::Egress) { + self.firewall_egress_sender.send(FirewallSignal::Kill)?; self.traffic_direction.terminate(TrafficDirection::Egress); } @@ -302,6 +310,7 @@ impl Filter { .selected_direction .contains(&TrafficDirection::Ingress) { + self.firewall_ingress_sender.send(FirewallSignal::Kill)?; self.traffic_direction.terminate(TrafficDirection::Ingress); } diff --git a/oryx-tui/src/main.rs b/oryx-tui/src/main.rs index e0e5816..82a1c7d 100644 --- a/oryx-tui/src/main.rs +++ b/oryx-tui/src/main.rs @@ -13,6 +13,7 @@ use oryx_tui::{ use ratatui::{backend::CrosstermBackend, Terminal}; fn main() -> AppResult<()> { + env_logger::init(); Command::new("oryx") .about(crate_description!()) .version(crate_version!()) diff --git a/oryx-tui/src/section.rs b/oryx-tui/src/section.rs index 110fc49..bec9732 100644 --- a/oryx-tui/src/section.rs +++ b/oryx-tui/src/section.rs @@ -7,7 +7,7 @@ use std::sync::{Arc, Mutex}; use alert::Alert; use crossterm::event::{KeyCode, KeyEvent}; -use firewall::{Firewall, FirewallRule}; +use firewall::{Firewall, FirewallSignal}; use inspection::Inspection; use ratatui::{ @@ -41,8 +41,8 @@ pub struct Section { impl Section { pub fn new( packets: Arc>>, - firewall_ingress_sender: kanal::Sender, - firewall_egress_sender: kanal::Sender, + firewall_ingress_sender: kanal::Sender, + firewall_egress_sender: kanal::Sender, ) -> Self { Self { focused_section: FocusedSection::Inspection, diff --git a/oryx-tui/src/section/firewall.rs b/oryx-tui/src/section/firewall.rs index 7891ef9..20fd35f 100644 --- a/oryx-tui/src/section/firewall.rs +++ b/oryx-tui/src/section/firewall.rs @@ -13,6 +13,12 @@ use uuid; use crate::{app::AppResult, filter::direction::TrafficDirection, notification::Notification}; +#[derive(Debug, Clone)] +pub enum FirewallSignal { + Rule(FirewallRule), + Kill, +} + #[derive(Debug, Clone)] pub struct FirewallRule { id: uuid::Uuid, @@ -285,14 +291,14 @@ pub struct Firewall { rules: Vec, state: TableState, user_input: Option, - ingress_sender: kanal::Sender, - egress_sender: kanal::Sender, + ingress_sender: kanal::Sender, + egress_sender: kanal::Sender, } impl Firewall { pub fn new( - ingress_sender: kanal::Sender, - egress_sender: kanal::Sender, + ingress_sender: kanal::Sender, + egress_sender: kanal::Sender, ) -> Self { Self { rules: Vec::new(), @@ -369,7 +375,8 @@ impl Firewall { TrafficDirection::Ingress => { if is_ingress_loaded { rule.enabled = !rule.enabled; - self.ingress_sender.send(rule.clone())?; + self.ingress_sender + .send(FirewallSignal::Rule(rule.clone()))?; } else { Notification::send( "Ingress is not loaded.", @@ -381,7 +388,8 @@ impl Firewall { TrafficDirection::Egress => { if is_egress_loaded { rule.enabled = !rule.enabled; - self.egress_sender.send(rule.clone())?; + self.egress_sender + .send(FirewallSignal::Rule(rule.clone()))?; } else { Notification::send( "Egress is not loaded.", @@ -503,9 +511,12 @@ impl Firewall { rule.enabled = false; match rule.direction { TrafficDirection::Ingress => { - self.ingress_sender.send(rule.clone())?; + self.ingress_sender + .send(FirewallSignal::Rule(rule.clone()))?; } - TrafficDirection::Egress => self.egress_sender.send(rule.clone())?, + TrafficDirection::Egress => self + .egress_sender + .send(FirewallSignal::Rule(rule.clone()))?, } self.rules.remove(index); From efcf2c5cba01a596203b153eeb89994e4b7ef2e9 Mon Sep 17 00:00:00 2001 From: Badr Date: Wed, 9 Oct 2024 12:32:36 +0200 Subject: [PATCH 32/41] unload filters properly --- oryx-tui/src/ebpf.rs | 56 ++++++++++++++++++------------ oryx-tui/src/filter.rs | 78 ++++++++++++++++++++++++++++++++++-------- 2 files changed, 98 insertions(+), 36 deletions(-) diff --git a/oryx-tui/src/ebpf.rs b/oryx-tui/src/ebpf.rs index 2d5cbfa..d8d3145 100644 --- a/oryx-tui/src/ebpf.rs +++ b/oryx-tui/src/ebpf.rs @@ -17,6 +17,7 @@ use oryx_common::{protocols::Protocol, RawPacket}; use crate::{ event::Event, + filter::FilterChannelSignal, notification::{Notification, NotificationLevel}, section::firewall::{BlockedPort, FirewallSignal}, }; @@ -201,7 +202,7 @@ impl Ebpf { iface: String, notification_sender: kanal::Sender, data_sender: kanal::Sender<[u8; RawPacket::LEN]>, - filter_channel_receiver: kanal::Receiver<(Protocol, bool)>, + filter_channel_receiver: kanal::Receiver, firewall_ingress_receiver: kanal::Receiver, terminate: Arc, ) { @@ -324,16 +325,21 @@ impl Ebpf { }); thread::spawn(move || loop { - if let Ok((filter, flag)) = filter_channel_receiver.recv() { - 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); + 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; } } } @@ -386,7 +392,7 @@ impl Ebpf { iface: String, notification_sender: kanal::Sender, data_sender: kanal::Sender<[u8; RawPacket::LEN]>, - filter_channel_receiver: kanal::Receiver<(Protocol, bool)>, + filter_channel_receiver: kanal::Receiver, firewall_egress_receiver: kanal::Receiver, terminate: Arc, ) { @@ -505,20 +511,26 @@ impl Ebpf { }); thread::spawn(move || loop { - if let Ok((filter, flag)) = filter_channel_receiver.recv() { - 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); + 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); poll.registry() diff --git a/oryx-tui/src/filter.rs b/oryx-tui/src/filter.rs index 8c44e01..cba3f3b 100644 --- a/oryx-tui/src/filter.rs +++ b/oryx-tui/src/filter.rs @@ -32,10 +32,16 @@ use crate::{ section::firewall::FirewallSignal, }; +#[derive(Debug, Clone)] +pub enum FilterChannelSignal { + Update((Protocol, bool)), + Kill, +} + #[derive(Debug, Clone)] pub struct Channels { - pub sender: kanal::Sender<(Protocol, bool)>, - pub receiver: kanal::Receiver<(Protocol, bool)>, + pub sender: kanal::Sender, + pub receiver: kanal::Receiver, } #[derive(Debug, Clone)] @@ -191,20 +197,32 @@ impl Filter { self.filter_chans .ingress .sender - .send((Protocol::Transport(*protocol), false))?; + .send(FilterChannelSignal::Update(( + Protocol::Transport(*protocol), + false, + )))?; self.filter_chans .egress .sender - .send((Protocol::Transport(*protocol), false))?; + .send(FilterChannelSignal::Update(( + Protocol::Transport(*protocol), + false, + )))?; } else { self.filter_chans .ingress .sender - .send((Protocol::Transport(*protocol), true))?; + .send(FilterChannelSignal::Update(( + Protocol::Transport(*protocol), + true, + )))?; self.filter_chans .egress .sender - .send((Protocol::Transport(*protocol), true))?; + .send(FilterChannelSignal::Update(( + Protocol::Transport(*protocol), + true, + )))?; } } @@ -213,20 +231,32 @@ impl Filter { self.filter_chans .ingress .sender - .send((Protocol::Network(*protocol), false))?; + .send(FilterChannelSignal::Update(( + Protocol::Network(*protocol), + false, + )))?; self.filter_chans .egress .sender - .send((Protocol::Network(*protocol), false))?; + .send(FilterChannelSignal::Update(( + Protocol::Network(*protocol), + false, + )))?; } else { self.filter_chans .ingress .sender - .send((Protocol::Network(*protocol), true))?; + .send(FilterChannelSignal::Update(( + Protocol::Network(*protocol), + true, + )))?; self.filter_chans .egress .sender - .send((Protocol::Network(*protocol), true))?; + .send(FilterChannelSignal::Update(( + Protocol::Network(*protocol), + true, + )))?; } } @@ -235,20 +265,32 @@ impl Filter { self.filter_chans .ingress .sender - .send((Protocol::Link(*protocol), false))?; + .send(FilterChannelSignal::Update(( + Protocol::Link(*protocol), + false, + )))?; self.filter_chans .egress .sender - .send((Protocol::Link(*protocol), false))?; + .send(FilterChannelSignal::Update(( + Protocol::Link(*protocol), + false, + )))?; } else { self.filter_chans .ingress .sender - .send((Protocol::Link(*protocol), true))?; + .send(FilterChannelSignal::Update(( + Protocol::Link(*protocol), + true, + )))?; self.filter_chans .egress .sender - .send((Protocol::Link(*protocol), true))?; + .send(FilterChannelSignal::Update(( + Protocol::Link(*protocol), + true, + )))?; } } @@ -271,6 +313,10 @@ impl Filter { .contains(&TrafficDirection::Egress) { self.firewall_egress_sender.send(FirewallSignal::Kill)?; + self.filter_chans + .egress + .sender + .send(FilterChannelSignal::Kill)?; self.traffic_direction.terminate(TrafficDirection::Egress); } @@ -311,6 +357,10 @@ impl Filter { .contains(&TrafficDirection::Ingress) { self.firewall_ingress_sender.send(FirewallSignal::Kill)?; + self.filter_chans + .ingress + .sender + .send(FilterChannelSignal::Kill)?; self.traffic_direction.terminate(TrafficDirection::Ingress); } From 7ea7f44946a6aaacb69e0d5395deeb29d4163cba Mon Sep 17 00:00:00 2001 From: adrien gaultier Date: Wed, 9 Oct 2024 12:51:33 +0200 Subject: [PATCH 33/41] add direction in Firewall::validate_duplicate_rules comparison --- oryx-tui/src/section/firewall.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/oryx-tui/src/section/firewall.rs b/oryx-tui/src/section/firewall.rs index 20fd35f..37f6e48 100644 --- a/oryx-tui/src/section/firewall.rs +++ b/oryx-tui/src/section/firewall.rs @@ -316,6 +316,7 @@ impl Firewall { fn validate_duplicate_rules(rules: &[FirewallRule], user_input: &UserInput) -> AppResult<()> { if let Some(exiting_rule_with_same_ip) = rules.iter().find(|rule| { rule.ip == IpAddr::from_str(user_input.ip.field.value()).unwrap() + && rule.direction == user_input.direction && match user_input.id { Some(uuid) => rule.id != uuid, None => true, From d827a17ebd2ef5645d927c76847fb2e8a0e44803 Mon Sep 17 00:00:00 2001 From: adrien gaultier Date: Wed, 9 Oct 2024 17:20:29 +0200 Subject: [PATCH 34/41] fix maps duplicates --- oryx-ebpf/src/main.rs | 57 ++++++++++++++++++++++++++++++++++--------- oryx-tui/src/ebpf.rs | 8 +++--- 2 files changed, 49 insertions(+), 16 deletions(-) diff --git a/oryx-ebpf/src/main.rs b/oryx-ebpf/src/main.rs index 6afd83c..2950b70 100644 --- a/oryx-ebpf/src/main.rs +++ b/oryx-ebpf/src/main.rs @@ -34,16 +34,11 @@ static TRANSPORT_FILTERS: Array = Array::with_max_entries(8, 0); static LINK_FILTERS: Array = Array::with_max_entries(8, 0); #[map] -static BLOCKLIST_IPV6_INGRESS: HashMap = +static BLOCKLIST_IPV6: HashMap = HashMap::::with_max_entries(128, 0); + #[map] -static BLOCKLIST_IPV6_EGRESS: HashMap = - HashMap::::with_max_entries(128, 0); -#[map] -static BLOCKLIST_IPV4_INGRESS: HashMap = - HashMap::::with_max_entries(128, 0); -#[map] -static BLOCKLIST_IPV4_EGRESS: HashMap = +static BLOCKLIST_IPV4: HashMap = HashMap::::with_max_entries(128, 0); #[classifier] @@ -96,6 +91,28 @@ fn filter_for_ipv4_address( false } +#[inline] +fn filter_for_ipv6_address( + addr: u128, + port: u16, + blocked_ports_map: &HashMap, +) -> bool { + if let Some(blocked_ports) = unsafe { blocked_ports_map.get(&addr) } { + for (idx, blocked_port) in blocked_ports.iter().enumerate() { + if *blocked_port == 0 { + if idx == 0 { + return true; + } else { + break; + } + } else if *blocked_port == port { + return true; + } + } + } + false +} + #[inline] fn filter_packet(protocol: Protocol) -> bool { match protocol { @@ -134,8 +151,8 @@ fn process(ctx: TcContext) -> Result { let src_port = u16::from_be(unsafe { (*tcphdr).source }); let dst_port = u16::from_be(unsafe { (*tcphdr).dest }); - if filter_for_ipv4_address(src_addr, src_port, &BLOCKLIST_IPV4_INGRESS) - || filter_for_ipv4_address(dst_addr, dst_port, &BLOCKLIST_IPV4_EGRESS) + if filter_for_ipv4_address(src_addr, src_port, &BLOCKLIST_IPV4) + || filter_for_ipv4_address(dst_addr, dst_port, &BLOCKLIST_IPV4) { return Ok(TC_ACT_SHOT); } @@ -155,8 +172,8 @@ fn process(ctx: TcContext) -> Result { let src_port = u16::from_be(unsafe { (*udphdr).source }); let dst_port = u16::from_be(unsafe { (*udphdr).dest }); - if filter_for_ipv4_address(src_addr, src_port, &BLOCKLIST_IPV4_INGRESS) - || filter_for_ipv4_address(dst_addr, dst_port, &BLOCKLIST_IPV4_EGRESS) + if filter_for_ipv4_address(src_addr, src_port, &BLOCKLIST_IPV4) + || filter_for_ipv4_address(dst_addr, dst_port, &BLOCKLIST_IPV4) { return Ok(TC_ACT_SHOT); } @@ -187,11 +204,20 @@ fn process(ctx: TcContext) -> Result { } EtherType::Ipv6 => { let header: Ipv6Hdr = ctx.load(EthHdr::LEN).map_err(|_| ())?; + let src_addr = header.src_addr().to_bits(); + let dst_addr = header.dst_addr().to_bits(); match header.next_hdr { IpProto::Tcp => { let tcphdr: *const TcpHdr = ptr_at(&ctx, EthHdr::LEN + Ipv6Hdr::LEN)?; + let src_port = u16::from_be(unsafe { (*tcphdr).source }); + let dst_port = u16::from_be(unsafe { (*tcphdr).dest }); + if filter_for_ipv6_address(src_addr, src_port, &BLOCKLIST_IPV6) + || filter_for_ipv6_address(dst_addr, dst_port, &BLOCKLIST_IPV6) + { + return Ok(TC_ACT_SHOT); + } if filter_packet(Protocol::Network(NetworkProtocol::Ipv6)) || filter_packet(Protocol::Transport(TransportProtocol::TCP)) { @@ -204,7 +230,14 @@ fn process(ctx: TcContext) -> Result { } IpProto::Udp => { let udphdr: *const UdpHdr = ptr_at(&ctx, EthHdr::LEN + Ipv6Hdr::LEN)?; + let src_port = u16::from_be(unsafe { (*udphdr).source }); + let dst_port = u16::from_be(unsafe { (*udphdr).dest }); + if filter_for_ipv6_address(src_addr, src_port, &BLOCKLIST_IPV6) + || filter_for_ipv6_address(dst_addr, dst_port, &BLOCKLIST_IPV6) + { + return Ok(TC_ACT_SHOT); + } if filter_packet(Protocol::Network(NetworkProtocol::Ipv6)) || filter_packet(Protocol::Transport(TransportProtocol::UDP)) { diff --git a/oryx-tui/src/ebpf.rs b/oryx-tui/src/ebpf.rs index d8d3145..11497cd 100644 --- a/oryx-tui/src/ebpf.rs +++ b/oryx-tui/src/ebpf.rs @@ -295,9 +295,9 @@ impl Ebpf { Array::try_from(bpf.take_map("LINK_FILTERS").unwrap()).unwrap(); // firewall-ebpf interface let mut ipv4_firewall: HashMap<_, u32, [u16; 32]> = - HashMap::try_from(bpf.take_map("BLOCKLIST_IPV4_INGRESS").unwrap()).unwrap(); + HashMap::try_from(bpf.take_map("BLOCKLIST_IPV4").unwrap()).unwrap(); let mut ipv6_firewall: HashMap<_, u128, [u16; 32]> = - HashMap::try_from(bpf.take_map("BLOCKLIST_IPV6_INGRESS").unwrap()).unwrap(); + HashMap::try_from(bpf.take_map("BLOCKLIST_IPV6").unwrap()).unwrap(); thread::spawn(move || loop { if let Ok(signal) = firewall_ingress_receiver.recv() { @@ -482,9 +482,9 @@ impl Ebpf { // firewall-ebpf interface let mut ipv4_firewall: HashMap<_, u32, [u16; 32]> = - HashMap::try_from(bpf.take_map("BLOCKLIST_IPV4_EGRESS").unwrap()).unwrap(); + HashMap::try_from(bpf.take_map("BLOCKLIST_IPV4").unwrap()).unwrap(); let mut ipv6_firewall: HashMap<_, u128, [u16; 32]> = - HashMap::try_from(bpf.take_map("BLOCKLIST_IPV6_EGRESS").unwrap()).unwrap(); + HashMap::try_from(bpf.take_map("BLOCKLIST_IPV6").unwrap()).unwrap(); thread::spawn(move || loop { if let Ok(signal) = firewall_egress_receiver.recv() { match signal { From ff2336e226e3aa31c0a2573e333df3d4bf95cddb Mon Sep 17 00:00:00 2001 From: adrien gaultier Date: Wed, 9 Oct 2024 17:21:19 +0200 Subject: [PATCH 35/41] add blocklist maps display just show_active_maps --- Justfile | 10 ++++++++++ showmaps.py | 9 +++++++++ 2 files changed, 19 insertions(+) create mode 100644 showmaps.py diff --git a/Justfile b/Justfile index f808523..c035e45 100644 --- a/Justfile +++ b/Justfile @@ -33,3 +33,13 @@ build: # Profile profile: CARGO_PROFILE_RELEASE_DEBUG=true cargo flamegraph --root --bin oryx + +show_active_maps: + @for MAPTYPE in BLOCKLIST_IPV4 BLOCKLIST_IPV6;do \ + map_ids=$(bpftool map show | grep "$MAPTYPE" | cut -f1 -d":" ); \ + for map_id in $map_ids;do \ + echo "$MAPTYPE($map_id)";\ + bpftool map dump id $map_id -j | jq "." | python3 showmaps.py 2>/dev/null || echo "\tempty";\ + done ;\ + done + diff --git a/showmaps.py b/showmaps.py new file mode 100644 index 0000000..c18123d --- /dev/null +++ b/showmaps.py @@ -0,0 +1,9 @@ +import sys,json; +rules=json.loads(sys.stdin.read()) +for rule in rules: + k = '.'.join([str(int(x,16)) for x in rule['key']]) + chunks =[rule['value'][idx: idx+2] for idx in range(0,len(rule['value']),2)] + ports = [int(f"{chunk[1]}{chunk[0]}".replace("0x",""),16) for chunk in chunks] + v = "[{}, ...]".format(', '.join([str(k) for k in ports if k!=0])) if not all(map(lambda x: x==0,ports)) else '*' + print(f"\t{k} : {v}") + From 093aff35e40ebce128dba7df6e98288d0be288c2 Mon Sep 17 00:00:00 2001 From: adrien gaultier Date: Wed, 9 Oct 2024 17:59:38 +0200 Subject: [PATCH 36/41] fix showmaps --- Justfile | 2 +- showmaps.py | 13 ++++++++++--- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/Justfile b/Justfile index c035e45..c3e9492 100644 --- a/Justfile +++ b/Justfile @@ -39,7 +39,7 @@ show_active_maps: map_ids=$(bpftool map show | grep "$MAPTYPE" | cut -f1 -d":" ); \ for map_id in $map_ids;do \ echo "$MAPTYPE($map_id)";\ - bpftool map dump id $map_id -j | jq "." | python3 showmaps.py 2>/dev/null || echo "\tempty";\ + bpftool map dump id $map_id -j | python3 showmaps.py 2>/dev/null || echo "\tempty";\ done ;\ done diff --git a/showmaps.py b/showmaps.py index c18123d..a9b7293 100644 --- a/showmaps.py +++ b/showmaps.py @@ -1,9 +1,16 @@ import sys,json; rules=json.loads(sys.stdin.read()) for rule in rules: - k = '.'.join([str(int(x,16)) for x in rule['key']]) - chunks =[rule['value'][idx: idx+2] for idx in range(0,len(rule['value']),2)] - ports = [int(f"{chunk[1]}{chunk[0]}".replace("0x",""),16) for chunk in chunks] + ip_version = "ipv4" if len(rule['key'])== 4 else "ipv6" + if ip_version=="ipv6": + chunks_k = [rule['key'][idx: idx+2] for idx in range(0,len(rule['key']),2)] + k = ':'.join( reversed([f"{chunk[1]}{chunk[0]}".replace("0x","") for chunk in chunks_k])) + elif ip_version=="ipv4": + k = '.'.join( reversed([str(int(k.replace("0x",""),16)) for k in rule['key']])) + else: + raise ValueError("wrong ip version") + chunks_v =[rule['value'][idx: idx+2] for idx in range(0,len(rule['value']),2)] + ports = [int(f"{chunk[1]}{chunk[0]}".replace("0x",""),16) for chunk in chunks_v] v = "[{}, ...]".format(', '.join([str(k) for k in ports if k!=0])) if not all(map(lambda x: x==0,ports)) else '*' print(f"\t{k} : {v}") From 530f37ace48786786b389feaf76a15cb79a49ddc Mon Sep 17 00:00:00 2001 From: Badr Date: Wed, 9 Oct 2024 19:35:49 +0200 Subject: [PATCH 37/41] indent just targets --- Justfile | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/Justfile b/Justfile index c3e9492..f36f9f3 100644 --- a/Justfile +++ b/Justfile @@ -36,10 +36,9 @@ profile: show_active_maps: @for MAPTYPE in BLOCKLIST_IPV4 BLOCKLIST_IPV6;do \ - map_ids=$(bpftool map show | grep "$MAPTYPE" | cut -f1 -d":" ); \ - for map_id in $map_ids;do \ - echo "$MAPTYPE($map_id)";\ - bpftool map dump id $map_id -j | python3 showmaps.py 2>/dev/null || echo "\tempty";\ - done ;\ + map_ids=$(sudo bpftool map show | grep "$MAPTYPE" | cut -f1 -d":" ); \ + for map_id in $map_ids;do \ + echo "$MAPTYPE($map_id)";\ + sudo bpftool map dump id $map_id -j | python3 showmaps.py 2>/dev/null || echo "\tempty";\ + done ;\ done - From 7c678915503eacaef47061fe9543b7581e6044fa Mon Sep 17 00:00:00 2001 From: Badr Date: Wed, 9 Oct 2024 19:36:10 +0200 Subject: [PATCH 38/41] fix edit enabled rule --- oryx-tui/src/handler.rs | 5 +++-- oryx-tui/src/section/firewall.rs | 1 + 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/oryx-tui/src/handler.rs b/oryx-tui/src/handler.rs index 0c042c3..0eba777 100644 --- a/oryx-tui/src/handler.rs +++ b/oryx-tui/src/handler.rs @@ -166,9 +166,10 @@ pub fn handle_key_events( } KeyCode::Char('n') | KeyCode::Char('e') => { - if app.section.focused_section == FocusedSection::Firewall { + if app.section.focused_section == FocusedSection::Firewall + && app.section.handle_keys(key_event, sender).is_ok() + { app.is_editing = true; - app.section.handle_keys(key_event, sender)?; app.active_popup = Some(ActivePopup::NewFirewallRule); } } diff --git a/oryx-tui/src/section/firewall.rs b/oryx-tui/src/section/firewall.rs index 37f6e48..67ce61c 100644 --- a/oryx-tui/src/section/firewall.rs +++ b/oryx-tui/src/section/firewall.rs @@ -499,6 +499,7 @@ impl Firewall { crate::notification::NotificationLevel::Warning, sender.clone(), )?; + return Err("Can not edit enabled rule".into()); } else { self.user_input = Some(rule.into()); } From 43b21021270334d3520de3668f5da59eb5ded3f6 Mon Sep 17 00:00:00 2001 From: Badr Date: Wed, 9 Oct 2024 19:36:20 +0200 Subject: [PATCH 39/41] update release --- Release.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Release.md b/Release.md index a5a2e52..8512aa7 100644 --- a/Release.md +++ b/Release.md @@ -1,3 +1,9 @@ +## v0.4 - TBA + +### Added + +- Firewall + ## v0.3 - 2024-09-25 ### Added From 8e3078630a9acbbd83b7937f49ed665b6b921b8d Mon Sep 17 00:00:00 2001 From: Badr Date: Wed, 9 Oct 2024 19:50:01 +0200 Subject: [PATCH 40/41] set max for rules --- oryx-common/src/lib.rs | 3 +++ oryx-ebpf/src/main.rs | 10 +++++----- oryx-tui/src/ebpf.rs | 34 ++++++++++++++++++++------------ oryx-tui/src/section/firewall.rs | 9 +++++++++ 4 files changed, 38 insertions(+), 18 deletions(-) diff --git a/oryx-common/src/lib.rs b/oryx-common/src/lib.rs index 32483fb..da65763 100644 --- a/oryx-common/src/lib.rs +++ b/oryx-common/src/lib.rs @@ -7,6 +7,9 @@ use network_types::{arp::ArpHdr, icmp::IcmpHdr, ip::IpHdr, tcp::TcpHdr, udp::Udp pub mod ip; pub mod protocols; +pub const MAX_FIREWALL_RULES: u32 = 1; +pub const MAX_RULES_PORT: usize = 32; + #[repr(C)] pub enum RawPacket { Ip(IpHdr, ProtoHdr), diff --git a/oryx-ebpf/src/main.rs b/oryx-ebpf/src/main.rs index 2950b70..40aa3c2 100644 --- a/oryx-ebpf/src/main.rs +++ b/oryx-ebpf/src/main.rs @@ -18,7 +18,7 @@ use network_types::{ }; use oryx_common::{ protocols::{LinkProtocol, NetworkProtocol, Protocol, TransportProtocol}, - ProtoHdr, RawPacket, + ProtoHdr, RawPacket, MAX_FIREWALL_RULES, MAX_RULES_PORT, }; #[map] @@ -34,12 +34,12 @@ static TRANSPORT_FILTERS: Array = Array::with_max_entries(8, 0); static LINK_FILTERS: Array = Array::with_max_entries(8, 0); #[map] -static BLOCKLIST_IPV6: HashMap = - HashMap::::with_max_entries(128, 0); +static BLOCKLIST_IPV6: HashMap = + HashMap::::with_max_entries(MAX_FIREWALL_RULES, 0); #[map] -static BLOCKLIST_IPV4: HashMap = - HashMap::::with_max_entries(128, 0); +static BLOCKLIST_IPV4: HashMap = + HashMap::::with_max_entries(MAX_FIREWALL_RULES, 0); #[classifier] pub fn oryx(ctx: TcContext) -> i32 { diff --git a/oryx-tui/src/ebpf.rs b/oryx-tui/src/ebpf.rs index 11497cd..652a017 100644 --- a/oryx-tui/src/ebpf.rs +++ b/oryx-tui/src/ebpf.rs @@ -13,7 +13,7 @@ use aya::{ programs::{tc, SchedClassifier, TcAttachType}, Bpf, }; -use oryx_common::{protocols::Protocol, RawPacket}; +use oryx_common::{protocols::Protocol, RawPacket, MAX_RULES_PORT}; use crate::{ event::Event, @@ -65,7 +65,7 @@ impl Source for RingBuffer<'_> { } fn update_ipv4_blocklist( - ipv4_firewall: &mut HashMap, + ipv4_firewall: &mut HashMap, addr: Ipv4Addr, port: BlockedPort, to_insert: bool, @@ -92,7 +92,7 @@ fn update_ipv4_blocklist( .filter(|p| (*p != 0 && *p != port)) .collect::>(); - let mut blocked_ports = [0; 32]; + let mut blocked_ports = [0; MAX_RULES_PORT]; for (idx, p) in not_null_ports.iter().enumerate() { blocked_ports[idx] = *p; @@ -109,14 +109,16 @@ fn update_ipv4_blocklist( } BlockedPort::All => { if to_insert { - ipv4_firewall.insert(addr.to_bits(), [0; 32], 0).unwrap(); + ipv4_firewall + .insert(addr.to_bits(), [0; MAX_RULES_PORT], 0) + .unwrap(); } else { ipv4_firewall.remove(&addr.to_bits()).unwrap(); } } } } else if to_insert { - let mut blocked_ports: [u16; 32] = [0; 32]; + let mut blocked_ports: [u16; MAX_RULES_PORT] = [0; MAX_RULES_PORT]; match port { BlockedPort::Single(port) => { blocked_ports[0] = port; @@ -131,7 +133,7 @@ fn update_ipv4_blocklist( } fn update_ipv6_blocklist( - ipv6_firewall: &mut HashMap, + ipv6_firewall: &mut HashMap, addr: Ipv6Addr, port: BlockedPort, to_insert: bool, @@ -159,7 +161,7 @@ fn update_ipv6_blocklist( .filter(|p| (*p != 0 && *p != port)) .collect::>(); - let mut blocked_ports = [0; 32]; + let mut blocked_ports = [0; MAX_RULES_PORT]; for (idx, p) in not_null_ports.iter().enumerate() { blocked_ports[idx] = *p; @@ -176,14 +178,16 @@ fn update_ipv6_blocklist( } BlockedPort::All => { if to_insert { - ipv6_firewall.insert(addr.to_bits(), [0; 32], 0).unwrap(); + ipv6_firewall + .insert(addr.to_bits(), [0; MAX_RULES_PORT], 0) + .unwrap(); } else { ipv6_firewall.remove(&addr.to_bits()).unwrap(); } } } } else if to_insert { - let mut blocked_ports: [u16; 32] = [0; 32]; + let mut blocked_ports: [u16; MAX_RULES_PORT] = [0; MAX_RULES_PORT]; match port { BlockedPort::Single(port) => { blocked_ports[0] = port; @@ -293,10 +297,12 @@ impl Ebpf { 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; 32]> = + 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; 32]> = + + let mut ipv6_firewall: HashMap<_, u128, [u16; MAX_RULES_PORT]> = HashMap::try_from(bpf.take_map("BLOCKLIST_IPV6").unwrap()).unwrap(); thread::spawn(move || loop { @@ -481,10 +487,12 @@ impl Ebpf { Array::try_from(bpf.take_map("LINK_FILTERS").unwrap()).unwrap(); // firewall-ebpf interface - let mut ipv4_firewall: HashMap<_, u32, [u16; 32]> = + 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; 32]> = + + 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 { diff --git a/oryx-tui/src/section/firewall.rs b/oryx-tui/src/section/firewall.rs index 67ce61c..f6b3b43 100644 --- a/oryx-tui/src/section/firewall.rs +++ b/oryx-tui/src/section/firewall.rs @@ -1,5 +1,6 @@ use core::fmt::Display; use crossterm::event::{Event, KeyCode, KeyEvent}; +use oryx_common::MAX_FIREWALL_RULES; use ratatui::{ layout::{Constraint, Direction, Flex, Layout, Margin, Rect}, style::{Color, Style, Stylize}, @@ -487,6 +488,14 @@ impl Firewall { } else { match key_event.code { KeyCode::Char('n') => { + if self.rules.len() == MAX_FIREWALL_RULES as usize { + Notification::send( + "Max rules reached", + crate::notification::NotificationLevel::Warning, + sender.clone(), + )?; + return Err("Can not edit enabled rule".into()); + } self.add_rule(); } From 977389f2944c11ef04f2d0900c607508d18e9693 Mon Sep 17 00:00:00 2001 From: Badr Date: Wed, 9 Oct 2024 19:53:46 +0200 Subject: [PATCH 41/41] update --- oryx-common/src/lib.rs | 2 +- oryx-tui/src/handler.rs | 30 +++++++++++++++--------------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/oryx-common/src/lib.rs b/oryx-common/src/lib.rs index da65763..36c7063 100644 --- a/oryx-common/src/lib.rs +++ b/oryx-common/src/lib.rs @@ -7,7 +7,7 @@ use network_types::{arp::ArpHdr, icmp::IcmpHdr, ip::IpHdr, tcp::TcpHdr, udp::Udp pub mod ip; pub mod protocols; -pub const MAX_FIREWALL_RULES: u32 = 1; +pub const MAX_FIREWALL_RULES: u32 = 32; pub const MAX_RULES_PORT: usize = 32; #[repr(C)] diff --git a/oryx-tui/src/handler.rs b/oryx-tui/src/handler.rs index 0eba777..68a9fe6 100644 --- a/oryx-tui/src/handler.rs +++ b/oryx-tui/src/handler.rs @@ -13,7 +13,7 @@ use ratatui::crossterm::event::{KeyCode, KeyEvent, KeyModifiers}; pub fn handle_key_events( key_event: KeyEvent, app: &mut App, - sender: kanal::Sender, + event_sender: kanal::Sender, ) -> AppResult<()> { // Start Phase if !app.start_sniffing { @@ -21,7 +21,7 @@ pub fn handle_key_events( KeyCode::Enter => { if app.filter.focused_block == FocusedBlock::Apply { app.filter - .start(sender.clone(), app.data_channel_sender.clone())?; + .start(event_sender.clone(), app.data_channel_sender.clone())?; app.start_sniffing = true; } @@ -64,7 +64,7 @@ pub fn handle_key_events( ActivePopup::NewFirewallRule => { app.section .firewall - .handle_keys(key_event, sender.clone())?; + .handle_keys(key_event, event_sender.clone())?; app.is_editing = false; } _ => {} @@ -74,7 +74,7 @@ pub fn handle_key_events( ActivePopup::UpdateFilters => { if app.filter.focused_block == FocusedBlock::Apply { app.filter - .update(sender.clone(), app.data_channel_sender.clone())?; + .update(event_sender.clone(), app.data_channel_sender.clone())?; if !app.filter.traffic_direction.is_ingress_loaded() { app.section.firewall.disable_ingress_rules(); } @@ -90,7 +90,7 @@ pub fn handle_key_events( if app .section .firewall - .handle_keys(key_event, sender.clone()) + .handle_keys(key_event, event_sender.clone()) .is_ok() { app.active_popup = None; @@ -107,7 +107,7 @@ pub fn handle_key_events( ActivePopup::NewFirewallRule => { app.section .firewall - .handle_keys(key_event, sender.clone())?; + .handle_keys(key_event, event_sender.clone())?; } _ => {} }, @@ -122,7 +122,7 @@ pub fn handle_key_events( _ => {} } - app.section.handle_keys(key_event, sender.clone())?; + app.section.handle_keys(key_event, event_sender.clone())?; return Ok(()); } @@ -140,7 +140,7 @@ pub fn handle_key_events( if key_event.modifiers == KeyModifiers::CONTROL { app.filter.terminate(); thread::sleep(Duration::from_millis(150)); - sender.send(Event::Reset)?; + event_sender.send(Event::Reset)?; } } @@ -161,13 +161,13 @@ pub fn handle_key_events( KeyCode::Char('/') => { if app.section.focused_section == FocusedSection::Inspection { app.is_editing = true; - app.section.handle_keys(key_event, sender.clone())?; + app.section.handle_keys(key_event, event_sender.clone())?; } } KeyCode::Char('n') | KeyCode::Char('e') => { if app.section.focused_section == FocusedSection::Firewall - && app.section.handle_keys(key_event, sender).is_ok() + && app.section.handle_keys(key_event, event_sender).is_ok() { app.is_editing = true; app.active_popup = Some(ActivePopup::NewFirewallRule); @@ -183,7 +183,7 @@ pub fn handle_key_events( KeyCode::Char(' ') => { if app.section.focused_section == FocusedSection::Firewall { app.section.firewall.load_rule( - sender.clone(), + event_sender.clone(), app.filter.traffic_direction.is_ingress_loaded(), app.filter.traffic_direction.is_egress_loaded(), )?; @@ -196,7 +196,7 @@ pub fn handle_key_events( Notification::send( "There is no packets".to_string(), NotificationLevel::Info, - sender, + event_sender, )?; } else { match export(&app_packets) { @@ -204,17 +204,17 @@ pub fn handle_key_events( Notification::send( "Packets exported to ~/oryx/capture file".to_string(), NotificationLevel::Info, - sender, + event_sender, )?; } Err(e) => { - Notification::send(e.to_string(), NotificationLevel::Error, sender)?; + Notification::send(e.to_string(), NotificationLevel::Error, event_sender)?; } } } } _ => { - app.section.handle_keys(key_event, sender.clone())?; + app.section.handle_keys(key_event, event_sender.clone())?; } }