From 890406a4dab19c3713adf1f29740285adb7738c9 Mon Sep 17 00:00:00 2001 From: ParrotXray Date: Thu, 28 May 2026 18:52:00 +0800 Subject: [PATCH] feat: skip XSK redirect for non-TCP/UDP and malformed packets and add Zeek-style server port heuristic for flow direction (#17) --- ingress-ebpf/src/main.rs | 16 ++-- mantis/src/detection/ml/flow_tracker.rs | 12 ++- mantis/src/detection/ml/mod.rs | 1 + mantis/src/detection/ml/server_ports.rs | 114 ++++++++++++++++++++++++ 4 files changed, 135 insertions(+), 8 deletions(-) create mode 100644 mantis/src/detection/ml/server_ports.rs diff --git a/ingress-ebpf/src/main.rs b/ingress-ebpf/src/main.rs index 1969d90..15afd65 100644 --- a/ingress-ebpf/src/main.rs +++ b/ingress-ebpf/src/main.rs @@ -24,21 +24,25 @@ static INGRESS_XSKS_MAP: XskMap = XskMap::pinned(64, 0); #[xdp] pub fn mantis(ctx: XdpContext) -> u32 { unsafe { - let _ = packet_intake(&ctx); - let _ = PROGRAM_ARRAY.tail_call(&ctx, TRANSMISSION); - xdp_action::XDP_PASS + match packet_intake(&ctx) { + Err(_) => xdp_action::XDP_PASS, + Ok(_) => { + let _ = PROGRAM_ARRAY.tail_call(&ctx, TRANSMISSION); + xdp_action::XDP_PASS + } + } } } #[inline(always)] -unsafe fn packet_intake(ctx: &XdpContext) -> Result { +unsafe fn packet_intake(ctx: &XdpContext) -> Result<(), ()> { unsafe { let start = ctx.data(); let end = ctx.data_end(); let ptr = PARSED_PACKET.get_ptr_mut(0).ok_or(())?; parsing::parse_packet(start, end, ptr)?; let _ = PROGRAM_ARRAY.tail_call(ctx, ACCESS_CONTROL); - Err(()) + Ok(()) } } @@ -124,4 +128,4 @@ pub fn transmission(ctx: XdpContext) -> u32 { #[panic_handler] fn panic(_info: &core::panic::PanicInfo) -> ! { unsafe { core::hint::unreachable_unchecked() } -} +} \ No newline at end of file diff --git a/mantis/src/detection/ml/flow_tracker.rs b/mantis/src/detection/ml/flow_tracker.rs index bf76fad..498292f 100644 --- a/mantis/src/detection/ml/flow_tracker.rs +++ b/mantis/src/detection/ml/flow_tracker.rs @@ -6,6 +6,8 @@ use common::model::event::Event; use crate::model::direction::Direction; use crate::model::ml_detection::{BulkState, FlowKey, PacketData}; +use super::server_ports; + #[derive(Debug, Clone)] pub struct FlowData { pub flow_key: FlowKey, @@ -280,7 +282,13 @@ impl FlowTracker { match detect_initiator(payload, packet_key.protocol, packet_key.src_port, packet_key.dst_port) { Some(true) => (packet_key, true), Some(false) => (reversed_key, false), - None => (packet_key, true), + None => { + if server_ports::is_server_port(packet_key.src_port, packet_key.protocol) { + (reversed_key, false) + } else { + (packet_key, true) + } + } } } }; @@ -366,4 +374,4 @@ fn detect_initiator(payload: &[u8], protocol: u8, src_port: u16, dst_port: u16) } None -} +} \ No newline at end of file diff --git a/mantis/src/detection/ml/mod.rs b/mantis/src/detection/ml/mod.rs index dd7d715..3f8db88 100644 --- a/mantis/src/detection/ml/mod.rs +++ b/mantis/src/detection/ml/mod.rs @@ -6,3 +6,4 @@ pub mod flow_tracker; pub mod inference; pub mod model_loader; pub mod traffic_logger; +pub mod server_ports; diff --git a/mantis/src/detection/ml/server_ports.rs b/mantis/src/detection/ml/server_ports.rs new file mode 100644 index 0000000..d33a7a1 --- /dev/null +++ b/mantis/src/detection/ml/server_ports.rs @@ -0,0 +1,114 @@ +// Well-known server-side ports derived from Zeek's likely_server_ports set, +// IANA well-known port assignments, and common production services. +// +// If a packet's src_port is in this list, the packet originated from the server +// (responder), so the flow key should be flipped to put the client as initiator. +// +// Source: Zeek protocol analyzers + IANA well-known ports (0-1023) + common practice. +// Arrays must remain sorted for binary_search to work correctly. + +const TCP: &[u16] = &[ + 20, // FTP data + 21, // FTP control + 22, // SSH + 23, // Telnet + 25, // SMTP + 53, // DNS + 80, // HTTP + 81, // HTTP alt + 88, // Kerberos + 110, // POP3 + 119, // NNTP + 135, // MS RPC + 139, // NetBIOS / SMB + 143, // IMAP + 179, // BGP + 389, // LDAP + 443, // HTTPS + 445, // SMB/CIFS + 465, // SMTPS + 502, // Modbus + 514, // Syslog + 563, // NNTPS + 585, // IMAP4+SSL (legacy) + 587, // SMTP submission + 614, // SSLshell + 631, // IPP (CUPS) + 636, // LDAPS + 989, // FTPS data + 990, // FTPS control + 992, // Telnet/SSL + 993, // IMAPS + 995, // POP3S + 1080, // SOCKS + 1194, // OpenVPN + 1433, // Microsoft SQL Server + 1434, // MSSQL monitor + 1521, // Oracle DB + 1883, // MQTT + 2049, // NFS + 2811, // FTP alt + 3128, // HTTP proxy + 3268, // LDAP Global Catalog + 3306, // MySQL / MariaDB + 3389, // RDP + 3690, // SVN + 5222, // XMPP client + 5223, // XMPP/Apple Push over SSL + 5269, // XMPP server + 5432, // PostgreSQL + 5900, // VNC + 6379, // Redis + 6443, // Kubernetes API + 6666, // IRC + 6667, // IRC + 6668, // IRC + 6669, // IRC + 7001, // Oracle WebLogic + 8000, // HTTP dev/alt + 8080, // HTTP alt + 8443, // HTTPS alt + 8883, // MQTT over TLS + 8888, // HTTP alt + 9092, // Kafka broker + 9200, // Elasticsearch HTTP + 9300, // Elasticsearch cluster + 20000, // DNP3 + 27017, // MongoDB + 27018, // MongoDB shard +]; + +const UDP: &[u16] = &[ + 53, // DNS + 67, // DHCP server + 69, // TFTP + 88, // Kerberos + 123, // NTP + 137, // NetBIOS Name Service + 161, // SNMP + 162, // SNMP traps + 389, // LDAP + 443, // QUIC / HTTP3 + 500, // IKE / IPsec + 514, // Syslog + 520, // RIP + 1194, // OpenVPN + 1812, // RADIUS authentication + 1813, // RADIUS accounting + 3389, // RDP over UDP + 4011, // PXE / ProxyDHCP + 4500, // IPsec NAT-T + 5060, // SIP + 5353, // mDNS + 5355, // LLMNR + 20000, // DNP3 + 51820, // WireGuard +]; + +pub fn is_server_port(port: u16, protocol: u8) -> bool { + match protocol { + 6 => TCP.binary_search(&port).is_ok(), + 17 => UDP.binary_search(&port).is_ok(), + _ => false, + } +} \ No newline at end of file