mirror of
https://github.com/ParrotXray/Mantis.git
synced 2026-08-24 19:00:27 +09:00
Add IPv6 support to TCP flag anomaly detection
- TcpAnomalyEvent: replace src_ip/dst_ip u32 with [u64; 2] to encode both IPv4 and IPv6 without u128 alignment issues; rename _pad to is_ipv6 - ingress/egress anomaly.rs: add ipv6_check_and_emit using hi/lo u64 split - ingress/egress main.rs: call ipv6_check_and_emit in IPv6 XDP arm and drop on hit - alert.rs: update from_tcp_anomaly to branch on is_ipv6 and reconstruct u128 from [hi, lo] before formatting; import format_ipv6 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0138PxtKH73hqxv7h1oaoSdS
This commit is contained in:
parent
701e329c2d
commit
784f360a52
@ -8,18 +8,21 @@ pub const DIRECTION_EGRESS: u8 = 1;
|
||||
|
||||
/// TCP flag anomaly event written to the ring buffer by XDP programs.
|
||||
///
|
||||
/// align_of::<TcpAnomalyEvent>() == 8, satisfying the aya ring buffer constraint
|
||||
/// that 8 % align_of::<T>() == 0.
|
||||
/// src_ip/dst_ip are stored as [hi_u64, lo_u64] (big-endian halves of the address).
|
||||
/// For IPv4: src_ip = [v4_addr as u64, 0], is_ipv6 = 0.
|
||||
/// For IPv6: src_ip = [addr >> 64, addr as u64], is_ipv6 = 1.
|
||||
///
|
||||
/// align_of == 8, satisfying the aya ring buffer constraint (8 % 8 == 0).
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct TcpAnomalyEvent {
|
||||
pub timestamp_ns: u64,
|
||||
pub src_ip: u32,
|
||||
pub dst_ip: u32,
|
||||
pub src_ip: [u64; 2],
|
||||
pub dst_ip: [u64; 2],
|
||||
pub src_port: u16,
|
||||
pub dst_port: u16,
|
||||
pub flags: u8,
|
||||
pub anomaly_type: u8,
|
||||
pub direction: u8,
|
||||
pub _pad: u8,
|
||||
pub is_ipv6: u8,
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
use aya_ebpf::macros::map;
|
||||
use aya_ebpf::maps::RingBuf;
|
||||
use common::model::event::IPv4Event;
|
||||
use common::model::event::{IPv4Event, IPv6Event};
|
||||
use common::model::tcp_anomaly::{
|
||||
ANOMALY_NULL_SCAN, ANOMALY_RST_SYN, ANOMALY_SYN_FIN, ANOMALY_XMAS_SCAN, DIRECTION_EGRESS,
|
||||
TcpAnomalyEvent,
|
||||
@ -10,41 +10,69 @@ use network_types::ip::IpProto;
|
||||
#[map]
|
||||
pub static TCP_ANOMALY_EVENTS: RingBuf = RingBuf::with_byte_size(1 << 18, 0);
|
||||
|
||||
/// Returns true if a TCP flag anomaly was detected (and emitted to the ring buffer).
|
||||
#[inline(always)]
|
||||
fn anomaly_type(flags_byte: u8) -> Option<u8> {
|
||||
if flags_byte == 0 {
|
||||
Some(ANOMALY_NULL_SCAN)
|
||||
} else if flags_byte & 0x03 == 0x03 {
|
||||
Some(ANOMALY_SYN_FIN)
|
||||
} else if flags_byte & 0x29 == 0x29 {
|
||||
Some(ANOMALY_XMAS_SCAN)
|
||||
} else if flags_byte & 0x06 == 0x06 {
|
||||
Some(ANOMALY_RST_SYN)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn emit(ev: TcpAnomalyEvent) {
|
||||
if let Some(mut entry) = TCP_ANOMALY_EVENTS.reserve::<TcpAnomalyEvent>(0) {
|
||||
entry.write(ev);
|
||||
entry.submit(0);
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns true if a TCP flag anomaly was detected and emitted to the ring buffer.
|
||||
#[inline(always)]
|
||||
pub fn ipv4_check_and_emit(event: &IPv4Event) -> bool {
|
||||
if event.protocol != IpProto::Tcp {
|
||||
return false;
|
||||
}
|
||||
let flags_byte = event.tcp_flags.to_byte();
|
||||
let anomaly_type = if flags_byte == 0 {
|
||||
ANOMALY_NULL_SCAN
|
||||
} else if flags_byte & 0x03 == 0x03 {
|
||||
ANOMALY_SYN_FIN
|
||||
} else if flags_byte & 0x29 == 0x29 {
|
||||
ANOMALY_XMAS_SCAN
|
||||
} else if flags_byte & 0x06 == 0x06 {
|
||||
ANOMALY_RST_SYN
|
||||
} else {
|
||||
return false;
|
||||
};
|
||||
|
||||
let ev = TcpAnomalyEvent {
|
||||
let Some(anomaly_type) = anomaly_type(flags_byte) else { return false };
|
||||
emit(TcpAnomalyEvent {
|
||||
timestamp_ns: event.timestamp_us.wrapping_mul(1000),
|
||||
src_ip: event.src_ip,
|
||||
dst_ip: event.dst_ip,
|
||||
src_ip: [event.src_ip as u64, 0],
|
||||
dst_ip: [event.dst_ip as u64, 0],
|
||||
src_port: event.src_port,
|
||||
dst_port: event.dst_port,
|
||||
flags: flags_byte,
|
||||
anomaly_type,
|
||||
direction: DIRECTION_EGRESS,
|
||||
_pad: 0,
|
||||
};
|
||||
|
||||
if let Some(mut entry) = TCP_ANOMALY_EVENTS.reserve::<TcpAnomalyEvent>(0) {
|
||||
entry.write(ev);
|
||||
entry.submit(0);
|
||||
}
|
||||
|
||||
is_ipv6: 0,
|
||||
});
|
||||
true
|
||||
}
|
||||
|
||||
/// Returns true if a TCP flag anomaly was detected and emitted to the ring buffer.
|
||||
#[inline(always)]
|
||||
pub fn ipv6_check_and_emit(event: &IPv6Event) -> bool {
|
||||
if event.protocol != IpProto::Tcp {
|
||||
return false;
|
||||
}
|
||||
let flags_byte = event.tcp_flags.to_byte();
|
||||
let Some(anomaly_type) = anomaly_type(flags_byte) else { return false };
|
||||
emit(TcpAnomalyEvent {
|
||||
timestamp_ns: event.timestamp_us.wrapping_mul(1000),
|
||||
src_ip: [(event.src_ip >> 64) as u64, event.src_ip as u64],
|
||||
dst_ip: [(event.dst_ip >> 64) as u64, event.dst_ip as u64],
|
||||
src_port: event.src_port,
|
||||
dst_port: event.dst_port,
|
||||
flags: flags_byte,
|
||||
anomaly_type,
|
||||
direction: DIRECTION_EGRESS,
|
||||
is_ipv6: 1,
|
||||
});
|
||||
true
|
||||
}
|
||||
|
||||
@ -80,6 +80,9 @@ unsafe fn try_access_control(ctx: &XdpContext) -> Result<u32, ()> {
|
||||
if access_control::ipv6_is_blacklisted(event) {
|
||||
return Ok(xdp_action::XDP_DROP);
|
||||
}
|
||||
if anomaly::ipv6_check_and_emit(event) {
|
||||
return Ok(xdp_action::XDP_DROP);
|
||||
}
|
||||
}
|
||||
}
|
||||
let _ = PROGRAM_ARRAY.tail_call(ctx, STATISTICS);
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
use aya_ebpf::macros::map;
|
||||
use aya_ebpf::maps::RingBuf;
|
||||
use common::model::event::IPv4Event;
|
||||
use common::model::event::{IPv4Event, IPv6Event};
|
||||
use common::model::tcp_anomaly::{
|
||||
ANOMALY_NULL_SCAN, ANOMALY_RST_SYN, ANOMALY_SYN_FIN, ANOMALY_XMAS_SCAN, DIRECTION_INGRESS,
|
||||
TcpAnomalyEvent,
|
||||
@ -10,41 +10,69 @@ use network_types::ip::IpProto;
|
||||
#[map]
|
||||
pub static TCP_ANOMALY_EVENTS: RingBuf = RingBuf::with_byte_size(1 << 18, 0);
|
||||
|
||||
/// Returns true if a TCP flag anomaly was detected (and emitted to the ring buffer).
|
||||
#[inline(always)]
|
||||
fn anomaly_type(flags_byte: u8) -> Option<u8> {
|
||||
if flags_byte == 0 {
|
||||
Some(ANOMALY_NULL_SCAN)
|
||||
} else if flags_byte & 0x03 == 0x03 {
|
||||
Some(ANOMALY_SYN_FIN)
|
||||
} else if flags_byte & 0x29 == 0x29 {
|
||||
Some(ANOMALY_XMAS_SCAN)
|
||||
} else if flags_byte & 0x06 == 0x06 {
|
||||
Some(ANOMALY_RST_SYN)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn emit(ev: TcpAnomalyEvent) {
|
||||
if let Some(mut entry) = TCP_ANOMALY_EVENTS.reserve::<TcpAnomalyEvent>(0) {
|
||||
entry.write(ev);
|
||||
entry.submit(0);
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns true if a TCP flag anomaly was detected and emitted to the ring buffer.
|
||||
#[inline(always)]
|
||||
pub fn ipv4_check_and_emit(event: &IPv4Event) -> bool {
|
||||
if event.protocol != IpProto::Tcp {
|
||||
return false;
|
||||
}
|
||||
let flags_byte = event.tcp_flags.to_byte();
|
||||
let anomaly_type = if flags_byte == 0 {
|
||||
ANOMALY_NULL_SCAN
|
||||
} else if flags_byte & 0x03 == 0x03 {
|
||||
ANOMALY_SYN_FIN
|
||||
} else if flags_byte & 0x29 == 0x29 {
|
||||
ANOMALY_XMAS_SCAN
|
||||
} else if flags_byte & 0x06 == 0x06 {
|
||||
ANOMALY_RST_SYN
|
||||
} else {
|
||||
return false;
|
||||
};
|
||||
|
||||
let ev = TcpAnomalyEvent {
|
||||
let Some(anomaly_type) = anomaly_type(flags_byte) else { return false };
|
||||
emit(TcpAnomalyEvent {
|
||||
timestamp_ns: event.timestamp_us.wrapping_mul(1000),
|
||||
src_ip: event.src_ip,
|
||||
dst_ip: event.dst_ip,
|
||||
src_ip: [event.src_ip as u64, 0],
|
||||
dst_ip: [event.dst_ip as u64, 0],
|
||||
src_port: event.src_port,
|
||||
dst_port: event.dst_port,
|
||||
flags: flags_byte,
|
||||
anomaly_type,
|
||||
direction: DIRECTION_INGRESS,
|
||||
_pad: 0,
|
||||
};
|
||||
|
||||
if let Some(mut entry) = TCP_ANOMALY_EVENTS.reserve::<TcpAnomalyEvent>(0) {
|
||||
entry.write(ev);
|
||||
entry.submit(0);
|
||||
}
|
||||
|
||||
is_ipv6: 0,
|
||||
});
|
||||
true
|
||||
}
|
||||
|
||||
/// Returns true if a TCP flag anomaly was detected and emitted to the ring buffer.
|
||||
#[inline(always)]
|
||||
pub fn ipv6_check_and_emit(event: &IPv6Event) -> bool {
|
||||
if event.protocol != IpProto::Tcp {
|
||||
return false;
|
||||
}
|
||||
let flags_byte = event.tcp_flags.to_byte();
|
||||
let Some(anomaly_type) = anomaly_type(flags_byte) else { return false };
|
||||
emit(TcpAnomalyEvent {
|
||||
timestamp_ns: event.timestamp_us.wrapping_mul(1000),
|
||||
src_ip: [(event.src_ip >> 64) as u64, event.src_ip as u64],
|
||||
dst_ip: [(event.dst_ip >> 64) as u64, event.dst_ip as u64],
|
||||
src_port: event.src_port,
|
||||
dst_port: event.dst_port,
|
||||
flags: flags_byte,
|
||||
anomaly_type,
|
||||
direction: DIRECTION_INGRESS,
|
||||
is_ipv6: 1,
|
||||
});
|
||||
true
|
||||
}
|
||||
|
||||
@ -82,6 +82,9 @@ unsafe fn try_access_control(ctx: &XdpContext) -> Result<u32, ()> {
|
||||
if access_control::ipv6_is_blacklisted(event) {
|
||||
return Ok(xdp_action::XDP_DROP);
|
||||
}
|
||||
if anomaly::ipv6_check_and_emit(event) {
|
||||
return Ok(xdp_action::XDP_DROP);
|
||||
}
|
||||
}
|
||||
}
|
||||
let _ = PROGRAM_ARRAY.tail_call(ctx, STATISTICS);
|
||||
|
||||
@ -6,7 +6,7 @@ use serde::Serialize;
|
||||
|
||||
use crate::model::ml_detection::DetectionResult;
|
||||
use crate::model::rule_detection::RuleMatch;
|
||||
use crate::utils::packet_parser::format_ipv4;
|
||||
use crate::utils::packet_parser::{format_ipv4, format_ipv6};
|
||||
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
@ -127,8 +127,16 @@ impl UnifiedAlert {
|
||||
}
|
||||
|
||||
pub fn from_tcp_anomaly(event: &TcpAnomalyEvent) -> Self {
|
||||
let src_ip = format_ipv4(event.src_ip).to_string();
|
||||
let dst_ip = format_ipv4(event.dst_ip).to_string();
|
||||
let (src_ip, dst_ip) = if event.is_ipv6 == 0 {
|
||||
(
|
||||
format_ipv4(event.src_ip[0] as u32).to_string(),
|
||||
format_ipv4(event.dst_ip[0] as u32).to_string(),
|
||||
)
|
||||
} else {
|
||||
let src_v6 = (event.src_ip[0] as u128) << 64 | event.src_ip[1] as u128;
|
||||
let dst_v6 = (event.dst_ip[0] as u128) << 64 | event.dst_ip[1] as u128;
|
||||
(format_ipv6(src_v6).to_string(), format_ipv6(dst_v6).to_string())
|
||||
};
|
||||
let flow_key = format!("{}:{}-{}:{}", src_ip, event.src_port, dst_ip, event.dst_port);
|
||||
let direction = if event.direction == DIRECTION_INGRESS { "ingress" } else { "egress" };
|
||||
let attack_type = match event.anomaly_type {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user