Skip to content

Commit b3f4f28

Browse files
ui: deprecate add and unload key features from UI
Signed-off-by: saiaunghlyanhtet <saiaunghlyanhtet2003@gmail.com>
1 parent f0db937 commit b3f4f28

6 files changed

Lines changed: 17 additions & 183 deletions

File tree

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -127,9 +127,9 @@ BPF maps used:
127127

128128
Built with [ratatui](https://docs.rs/ratatui) and [crossterm](https://docs.rs/crossterm):
129129

130-
- **`app.rs`** — Application state: active rules, log buffer, per-rule stats, input mode.
130+
- **`app.rs`** — Application state: active rules, log buffer, per-rule stats.
131131
- **`widgets.rs`** — Renders a rules table (name, IP, protocol, direction, ports, action, stats, description), a scrolling log pane, and help text.
132-
- **`events.rs`** — Keyboard event handling: `Q` to quit, `A` to add a rule interactively, `U` to unload the firewall (with confirmation).
132+
- **`events.rs`** — Keyboard event handling: `Q` to quit. Rules are managed via the CLI (`firebee add/delete`).
133133

134134
## Usage
135135

src/bpf_user/handler.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,16 @@
11
use crate::bpf_user::{loader::BpfLoader, maps::BpfMaps};
22
use crate::models::rule::Rule;
3-
use crate::ui::app::Command;
43
use libbpf_rs::{Link, RingBufferBuilder};
54
use std::net::Ipv4Addr;
65
use tokio::sync::mpsc;
76

7+
#[allow(dead_code)]
8+
pub enum Command {
9+
AddRule(Rule),
10+
RemoveRule(Ipv4Addr),
11+
Unload,
12+
}
13+
814
#[allow(dead_code)]
915
pub struct BpfHandler {
1016
bpf_object: Option<Box<libbpf_rs::Object>>,

src/main.rs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ use once_cell::sync::OnceCell;
1212
use ratatui::prelude::*;
1313
use std::io;
1414
use std::path::PathBuf;
15-
use tokio::sync::mpsc;
1615

1716
mod bpf_user;
1817
mod models;
@@ -183,8 +182,7 @@ async fn run_firewall(interface: &str, policy: Option<PathBuf>) -> Result<()> {
183182
}
184183

185184
async fn run_tui() -> Result<()> {
186-
let (tx_cmd, _rx_cmd) = mpsc::channel(32);
187-
let (tx_log, rx_log) = mpsc::channel(32);
185+
let (tx_log, rx_log) = tokio::sync::mpsc::channel(32);
188186

189187
let maps = open_pinned_maps()?;
190188
let existing_rules = RulesState::list_rules(&maps).unwrap_or_else(|e| {
@@ -242,7 +240,7 @@ async fn run_tui() -> Result<()> {
242240
let backend = CrosstermBackend::new(stdout);
243241
let mut terminal = Terminal::new(backend)?;
244242

245-
let mut app = App::new(tx_cmd, rx_log, existing_rules);
243+
let mut app = App::new(rx_log, existing_rules);
246244

247245
let mut stats_interval = tokio::time::interval(tokio::time::Duration::from_secs(1));
248246

src/ui/app.rs

Lines changed: 1 addition & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,20 @@
1-
use crate::models::rule::{Action, Direction, Rule};
21
use crate::policy::PolicyRule;
32
use std::collections::HashMap;
4-
use std::net::{IpAddr, Ipv4Addr};
53
use tokio::sync::mpsc;
64

7-
#[allow(dead_code)]
8-
pub enum Command {
9-
AddRule(Rule),
10-
RemoveRule(Ipv4Addr),
11-
Unload,
12-
}
13-
145
pub struct App {
156
pub rules: Vec<PolicyRule>,
167
pub logs: Vec<String>,
17-
pub input: String,
18-
pub input_mode: bool,
19-
pub confirm_unload: bool,
20-
pub unload_requested: bool,
218
pub rule_stats: HashMap<String, (u64, u64)>,
22-
cmd_tx: mpsc::Sender<Command>,
239
log_rx: mpsc::Receiver<String>,
2410
}
2511

2612
impl App {
27-
pub fn new(
28-
cmd_tx: mpsc::Sender<Command>,
29-
log_rx: mpsc::Receiver<String>,
30-
initial_rules: Vec<PolicyRule>,
31-
) -> Self {
13+
pub fn new(log_rx: mpsc::Receiver<String>, initial_rules: Vec<PolicyRule>) -> Self {
3214
Self {
3315
rules: initial_rules,
3416
logs: vec![],
35-
input: String::new(),
36-
input_mode: false,
37-
confirm_unload: false,
38-
unload_requested: false,
3917
rule_stats: HashMap::new(),
40-
cmd_tx,
4118
log_rx,
4219
}
4320
}
@@ -54,51 +31,4 @@ impl App {
5431
}
5532
}
5633
}
57-
58-
pub async fn add_rule(&mut self, ip: &str, action: Action) -> bool {
59-
if let Ok(ip_addr) = ip.parse::<Ipv4Addr>() {
60-
let rule = Rule {
61-
ip: IpAddr::V4(ip_addr),
62-
subnet_mask: None,
63-
action: action.clone(),
64-
protocol: crate::models::rule::Protocol::Any,
65-
direction: Direction::Ingress,
66-
src_port: None,
67-
dst_port: None,
68-
};
69-
let policy_rule = PolicyRule {
70-
name: format!("rule_{}", ip),
71-
ip: ip.to_string(),
72-
action: match action {
73-
Action::Allow => "allow".to_string(),
74-
Action::Drop => "drop".to_string(),
75-
},
76-
description: None,
77-
protocol: "any".to_string(),
78-
direction: "ingress".to_string(),
79-
src_port: None,
80-
dst_port: None,
81-
};
82-
self.rules.push(policy_rule);
83-
self.cmd_tx.send(Command::AddRule(rule)).await.is_ok()
84-
} else {
85-
false
86-
}
87-
}
88-
89-
#[allow(dead_code)]
90-
pub async fn remove_rule(&mut self, ip: Ipv4Addr) -> bool {
91-
self.rules.retain(|r| r.ip != ip.to_string());
92-
self.cmd_tx.send(Command::RemoveRule(ip)).await.is_ok()
93-
}
94-
95-
pub async fn unload(&mut self) -> bool {
96-
match self.cmd_tx.send(Command::Unload).await {
97-
Ok(_) => {
98-
self.unload_requested = true;
99-
true
100-
}
101-
Err(_) => false,
102-
}
103-
}
10434
}

src/ui/events.rs

Lines changed: 3 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,57 +1,12 @@
1-
use crate::models::rule::Action;
21
use crate::ui::app::App;
32
use crossterm::event::{Event, KeyCode};
43

54
pub async fn handle_events(app: &mut App) -> anyhow::Result<bool> {
65
if crossterm::event::poll(std::time::Duration::from_millis(50))? {
76
if let Event::Key(key_event) = crossterm::event::read()? {
8-
if app.confirm_unload {
9-
match key_event.code {
10-
KeyCode::Char('Y') | KeyCode::Char('y') => {
11-
if app.unload().await {
12-
return Ok(true);
13-
} else {
14-
log::error!("Failed to send unload command");
15-
app.confirm_unload = false;
16-
}
17-
}
18-
KeyCode::Char('N') | KeyCode::Char('n') | KeyCode::Esc => {
19-
app.confirm_unload = false;
20-
}
21-
_ => {}
22-
}
23-
} else if app.input_mode {
24-
match key_event.code {
25-
KeyCode::Enter => {
26-
let action = Action::Drop;
27-
let input_copy = app.input.clone();
28-
let _ = app.add_rule(&input_copy, action).await;
29-
app.input.clear();
30-
app.input_mode = false;
31-
}
32-
KeyCode::Char(c) => {
33-
app.input.push(c);
34-
}
35-
KeyCode::Backspace => {
36-
app.input.pop();
37-
}
38-
KeyCode::Esc => {
39-
app.input.clear();
40-
app.input_mode = false;
41-
}
42-
_ => {}
43-
}
44-
} else {
45-
match key_event.code {
46-
KeyCode::Char('Q') => return Ok(true),
47-
KeyCode::Char('A') => {
48-
app.input_mode = true;
49-
}
50-
KeyCode::Char('U') => {
51-
app.confirm_unload = true;
52-
}
53-
_ => {}
54-
}
7+
match key_event.code {
8+
KeyCode::Char('Q') | KeyCode::Char('q') => return Ok(true),
9+
_ => {}
5510
}
5611
}
5712
}

src/ui/widgets.rs

Lines changed: 2 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use crate::ui::app::App;
2-
use ratatui::{prelude::*, text::ToText, widgets::*};
2+
use ratatui::{prelude::*, widgets::*};
33

44
pub fn render_ui(f: &mut Frame, app: &mut App) {
55
let chunks = Layout::default()
@@ -97,47 +97,12 @@ pub fn render_ui(f: &mut Frame, app: &mut App) {
9797
.block(Block::default().borders(Borders::ALL).title("Logs"));
9898
f.render_widget(logs, chunks[1]);
9999

100-
let help_text = "Shortcuts: [Q] Quit | Use 'firebee add' command to add rules";
100+
let help_text = "[Q] Quit | Manage rules via CLI: firebee add/delete/get";
101101
let help = Paragraph::new(help_text)
102102
.style(Style::default().fg(Color::Gray))
103103
.alignment(ratatui::layout::Alignment::Center)
104104
.block(Block::default().borders(Borders::ALL));
105105
f.render_widget(help, chunks[2]);
106-
107-
if app.input_mode {
108-
let popup = Paragraph::new(app.input.to_text()).block(
109-
Block::default()
110-
.borders(Borders::ALL)
111-
.title("Enter IP (e.g., 192.168.1.1)"),
112-
);
113-
let area = centered_rect(40, 20, f.area());
114-
f.render_widget(Clear, area);
115-
f.render_widget(popup, area);
116-
}
117-
118-
if app.confirm_unload {
119-
let warning_text = [
120-
"WARNING: This will unload the XDP firewall program!",
121-
"",
122-
"Are you sure you want to continue?",
123-
"",
124-
"Press Y to confirm, N or ESC to cancel",
125-
];
126-
127-
let popup = Paragraph::new(warning_text.join("\n"))
128-
.block(
129-
Block::default()
130-
.borders(Borders::ALL)
131-
.title("Confirm Unload")
132-
.style(Style::default().fg(Color::Red)),
133-
)
134-
.alignment(ratatui::layout::Alignment::Center)
135-
.wrap(Wrap { trim: true });
136-
137-
let area = centered_rect(60, 40, f.area());
138-
f.render_widget(Clear, area);
139-
f.render_widget(popup, area);
140-
}
141106
}
142107

143108
fn format_number(n: u64) -> String {
@@ -164,26 +129,6 @@ fn format_bytes(bytes: u64) -> String {
164129
}
165130
}
166131

167-
fn centered_rect(percent_x: u16, percent_y: u16, r: Rect) -> Rect {
168-
let popup_layout = Layout::default()
169-
.direction(Direction::Vertical)
170-
.constraints([
171-
Constraint::Percentage((100 - percent_y) / 2),
172-
Constraint::Percentage(percent_y),
173-
Constraint::Percentage((100 - percent_y) / 2),
174-
])
175-
.split(r);
176-
177-
Layout::default()
178-
.direction(Direction::Horizontal)
179-
.constraints([
180-
Constraint::Percentage((100 - percent_x) / 2),
181-
Constraint::Percentage(percent_x),
182-
Constraint::Percentage((100 - percent_x) / 2),
183-
])
184-
.split(popup_layout[1])[1]
185-
}
186-
187132
#[cfg(test)]
188133
mod tests {
189134
use super::*;

0 commit comments

Comments
 (0)