From a4b991a51a942c54c84d82c484ebd01ffda251d6 Mon Sep 17 00:00:00 2001 From: Badr Date: Thu, 10 Oct 2024 00:06:24 +0200 Subject: [PATCH 01/11] Add help footer section --- oryx-tui/src/filter.rs | 103 ++++++++++++++++++++++++++++--- oryx-tui/src/section.rs | 92 ++++++++++++++++++++++++--- oryx-tui/src/section/firewall.rs | 50 +++++++++++++-- 3 files changed, 227 insertions(+), 18 deletions(-) diff --git a/oryx-tui/src/filter.rs b/oryx-tui/src/filter.rs index cba3f3b..4c2e8c4 100644 --- a/oryx-tui/src/filter.rs +++ b/oryx-tui/src/filter.rs @@ -18,9 +18,9 @@ use oryx_common::{ RawPacket, }; use ratatui::{ - layout::{Alignment, Constraint, Direction, Flex, Layout, Rect}, + layout::{Alignment, Constraint, Direction, Flex, Layout, Margin, Rect}, style::{Style, Stylize}, - text::{Line, Span}, + text::{Line, Span, Text}, widgets::{Block, BorderType, Borders, Clear, Padding, Row, Table, TableState}, Frame, }; @@ -570,6 +570,16 @@ impl Filter { } pub fn render_on_setup(&mut self, frame: &mut Frame) { + let (filters_block, help_block) = { + let chunks = Layout::default() + .direction(Direction::Vertical) + .constraints([Constraint::Fill(1), Constraint::Length(3)]) + .flex(ratatui::layout::Flex::SpaceBetween) + .split(frame.area()); + + (chunks[0], chunks[1]) + }; + let ( interface_block, transport_filter_block, @@ -590,7 +600,7 @@ impl Filter { ]) .margin(1) .flex(Flex::SpaceAround) - .split(frame.area()); + .split(filters_block); ( chunks[0], chunks[1], chunks[2], chunks[3], chunks[4], chunks[5], ) @@ -638,6 +648,38 @@ impl Filter { .build(); frame.render_widget(start, start_block); + + let help = Text::from(vec![ + Line::from(""), + Line::from(vec![ + Span::from("k,⬆").bold(), + Span::from(": Scroll up").bold(), + Span::from(" | ").bold(), + Span::from("j,⬇").bold(), + Span::from(": Scroll down").bold(), + Span::from(" | ").bold(), + Span::from("󱞦 ").bold(), + Span::from(": Apply").bold(), + Span::from(" | ").bold(), + Span::from(" ").bold(), + Span::from(": Naviguate").bold(), + ]), + ]) + .centered(); + frame.render_widget( + Block::new() + .borders(Borders::ALL) + .blue() + .border_type(BorderType::Rounded), + help_block, + ); + frame.render_widget( + help, + help_block.inner(Margin { + horizontal: 1, + vertical: 0, + }), + ); } pub fn render_on_sniffing(&mut self, frame: &mut Frame, block: Rect) { @@ -762,12 +804,22 @@ impl Filter { .direction(Direction::Horizontal) .constraints([ Constraint::Fill(1), - Constraint::Length(60), + Constraint::Max(82), Constraint::Fill(1), ]) .flex(ratatui::layout::Flex::SpaceBetween) .split(layout[1])[1]; + let (filters_block, help_block) = { + let chunks = Layout::default() + .direction(Direction::Vertical) + .constraints([Constraint::Fill(1), Constraint::Length(3)]) + .flex(ratatui::layout::Flex::SpaceBetween) + .split(block); + + (chunks[0], chunks[1]) + }; + let ( transport_filter_block, network_filter_block, @@ -778,6 +830,7 @@ impl Filter { let chunks = Layout::default() .direction(Direction::Vertical) .constraints([ + Constraint::Length(1), Constraint::Length(NB_TRANSPORT_PROTOCOL + 4), Constraint::Length(NB_NETWORK_PROTOCOL + 4), Constraint::Length(NB_LINK_PROTOCOL + 4), @@ -786,17 +839,18 @@ impl Filter { ]) .margin(1) .flex(Flex::SpaceBetween) - .split(block); - (chunks[0], chunks[1], chunks[2], chunks[3], chunks[4]) + .split(filters_block); + (chunks[1], chunks[2], chunks[3], chunks[4], chunks[5]) }; frame.render_widget(Clear, block); + frame.render_widget( Block::new() .borders(Borders::all()) .border_type(BorderType::Thick) .border_style(Style::default().green()), - block, + filters_block, ); self.network.render( @@ -834,5 +888,40 @@ impl Filter { .centered() .build(); frame.render_widget(apply, apply_block); + + let help = Text::from(vec![ + Line::from(""), + Line::from(vec![ + Span::from("k,⬆").bold(), + Span::from(": Move up").bold(), + Span::from(" | ").bold(), + Span::from("j,⬇").bold(), + Span::from(": Move down").bold(), + Span::from(" | ").bold(), + Span::from("󱊷 ").bold(), + Span::from(": Discard").bold(), + Span::from(" | ").bold(), + Span::from("󱞦 ").bold(), + Span::from(": Apply").bold(), + Span::from(" | ").bold(), + Span::from(" ").bold(), + Span::from(": Naviguate").bold(), + ]), + ]) + .centered(); + frame.render_widget( + Block::new() + .borders(Borders::ALL) + .blue() + .border_type(BorderType::Rounded), + help_block, + ); + frame.render_widget( + help, + help_block.inner(Margin { + horizontal: 1, + vertical: 0, + }), + ); } } diff --git a/oryx-tui/src/section.rs b/oryx-tui/src/section.rs index bec9732..f394e2a 100644 --- a/oryx-tui/src/section.rs +++ b/oryx-tui/src/section.rs @@ -11,9 +11,9 @@ use firewall::{Firewall, FirewallSignal}; use inspection::Inspection; use ratatui::{ - layout::{Alignment, Rect}, + layout::{Alignment, Constraint, Direction, Layout, Margin, Rect}, style::{Color, Style, Stylize}, - text::{Line, Span}, + text::{Line, Span, Text}, widgets::{Block, BorderType, Borders, Padding}, Frame, }; @@ -89,6 +89,72 @@ impl Section { } } + fn render_footer_help(&self, frame: &mut Frame, block: Rect) { + let message = match self.focused_section { + FocusedSection::Inspection => Line::from(vec![ + Span::from("k,⬆").bold(), + Span::from(": Move up").bold(), + Span::from(" | ").bold(), + Span::from("j,⬇").bold(), + Span::from(": Move down").bold(), + Span::from(" | ").bold(), + Span::from("/").bold(), + Span::from(": Search").bold(), + Span::from(" | ").bold(), + Span::from("i").bold(), + Span::from(": Infos").bold(), + Span::from(" | ").bold(), + Span::from("f").bold(), + Span::from(": Filters").bold(), + Span::from(" | ").bold(), + Span::from(" ").bold(), + Span::from(": Naviguate").bold(), + ]), + FocusedSection::Firewall => Line::from(vec![ + Span::from("k,⬆").bold(), + Span::from(": Move up").bold(), + Span::from(" | ").bold(), + Span::from("j,⬇").bold(), + Span::from(": Move down").bold(), + Span::from(" | ").bold(), + Span::from("n").bold(), + Span::from(": New Rule").bold(), + Span::from(" | ").bold(), + Span::from("d").bold(), + Span::from(": Delete Rule").bold(), + Span::from(" | ").bold(), + Span::from("f").bold(), + Span::from(": Filters").bold(), + Span::from(" | ").bold(), + Span::from(" ").bold(), + Span::from(": Naviguate").bold(), + ]), + _ => Line::from(vec![ + Span::from("f").bold(), + Span::from(": Filters").bold(), + Span::from(" | ").bold(), + Span::from(" ").bold(), + Span::from(": Naviguate").bold(), + ]), + }; + + let help = Text::from(vec![Line::from(""), message]).centered(); + frame.render_widget( + Block::new() + .borders(Borders::ALL) + .blue() + .border_type(BorderType::Rounded), + block, + ); + frame.render_widget( + help, + block.inner(Margin { + horizontal: 1, + vertical: 0, + }), + ); + } + pub fn render_header(&mut self, frame: &mut Frame, block: Rect) { frame.render_widget( Block::default() @@ -110,12 +176,24 @@ impl Section { ); } pub fn render(&mut self, frame: &mut Frame, block: Rect, network_interace: &str) { - self.render_header(frame, block); + let (section_block, help_block) = { + let chunks = Layout::default() + .direction(Direction::Vertical) + .constraints([Constraint::Fill(1), Constraint::Length(3)]) + .flex(ratatui::layout::Flex::SpaceBetween) + .split(block); + + (chunks[0], chunks[1]) + }; + + self.render_header(frame, section_block); + self.render_footer_help(frame, help_block); + match self.focused_section { - FocusedSection::Inspection => self.inspection.render(frame, block), - FocusedSection::Stats => self.stats.render(frame, block, network_interace), - FocusedSection::Alerts => self.alert.render(frame, block), - FocusedSection::Firewall => self.firewall.render(frame, block), + FocusedSection::Inspection => self.inspection.render(frame, section_block), + FocusedSection::Stats => self.stats.render(frame, section_block, network_interace), + FocusedSection::Alerts => self.alert.render(frame, section_block), + FocusedSection::Firewall => self.firewall.render(frame, section_block), } } diff --git a/oryx-tui/src/section/firewall.rs b/oryx-tui/src/section/firewall.rs index f6b3b43..5c5a3c0 100644 --- a/oryx-tui/src/section/firewall.rs +++ b/oryx-tui/src/section/firewall.rs @@ -4,8 +4,10 @@ use oryx_common::MAX_FIREWALL_RULES; use ratatui::{ layout::{Constraint, Direction, Flex, Layout, Margin, Rect}, style::{Color, Style, Stylize}, - text::{Line, Text}, - widgets::{Block, Borders, Cell, Clear, HighlightSpacing, Padding, Row, Table, TableState}, + text::{Line, Span, Text}, + widgets::{ + Block, BorderType, Borders, Cell, Clear, HighlightSpacing, Padding, Row, Table, TableState, + }, Frame, }; use std::{net::IpAddr, num::ParseIntError, str::FromStr}; @@ -139,7 +141,7 @@ impl UserInput { .direction(Direction::Vertical) .constraints([ Constraint::Fill(1), - Constraint::Length(9), + Constraint::Length(12), Constraint::Fill(1), ]) .flex(ratatui::layout::Flex::SpaceBetween) @@ -155,6 +157,16 @@ impl UserInput { .flex(ratatui::layout::Flex::SpaceBetween) .split(layout[1])[1]; + let (user_input_block, help_block) = { + let chunks = Layout::default() + .direction(Direction::Vertical) + .constraints([Constraint::Fill(1), Constraint::Length(3)]) + .flex(ratatui::layout::Flex::SpaceBetween) + .split(block); + + (chunks[0], chunks[1]) + }; + let rows = [ Row::new(vec![ Cell::from(self.name.field.to_string()) @@ -253,6 +265,7 @@ impl UserInput { .block( Block::default() .title(" Firewall Rule ") + .bold() .title_alignment(ratatui::layout::Alignment::Center) .borders(Borders::all()) .border_type(ratatui::widgets::BorderType::Thick) @@ -261,7 +274,36 @@ impl UserInput { ); frame.render_widget(Clear, block); - frame.render_widget(table, block); + frame.render_widget(table, user_input_block); + + let help = Text::from(vec![ + Line::from(""), + Line::from(vec![ + Span::from("󱊷 ").bold(), + Span::from(": Discard").bold(), + Span::from(" | ").bold(), + Span::from("󱞦 ").bold(), + Span::from(": Save").bold(), + Span::from(" | ").bold(), + Span::from(" ").bold(), + Span::from(": Naviguate").bold(), + ]), + ]) + .centered(); + frame.render_widget( + Block::new() + .borders(Borders::ALL) + .blue() + .border_type(BorderType::Rounded), + help_block, + ); + frame.render_widget( + help, + help_block.inner(Margin { + horizontal: 1, + vertical: 0, + }), + ); } } From cb70046c88ac301c3709b5a92712fb442e228a03 Mon Sep 17 00:00:00 2001 From: Badr Date: Thu, 10 Oct 2024 12:36:17 +0200 Subject: [PATCH 02/11] fix firewall crash when no rules --- oryx-tui/src/section/firewall.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/oryx-tui/src/section/firewall.rs b/oryx-tui/src/section/firewall.rs index 5c5a3c0..a96a72d 100644 --- a/oryx-tui/src/section/firewall.rs +++ b/oryx-tui/src/section/firewall.rs @@ -577,6 +577,10 @@ impl Firewall { } KeyCode::Char('j') | KeyCode::Down => { + if self.rules.is_empty() { + return Ok(()); + } + let i = match self.state.selected() { Some(i) => { if i < self.rules.len() - 1 { @@ -592,6 +596,9 @@ impl Firewall { } KeyCode::Char('k') | KeyCode::Up => { + if self.rules.is_empty() { + return Ok(()); + } let i = match self.state.selected() { Some(i) => { if i > 1 { From 29b0632028eef0198e93e2ce83987b7e2d7d3646 Mon Sep 17 00:00:00 2001 From: Badr Date: Thu, 10 Oct 2024 14:17:30 +0200 Subject: [PATCH 03/11] update footer display --- oryx-tui/src/app.rs | 1 + oryx-tui/src/filter.rs | 73 +++---------- oryx-tui/src/section.rs | 173 ++++++++++++++++++++----------- oryx-tui/src/section/firewall.rs | 49 +-------- 4 files changed, 133 insertions(+), 163 deletions(-) diff --git a/oryx-tui/src/app.rs b/oryx-tui/src/app.rs index cd47f60..ac3818d 100644 --- a/oryx-tui/src/app.rs +++ b/oryx-tui/src/app.rs @@ -119,6 +119,7 @@ impl App { frame, section_block, &self.filter.interface.selected_interface.name, + self.active_popup.as_ref(), ); self.filter.render_on_sniffing(frame, settings_block); diff --git a/oryx-tui/src/filter.rs b/oryx-tui/src/filter.rs index 4c2e8c4..a3241db 100644 --- a/oryx-tui/src/filter.rs +++ b/oryx-tui/src/filter.rs @@ -652,27 +652,25 @@ impl Filter { let help = Text::from(vec![ Line::from(""), Line::from(vec![ - Span::from("k,⬆").bold(), - Span::from(": Scroll up").bold(), + Span::from("k,").bold(), + Span::from(" Up").bold(), Span::from(" | ").bold(), - Span::from("j,⬇").bold(), - Span::from(": Scroll down").bold(), + Span::from("j,").bold(), + Span::from(" Down").bold(), + Span::from(" | ").bold(), + Span::from("󱁐 ").bold(), + Span::from(" Toggle Select").bold(), Span::from(" | ").bold(), Span::from("󱞦 ").bold(), - Span::from(": Apply").bold(), + Span::from(" Apply").bold(), Span::from(" | ").bold(), Span::from(" ").bold(), - Span::from(": Naviguate").bold(), + Span::from(" Nav").bold(), ]), ]) + .blue() .centered(); - frame.render_widget( - Block::new() - .borders(Borders::ALL) - .blue() - .border_type(BorderType::Rounded), - help_block, - ); + frame.render_widget( help, help_block.inner(Margin { @@ -810,16 +808,6 @@ impl Filter { .flex(ratatui::layout::Flex::SpaceBetween) .split(layout[1])[1]; - let (filters_block, help_block) = { - let chunks = Layout::default() - .direction(Direction::Vertical) - .constraints([Constraint::Fill(1), Constraint::Length(3)]) - .flex(ratatui::layout::Flex::SpaceBetween) - .split(block); - - (chunks[0], chunks[1]) - }; - let ( transport_filter_block, network_filter_block, @@ -839,7 +827,7 @@ impl Filter { ]) .margin(1) .flex(Flex::SpaceBetween) - .split(filters_block); + .split(block); (chunks[1], chunks[2], chunks[3], chunks[4], chunks[5]) }; @@ -850,7 +838,7 @@ impl Filter { .borders(Borders::all()) .border_type(BorderType::Thick) .border_style(Style::default().green()), - filters_block, + block, ); self.network.render( @@ -888,40 +876,5 @@ impl Filter { .centered() .build(); frame.render_widget(apply, apply_block); - - let help = Text::from(vec![ - Line::from(""), - Line::from(vec![ - Span::from("k,⬆").bold(), - Span::from(": Move up").bold(), - Span::from(" | ").bold(), - Span::from("j,⬇").bold(), - Span::from(": Move down").bold(), - Span::from(" | ").bold(), - Span::from("󱊷 ").bold(), - Span::from(": Discard").bold(), - Span::from(" | ").bold(), - Span::from("󱞦 ").bold(), - Span::from(": Apply").bold(), - Span::from(" | ").bold(), - Span::from(" ").bold(), - Span::from(": Naviguate").bold(), - ]), - ]) - .centered(); - frame.render_widget( - Block::new() - .borders(Borders::ALL) - .blue() - .border_type(BorderType::Rounded), - help_block, - ); - frame.render_widget( - help, - help_block.inner(Margin { - horizontal: 1, - vertical: 0, - }), - ); } } diff --git a/oryx-tui/src/section.rs b/oryx-tui/src/section.rs index f394e2a..7be8994 100644 --- a/oryx-tui/src/section.rs +++ b/oryx-tui/src/section.rs @@ -19,7 +19,11 @@ use ratatui::{ }; use stats::Stats; -use crate::{app::AppResult, event::Event, packet::AppPacket}; +use crate::{ + app::{ActivePopup, AppResult}, + event::Event, + packet::AppPacket, +}; #[derive(Debug, PartialEq)] pub enum FocusedSection { @@ -89,63 +93,110 @@ impl Section { } } - fn render_footer_help(&self, frame: &mut Frame, block: Rect) { - let message = match self.focused_section { - FocusedSection::Inspection => Line::from(vec![ - Span::from("k,⬆").bold(), - Span::from(": Move up").bold(), - Span::from(" | ").bold(), - Span::from("j,⬇").bold(), - Span::from(": Move down").bold(), - Span::from(" | ").bold(), - Span::from("/").bold(), - Span::from(": Search").bold(), - Span::from(" | ").bold(), - Span::from("i").bold(), - Span::from(": Infos").bold(), - Span::from(" | ").bold(), - Span::from("f").bold(), - Span::from(": Filters").bold(), - Span::from(" | ").bold(), - Span::from(" ").bold(), - Span::from(": Naviguate").bold(), - ]), - FocusedSection::Firewall => Line::from(vec![ - Span::from("k,⬆").bold(), - Span::from(": Move up").bold(), - Span::from(" | ").bold(), - Span::from("j,⬇").bold(), - Span::from(": Move down").bold(), - Span::from(" | ").bold(), - Span::from("n").bold(), - Span::from(": New Rule").bold(), - Span::from(" | ").bold(), - Span::from("d").bold(), - Span::from(": Delete Rule").bold(), - Span::from(" | ").bold(), - Span::from("f").bold(), - Span::from(": Filters").bold(), - Span::from(" | ").bold(), - Span::from(" ").bold(), - Span::from(": Naviguate").bold(), - ]), - _ => Line::from(vec![ - Span::from("f").bold(), - Span::from(": Filters").bold(), - Span::from(" | ").bold(), - Span::from(" ").bold(), - Span::from(": Naviguate").bold(), - ]), + fn render_footer_help( + &self, + frame: &mut Frame, + block: Rect, + active_popup: Option<&ActivePopup>, + ) { + let message = { + match active_popup { + Some(ActivePopup::UpdateFilters) => Line::from(vec![ + Span::from("k,").bold(), + Span::from(" Up").bold(), + Span::from(" | ").bold(), + Span::from("j,").bold(), + Span::from(" Down").bold(), + Span::from(" | ").bold(), + Span::from("󱁐 ").bold(), + Span::from(" Toggle Select").bold(), + Span::from(" | ").bold(), + Span::from("󱊷 ").bold(), + Span::from(": Discard").bold(), + Span::from(" | ").bold(), + Span::from("󱞦 ").bold(), + Span::from(" Apply").bold(), + Span::from(" | ").bold(), + Span::from(" ").bold(), + Span::from(" Nav").bold(), + ]), + Some(ActivePopup::NewFirewallRule) => Line::from(vec![ + Span::from("j,k,,").bold(), + Span::from(": Toggle Direction").bold(), + Span::from(" | ").bold(), + Span::from("󱊷 ").bold(), + Span::from(": Discard").bold(), + Span::from(" | ").bold(), + Span::from("󱞦 ").bold(), + Span::from(": Save").bold(), + Span::from(" | ").bold(), + Span::from(" ").bold(), + Span::from(": Naviguate").bold(), + ]), + Some(ActivePopup::PacketInfos) | Some(ActivePopup::Help) => Line::from(vec![ + Span::from("󱊷 ").bold(), + Span::from(": Discard Popup").bold(), + ]), + _ => match self.focused_section { + FocusedSection::Inspection => Line::from(vec![ + Span::from("k,").bold(), + Span::from(" Up").bold(), + Span::from(" | ").bold(), + Span::from("j,").bold(), + Span::from(" Down").bold(), + Span::from(" | ").bold(), + Span::from("/").bold(), + Span::from(" Search").bold(), + Span::from(" | ").bold(), + Span::from("i").bold(), + Span::from(" Infos").bold(), + Span::from(" | ").bold(), + Span::from("f").bold(), + Span::from(" Filters").bold(), + Span::from(" | ").bold(), + Span::from("󱊷 ").bold(), + Span::from(": Discard").bold(), + Span::from(" | ").bold(), + Span::from(" ").bold(), + Span::from(" Nav").bold(), + ]), + FocusedSection::Firewall => Line::from(vec![ + Span::from("k,").bold(), + Span::from(" Up").bold(), + Span::from(" | ").bold(), + Span::from("j,").bold(), + Span::from(" Down").bold(), + Span::from(" | ").bold(), + Span::from("n").bold(), + Span::from(" New").bold(), + Span::from(" | ").bold(), + Span::from("d").bold(), + Span::from(" Delete").bold(), + Span::from(" | ").bold(), + Span::from("e").bold(), + Span::from(" Edit").bold(), + Span::from(" | ").bold(), + Span::from("󱁐 ").bold(), + Span::from(" Toggle").bold(), + Span::from(" | ").bold(), + Span::from("f").bold(), + Span::from(" Filters").bold(), + Span::from(" | ").bold(), + Span::from(" ").bold(), + Span::from(" Nav").bold(), + ]), + _ => Line::from(vec![ + Span::from("f").bold(), + Span::from(" Filters").bold(), + Span::from(" | ").bold(), + Span::from(" ").bold(), + Span::from(" Nav").bold(), + ]), + }, + } }; - let help = Text::from(vec![Line::from(""), message]).centered(); - frame.render_widget( - Block::new() - .borders(Borders::ALL) - .blue() - .border_type(BorderType::Rounded), - block, - ); + let help = Text::from(vec![Line::from(""), message]).blue().centered(); frame.render_widget( help, block.inner(Margin { @@ -175,7 +226,13 @@ impl Section { block, ); } - pub fn render(&mut self, frame: &mut Frame, block: Rect, network_interace: &str) { + pub fn render( + &mut self, + frame: &mut Frame, + block: Rect, + network_interace: &str, + active_popup: Option<&ActivePopup>, + ) { let (section_block, help_block) = { let chunks = Layout::default() .direction(Direction::Vertical) @@ -187,7 +244,7 @@ impl Section { }; self.render_header(frame, section_block); - self.render_footer_help(frame, help_block); + self.render_footer_help(frame, help_block, active_popup); match self.focused_section { FocusedSection::Inspection => self.inspection.render(frame, section_block), diff --git a/oryx-tui/src/section/firewall.rs b/oryx-tui/src/section/firewall.rs index a96a72d..02317ef 100644 --- a/oryx-tui/src/section/firewall.rs +++ b/oryx-tui/src/section/firewall.rs @@ -4,10 +4,8 @@ use oryx_common::MAX_FIREWALL_RULES; use ratatui::{ layout::{Constraint, Direction, Flex, Layout, Margin, Rect}, style::{Color, Style, Stylize}, - text::{Line, Span, Text}, - widgets::{ - Block, BorderType, Borders, Cell, Clear, HighlightSpacing, Padding, Row, Table, TableState, - }, + text::{Line, Text}, + widgets::{Block, Borders, Cell, Clear, HighlightSpacing, Padding, Row, Table, TableState}, Frame, }; use std::{net::IpAddr, num::ParseIntError, str::FromStr}; @@ -141,7 +139,7 @@ impl UserInput { .direction(Direction::Vertical) .constraints([ Constraint::Fill(1), - Constraint::Length(12), + Constraint::Length(9), Constraint::Fill(1), ]) .flex(ratatui::layout::Flex::SpaceBetween) @@ -157,16 +155,6 @@ impl UserInput { .flex(ratatui::layout::Flex::SpaceBetween) .split(layout[1])[1]; - let (user_input_block, help_block) = { - let chunks = Layout::default() - .direction(Direction::Vertical) - .constraints([Constraint::Fill(1), Constraint::Length(3)]) - .flex(ratatui::layout::Flex::SpaceBetween) - .split(block); - - (chunks[0], chunks[1]) - }; - let rows = [ Row::new(vec![ Cell::from(self.name.field.to_string()) @@ -274,36 +262,7 @@ impl UserInput { ); frame.render_widget(Clear, block); - frame.render_widget(table, user_input_block); - - let help = Text::from(vec![ - Line::from(""), - Line::from(vec![ - Span::from("󱊷 ").bold(), - Span::from(": Discard").bold(), - Span::from(" | ").bold(), - Span::from("󱞦 ").bold(), - Span::from(": Save").bold(), - Span::from(" | ").bold(), - Span::from(" ").bold(), - Span::from(": Naviguate").bold(), - ]), - ]) - .centered(); - frame.render_widget( - Block::new() - .borders(Borders::ALL) - .blue() - .border_type(BorderType::Rounded), - help_block, - ); - frame.render_widget( - help, - help_block.inner(Margin { - horizontal: 1, - vertical: 0, - }), - ); + frame.render_widget(table, block); } } From 9736f44b14aedf17886b3a1a6b70b322d6365288 Mon Sep 17 00:00:00 2001 From: Badr Date: Thu, 10 Oct 2024 14:57:05 +0200 Subject: [PATCH 04/11] bump aya version --- Cargo.lock | 88 ++---- oryx-tui/Cargo.toml | 2 +- oryx-tui/src/ebpf.rs | 628 ++++++++++++++++++++--------------------- oryx-tui/src/filter.rs | 13 +- 4 files changed, 351 insertions(+), 380 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 183fe81..bd96721 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,18 +2,6 @@ # It is not intended for manual editing. version = 3 -[[package]] -name = "ahash" -version = "0.8.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" -dependencies = [ - "cfg-if", - "once_cell", - "version_check", - "zerocopy", -] - [[package]] name = "aho-corasick" version = "1.1.3" @@ -98,30 +86,30 @@ checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" [[package]] name = "aya" -version = "0.12.0" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90eea657cc8028447cbda5068f4e10c4fadba0131624f4f7dd1a9c46ffc8d81f" +checksum = "7127cbe933572dfabb7a87d2c740f5b720c3e19b4d2b8c99a262ffa35c8761ff" dependencies = [ "assert_matches", "aya-obj", "bitflags", "bytes", - "lazy_static", "libc", "log", "object", + "once_cell", "thiserror", ] [[package]] name = "aya-obj" -version = "0.1.0" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c02024a307161cf3d1f052161958fd13b1a33e3e038083e58082c0700fdab85" +checksum = "56e133d505de14d5948a312060b8b12752c22a965632cc57da12f5db653de4c0" dependencies = [ "bytes", "core-error", - "hashbrown 0.14.5", + "hashbrown", "log", "object", "thiserror", @@ -238,6 +226,15 @@ dependencies = [ "version_check", ] +[[package]] +name = "crc32fast" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" +dependencies = [ + "cfg-if", +] + [[package]] name = "crossterm" version = "0.28.1" @@ -442,16 +439,6 @@ dependencies = [ "wasi", ] -[[package]] -name = "hashbrown" -version = "0.14.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" -dependencies = [ - "ahash", - "allocator-api2", -] - [[package]] name = "hashbrown" version = "0.15.0" @@ -487,6 +474,16 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" +[[package]] +name = "indexmap" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" +dependencies = [ + "equivalent", + "hashbrown", +] + [[package]] name = "instability" version = "0.3.2" @@ -528,12 +525,6 @@ dependencies = [ "lock_api", ] -[[package]] -name = "lazy_static" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" - [[package]] name = "libc" version = "0.2.159" @@ -588,7 +579,7 @@ version = "0.12.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "234cf4f4a04dc1f57e24b96cc0cd600cf2af460d4161ac5ecdd0af8e1f3b2a38" dependencies = [ - "hashbrown 0.15.0", + "hashbrown", ] [[package]] @@ -627,10 +618,13 @@ checksum = "e82e9f64c09f56aa7c80c3fa087997bd99a913f91d9c74d36cf5fd75dd5773e6" [[package]] name = "object" -version = "0.32.2" +version = "0.36.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" +checksum = "aedf0a2d09c573ed1d8d85b30c119153926a2b36dce0ab28322c09a117a4683e" dependencies = [ + "crc32fast", + "hashbrown", + "indexmap", "memchr", ] @@ -1188,23 +1182,3 @@ dependencies = [ "anyhow", "clap", ] - -[[package]] -name = "zerocopy" -version = "0.7.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" -dependencies = [ - "zerocopy-derive", -] - -[[package]] -name = "zerocopy-derive" -version = "0.7.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] diff --git a/oryx-tui/Cargo.toml b/oryx-tui/Cargo.toml index 8466043..68513af 100644 --- a/oryx-tui/Cargo.toml +++ b/oryx-tui/Cargo.toml @@ -14,7 +14,7 @@ ratatui = "0.28" tui-big-text = "0.6" tui-input = "0.10" libc = "0.2" -aya = "0.12" +aya = "0.13" oryx-common = { path = "../oryx-common" } mio = { version = "1", features = ["os-poll", "os-ext"] } itertools = "0.13" diff --git a/oryx-tui/src/ebpf.rs b/oryx-tui/src/ebpf.rs index 652a017..60147f1 100644 --- a/oryx-tui/src/ebpf.rs +++ b/oryx-tui/src/ebpf.rs @@ -11,7 +11,7 @@ use aya::{ include_bytes_aligned, maps::{ring_buf::RingBufItem, Array, HashMap, MapData, RingBuf}, programs::{tc, SchedClassifier, TcAttachType}, - Bpf, + Ebpf, }; use oryx_common::{protocols::Protocol, RawPacket, MAX_RULES_PORT}; @@ -23,15 +23,13 @@ use crate::{ }; use mio::{event::Source, unix::SourceFd, Events, Interest, Poll, Registry, Token}; -pub struct Ebpf; - pub struct RingBuffer<'a> { buffer: RingBuf<&'a mut MapData>, } impl<'a> RingBuffer<'a> { - fn new(bpf: &'a mut Bpf) -> Self { - let buffer = RingBuf::try_from(bpf.map_mut("DATA").unwrap()).unwrap(); + fn new(ebpf: &'a mut Ebpf) -> Self { + let buffer = RingBuf::try_from(ebpf.map_mut("DATA").unwrap()).unwrap(); Self { buffer } } @@ -201,384 +199,380 @@ fn update_ipv6_blocklist( } } -impl Ebpf { - pub fn load_ingress( - iface: String, - notification_sender: kanal::Sender, - data_sender: kanal::Sender<[u8; RawPacket::LEN]>, - filter_channel_receiver: kanal::Receiver, - firewall_ingress_receiver: kanal::Receiver, - terminate: Arc, - ) { - thread::spawn({ - let iface = iface.to_owned(); - let notification_sender = notification_sender.clone(); - - move || { - let rlim = libc::rlimit { - rlim_cur: libc::RLIM_INFINITY, - rlim_max: libc::RLIM_INFINITY, - }; - - unsafe { libc::setrlimit(libc::RLIMIT_MEMLOCK, &rlim) }; - - #[cfg(debug_assertions)] - let mut bpf = match Bpf::load(include_bytes_aligned!( - "../../target/bpfel-unknown-none/debug/oryx" - )) { - Ok(v) => v, - Err(e) => { - Notification::send( - format!("Failed to load the ingress eBPF bytecode\n {}", e), - NotificationLevel::Error, - notification_sender, - ) - .unwrap(); - return; - } - }; - - #[cfg(not(debug_assertions))] - let mut bpf = match Bpf::load(include_bytes_aligned!( - "../../target/bpfel-unknown-none/release/oryx" - )) { - Ok(v) => v, - Err(e) => { - Notification::send( - format!("Failed to load the ingress eBPF bytecode\n {}", e), - NotificationLevel::Error, - notification_sender, - ) - .unwrap(); - return; - } - }; - - let _ = tc::qdisc_add_clsact(&iface); - - let program: &mut SchedClassifier = - bpf.program_mut("oryx").unwrap().try_into().unwrap(); - - if let Err(e) = program.load() { +pub fn load_ingress( + iface: String, + notification_sender: kanal::Sender, + data_sender: kanal::Sender<[u8; RawPacket::LEN]>, + filter_channel_receiver: kanal::Receiver, + firewall_ingress_receiver: kanal::Receiver, + terminate: Arc, +) { + thread::spawn({ + let iface = iface.to_owned(); + let notification_sender = notification_sender.clone(); + + move || { + let rlim = libc::rlimit { + rlim_cur: libc::RLIM_INFINITY, + rlim_max: libc::RLIM_INFINITY, + }; + + unsafe { libc::setrlimit(libc::RLIMIT_MEMLOCK, &rlim) }; + + #[cfg(debug_assertions)] + let mut bpf = match Ebpf::load(include_bytes_aligned!( + "../../target/bpfel-unknown-none/debug/oryx" + )) { + Ok(v) => v, + Err(e) => { Notification::send( - format!( - "Failed to load the ingress eBPF program to the kernel\n{}", - e - ), + format!("Failed to load the ingress eBPF bytecode\n {}", e), NotificationLevel::Error, notification_sender, ) .unwrap(); return; - }; - - if let Err(e) = program.attach(&iface, TcAttachType::Ingress) { + } + }; + + #[cfg(not(debug_assertions))] + let mut bpf = match Bpf::load(include_bytes_aligned!( + "../../target/bpfel-unknown-none/release/oryx" + )) { + Ok(v) => v, + Err(e) => { Notification::send( - format!( - "Failed to attach the ingress eBPF program to the interface\n{}", - e - ), + format!("Failed to load the ingress eBPF bytecode\n {}", e), NotificationLevel::Error, notification_sender, ) .unwrap(); return; - }; - - let mut poll = Poll::new().unwrap(); - let mut events = Events::with_capacity(128); - - //filter-ebpf interface - let mut transport_filters: Array<_, u32> = - Array::try_from(bpf.take_map("TRANSPORT_FILTERS").unwrap()).unwrap(); - - let mut network_filters: Array<_, u32> = - Array::try_from(bpf.take_map("NETWORK_FILTERS").unwrap()).unwrap(); - - let mut link_filters: Array<_, u32> = - Array::try_from(bpf.take_map("LINK_FILTERS").unwrap()).unwrap(); - - // firewall-ebpf interface - let mut ipv4_firewall: HashMap<_, u32, [u16; MAX_RULES_PORT]> = - HashMap::try_from(bpf.take_map("BLOCKLIST_IPV4").unwrap()).unwrap(); - - let mut ipv6_firewall: HashMap<_, u128, [u16; MAX_RULES_PORT]> = - HashMap::try_from(bpf.take_map("BLOCKLIST_IPV6").unwrap()).unwrap(); - - thread::spawn(move || loop { - if let Ok(signal) = firewall_ingress_receiver.recv() { - match signal { - FirewallSignal::Rule(rule) => match rule.ip { - IpAddr::V4(addr) => update_ipv4_blocklist( - &mut ipv4_firewall, - addr, - rule.port, - rule.enabled, - ), - - IpAddr::V6(addr) => update_ipv6_blocklist( - &mut ipv6_firewall, - addr, - rule.port, - rule.enabled, - ), - }, - FirewallSignal::Kill => { - break; - } + } + }; + + let _ = tc::qdisc_add_clsact(&iface); + + let program: &mut SchedClassifier = + bpf.program_mut("oryx").unwrap().try_into().unwrap(); + + if let Err(e) = program.load() { + Notification::send( + format!( + "Failed to load the ingress eBPF program to the kernel\n{}", + e + ), + NotificationLevel::Error, + notification_sender, + ) + .unwrap(); + return; + }; + + if let Err(e) = program.attach(&iface, TcAttachType::Ingress) { + Notification::send( + format!( + "Failed to attach the ingress eBPF program to the interface\n{}", + e + ), + NotificationLevel::Error, + notification_sender, + ) + .unwrap(); + return; + }; + + let mut poll = Poll::new().unwrap(); + let mut events = Events::with_capacity(128); + + //filter-ebpf interface + let mut transport_filters: Array<_, u32> = + Array::try_from(bpf.take_map("TRANSPORT_FILTERS").unwrap()).unwrap(); + + let mut network_filters: Array<_, u32> = + Array::try_from(bpf.take_map("NETWORK_FILTERS").unwrap()).unwrap(); + + let mut link_filters: Array<_, u32> = + Array::try_from(bpf.take_map("LINK_FILTERS").unwrap()).unwrap(); + + // firewall-ebpf interface + let mut ipv4_firewall: HashMap<_, u32, [u16; MAX_RULES_PORT]> = + HashMap::try_from(bpf.take_map("BLOCKLIST_IPV4").unwrap()).unwrap(); + + let mut ipv6_firewall: HashMap<_, u128, [u16; MAX_RULES_PORT]> = + HashMap::try_from(bpf.take_map("BLOCKLIST_IPV6").unwrap()).unwrap(); + + thread::spawn(move || loop { + if let Ok(signal) = firewall_ingress_receiver.recv() { + match signal { + FirewallSignal::Rule(rule) => match rule.ip { + IpAddr::V4(addr) => update_ipv4_blocklist( + &mut ipv4_firewall, + addr, + rule.port, + rule.enabled, + ), + + IpAddr::V6(addr) => update_ipv6_blocklist( + &mut ipv6_firewall, + addr, + rule.port, + rule.enabled, + ), + }, + FirewallSignal::Kill => { + break; } } - }); - - thread::spawn(move || loop { - if let Ok(signal) = filter_channel_receiver.recv() { - match signal { - FilterChannelSignal::Update((filter, flag)) => match filter { - Protocol::Transport(p) => { - let _ = transport_filters.set(p as u32, flag as u32, 0); - } - Protocol::Network(p) => { - let _ = network_filters.set(p as u32, flag as u32, 0); - } - Protocol::Link(p) => { - let _ = link_filters.set(p as u32, flag as u32, 0); - } - }, - FilterChannelSignal::Kill => { - break; + } + }); + + thread::spawn(move || loop { + if let Ok(signal) = filter_channel_receiver.recv() { + match signal { + FilterChannelSignal::Update((filter, flag)) => match filter { + Protocol::Transport(p) => { + let _ = transport_filters.set(p as u32, flag as u32, 0); + } + Protocol::Network(p) => { + let _ = network_filters.set(p as u32, flag as u32, 0); + } + Protocol::Link(p) => { + let _ = link_filters.set(p as u32, flag as u32, 0); } + }, + FilterChannelSignal::Kill => { + break; } } - }); + } + }); - let mut ring_buf = RingBuffer::new(&mut bpf); + let mut ring_buf = RingBuffer::new(&mut bpf); - poll.registry() - .register( - &mut SourceFd(&ring_buf.buffer.as_raw_fd()), - Token(0), - Interest::READABLE, - ) - .unwrap(); + poll.registry() + .register( + &mut SourceFd(&ring_buf.buffer.as_raw_fd()), + Token(0), + Interest::READABLE, + ) + .unwrap(); - loop { - poll.poll(&mut events, Some(Duration::from_millis(100))) - .unwrap(); + loop { + poll.poll(&mut events, Some(Duration::from_millis(100))) + .unwrap(); + if terminate.load(std::sync::atomic::Ordering::Relaxed) { + break; + } + for event in &events { if terminate.load(std::sync::atomic::Ordering::Relaxed) { break; } - for event in &events { + if event.token() == Token(0) && event.is_readable() { if terminate.load(std::sync::atomic::Ordering::Relaxed) { break; } - if event.token() == Token(0) && event.is_readable() { + while let Some(item) = ring_buf.next() { if terminate.load(std::sync::atomic::Ordering::Relaxed) { break; } - while let Some(item) = ring_buf.next() { - if terminate.load(std::sync::atomic::Ordering::Relaxed) { - break; - } - let packet: [u8; RawPacket::LEN] = - item.to_owned().try_into().unwrap(); - data_sender.send(packet).ok(); - } + let packet: [u8; RawPacket::LEN] = item.to_owned().try_into().unwrap(); + data_sender.send(packet).ok(); } } } - - let _ = poll - .registry() - .deregister(&mut SourceFd(&ring_buf.buffer.as_raw_fd())); } - }); - } - pub fn load_egress( - iface: String, - notification_sender: kanal::Sender, - data_sender: kanal::Sender<[u8; RawPacket::LEN]>, - filter_channel_receiver: kanal::Receiver, - firewall_egress_receiver: kanal::Receiver, - terminate: Arc, - ) { - thread::spawn({ - let iface = iface.to_owned(); - let notification_sender = notification_sender.clone(); - - move || { - let rlim = libc::rlimit { - rlim_cur: libc::RLIM_INFINITY, - rlim_max: libc::RLIM_INFINITY, - }; - - unsafe { libc::setrlimit(libc::RLIMIT_MEMLOCK, &rlim) }; - - #[cfg(debug_assertions)] - let mut bpf = match Bpf::load(include_bytes_aligned!( - "../../target/bpfel-unknown-none/debug/oryx" - )) { - Ok(v) => v, - Err(e) => { - Notification::send( - format!("Fail to load the egress eBPF bytecode\n {}", e), - NotificationLevel::Error, - notification_sender, - ) - .unwrap(); - return; - } - }; - - #[cfg(not(debug_assertions))] - let mut bpf = match Bpf::load(include_bytes_aligned!( - "../../target/bpfel-unknown-none/release/oryx" - )) { - Ok(v) => v, - Err(e) => { - Notification::send( - format!("Failed to load the egress eBPF bytecode\n {}", e), - NotificationLevel::Error, - notification_sender, - ) - .unwrap(); - return; - } - }; - - let _ = tc::qdisc_add_clsact(&iface); - let program: &mut SchedClassifier = - bpf.program_mut("oryx").unwrap().try_into().unwrap(); + let _ = poll + .registry() + .deregister(&mut SourceFd(&ring_buf.buffer.as_raw_fd())); + } + }); +} - if let Err(e) = program.load() { +pub fn load_egress( + iface: String, + notification_sender: kanal::Sender, + data_sender: kanal::Sender<[u8; RawPacket::LEN]>, + filter_channel_receiver: kanal::Receiver, + firewall_egress_receiver: kanal::Receiver, + terminate: Arc, +) { + thread::spawn({ + let iface = iface.to_owned(); + let notification_sender = notification_sender.clone(); + + move || { + let rlim = libc::rlimit { + rlim_cur: libc::RLIM_INFINITY, + rlim_max: libc::RLIM_INFINITY, + }; + + unsafe { libc::setrlimit(libc::RLIMIT_MEMLOCK, &rlim) }; + + #[cfg(debug_assertions)] + let mut bpf = match Ebpf::load(include_bytes_aligned!( + "../../target/bpfel-unknown-none/debug/oryx" + )) { + Ok(v) => v, + Err(e) => { Notification::send( - format!("Fail to load the egress eBPF program to the kernel\n{}", e), + format!("Fail to load the egress eBPF bytecode\n {}", e), NotificationLevel::Error, notification_sender, ) .unwrap(); return; - }; - - if let Err(e) = program.attach(&iface, TcAttachType::Egress) { + } + }; + + #[cfg(not(debug_assertions))] + let mut bpf = match Bpf::load(include_bytes_aligned!( + "../../target/bpfel-unknown-none/release/oryx" + )) { + Ok(v) => v, + Err(e) => { Notification::send( - format!( - "Failed to attach the egress eBPF program to the interface\n{}", - e - ), + format!("Failed to load the egress eBPF bytecode\n {}", e), NotificationLevel::Error, notification_sender, ) .unwrap(); return; - }; - - let mut poll = Poll::new().unwrap(); - let mut events = Events::with_capacity(128); - - //filter-ebpf interface - let mut transport_filters: Array<_, u32> = - Array::try_from(bpf.take_map("TRANSPORT_FILTERS").unwrap()).unwrap(); - - let mut network_filters: Array<_, u32> = - Array::try_from(bpf.take_map("NETWORK_FILTERS").unwrap()).unwrap(); - - let mut link_filters: Array<_, u32> = - Array::try_from(bpf.take_map("LINK_FILTERS").unwrap()).unwrap(); - - // firewall-ebpf interface - let mut ipv4_firewall: HashMap<_, u32, [u16; MAX_RULES_PORT]> = - HashMap::try_from(bpf.take_map("BLOCKLIST_IPV4").unwrap()).unwrap(); - - let mut ipv6_firewall: HashMap<_, u128, [u16; MAX_RULES_PORT]> = - HashMap::try_from(bpf.take_map("BLOCKLIST_IPV6").unwrap()).unwrap(); - - thread::spawn(move || loop { - if let Ok(signal) = firewall_egress_receiver.recv() { - match signal { - FirewallSignal::Rule(rule) => match rule.ip { - IpAddr::V4(addr) => update_ipv4_blocklist( - &mut ipv4_firewall, - addr, - rule.port, - rule.enabled, - ), - - IpAddr::V6(addr) => update_ipv6_blocklist( - &mut ipv6_firewall, - addr, - rule.port, - rule.enabled, - ), - }, - FirewallSignal::Kill => { - break; - } + } + }; + + let _ = tc::qdisc_add_clsact(&iface); + let program: &mut SchedClassifier = + bpf.program_mut("oryx").unwrap().try_into().unwrap(); + + if let Err(e) = program.load() { + Notification::send( + format!("Fail to load the egress eBPF program to the kernel\n{}", e), + NotificationLevel::Error, + notification_sender, + ) + .unwrap(); + return; + }; + + if let Err(e) = program.attach(&iface, TcAttachType::Egress) { + Notification::send( + format!( + "Failed to attach the egress eBPF program to the interface\n{}", + e + ), + NotificationLevel::Error, + notification_sender, + ) + .unwrap(); + return; + }; + + let mut poll = Poll::new().unwrap(); + let mut events = Events::with_capacity(128); + + //filter-ebpf interface + let mut transport_filters: Array<_, u32> = + Array::try_from(bpf.take_map("TRANSPORT_FILTERS").unwrap()).unwrap(); + + let mut network_filters: Array<_, u32> = + Array::try_from(bpf.take_map("NETWORK_FILTERS").unwrap()).unwrap(); + + let mut link_filters: Array<_, u32> = + Array::try_from(bpf.take_map("LINK_FILTERS").unwrap()).unwrap(); + + // firewall-ebpf interface + let mut ipv4_firewall: HashMap<_, u32, [u16; MAX_RULES_PORT]> = + HashMap::try_from(bpf.take_map("BLOCKLIST_IPV4").unwrap()).unwrap(); + + let mut ipv6_firewall: HashMap<_, u128, [u16; MAX_RULES_PORT]> = + HashMap::try_from(bpf.take_map("BLOCKLIST_IPV6").unwrap()).unwrap(); + + thread::spawn(move || loop { + if let Ok(signal) = firewall_egress_receiver.recv() { + match signal { + FirewallSignal::Rule(rule) => match rule.ip { + IpAddr::V4(addr) => update_ipv4_blocklist( + &mut ipv4_firewall, + addr, + rule.port, + rule.enabled, + ), + + IpAddr::V6(addr) => update_ipv6_blocklist( + &mut ipv6_firewall, + addr, + rule.port, + rule.enabled, + ), + }, + FirewallSignal::Kill => { + break; } } - }); - - thread::spawn(move || loop { - if let Ok(signal) = filter_channel_receiver.recv() { - match signal { - FilterChannelSignal::Update((filter, flag)) => match filter { - Protocol::Transport(p) => { - let _ = transport_filters.set(p as u32, flag as u32, 0); - } - Protocol::Network(p) => { - let _ = network_filters.set(p as u32, flag as u32, 0); - } - Protocol::Link(p) => { - let _ = link_filters.set(p as u32, flag as u32, 0); - } - }, - FilterChannelSignal::Kill => { - break; + } + }); + + thread::spawn(move || loop { + if let Ok(signal) = filter_channel_receiver.recv() { + match signal { + FilterChannelSignal::Update((filter, flag)) => match filter { + Protocol::Transport(p) => { + let _ = transport_filters.set(p as u32, flag as u32, 0); } + Protocol::Network(p) => { + let _ = network_filters.set(p as u32, flag as u32, 0); + } + Protocol::Link(p) => { + let _ = link_filters.set(p as u32, flag as u32, 0); + } + }, + FilterChannelSignal::Kill => { + break; } } - }); + } + }); - let mut ring_buf = RingBuffer::new(&mut bpf); + let mut ring_buf = RingBuffer::new(&mut bpf); - poll.registry() - .register( - &mut SourceFd(&ring_buf.buffer.as_raw_fd()), - Token(0), - Interest::READABLE, - ) - .unwrap(); + poll.registry() + .register( + &mut SourceFd(&ring_buf.buffer.as_raw_fd()), + Token(0), + Interest::READABLE, + ) + .unwrap(); - loop { - poll.poll(&mut events, Some(Duration::from_millis(100))) - .unwrap(); + loop { + poll.poll(&mut events, Some(Duration::from_millis(100))) + .unwrap(); + if terminate.load(std::sync::atomic::Ordering::Relaxed) { + break; + } + for event in &events { if terminate.load(std::sync::atomic::Ordering::Relaxed) { break; } - for event in &events { + if event.token() == Token(0) && event.is_readable() { if terminate.load(std::sync::atomic::Ordering::Relaxed) { break; } - if event.token() == Token(0) && event.is_readable() { + while let Some(item) = ring_buf.next() { if terminate.load(std::sync::atomic::Ordering::Relaxed) { break; } - while let Some(item) = ring_buf.next() { - if terminate.load(std::sync::atomic::Ordering::Relaxed) { - break; - } - let packet: [u8; RawPacket::LEN] = - item.to_owned().try_into().unwrap(); - data_sender.send(packet).ok(); - } + let packet: [u8; RawPacket::LEN] = item.to_owned().try_into().unwrap(); + data_sender.send(packet).ok(); } } } - - let _ = poll - .registry() - .deregister(&mut SourceFd(&ring_buf.buffer.as_raw_fd())); } - }); - } + + let _ = poll + .registry() + .deregister(&mut SourceFd(&ring_buf.buffer.as_raw_fd())); + } + }); } diff --git a/oryx-tui/src/filter.rs b/oryx-tui/src/filter.rs index a3241db..7eb057e 100644 --- a/oryx-tui/src/filter.rs +++ b/oryx-tui/src/filter.rs @@ -28,7 +28,10 @@ use transport::TransportFilter; use tui_big_text::{BigText, PixelSize}; use crate::{ - app::AppResult, ebpf::Ebpf, event::Event, interface::Interface, + app::AppResult, + ebpf::{load_egress, load_ingress}, + event::Event, + interface::Interface, section::firewall::FirewallSignal, }; @@ -146,7 +149,7 @@ impl Filter { .applied_direction .contains(&TrafficDirection::Ingress) { - Ebpf::load_ingress( + load_ingress( iface.clone(), notification_sender.clone(), data_sender.clone(), @@ -161,7 +164,7 @@ impl Filter { .applied_direction .contains(&TrafficDirection::Egress) { - Ebpf::load_egress( + load_egress( iface, notification_sender, data_sender, @@ -336,7 +339,7 @@ impl Filter { let iface = self.interface.selected_interface.name.clone(); - Ebpf::load_egress( + load_egress( iface, notification_sender.clone(), data_sender.clone(), @@ -378,7 +381,7 @@ impl Filter { self.traffic_direction .terminate_ingress .store(false, std::sync::atomic::Ordering::Relaxed); - Ebpf::load_ingress( + load_ingress( iface, notification_sender.clone(), data_sender.clone(), From 4aeb7257145b91bfbd5563b0da7670c8931fdc4a Mon Sep 17 00:00:00 2001 From: adrien gaultier Date: Thu, 10 Oct 2024 12:33:10 +0200 Subject: [PATCH 05/11] firewall rules persistence --- Cargo.lock | 35 ++++++++++++++++ Justfile | 2 +- oryx-tui/Cargo.toml | 4 +- oryx-tui/src/filter/direction.rs | 3 +- oryx-tui/src/handler.rs | 44 +++++++++++++-------- oryx-tui/src/section/firewall.rs | 68 ++++++++++++++++++++++++++++++-- 6 files changed, 133 insertions(+), 23 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index bd96721..20b05b6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -666,6 +666,8 @@ dependencies = [ "network-types", "oryx-common", "ratatui", + "serde", + "serde_json", "tui-big-text", "tui-input", "uuid", @@ -819,6 +821,38 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +[[package]] +name = "serde" +version = "1.0.210" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.210" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.128" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", +] + [[package]] name = "shlex" version = "1.3.0" @@ -1000,6 +1034,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "81dfa00651efa65069b0b6b651f4aaa31ba9e3c3ce0137aaad053604ee7e0314" dependencies = [ "getrandom", + "serde", ] [[package]] diff --git a/Justfile b/Justfile index f36f9f3..a46f882 100644 --- a/Justfile +++ b/Justfile @@ -17,7 +17,7 @@ show interface: # Run oryx debug run-debug: echo "" > log-file - RUST_LOG=info cargo xtask run 2> log-file + RUST_LOG=info RUST_BACKTRACE=1 cargo xtask run 2> log-file run: cargo xtask run diff --git a/oryx-tui/Cargo.toml b/oryx-tui/Cargo.toml index 68513af..599f5b5 100644 --- a/oryx-tui/Cargo.toml +++ b/oryx-tui/Cargo.toml @@ -24,9 +24,11 @@ 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"] } +uuid = { version = "1", default-features = false, features = ["v4","serde"] } log = "0.4" env_logger = "0.11" +serde_json = "1.0.128" +serde = {version = "1.0", features = ["derive"] } [[bin]] name = "oryx" diff --git a/oryx-tui/src/filter/direction.rs b/oryx-tui/src/filter/direction.rs index 0d27105..2e5cb2d 100644 --- a/oryx-tui/src/filter/direction.rs +++ b/oryx-tui/src/filter/direction.rs @@ -12,6 +12,7 @@ use ratatui::{ widgets::{Block, BorderType, Borders, Row, Table, TableState}, Frame, }; +use serde::{Deserialize, Serialize}; #[derive(Debug)] pub struct TrafficDirectionFilter { @@ -22,7 +23,7 @@ pub struct TrafficDirectionFilter { pub terminate_egress: Arc, } -#[derive(Debug, Copy, Clone, PartialEq)] +#[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)] pub enum TrafficDirection { Ingress, Egress, diff --git a/oryx-tui/src/handler.rs b/oryx-tui/src/handler.rs index 68a9fe6..3d5d636 100644 --- a/oryx-tui/src/handler.rs +++ b/oryx-tui/src/handler.rs @@ -191,24 +191,34 @@ pub fn handle_key_events( } KeyCode::Char('s') => { - let app_packets = app.packets.lock().unwrap(); - if app_packets.is_empty() { - Notification::send( - "There is no packets".to_string(), - NotificationLevel::Info, - event_sender, - )?; + if app.section.focused_section == FocusedSection::Firewall { + app.section + .firewall + .handle_keys(key_event, event_sender.clone())? } else { - match export(&app_packets) { - Ok(_) => { - Notification::send( - "Packets exported to ~/oryx/capture file".to_string(), - NotificationLevel::Info, - event_sender, - )?; - } - Err(e) => { - Notification::send(e.to_string(), NotificationLevel::Error, event_sender)?; + let app_packets = app.packets.lock().unwrap(); + if app_packets.is_empty() { + Notification::send( + "There is no packets".to_string(), + NotificationLevel::Info, + event_sender, + )?; + } else { + match export(&app_packets) { + Ok(_) => { + Notification::send( + "Packets exported to ~/oryx/capture file".to_string(), + NotificationLevel::Info, + event_sender, + )?; + } + Err(e) => { + Notification::send( + e.to_string(), + NotificationLevel::Error, + event_sender, + )?; + } } } } diff --git a/oryx-tui/src/section/firewall.rs b/oryx-tui/src/section/firewall.rs index 02317ef..94e9779 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 log::info; use oryx_common::MAX_FIREWALL_RULES; use ratatui::{ layout::{Constraint, Direction, Flex, Layout, Margin, Rect}, @@ -8,7 +9,9 @@ use ratatui::{ widgets::{Block, Borders, Cell, Clear, HighlightSpacing, Padding, Row, Table, TableState}, Frame, }; -use std::{net::IpAddr, num::ParseIntError, str::FromStr}; +use serde::{Deserialize, Serialize}; +use serde_json; +use std::{fs, net::IpAddr, num::ParseIntError, os::unix::fs::chown, str::FromStr}; use tui_input::{backend::crossterm::EventHandler, Input}; use uuid; @@ -20,7 +23,7 @@ pub enum FirewallSignal { Kill, } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Serialize, Deserialize)] pub struct FirewallRule { id: uuid::Uuid, name: String, @@ -30,7 +33,7 @@ pub struct FirewallRule { direction: TrafficDirection, } -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub enum BlockedPort { Single(u16), All, @@ -315,6 +318,42 @@ impl Firewall { self.user_input = Some(UserInput::new()); } + pub fn save_rules(&self) -> AppResult<()> { + info!("saving rules"); + info!("{:#?}", self.rules); + + let json = serde_json::to_string(&self.rules).unwrap(); + + let uid = unsafe { libc::geteuid() }; + + let oryx_export_dir = dirs::home_dir().unwrap().join("oryx"); + + if !oryx_export_dir.exists() { + fs::create_dir(&oryx_export_dir)?; + chown(&oryx_export_dir, Some(uid), Some(uid))?; + } + + let oryx_export_file = oryx_export_dir.join("firewall.json"); + fs::write(oryx_export_file, json).expect("Could not save Firewall Rules"); + info!("rules saved"); + Ok(()) + } + + pub fn load_rules(&mut self) -> AppResult<()> { + info!("loading rules"); + let oryx_export_file = dirs::home_dir().unwrap().join("oryx").join("firewall.json"); + if oryx_export_file.exists() { + info!("EXISTS"); + let json_string = + fs::read_to_string(oryx_export_file).expect("Could not load Firewall Rules"); + let parsed_rules: Vec = + serde_json::from_str(&json_string).expect("Could not load Firewall Rules"); + info!("rules loaded"); + self.rules = parsed_rules; + } + + Ok(()) + } 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() @@ -411,6 +450,29 @@ impl Firewall { key_event: KeyEvent, sender: kanal::Sender, ) -> AppResult<()> { + match key_event.code { + KeyCode::Char('s') => { + if let Err(e) = self.save_rules() { + Notification::send( + "Error saving rules", + crate::notification::NotificationLevel::Warning, + sender.clone(), + )?; + return Err(e); + } + } + KeyCode::Char('l') => { + if let Err(e) = self.load_rules() { + Notification::send( + "Error loading rules", + crate::notification::NotificationLevel::Warning, + sender.clone(), + )?; + return Err(e); + } + } + _ => {} + } if let Some(user_input) = &mut self.user_input { match key_event.code { KeyCode::Esc => { From 5a6f220c4bd7d4a051919dcf2f10ab5454bfb1b3 Mon Sep 17 00:00:00 2001 From: adrien gaultier Date: Thu, 10 Oct 2024 15:08:35 +0200 Subject: [PATCH 06/11] handle firewall rules persistence --- oryx-tui/src/app.rs | 7 +++ oryx-tui/src/handler.rs | 44 +++++++---------- oryx-tui/src/section/firewall.rs | 83 +++++++++++++++----------------- 3 files changed, 62 insertions(+), 72 deletions(-) diff --git a/oryx-tui/src/app.rs b/oryx-tui/src/app.rs index ac3818d..e16788e 100644 --- a/oryx-tui/src/app.rs +++ b/oryx-tui/src/app.rs @@ -1,3 +1,4 @@ +use log::error; use oryx_common::RawPacket; use ratatui::{ layout::{Constraint, Direction, Layout}, @@ -134,6 +135,12 @@ impl App { } pub fn quit(&mut self) { + match self.section.firewall.save_rules() { + Ok(()) => {} + Err(err) => { + error!("{}", err) + } + } self.running = false; } } diff --git a/oryx-tui/src/handler.rs b/oryx-tui/src/handler.rs index 3d5d636..68a9fe6 100644 --- a/oryx-tui/src/handler.rs +++ b/oryx-tui/src/handler.rs @@ -191,34 +191,24 @@ pub fn handle_key_events( } KeyCode::Char('s') => { - if app.section.focused_section == FocusedSection::Firewall { - app.section - .firewall - .handle_keys(key_event, event_sender.clone())? + let app_packets = app.packets.lock().unwrap(); + if app_packets.is_empty() { + Notification::send( + "There is no packets".to_string(), + NotificationLevel::Info, + event_sender, + )?; } else { - let app_packets = app.packets.lock().unwrap(); - if app_packets.is_empty() { - Notification::send( - "There is no packets".to_string(), - NotificationLevel::Info, - event_sender, - )?; - } else { - match export(&app_packets) { - Ok(_) => { - Notification::send( - "Packets exported to ~/oryx/capture file".to_string(), - NotificationLevel::Info, - event_sender, - )?; - } - Err(e) => { - Notification::send( - e.to_string(), - NotificationLevel::Error, - event_sender, - )?; - } + match export(&app_packets) { + Ok(_) => { + Notification::send( + "Packets exported to ~/oryx/capture file".to_string(), + NotificationLevel::Info, + event_sender, + )?; + } + Err(e) => { + Notification::send(e.to_string(), NotificationLevel::Error, event_sender)?; } } } diff --git a/oryx-tui/src/section/firewall.rs b/oryx-tui/src/section/firewall.rs index 94e9779..3e13dfd 100644 --- a/oryx-tui/src/section/firewall.rs +++ b/oryx-tui/src/section/firewall.rs @@ -1,6 +1,6 @@ use core::fmt::Display; use crossterm::event::{Event, KeyCode, KeyEvent}; -use log::info; +use log::{error, info}; use oryx_common::MAX_FIREWALL_RULES; use ratatui::{ layout::{Constraint, Direction, Flex, Layout, Margin, Rect}, @@ -305,8 +305,16 @@ impl Firewall { ingress_sender: kanal::Sender, egress_sender: kanal::Sender, ) -> Self { + let rules_list: Vec = match Self::load_saved_rules() { + Ok(maybe_saved_rules) => maybe_saved_rules.unwrap_or_default(), + + Err(err) => { + error!("{}", err.to_string()); + Vec::new() + } + }; Self { - rules: Vec::new(), + rules: rules_list, state: TableState::default(), user_input: None, ingress_sender, @@ -319,41 +327,49 @@ impl Firewall { } pub fn save_rules(&self) -> AppResult<()> { - info!("saving rules"); - info!("{:#?}", self.rules); + if self.rules.len() > 0 { + info!("Saving Firewall Rules"); - let json = serde_json::to_string(&self.rules).unwrap(); + let json = serde_json::to_string(&self.rules).unwrap(); - let uid = unsafe { libc::geteuid() }; + let uid = unsafe { libc::geteuid() }; - let oryx_export_dir = dirs::home_dir().unwrap().join("oryx"); + let oryx_export_dir = dirs::home_dir().unwrap().join("oryx"); - if !oryx_export_dir.exists() { - fs::create_dir(&oryx_export_dir)?; - chown(&oryx_export_dir, Some(uid), Some(uid))?; - } + if !oryx_export_dir.exists() { + fs::create_dir(&oryx_export_dir)?; + chown(&oryx_export_dir, Some(uid), Some(uid))?; + } - let oryx_export_file = oryx_export_dir.join("firewall.json"); - fs::write(oryx_export_file, json).expect("Could not save Firewall Rules"); - info!("rules saved"); + let oryx_export_file = oryx_export_dir.join("firewall.json"); + fs::write(oryx_export_file, json).expect("Could not save Firewall Rules"); + info!("Firewall Rules saved"); + } Ok(()) } - pub fn load_rules(&mut self) -> AppResult<()> { - info!("loading rules"); + fn load_saved_rules() -> AppResult>> { + info!("Loading Firewall Rules"); let oryx_export_file = dirs::home_dir().unwrap().join("oryx").join("firewall.json"); if oryx_export_file.exists() { - info!("EXISTS"); + info!("Found previously saved Firewall Rules"); let json_string = fs::read_to_string(oryx_export_file).expect("Could not load Firewall Rules"); - let parsed_rules: Vec = + let mut parsed_rules: Vec = serde_json::from_str(&json_string).expect("Could not load Firewall Rules"); - info!("rules loaded"); - self.rules = parsed_rules; - } + // as we don't know if ingress/egress programs are loaded we have to disable all rules + for rule in &mut parsed_rules { + rule.enabled = false + } - Ok(()) + info!("Firewall Rules loaded"); + Ok(Some(parsed_rules)) + } else { + info!("No saved Firewall Rules found"); + Ok(None) + } } + 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() @@ -450,29 +466,6 @@ impl Firewall { key_event: KeyEvent, sender: kanal::Sender, ) -> AppResult<()> { - match key_event.code { - KeyCode::Char('s') => { - if let Err(e) = self.save_rules() { - Notification::send( - "Error saving rules", - crate::notification::NotificationLevel::Warning, - sender.clone(), - )?; - return Err(e); - } - } - KeyCode::Char('l') => { - if let Err(e) = self.load_rules() { - Notification::send( - "Error loading rules", - crate::notification::NotificationLevel::Warning, - sender.clone(), - )?; - return Err(e); - } - } - _ => {} - } if let Some(user_input) = &mut self.user_input { match key_event.code { KeyCode::Esc => { From 156b318ea99ff06346b0cb8f7f54fc41ba417214 Mon Sep 17 00:00:00 2001 From: adrien gaultier Date: Thu, 10 Oct 2024 15:19:42 +0200 Subject: [PATCH 07/11] clippy my way out --- 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 3e13dfd..58b7c3f 100644 --- a/oryx-tui/src/section/firewall.rs +++ b/oryx-tui/src/section/firewall.rs @@ -327,7 +327,7 @@ impl Firewall { } pub fn save_rules(&self) -> AppResult<()> { - if self.rules.len() > 0 { + if !self.rules.is_empty() { info!("Saving Firewall Rules"); let json = serde_json::to_string(&self.rules).unwrap(); From 0291100bfe095ce7cccc27145264a399d841a136 Mon Sep 17 00:00:00 2001 From: Badr Date: Thu, 10 Oct 2024 15:35:10 +0200 Subject: [PATCH 08/11] fix --- oryx-tui/Cargo.toml | 6 +++--- oryx-tui/src/app.rs | 8 +++----- oryx-tui/src/section/firewall.rs | 19 ++++++++++--------- 3 files changed, 16 insertions(+), 17 deletions(-) diff --git a/oryx-tui/Cargo.toml b/oryx-tui/Cargo.toml index 599f5b5..4826bf7 100644 --- a/oryx-tui/Cargo.toml +++ b/oryx-tui/Cargo.toml @@ -24,11 +24,11 @@ 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","serde"] } +uuid = { version = "1", default-features = false, features = ["v4", "serde"] } log = "0.4" env_logger = "0.11" -serde_json = "1.0.128" -serde = {version = "1.0", features = ["derive"] } +serde_json = "1" +serde = { version = "1", features = ["derive"] } [[bin]] name = "oryx" diff --git a/oryx-tui/src/app.rs b/oryx-tui/src/app.rs index e16788e..e21e474 100644 --- a/oryx-tui/src/app.rs +++ b/oryx-tui/src/app.rs @@ -135,12 +135,10 @@ impl App { } pub fn quit(&mut self) { - match self.section.firewall.save_rules() { - Ok(()) => {} - Err(err) => { - error!("{}", err) - } + if let Err(e) = self.section.firewall.save_rules() { + error!("{}", e) } + self.running = false; } } diff --git a/oryx-tui/src/section/firewall.rs b/oryx-tui/src/section/firewall.rs index 58b7c3f..94b1338 100644 --- a/oryx-tui/src/section/firewall.rs +++ b/oryx-tui/src/section/firewall.rs @@ -306,7 +306,7 @@ impl Firewall { egress_sender: kanal::Sender, ) -> Self { let rules_list: Vec = match Self::load_saved_rules() { - Ok(maybe_saved_rules) => maybe_saved_rules.unwrap_or_default(), + Ok(saved_rules) => saved_rules, Err(err) => { error!("{}", err.to_string()); @@ -342,31 +342,32 @@ impl Firewall { } let oryx_export_file = oryx_export_dir.join("firewall.json"); - fs::write(oryx_export_file, json).expect("Could not save Firewall Rules"); + fs::write(oryx_export_file, json)?; info!("Firewall Rules saved"); } Ok(()) } - fn load_saved_rules() -> AppResult>> { + fn load_saved_rules() -> AppResult> { info!("Loading Firewall Rules"); let oryx_export_file = dirs::home_dir().unwrap().join("oryx").join("firewall.json"); if oryx_export_file.exists() { info!("Found previously saved Firewall Rules"); - let json_string = - fs::read_to_string(oryx_export_file).expect("Could not load Firewall Rules"); - let mut parsed_rules: Vec = - serde_json::from_str(&json_string).expect("Could not load Firewall Rules"); + + let json_string = fs::read_to_string(oryx_export_file)?; + + let mut parsed_rules: Vec = serde_json::from_str(&json_string)?; + // as we don't know if ingress/egress programs are loaded we have to disable all rules for rule in &mut parsed_rules { rule.enabled = false } info!("Firewall Rules loaded"); - Ok(Some(parsed_rules)) + Ok(parsed_rules) } else { info!("No saved Firewall Rules found"); - Ok(None) + Ok(Vec::new()) } } From 69e9e1f86339ef2f96930b82f98daa3d045fc8a7 Mon Sep 17 00:00:00 2001 From: Badr Date: Thu, 10 Oct 2024 19:46:31 +0200 Subject: [PATCH 09/11] add key handler for saving rules --- Readme.md | 2 ++ oryx-tui/src/handler.rs | 25 -------------------- oryx-tui/src/help.rs | 4 ++++ oryx-tui/src/section.rs | 10 +++++++- oryx-tui/src/section/firewall.rs | 35 ++++++++++++++++++++------- oryx-tui/src/section/inspection.rs | 38 +++++++++++++++++++++++++++++- 6 files changed, 78 insertions(+), 36 deletions(-) diff --git a/Readme.md b/Readme.md index 6a806ca..a771349 100644 --- a/Readme.md +++ b/Readme.md @@ -122,6 +122,8 @@ sudo oryx `e`: Edit a firewall rule. +`s`: Save firewall rules to `~/oryx/firewall.json` + `Enter`: Create or Save a firewall rule. ## ⚖️ License diff --git a/oryx-tui/src/handler.rs b/oryx-tui/src/handler.rs index 68a9fe6..bc92abe 100644 --- a/oryx-tui/src/handler.rs +++ b/oryx-tui/src/handler.rs @@ -3,9 +3,7 @@ use std::{thread, time::Duration}; use crate::{ app::{ActivePopup, App, AppResult}, event::Event, - export::export, filter::FocusedBlock, - notification::{Notification, NotificationLevel}, section::FocusedSection, }; use ratatui::crossterm::event::{KeyCode, KeyEvent, KeyModifiers}; @@ -190,29 +188,6 @@ pub fn handle_key_events( } } - KeyCode::Char('s') => { - let app_packets = app.packets.lock().unwrap(); - if app_packets.is_empty() { - Notification::send( - "There is no packets".to_string(), - NotificationLevel::Info, - event_sender, - )?; - } else { - match export(&app_packets) { - Ok(_) => { - Notification::send( - "Packets exported to ~/oryx/capture file".to_string(), - NotificationLevel::Info, - event_sender, - )?; - } - Err(e) => { - Notification::send(e.to_string(), NotificationLevel::Error, event_sender)?; - } - } - } - } _ => { app.section.handle_keys(key_event, event_sender.clone())?; } diff --git a/oryx-tui/src/help.rs b/oryx-tui/src/help.rs index 3685a8d..bd68290 100644 --- a/oryx-tui/src/help.rs +++ b/oryx-tui/src/help.rs @@ -57,6 +57,10 @@ impl Help { (Cell::from("## Firewall").bold().yellow(), ""), (Cell::from("n").bold(), "Add new firewall rule"), (Cell::from("e").bold(), "Edit a firewall rule"), + ( + Cell::from("s").bold(), + "Save firewall rules to ~/oryx/firewall.json ", + ), (Cell::from("Space").bold(), "Toggle firewall rule status"), (Cell::from("Enter").bold(), "Create or Save a firewall rule"), ], diff --git a/oryx-tui/src/section.rs b/oryx-tui/src/section.rs index 7be8994..1c080df 100644 --- a/oryx-tui/src/section.rs +++ b/oryx-tui/src/section.rs @@ -151,6 +151,9 @@ impl Section { Span::from("i").bold(), Span::from(" Infos").bold(), Span::from(" | ").bold(), + Span::from("s").bold(), + Span::from(" Save").bold(), + Span::from(" | ").bold(), Span::from("f").bold(), Span::from(" Filters").bold(), Span::from(" | ").bold(), @@ -176,6 +179,9 @@ impl Section { Span::from("e").bold(), Span::from(" Edit").bold(), Span::from(" | ").bold(), + Span::from("s").bold(), + Span::from(" Save").bold(), + Span::from(" | ").bold(), Span::from("󱁐 ").bold(), Span::from(" Toggle").bold(), Span::from(" | ").bold(), @@ -275,7 +281,9 @@ impl Section { }, _ => match self.focused_section { - FocusedSection::Inspection => self.inspection.handle_keys(key_event), + FocusedSection::Inspection => self + .inspection + .handle_keys(key_event, notification_sender.clone())?, 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 94b1338..b2dc984 100644 --- a/oryx-tui/src/section/firewall.rs +++ b/oryx-tui/src/section/firewall.rs @@ -330,15 +330,15 @@ impl Firewall { if !self.rules.is_empty() { info!("Saving Firewall Rules"); - let json = serde_json::to_string(&self.rules).unwrap(); + let json = serde_json::to_string(&self.rules)?; - let uid = unsafe { libc::geteuid() }; + let user_uid = unsafe { libc::geteuid() }; let oryx_export_dir = dirs::home_dir().unwrap().join("oryx"); if !oryx_export_dir.exists() { fs::create_dir(&oryx_export_dir)?; - chown(&oryx_export_dir, Some(uid), Some(uid))?; + chown(&oryx_export_dir, Some(user_uid), Some(user_uid))?; } let oryx_export_file = oryx_export_dir.join("firewall.json"); @@ -349,24 +349,23 @@ impl Firewall { } fn load_saved_rules() -> AppResult> { - info!("Loading Firewall Rules"); let oryx_export_file = dirs::home_dir().unwrap().join("oryx").join("firewall.json"); if oryx_export_file.exists() { - info!("Found previously saved Firewall Rules"); + info!("Loading Firewall Rules"); let json_string = fs::read_to_string(oryx_export_file)?; let mut parsed_rules: Vec = serde_json::from_str(&json_string)?; // as we don't know if ingress/egress programs are loaded we have to disable all rules - for rule in &mut parsed_rules { - rule.enabled = false - } + parsed_rules + .iter_mut() + .for_each(|rule| rule.enabled = false); info!("Firewall Rules loaded"); Ok(parsed_rules) } else { - info!("No saved Firewall Rules found"); + info!("Firewall Rules file not found"); Ok(Vec::new()) } } @@ -556,6 +555,24 @@ impl Firewall { self.add_rule(); } + KeyCode::Char('s') => match self.save_rules() { + Ok(_) => { + Notification::send( + "Firewall rules saved to ~/oryx/firewall.json file", + crate::notification::NotificationLevel::Info, + sender.clone(), + )?; + } + Err(e) => { + Notification::send( + "Error while saving firewall rules.", + crate::notification::NotificationLevel::Error, + sender.clone(), + )?; + error!("Error while saving firewall rules. {}", e); + } + }, + KeyCode::Char('e') => { if let Some(index) = self.state.selected() { let rule = self.rules[index].clone(); diff --git a/oryx-tui/src/section/inspection.rs b/oryx-tui/src/section/inspection.rs index 79bb9ae..8e02533 100644 --- a/oryx-tui/src/section/inspection.rs +++ b/oryx-tui/src/section/inspection.rs @@ -14,7 +14,10 @@ use ratatui::{ use tui_input::backend::crossterm::EventHandler; use crate::{ + app::AppResult, + export, filter::fuzzy::{self, Fuzzy}, + notification::{Notification, NotificationLevel}, packet::{ network::{IpPacket, IpProto}, AppPacket, @@ -56,7 +59,11 @@ impl Inspection { } } - pub fn handle_keys(&mut self, key_event: KeyEvent) { + pub fn handle_keys( + &mut self, + key_event: KeyEvent, + event_sender: kanal::Sender, + ) -> AppResult<()> { let fuzzy_is_enabled = { self.fuzzy.lock().unwrap().is_enabled() }; if fuzzy_is_enabled { @@ -126,9 +133,38 @@ impl Inspection { self.scroll_up(); } + KeyCode::Char('s') => { + let app_packets = self.packets.lock().unwrap(); + if app_packets.is_empty() { + Notification::send( + "There is no packets".to_string(), + NotificationLevel::Info, + event_sender, + )?; + } else { + match export::export(&app_packets) { + Ok(_) => { + Notification::send( + "Packets exported to ~/oryx/capture file".to_string(), + NotificationLevel::Info, + event_sender, + )?; + } + Err(e) => { + Notification::send( + e.to_string(), + NotificationLevel::Error, + event_sender, + )?; + } + } + } + } + _ => {} } } + Ok(()) } pub fn scroll_up(&mut self) { From 59e3ed8f1f229a791f8b52d79872c53defcb541b Mon Sep 17 00:00:00 2001 From: adrien gaultier Date: Thu, 10 Oct 2024 20:10:04 +0200 Subject: [PATCH 10/11] save empty rules list --- oryx-tui/src/section/firewall.rs | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/oryx-tui/src/section/firewall.rs b/oryx-tui/src/section/firewall.rs index b2dc984..33849d8 100644 --- a/oryx-tui/src/section/firewall.rs +++ b/oryx-tui/src/section/firewall.rs @@ -327,24 +327,23 @@ impl Firewall { } pub fn save_rules(&self) -> AppResult<()> { - if !self.rules.is_empty() { - info!("Saving Firewall Rules"); + info!("Saving Firewall Rules"); - let json = serde_json::to_string(&self.rules)?; + let json = serde_json::to_string(&self.rules)?; - let user_uid = unsafe { libc::geteuid() }; + let user_uid = unsafe { libc::geteuid() }; - let oryx_export_dir = dirs::home_dir().unwrap().join("oryx"); + let oryx_export_dir = dirs::home_dir().unwrap().join("oryx"); - if !oryx_export_dir.exists() { - fs::create_dir(&oryx_export_dir)?; - chown(&oryx_export_dir, Some(user_uid), Some(user_uid))?; - } - - let oryx_export_file = oryx_export_dir.join("firewall.json"); - fs::write(oryx_export_file, json)?; - info!("Firewall Rules saved"); + if !oryx_export_dir.exists() { + fs::create_dir(&oryx_export_dir)?; + chown(&oryx_export_dir, Some(user_uid), Some(user_uid))?; } + + let oryx_export_file = oryx_export_dir.join("firewall.json"); + fs::write(oryx_export_file, json)?; + info!("Firewall Rules saved"); + Ok(()) } From d9bc7e242c02e54edbb0604095f33e6fca1bc5ad Mon Sep 17 00:00:00 2001 From: Badr Date: Thu, 10 Oct 2024 21:09:52 +0200 Subject: [PATCH 11/11] update release file --- Release.md | 1 + 1 file changed, 1 insertion(+) diff --git a/Release.md b/Release.md index 8512aa7..73bf84f 100644 --- a/Release.md +++ b/Release.md @@ -3,6 +3,7 @@ ### Added - Firewall +- Save and Load firewall rules. ## v0.3 - 2024-09-25