Skip to content

Commit

Permalink
refactor: restructre app tui components (#21)
Browse files Browse the repository at this point in the history
  • Loading branch information
pythops authored Sep 26, 2024
1 parent af7b78b commit c7d5fe0
Show file tree
Hide file tree
Showing 22 changed files with 1,235 additions and 1,071 deletions.
124 changes: 124 additions & 0 deletions oryx-tui/src/alerts/alert.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
use ratatui::{
layout::{Alignment, Constraint, Direction, Layout, Margin, Rect},
style::{Color, Style, Stylize},
text::{Line, Span, Text},
widgets::{Block, BorderType, Borders, Padding},
Frame,
};
use std::sync::{atomic::Ordering, Arc, Mutex};

use crate::packets::packet::AppPacket;

use super::syn_flood::SynFlood;

#[derive(Debug)]
pub struct Alert {
syn_flood: SynFlood,
pub flash_count: usize,
pub detected: bool,
}

impl Alert {
pub fn new(packets: Arc<Mutex<Vec<AppPacket>>>) -> Self {
Self {
syn_flood: SynFlood::new(packets),
flash_count: 1,
detected: false,
}
}

pub fn check(&mut self) {
if self.syn_flood.detected.load(Ordering::Relaxed) {
self.detected = true;
self.flash_count += 1;
} else {
self.detected = false;
self.flash_count = 1;
}
}

pub fn render(&self, frame: &mut Frame, block: Rect) {
frame.render_widget(
Block::default()
.title({
Line::from(vec![
Span::from(" Packet ").fg(Color::DarkGray),
Span::from(" Stats ").fg(Color::DarkGray),
{
if self.detected {
if self.flash_count % 12 == 0 {
Span::from(" Alert 󰐼 ").fg(Color::White).bg(Color::Red)
} else {
Span::from(" Alert 󰐼 ").bg(Color::Red)
}
} else {
Span::styled(
" Alert ",
Style::default().bg(Color::Green).fg(Color::White).bold(),
)
}
},
])
})
.title_alignment(Alignment::Left)
.padding(Padding::top(1))
.borders(Borders::ALL)
.style(Style::default())
.border_type(BorderType::default())
.border_style(Style::default().green()),
block.inner(Margin {
horizontal: 1,
vertical: 0,
}),
);

if !self.detected {
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 alerts").bold().centered();
frame.render_widget(text, text_block);
return;
}

let syn_flood_block = Layout::default()
.direction(Direction::Vertical)
.constraints([Constraint::Length(10), Constraint::Fill(1)])
.flex(ratatui::layout::Flex::SpaceBetween)
.margin(2)
.split(block)[0];

let syn_flood_block = Layout::default()
.direction(Direction::Vertical)
.constraints([
Constraint::Fill(1),
Constraint::Max(60),
Constraint::Fill(1),
])
.flex(ratatui::layout::Flex::SpaceBetween)
.margin(2)
.split(syn_flood_block)[1];

self.syn_flood.render(frame, syn_flood_block);
}

pub fn title_span(&self) -> Span<'_> {
if self.detected {
if self.flash_count % 12 == 0 {
Span::from(" Alert 󰐼 ").fg(Color::White).bg(Color::Red)
} else {
Span::from(" Alert 󰐼 ").fg(Color::Red)
}
} else {
Span::from(" Alert ").fg(Color::DarkGray)
}
}
}
146 changes: 146 additions & 0 deletions oryx-tui/src/alerts/syn_flood.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
use std::{
collections::HashMap,
net::IpAddr,
sync::{atomic::AtomicBool, Arc, Mutex},
thread,
time::Duration,
};

use ratatui::{
layout::{Alignment, Constraint, Flex, Rect},
style::{Style, Stylize},
text::Line,
widgets::{Block, Borders, Row, Table},
Frame,
};

use crate::packets::{
network::{IpPacket, IpProto},
packet::AppPacket,
};

const WIN_SIZE: usize = 100_000;

#[derive(Debug)]
pub struct SynFlood {
pub detected: Arc<AtomicBool>,
pub map: Arc<Mutex<HashMap<IpAddr, usize>>>,
}

impl SynFlood {
pub fn new(packets: Arc<Mutex<Vec<AppPacket>>>) -> Self {
let map: Arc<Mutex<HashMap<IpAddr, usize>>> = Arc::new(Mutex::new(HashMap::new()));

let detected = Arc::new(AtomicBool::new(false));

thread::spawn({
let packets = packets.clone();
let map = map.clone();
let detected = detected.clone();
move || loop {
let start_index = {
let packets = packets.lock().unwrap();
packets.len().saturating_sub(1)
};
thread::sleep(Duration::from_secs(5));
let app_packets = {
let packets = packets.lock().unwrap();
packets.clone()
};

let mut map = map.lock().unwrap();
map.clear();

if app_packets.len() < WIN_SIZE {
continue;
}

let mut nb_syn_packets = 0;

app_packets[start_index..app_packets.len().saturating_sub(1)]
.iter()
.for_each(|packet| {
if let AppPacket::Ip(ip_packet) = packet {
if let IpPacket::V4(ipv4_packet) = ip_packet {
if let IpProto::Tcp(tcp_packet) = ipv4_packet.proto {
if tcp_packet.syn == 1 {
nb_syn_packets += 1;
if let Some(count) =
map.get_mut(&IpAddr::V4(ipv4_packet.src_ip))
{
*count += 1;
} else {
map.insert(IpAddr::V4(ipv4_packet.src_ip), 1);
}
}
}
}
if let IpPacket::V6(ipv6_packet) = ip_packet {
if let IpProto::Tcp(tcp_packet) = ipv6_packet.proto {
if tcp_packet.syn == 1 {
nb_syn_packets += 1;
if let Some(count) =
map.get_mut(&IpAddr::V6(ipv6_packet.src_ip))
{
*count += 1;
} else {
map.insert(IpAddr::V6(ipv6_packet.src_ip), 1);
}
}
}
}
}
});

if (nb_syn_packets as f64 / WIN_SIZE as f64) > 0.45 {
detected.store(true, std::sync::atomic::Ordering::Relaxed);
} else {
detected.store(false, std::sync::atomic::Ordering::Relaxed);
}
}
});

Self { map, detected }
}

pub fn render(&self, frame: &mut Frame, block: Rect) {
let mut ips: Vec<(IpAddr, usize)> = {
let map = self.map.lock().unwrap();
map.clone().into_iter().collect()
};
ips.sort_by(|a, b| b.1.cmp(&a.1));

ips.retain(|(_, count)| *count > 10_000);

let top_3_ips = ips.into_iter().take(3);

let widths = [Constraint::Min(30), Constraint::Min(20)];

let rows = top_3_ips.map(|(ip, count)| {
Row::new(vec![
Line::from(ip.to_string()).centered().bold(),
Line::from(count.to_string()).centered(),
])
});
let table = Table::new(rows, widths)
.column_spacing(2)
.flex(Flex::SpaceBetween)
.header(
Row::new(vec![
Line::from("IP Address").centered(),
Line::from("Number of SYN packets").centered(),
])
.style(Style::new().bold())
.bottom_margin(1),
)
.block(
Block::new()
.title(" SYN Flood Attack ")
.borders(Borders::all())
.border_style(Style::new().yellow())
.title_alignment(Alignment::Center),
);

frame.render_widget(table, block);
}
}
Loading

0 comments on commit c7d5fe0

Please sign in to comment.