Features: - High-performance packet filtering via eBPF/XDP - Instant blocklist with dynamic CLI management - Exact-match rules with Drop/Pass/Log actions - CIDR-based IP range dropping via LPM trie - Token-bucket rate limiting (IP-based and flow-based) - Auto temp bans for rate limit violators - Real-time event logging via BPF ring buffer - Interactive TUI monitor with live stats Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
85 lines
2.3 KiB
Rust
85 lines
2.3 KiB
Rust
use std::collections::VecDeque;
|
|
|
|
use xdp_firewall_common::{FirewallEvent, RuleKey, RuleValue, Stats};
|
|
|
|
#[derive(Clone, Copy, PartialEq, Eq)]
|
|
pub enum Pane {
|
|
Rules,
|
|
Blocklist,
|
|
Logs,
|
|
}
|
|
|
|
pub struct App {
|
|
pub iface: String,
|
|
pub quit: bool,
|
|
pub pane: Pane,
|
|
pub global_stats: Stats,
|
|
pub prev_global_stats: Stats,
|
|
pub rules: Vec<(RuleKey, RuleValue, Stats)>,
|
|
pub blocklist: Vec<(u32, u64)>,
|
|
pub logs: VecDeque<FirewallEvent>,
|
|
pub max_logs: usize,
|
|
pub pps: u64,
|
|
pub bps: u64,
|
|
pub dialog: Dialog,
|
|
pub rules_selected: usize,
|
|
pub blocklist_selected: usize,
|
|
}
|
|
|
|
#[derive(Clone, Default, PartialEq, Eq)]
|
|
pub enum Dialog {
|
|
#[default]
|
|
None,
|
|
BlockIp { input: String },
|
|
AddRule { src_ip: String, dst_ip: String, proto: String, src_port: String, dst_port: String, action: String },
|
|
ConfirmDelete,
|
|
}
|
|
|
|
impl App {
|
|
pub fn new(iface: String) -> Self {
|
|
Self {
|
|
iface,
|
|
quit: false,
|
|
pane: Pane::Rules,
|
|
global_stats: Stats::default(),
|
|
prev_global_stats: Stats::default(),
|
|
rules: Vec::new(),
|
|
blocklist: Vec::new(),
|
|
logs: VecDeque::new(),
|
|
max_logs: 100,
|
|
pps: 0,
|
|
bps: 0,
|
|
dialog: Dialog::None,
|
|
rules_selected: 0,
|
|
blocklist_selected: 0,
|
|
}
|
|
}
|
|
|
|
pub fn on_tick(&mut self, global: Stats, rules: Vec<(RuleKey, RuleValue, Stats)>, blocklist: Vec<(u32, u64)>) {
|
|
let elapsed = 0.1;
|
|
let pkts = global.packets.saturating_sub(self.prev_global_stats.packets);
|
|
let bytes = global.bytes.saturating_sub(self.prev_global_stats.bytes);
|
|
self.pps = (pkts as f64 / elapsed) as u64;
|
|
self.bps = (bytes as f64 / elapsed) as u64;
|
|
self.prev_global_stats = self.global_stats;
|
|
self.global_stats = global;
|
|
self.rules = rules;
|
|
self.blocklist = blocklist;
|
|
}
|
|
|
|
pub fn add_log(&mut self, ev: FirewallEvent) {
|
|
if self.logs.len() >= self.max_logs {
|
|
self.logs.pop_back();
|
|
}
|
|
self.logs.push_front(ev);
|
|
}
|
|
|
|
pub fn selected_rule_id(&self) -> Option<u32> {
|
|
self.rules.get(self.rules_selected).map(|(_, v, _)| v.rule_id)
|
|
}
|
|
|
|
pub fn selected_block_ip(&self) -> Option<u32> {
|
|
self.blocklist.get(self.blocklist_selected).map(|(ip, _)| *ip)
|
|
}
|
|
}
|