refactor: Remove Ebpf* type, replace with normal struct with Pod trait (#2)

This commit is contained in:
DaLaw2 2025-08-30 03:38:12 +08:00 committed by GitHub
parent f021c8ebe2
commit d01045e74a
36 changed files with 353 additions and 630 deletions

1
Cargo.lock generated
View File

@ -1193,6 +1193,7 @@ version = "0.1.0"
dependencies = [
"aya",
"network-types",
"serde",
]
[[package]]

View File

@ -20,6 +20,8 @@ libc = { version = "0.2.159", default-features = false }
log = { version = "0.4.22", default-features = false }
tokio = { version = "1.40.0", default-features = false }
which = { version = "7.0.0", default-features = false }
serde = { version = "1.0.215", features = ["derive"] }
network-types = "0.0.7"
[profile.dev]
panic = "abort"

View File

@ -5,11 +5,12 @@ edition = "2024"
[features]
default = []
user = ["aya"]
user = ["aya", "serde"]
[dependencies]
aya = { workspace = true, optional = true }
network-types = "0.0.7"
serde = { workspace = true, optional = true }
network-types = { workspace = true }
[lib]
path = "src/lib.rs"

View File

@ -1,5 +1,8 @@
#![no_std]
#[cfg(feature = "user")]
extern crate std;
pub mod model;
/// Maximum number of statistics entries that can be stored

View File

@ -1,5 +1,5 @@
use crate::model::ip_address::{AddrPortV4, AddrPortV6};
use network_types::eth::EtherType;
use crate::model::ip_address::{EbpfAddrPortV4, EbpfAddrPortV6};
use network_types::ip::IpProto;
pub struct Event {
@ -10,7 +10,7 @@ pub struct Event {
pub source_port: u16,
pub destination_port: u16,
pub len: u32,
pub timestamp: u64
pub timestamp: u64,
}
impl Event {
@ -74,18 +74,24 @@ pub struct IPv4Event {
pub source_port: u16,
pub destination_port: u16,
pub len: u32,
pub timestamp: u64
pub timestamp: u64,
}
impl IPv4Event {
#[inline(always)]
pub fn get_source(&self) -> EbpfAddrPortV4 {
[self.source_ip, self.source_port as u32]
pub fn get_source(&self) -> AddrPortV4 {
AddrPortV4 {
ip: self.source_ip,
port: self.source_port,
}
}
#[inline(always)]
pub fn get_destination(&self) -> EbpfAddrPortV4 {
[self.destination_ip, self.destination_port as u32]
pub fn get_destination(&self) -> AddrPortV4 {
AddrPortV4 {
ip: self.destination_ip,
port: self.destination_port,
}
}
}
@ -96,17 +102,23 @@ pub struct IPv6Event {
pub source_port: u16,
pub destination_port: u16,
pub len: u32,
pub timestamp: u64
pub timestamp: u64,
}
impl IPv6Event {
#[inline(always)]
pub fn get_source(&self) -> EbpfAddrPortV6 {
[self.source_ip, self.source_port as u128]
pub fn get_source(&self) -> AddrPortV6 {
AddrPortV6 {
ip: self.source_ip,
port: self.source_port,
}
}
#[inline(always)]
pub fn get_destination(&self) -> EbpfAddrPortV6 {
[self.destination_ip, self.destination_port as u128]
pub fn get_destination(&self) -> AddrPortV6 {
AddrPortV6 {
ip: self.destination_ip,
port: self.destination_port,
}
}
}

View File

@ -1,37 +1,26 @@
/// Network flow statistics tracking bytes, packets count, and timing
///
/// # Layout
/// ```text
/// [0]: Total bytes count (u64)
/// [1]: Total packets count (u64)
/// [2]: Last seen timestamp (u64) in nanoseconds from system boot
/// ```
///
/// # Example
/// ```ignore
/// // Create new flow status
/// let now = bpf_ktime_get_ns();
/// let status: FlowStats = [
/// 1500, // 1500 bytes
/// 1, // 1 packet
/// now, // Current timestamp
/// ];
///
/// // Update existing flow
/// status[0] += packet_size; // Add bytes
/// status[1] += 1; // Increment packet count
/// status[2] = new_timestamp; // Update last seen
/// ```
///
/// # Notes
/// - All counters are monotonically increasing
/// - Timestamp uses kernel time (bpf_ktime_get_ns)
/// - Counters may wrap around on very high traffic flows
///
/// # Memory Layout
/// ```text
/// [0]: [------------------- Bytes (64 bits) ------------------]
/// [1]: [------------------ Packets (64 bits) -----------------]
/// [2]: [----------------- Timestamp (64 bits) ----------------]
/// ```
pub type EbpfFlowStats = [u64; 3];
#[cfg(feature = "user")]
use aya::Pod;
#[cfg(feature = "user")]
use serde::Serialize;
#[repr(C, align(8))]
#[derive(Clone, Copy)]
#[cfg_attr(feature = "user", derive(Serialize, Debug))]
pub struct FlowStats {
pub bytes: u64,
pub packets: u64,
pub last_seen: u64,
}
impl FlowStats {
pub fn new(bytes: u64, packets: u64, last_seen: u64) -> Self {
Self {
bytes,
packets,
last_seen,
}
}
}
#[cfg(feature = "user")]
unsafe impl Pod for FlowStats {}

View File

@ -1,22 +1,55 @@
/// HTTP methods bitmap type for eBPF programs.
///
/// Each bit represents whether a specific HTTP method is allowed:
/// - Bit 0: GET (0b0000_0000_0000_0001)
/// - Bit 1: POST (0b0000_0000_0000_0010)
/// - Bit 2: PUT (0b0000_0000_0000_0100)
/// - Bit 3: DELETE (0b0000_0000_0000_1000)
/// - Bit 4: HEAD (0b0000_0000_0001_0000)
/// - Bit 5: OPTIONS (0b0000_0000_0010_0000)
/// - Bit 6: PATCH (0b0000_0000_0100_0000)
/// - Bit 7: TRACE (0b0000_0000_1000_0000)
/// - Bit 8: CONNECT (0b0000_0001_0000_0000)
///
/// # Examples
/// ```ignore
/// // Allow GET and POST
/// let methods: EbpfHttpMethod = 0b0000_0000_0000_0011;
///
/// // Allow all common methods (GET, POST, PUT, DELETE, PATCH)
/// let methods: EbpfHttpMethod = 0b0000_0000_0100_1111;
/// ```
pub type EbpfHttpMethod = u16;
#[cfg(feature = "user")]
use serde::{Deserialize, Serialize};
#[cfg(all(feature = "user"))]
use std::vec::Vec;
pub type HttpMethodBitmap = u16;
#[derive(Copy, Clone)]
#[cfg_attr(feature = "user", derive(Serialize, Deserialize, Debug, Eq, PartialEq))]
pub enum HttpMethod {
GET = 0b0000_0000_0000_0001,
POST = 0b0000_0000_0000_0010,
PUT = 0b0000_0000_0000_0100,
DELETE = 0b0000_0000_0000_1000,
HEAD = 0b0000_0000_0001_0000,
OPTIONS = 0b0000_0000_0010_0000,
PATCH = 0b0000_0000_0100_0000,
TRACE = 0b0000_0000_1000_0000,
CONNECT = 0b0000_0001_0000_0000,
}
#[cfg(feature = "user")]
impl HttpMethod {
pub fn convert_from_bitmap(http_method_bitmap: HttpMethodBitmap) -> Vec<HttpMethod> {
let value = http_method_bitmap as u16;
let mut http_methods = Vec::new();
let all_methods = [
HttpMethod::GET,
HttpMethod::POST,
HttpMethod::PUT,
HttpMethod::DELETE,
HttpMethod::HEAD,
HttpMethod::OPTIONS,
HttpMethod::PATCH,
HttpMethod::TRACE,
HttpMethod::CONNECT,
];
for method in all_methods {
if value & (method as u16) != 0 {
http_methods.push(method);
}
}
http_methods
}
pub fn convert_to_bitmap(http_methods: Vec<HttpMethod>) -> HttpMethodBitmap {
let mut ebpf_http_method = 0_u16;
for http_method in http_methods {
ebpf_http_method |= http_method as u16;
}
ebpf_http_method
}
}

View File

@ -1,98 +1,44 @@
/// IPv4 address stored as a u32 in network byte order (big-endian)
///
/// # Example
/// ```ignore
/// let ip_bytes = [192, 168, 1, 1];
/// let ip: IPv4 = u32::from_be_bytes(ip_bytes);
/// ```
#[cfg(feature = "user")]
use aya::Pod;
pub type IPv4 = u32;
/// IPv6 address stored as a u128 in network byte order (big-endian)
///
/// # Example
/// ```ignore
/// let ip_bytes = [
/// 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00,
/// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01
/// ];
/// let ip: IPv6 = u128::from_be_bytes(ip_bytes);
/// ```
pub type IPv6 = u128;
/// Network port number stored as a u16
///
/// Valid values range from 0 to 65535 (inclusive).
/// Common well-known ports are 0-1023, registered ports are 1024-49151,
/// and dynamic/private ports are 49152-65535.
///
/// # Example
/// ```ignore
/// let http_port: Port = 80;
/// let https_port: Port = 443;
/// let dynamic_port: Port = 49152;
/// ```
pub type Port = u16;
/// Network endpoint identifier combining IP address and port number
///
/// # Layout
/// ```text
/// [0]: IPv4 address (u32) in network byte order
/// [1]: Port number (u16) in network byte order, stored in lower 16 bits
/// ```
///
/// # Example
/// ```ignore
/// // Create key for 192.168.1.1:8080
/// let ip_bytes = [192, 168, 1, 1];
/// let ip = u32::from_be_bytes(ip_bytes);
/// let port = 8080_u16;
/// let key: EbpfAddrPortV4 = [ip, port as u32];
/// ```
///
/// # Note
/// - IP address should be in network byte order (big-endian)
/// - Port number is stored in the lower 16 bits of the second u32
/// - Upper 16 bits of second u32 are unused and should be zero
///
/// # Memory Layout
/// ```text
/// [0]: [------------ IP Address (32 bits) ------------]
/// [1]: [-- Unused (16 bits) --][--- Port (16 bits) ---]
/// ```
pub type EbpfAddrPortV4 = [u32; 2];
#[repr(C, align(8))]
#[derive(Copy, Clone)]
pub struct AddrPortV4 {
pub ip: IPv4,
pub port: Port,
}
/// Network endpoint identifier combining IPv6 address and port number
///
/// A compact representation of an IPv6 endpoint using two u128 values,
/// storing the IP address and port number in network byte order.
///
/// # Layout
/// ```text
/// [0]: IPv6 address (u128) in network byte order
/// [1]: Port number (u16) in network byte order, stored in lower 16 bits
/// ```
///
/// # Example
/// ```ignore
/// // Create key for 2001:db8::1:8080
/// let ip_bytes = [
/// 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00,
/// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01
/// ];
/// let ip = u128::from_be_bytes(ip_bytes);
/// let port = 8080_u16;
/// let key: EbpfAddrPortV6 = [ip, port as u128];
/// ```
///
/// # Notes
/// - IP address should be in network byte order (big-endian)
/// - Port number is stored in the lower 16 bits of the second u128
/// - Upper 112 bits of second u128 are unused and should be zero
///
/// # Memory Layout
/// ```text
/// [0]: [---------------------- IPv6 Address (128 bits) ----------------------]
/// [1]: [-------------- Unused (112 bits) --------------][-- Port (16 bits) --]
/// ```
pub type EbpfAddrPortV6 = [u128; 2];
impl AddrPortV4 {
pub fn new(ip: IPv4, port: Port) -> Self {
AddrPortV4 {
ip,
port,
}
}
}
#[cfg(feature = "user")]
unsafe impl Pod for AddrPortV4 {}
#[repr(C, align(8))]
#[derive(Copy, Clone)]
pub struct AddrPortV6 {
pub ip: IPv6,
pub port: Port,
}
impl AddrPortV6 {
pub fn new(ip: IPv6, port: Port) -> Self {
AddrPortV6 {
ip,
port,
}
}
}
#[cfg(feature = "user")]
unsafe impl Pod for AddrPortV6 {}

View File

@ -2,5 +2,6 @@ pub mod event;
pub mod flow_stats;
pub mod http_method;
pub mod ip_address;
pub mod packet;
pub mod placeholder;
pub mod pseudo_header;

View File

@ -0,0 +1,4 @@
#[repr(C)]
struct Ipv4Packet {
}

View File

@ -8,7 +8,7 @@ net-guardia-common = { path = "../net-guardia-common" }
aya-ebpf = { workspace = true }
aya-log-ebpf = { workspace = true }
network-types = "0.0.7"
network-types = { workspace = true }
[build-dependencies]
which = { workspace = true }

View File

@ -2,89 +2,93 @@ use aya_ebpf::helpers::bpf_ktime_get_ns;
use aya_ebpf::macros::map;
use aya_ebpf::maps::LruHashMap;
use net_guardia_common::model::event::{IPv4Event, IPv6Event};
use net_guardia_common::model::flow_stats::EbpfFlowStats;
use net_guardia_common::model::ip_address::{EbpfAddrPortV4, EbpfAddrPortV6};
use net_guardia_common::model::flow_stats::FlowStats;
use net_guardia_common::model::ip_address::{AddrPortV4, AddrPortV6};
use net_guardia_common::MAX_STATS;
#[map]
static IPV4_EGRESS_SRC_1MIN: LruHashMap<EbpfAddrPortV4, EbpfFlowStats> =
static IPV4_INGRESS_SRC_1MIN: LruHashMap<AddrPortV4, FlowStats> =
LruHashMap::with_max_entries(MAX_STATS, 0);
#[map]
static IPV4_EGRESS_SRC_10MIN: LruHashMap<EbpfAddrPortV4, EbpfFlowStats> =
static IPV4_INGRESS_SRC_10MIN: LruHashMap<AddrPortV4, FlowStats> =
LruHashMap::with_max_entries(MAX_STATS, 0);
#[map]
static IPV4_EGRESS_SRC_1HOUR: LruHashMap<EbpfAddrPortV4, EbpfFlowStats> =
static IPV4_INGRESS_SRC_1HOUR: LruHashMap<AddrPortV4, FlowStats> =
LruHashMap::with_max_entries(MAX_STATS, 0);
#[map]
static IPV6_EGRESS_SRC_1MIN: LruHashMap<EbpfAddrPortV6, EbpfFlowStats> =
static IPV6_INGRESS_SRC_1MIN: LruHashMap<AddrPortV6, FlowStats> =
LruHashMap::with_max_entries(MAX_STATS, 0);
#[map]
static IPV6_EGRESS_SRC_10MIN: LruHashMap<EbpfAddrPortV6, EbpfFlowStats> =
static IPV6_INGRESS_SRC_10MIN: LruHashMap<AddrPortV6, FlowStats> =
LruHashMap::with_max_entries(MAX_STATS, 0);
#[map]
static IPV6_EGRESS_SRC_1HOUR: LruHashMap<EbpfAddrPortV6, EbpfFlowStats> =
static IPV6_INGRESS_SRC_1HOUR: LruHashMap<AddrPortV6, FlowStats> =
LruHashMap::with_max_entries(MAX_STATS, 0);
#[map]
static IPV4_EGRESS_DST_1MIN: LruHashMap<EbpfAddrPortV4, EbpfFlowStats> =
static IPV4_INGRESS_DST_1MIN: LruHashMap<AddrPortV4, FlowStats> =
LruHashMap::with_max_entries(MAX_STATS, 0);
#[map]
static IPV4_EGRESS_DST_10MIN: LruHashMap<EbpfAddrPortV4, EbpfFlowStats> =
static IPV4_INGRESS_DST_10MIN: LruHashMap<AddrPortV4, FlowStats> =
LruHashMap::with_max_entries(MAX_STATS, 0);
#[map]
static IPV4_EGRESS_DST_1HOUR: LruHashMap<EbpfAddrPortV4, EbpfFlowStats> =
static IPV4_INGRESS_DST_1HOUR: LruHashMap<AddrPortV4, FlowStats> =
LruHashMap::with_max_entries(MAX_STATS, 0);
#[map]
static IPV6_EGRESS_DST_1MIN: LruHashMap<EbpfAddrPortV6, EbpfFlowStats> =
static IPV6_INGRESS_DST_1MIN: LruHashMap<AddrPortV6, FlowStats> =
LruHashMap::with_max_entries(MAX_STATS, 0);
#[map]
static IPV6_EGRESS_DST_10MIN: LruHashMap<EbpfAddrPortV6, EbpfFlowStats> =
static IPV6_INGRESS_DST_10MIN: LruHashMap<AddrPortV6, FlowStats> =
LruHashMap::with_max_entries(MAX_STATS, 0);
#[map]
static IPV6_EGRESS_DST_1HOUR: LruHashMap<EbpfAddrPortV6, EbpfFlowStats> =
static IPV6_INGRESS_DST_1HOUR: LruHashMap<AddrPortV6, FlowStats> =
LruHashMap::with_max_entries(MAX_STATS, 0);
pub fn ipv4_update_stats(event: &IPv4Event) {
unsafe {
let now = bpf_ktime_get_ns();
let source = [event.source_ip, event.source_port as u32];
let destination = [event.destination_ip, event.destination_port as u32];
ipv4_update_flow_stats(&IPV4_EGRESS_SRC_1MIN, &source, event, now);
ipv4_update_flow_stats(&IPV4_EGRESS_SRC_10MIN, &source, event, now);
ipv4_update_flow_stats(&IPV4_EGRESS_SRC_1HOUR, &source, event, now);
ipv4_update_flow_stats(&IPV4_EGRESS_DST_1MIN, &destination, event, now);
ipv4_update_flow_stats(&IPV4_EGRESS_DST_10MIN, &destination, event, now);
ipv4_update_flow_stats(&IPV4_EGRESS_DST_1HOUR, &destination, event, now);
let source = event.get_source();
let destination = event.get_destination();
ipv4_update_flow_stats(&IPV4_INGRESS_SRC_1MIN, &source, event, now);
ipv4_update_flow_stats(&IPV4_INGRESS_SRC_10MIN, &source, event, now);
ipv4_update_flow_stats(&IPV4_INGRESS_SRC_1HOUR, &source, event, now);
ipv4_update_flow_stats(&IPV4_INGRESS_DST_1MIN, &destination, event, now);
ipv4_update_flow_stats(&IPV4_INGRESS_DST_10MIN, &destination, event, now);
ipv4_update_flow_stats(&IPV4_INGRESS_DST_1HOUR, &destination, event, now);
}
}
pub fn ipv6_update_stats(event: &IPv6Event) {
unsafe {
let now = bpf_ktime_get_ns();
let source = [event.source_ip, event.source_port as u128];
let destination = [event.destination_ip, event.destination_port as u128];
ipv6_update_flow_status(&IPV6_EGRESS_SRC_1MIN, &source, event, now);
ipv6_update_flow_status(&IPV6_EGRESS_SRC_10MIN, &source, event, now);
ipv6_update_flow_status(&IPV6_EGRESS_SRC_1HOUR, &source, event, now);
ipv6_update_flow_status(&IPV6_EGRESS_DST_1MIN, &destination, event, now);
ipv6_update_flow_status(&IPV6_EGRESS_DST_10MIN, &destination, event, now);
ipv6_update_flow_status(&IPV6_EGRESS_DST_1HOUR, &destination, event, now);
let source = event.get_source();
let destination = event.get_destination();
ipv6_update_flow_status(&IPV6_INGRESS_SRC_1MIN, &source, event, now);
ipv6_update_flow_status(&IPV6_INGRESS_SRC_10MIN, &source, event, now);
ipv6_update_flow_status(&IPV6_INGRESS_SRC_1HOUR, &source, event, now);
ipv6_update_flow_status(&IPV6_INGRESS_DST_1MIN, &destination, event, now);
ipv6_update_flow_status(&IPV6_INGRESS_DST_10MIN, &destination, event, now);
ipv6_update_flow_status(&IPV6_INGRESS_DST_1HOUR, &destination, event, now);
}
}
#[inline(always)]
unsafe fn ipv4_update_flow_stats(
map: &LruHashMap<EbpfAddrPortV4, EbpfFlowStats>,
key: &EbpfAddrPortV4,
map: &LruHashMap<AddrPortV4, FlowStats>,
key: &AddrPortV4,
event: &IPv4Event,
now: u64,
) {
unsafe {
if let Some(status) = map.get_ptr_mut(key) {
(*status)[0] += event.len as u64;
(*status)[1] += 1;
(*status)[2] = now;
(*status).bytes += event.len as u64;
(*status).packets += 1;
(*status).last_seen = now;
} else {
let new_stats = [event.len as u64, 1, now];
let new_stats = FlowStats {
bytes: event.len as u64,
packets: 1,
last_seen: now
};
let _ = map.insert(key, &new_stats, 0);
}
}
@ -92,18 +96,22 @@ unsafe fn ipv4_update_flow_stats(
#[inline(always)]
unsafe fn ipv6_update_flow_status(
map: &LruHashMap<EbpfAddrPortV6, EbpfFlowStats>,
key: &EbpfAddrPortV6,
map: &LruHashMap<AddrPortV6, FlowStats>,
key: &AddrPortV6,
event: &IPv6Event,
now: u64,
) {
unsafe {
if let Some(status) = map.get_ptr_mut(key) {
(*status)[0] += event.len as u64;
(*status)[1] += 1;
(*status)[2] = now;
(*status).bytes += event.len as u64;
(*status).packets += 1;
(*status).last_seen = now;
} else {
let new_stats = [event.len as u64, 1, now];
let new_stats = FlowStats {
bytes: event.len as u64,
packets: 1,
last_seen: now
};
let _ = map.insert(key, &new_stats, 0);
}
}

View File

@ -15,7 +15,7 @@ pub fn parse_packet(start: usize, end: usize) -> Result<Event, ()> {
match eth.ether_type {
EtherType::Ipv4 => parse_ipv4_packet(start, end),
EtherType::Ipv6 => parse_ipv6_packet(start, end),
_ => Err(())
_ => Err(()),
}
}
@ -83,24 +83,22 @@ pub fn parse_ipv6_packet(start: usize, end: usize) -> Result<Event, ()> {
#[inline(always)]
fn parse_tcp_port(start: usize, end: usize, offset: usize) -> Result<(u16, u16), ()> {
let tcp: *const TcpHdr = (start + offset) as *const TcpHdr;
if start + offset + size_of::<TcpHdr>() > end {
return Err(());
unsafe {
let tcp: *const TcpHdr = (start + offset) as *const TcpHdr;
if start + offset + size_of::<TcpHdr>() > end {
return Err(());
}
Ok(((*tcp).source, (*tcp).dest))
}
Ok((
u16::from_be(unsafe { (*tcp).source }),
u16::from_be(unsafe { (*tcp).dest }),
))
}
#[inline(always)]
fn parse_udp_port(start: usize, end: usize, offset: usize) -> Result<(u16, u16), ()> {
let udp: *const UdpHdr = (start + offset) as *const UdpHdr;
if start + offset + size_of::<UdpHdr>() > end {
return Err(());
unsafe {
let udp: *const UdpHdr = (start + offset) as *const UdpHdr;
if start + offset + size_of::<UdpHdr>() > end {
return Err(());
}
Ok(((*udp).source, (*udp).dest))
}
Ok((
u16::from_be(unsafe { (*udp).source }),
u16::from_be(unsafe { (*udp).dest }),
))
}

View File

@ -8,7 +8,7 @@ net-guardia-common = { path = "../net-guardia-common" }
aya-ebpf = { workspace = true }
aya-log-ebpf = { workspace = true }
network-types = "0.0.7"
network-types = { workspace = true }
[build-dependencies]
which = { workspace = true }

View File

@ -1,169 +0,0 @@
use aya_ebpf::macros::map;
use aya_ebpf::maps::{HashMap, LruHashMap};
use net_guardia_common::model::event::{IPv4Event, IPv6Event};
use net_guardia_common::model::ip_address::{EbpfAddrPortV4, EbpfAddrPortV6, IPv4, IPv6};
use net_guardia_common::model::placeholder::PlaceHolder;
use net_guardia_common::{MAX_PORT_ACCESS, MAX_RULES};
const PORT_EXPIRE_TIME: u64 = 60_000_000_000;
#[map]
static IPV4_ACTIVE_PORTS: LruHashMap<IPv4, [u16; MAX_PORT_ACCESS]> = LruHashMap::with_max_entries(10000, 0);
#[map]
static IPV4_PORT_TIMESTAMPS: LruHashMap<EbpfAddrPortV4, u64> = LruHashMap::with_max_entries(10000, 0);
#[map]
static IPV4_SCANNER_LIST: HashMap<IPv4, PlaceHolder> = HashMap::with_max_entries(MAX_RULES, 0);
#[map]
static IPV6_ACTIVE_PORTS: LruHashMap<IPv6, [u16; MAX_PORT_ACCESS]> = LruHashMap::with_max_entries(10000, 0);
#[map]
static IPV6_PORT_TIMESTAMPS: LruHashMap<EbpfAddrPortV6, u64> = LruHashMap::with_max_entries(10000, 0);
#[map]
static IPV6_SCANNER_LIST: HashMap<IPv6, PlaceHolder> = HashMap::with_max_entries(MAX_RULES, 0);
pub fn ipv4_is_attack(event: &IPv4Event) -> bool {
unsafe {
let ip = event.source_ip;
let port = event.source_port;
let current_time = event.timestamp;
if IPV4_SCANNER_LIST.get(&ip).is_some() {
return true;
}
let port_key = [ip, port as u32];
let expire_time = current_time - PORT_EXPIRE_TIME;
if let Some(ports) = IPV4_ACTIVE_PORTS.get_ptr_mut(&ip) {
let mut active_count = 1;
let mut found_port = false;
let mut empty_index = MAX_PORT_ACCESS;
let mut expired_index = MAX_PORT_ACCESS;
for i in 0..MAX_PORT_ACCESS {
let stored_port = (*ports)[i];
if stored_port == port {
found_port = true;
let _ = IPV4_PORT_TIMESTAMPS.insert(&port_key, &current_time, 0);
} else if stored_port == 0 {
empty_index = i;
} else if stored_port != 0 {
let stored_key = [ip, stored_port as u32];
if let Some(&timestamp) = IPV4_PORT_TIMESTAMPS.get(&stored_key) {
if timestamp > expire_time {
active_count += 1;
} else {
(*ports)[i] = 0;
let _ = IPV4_PORT_TIMESTAMPS.remove(&stored_key);
expired_index = i;
}
} else {
(*ports)[i] = 0;
let _ = IPV4_PORT_TIMESTAMPS.remove(&stored_key);
expired_index = i;
}
}
}
if !found_port {
let insert_index = if empty_index != MAX_PORT_ACCESS {
empty_index
} else if expired_index != MAX_PORT_ACCESS {
expired_index
} else {
let _ = IPV4_SCANNER_LIST.insert(&ip, &0_u8, 0);
return true;
};
(*ports)[insert_index] = port;
let _ = IPV4_PORT_TIMESTAMPS.insert(&port_key, &current_time, 0);
active_count += 1;
}
if active_count == MAX_PORT_ACCESS {
let _ = IPV4_SCANNER_LIST.insert(&ip, &0_u8, 0);
return true;
}
} else {
let mut new_ports = [0u16; MAX_PORT_ACCESS];
new_ports[0] = port;
let _ = IPV4_ACTIVE_PORTS.insert(&ip, &new_ports, 0);
let _ = IPV4_PORT_TIMESTAMPS.insert(&port_key, &current_time, 0);
}
false
}
}
pub fn ipv6_is_attack(event: &IPv6Event) -> bool {
unsafe {
let ip = event.source_ip;
let port = event.source_port;
let current_time = event.timestamp;
if IPV6_SCANNER_LIST.get(&ip).is_some() {
return true;
}
let port_key = [ip, port as u128];
let expire_time = current_time - PORT_EXPIRE_TIME;
if let Some(ports) = IPV6_ACTIVE_PORTS.get_ptr_mut(&ip) {
let mut active_count = 1;
let mut found_port = false;
let mut empty_index = MAX_PORT_ACCESS;
let mut expired_index = MAX_PORT_ACCESS;
for i in 0..MAX_PORT_ACCESS {
let stored_port = (*ports)[i];
if stored_port == port {
found_port = true;
let _ = IPV6_PORT_TIMESTAMPS.insert(&port_key, &current_time, 0);
} else if stored_port == 0 {
empty_index = i;
} else if stored_port != 0 {
let stored_key = [ip, stored_port as u128];
if let Some(&timestamp) = IPV6_PORT_TIMESTAMPS.get(&stored_key) {
if timestamp > expire_time {
active_count += 1;
} else {
(*ports)[i] = 0;
let _ = IPV6_PORT_TIMESTAMPS.remove(&stored_key);
expired_index = i;
}
} else {
(*ports)[i] = 0;
let _ = IPV6_PORT_TIMESTAMPS.remove(&stored_key);
expired_index = i;
}
}
}
if !found_port {
let insert_index = if empty_index != MAX_PORT_ACCESS {
empty_index
} else if expired_index != MAX_PORT_ACCESS {
expired_index
} else {
let _ = IPV6_SCANNER_LIST.insert(&ip, &0_u8, 0);
return true;
};
(*ports)[insert_index] = port;
let _ = IPV6_PORT_TIMESTAMPS.insert(&port_key, &current_time, 0);
active_count += 1;
}
if active_count == MAX_PORT_ACCESS {
let _ = IPV6_SCANNER_LIST.insert(&ip, &0_u8, 0);
return true;
}
} else {
let mut new_ports = [0u16; MAX_PORT_ACCESS];
new_ports[0] = port;
let _ = IPV6_ACTIVE_PORTS.insert(&ip, &new_ports, 0);
let _ = IPV6_PORT_TIMESTAMPS.insert(&port_key, &current_time, 0);
}
false
}
}

View File

@ -1,5 +1,4 @@
pub mod access_control;
pub mod defence;
pub mod sampling;
pub mod service;
pub mod statistics;
pub mod transmission;

View File

@ -1,8 +1,8 @@
use aya_ebpf::macros::map;
use aya_ebpf::maps::{Array, HashMap};
use net_guardia_common::model::event::{IPv4Event, IPv6Event};
use net_guardia_common::model::http_method::EbpfHttpMethod;
use net_guardia_common::model::ip_address::{EbpfAddrPortV4, EbpfAddrPortV6, IPv4, IPv6};
use net_guardia_common::model::http_method::HttpMethodBitmap;
use net_guardia_common::model::ip_address::*;
use net_guardia_common::model::placeholder::PlaceHolder;
use net_guardia_common::MAX_RULES;
use network_types::eth::EthHdr;
@ -10,19 +10,15 @@ use network_types::ip::{IpProto, Ipv4Hdr, Ipv6Hdr};
use network_types::tcp::TcpHdr;
#[map]
static IPV4_HTTP_SERVICE: HashMap<EbpfAddrPortV4, EbpfHttpMethod> =
HashMap::with_max_entries(MAX_RULES, 0);
static IPV4_HTTP_SERVICE: HashMap<AddrPortV4, HttpMethodBitmap> = HashMap::with_max_entries(MAX_RULES, 0);
#[map]
static IPV6_HTTP_SERVICE: HashMap<EbpfAddrPortV6, EbpfHttpMethod> =
HashMap::with_max_entries(MAX_RULES, 0);
static IPV6_HTTP_SERVICE: HashMap<AddrPortV6, HttpMethodBitmap> = HashMap::with_max_entries(MAX_RULES, 0);
#[map]
static SSH_WHITE_LIST_ENABLE: Array<PlaceHolder> = Array::with_max_entries(1, 0);
#[map]
static IPV4_SSH_SERVICE: HashMap<EbpfAddrPortV4, PlaceHolder> =
HashMap::with_max_entries(MAX_RULES, 0);
static IPV4_SSH_SERVICE: HashMap<AddrPortV4, PlaceHolder> = HashMap::with_max_entries(MAX_RULES, 0);
#[map]
static IPV6_SSH_SERVICE: HashMap<EbpfAddrPortV6, PlaceHolder> =
HashMap::with_max_entries(MAX_RULES, 0);
static IPV6_SSH_SERVICE: HashMap<AddrPortV6, PlaceHolder> = HashMap::with_max_entries(MAX_RULES, 0);
#[map]
static IPV4_SSH_WHITE_LIST: HashMap<IPv4, PlaceHolder> = HashMap::with_max_entries(MAX_RULES, 0);
#[map]
@ -53,7 +49,7 @@ fn ipv4_http_service_violation(
start: usize,
end: usize,
protocol: &IpProto,
destination: &EbpfAddrPortV4,
destination: &AddrPortV4,
) -> bool {
match IPV4_HTTP_SERVICE.get_ptr_mut(destination) {
Some(allow_method) => {
@ -87,7 +83,7 @@ fn ipv6_http_service_violation(
start: usize,
end: usize,
protocol: &IpProto,
destination: &EbpfAddrPortV6,
destination: &AddrPortV6,
) -> bool {
match IPV6_HTTP_SERVICE.get_ptr_mut(destination) {
Some(allow_method) => {
@ -117,7 +113,7 @@ fn ipv6_http_service_violation(
}
#[inline(always)]
fn get_http_request_method(start: usize, end: usize, offset: usize) -> Option<EbpfHttpMethod> {
fn get_http_request_method(start: usize, end: usize, offset: usize) -> Option<HttpMethodBitmap> {
if start + offset + 8 > end {
return None;
}
@ -137,13 +133,13 @@ fn get_http_request_method(start: usize, end: usize, offset: usize) -> Option<Eb
}
#[inline(always)]
fn ipv4_ssh_service_violation(source: &EbpfAddrPortV4, destination: &EbpfAddrPortV4) -> bool {
fn ipv4_ssh_service_violation(source: &AddrPortV4, destination: &AddrPortV4) -> bool {
unsafe {
if IPV4_SSH_SERVICE.get(destination).is_some() {
if SSH_WHITE_LIST_ENABLE.get(0).is_some() {
IPV4_SSH_WHITE_LIST.get(&source[0]).is_none()
IPV4_SSH_WHITE_LIST.get(&source.ip).is_none()
} else {
IPV4_SSH_BLACK_LIST.get(&source[0]).is_some()
IPV4_SSH_BLACK_LIST.get(&source.ip).is_some()
}
} else {
false
@ -152,13 +148,13 @@ fn ipv4_ssh_service_violation(source: &EbpfAddrPortV4, destination: &EbpfAddrPor
}
#[inline(always)]
fn ipv6_ssh_service_violation(source_ip: &EbpfAddrPortV6, destination: &EbpfAddrPortV6) -> bool {
fn ipv6_ssh_service_violation(source_ip: &AddrPortV6, destination: &AddrPortV6) -> bool {
unsafe {
if IPV6_SSH_SERVICE.get(destination).is_some() {
if SSH_WHITE_LIST_ENABLE.get(0).is_some() {
IPV6_SSH_WHITE_LIST.get(&source_ip[0]).is_none()
IPV6_SSH_WHITE_LIST.get(&source_ip.ip).is_none()
} else {
IPV6_SSH_BLACK_LIST.get(&source_ip[0]).is_some()
IPV6_SSH_BLACK_LIST.get(&source_ip.ip).is_some()
}
} else {
false

View File

@ -2,52 +2,52 @@ use aya_ebpf::helpers::bpf_ktime_get_ns;
use aya_ebpf::macros::map;
use aya_ebpf::maps::LruHashMap;
use net_guardia_common::model::event::{IPv4Event, IPv6Event};
use net_guardia_common::model::flow_stats::EbpfFlowStats;
use net_guardia_common::model::ip_address::{EbpfAddrPortV4, EbpfAddrPortV6};
use net_guardia_common::model::flow_stats::FlowStats;
use net_guardia_common::model::ip_address::{AddrPortV4, AddrPortV6};
use net_guardia_common::MAX_STATS;
#[map]
static IPV4_INGRESS_SRC_1MIN: LruHashMap<EbpfAddrPortV4, EbpfFlowStats> =
static IPV4_INGRESS_SRC_1MIN: LruHashMap<AddrPortV4, FlowStats> =
LruHashMap::with_max_entries(MAX_STATS, 0);
#[map]
static IPV4_INGRESS_SRC_10MIN: LruHashMap<EbpfAddrPortV4, EbpfFlowStats> =
static IPV4_INGRESS_SRC_10MIN: LruHashMap<AddrPortV4, FlowStats> =
LruHashMap::with_max_entries(MAX_STATS, 0);
#[map]
static IPV4_INGRESS_SRC_1HOUR: LruHashMap<EbpfAddrPortV4, EbpfFlowStats> =
static IPV4_INGRESS_SRC_1HOUR: LruHashMap<AddrPortV4, FlowStats> =
LruHashMap::with_max_entries(MAX_STATS, 0);
#[map]
static IPV6_INGRESS_SRC_1MIN: LruHashMap<EbpfAddrPortV6, EbpfFlowStats> =
static IPV6_INGRESS_SRC_1MIN: LruHashMap<AddrPortV6, FlowStats> =
LruHashMap::with_max_entries(MAX_STATS, 0);
#[map]
static IPV6_INGRESS_SRC_10MIN: LruHashMap<EbpfAddrPortV6, EbpfFlowStats> =
static IPV6_INGRESS_SRC_10MIN: LruHashMap<AddrPortV6, FlowStats> =
LruHashMap::with_max_entries(MAX_STATS, 0);
#[map]
static IPV6_INGRESS_SRC_1HOUR: LruHashMap<EbpfAddrPortV6, EbpfFlowStats> =
static IPV6_INGRESS_SRC_1HOUR: LruHashMap<AddrPortV6, FlowStats> =
LruHashMap::with_max_entries(MAX_STATS, 0);
#[map]
static IPV4_INGRESS_DST_1MIN: LruHashMap<EbpfAddrPortV4, EbpfFlowStats> =
static IPV4_INGRESS_DST_1MIN: LruHashMap<AddrPortV4, FlowStats> =
LruHashMap::with_max_entries(MAX_STATS, 0);
#[map]
static IPV4_INGRESS_DST_10MIN: LruHashMap<EbpfAddrPortV4, EbpfFlowStats> =
static IPV4_INGRESS_DST_10MIN: LruHashMap<AddrPortV4, FlowStats> =
LruHashMap::with_max_entries(MAX_STATS, 0);
#[map]
static IPV4_INGRESS_DST_1HOUR: LruHashMap<EbpfAddrPortV4, EbpfFlowStats> =
static IPV4_INGRESS_DST_1HOUR: LruHashMap<AddrPortV4, FlowStats> =
LruHashMap::with_max_entries(MAX_STATS, 0);
#[map]
static IPV6_INGRESS_DST_1MIN: LruHashMap<EbpfAddrPortV6, EbpfFlowStats> =
static IPV6_INGRESS_DST_1MIN: LruHashMap<AddrPortV6, FlowStats> =
LruHashMap::with_max_entries(MAX_STATS, 0);
#[map]
static IPV6_INGRESS_DST_10MIN: LruHashMap<EbpfAddrPortV6, EbpfFlowStats> =
static IPV6_INGRESS_DST_10MIN: LruHashMap<AddrPortV6, FlowStats> =
LruHashMap::with_max_entries(MAX_STATS, 0);
#[map]
static IPV6_INGRESS_DST_1HOUR: LruHashMap<EbpfAddrPortV6, EbpfFlowStats> =
static IPV6_INGRESS_DST_1HOUR: LruHashMap<AddrPortV6, FlowStats> =
LruHashMap::with_max_entries(MAX_STATS, 0);
pub fn ipv4_update_stats(event: &IPv4Event) {
unsafe {
let now = bpf_ktime_get_ns();
let source = [event.source_ip, event.source_port as u32];
let destination = [event.destination_ip, event.destination_port as u32];
let source = event.get_source();
let destination = event.get_destination();
ipv4_update_flow_stats(&IPV4_INGRESS_SRC_1MIN, &source, event, now);
ipv4_update_flow_stats(&IPV4_INGRESS_SRC_10MIN, &source, event, now);
ipv4_update_flow_stats(&IPV4_INGRESS_SRC_1HOUR, &source, event, now);
@ -60,8 +60,8 @@ pub fn ipv4_update_stats(event: &IPv4Event) {
pub fn ipv6_update_stats(event: &IPv6Event) {
unsafe {
let now = bpf_ktime_get_ns();
let source = [event.source_ip, event.source_port as u128];
let destination = [event.destination_ip, event.destination_port as u128];
let source = event.get_source();
let destination = event.get_destination();
ipv6_update_flow_status(&IPV6_INGRESS_SRC_1MIN, &source, event, now);
ipv6_update_flow_status(&IPV6_INGRESS_SRC_10MIN, &source, event, now);
ipv6_update_flow_status(&IPV6_INGRESS_SRC_1HOUR, &source, event, now);
@ -73,18 +73,22 @@ pub fn ipv6_update_stats(event: &IPv6Event) {
#[inline(always)]
unsafe fn ipv4_update_flow_stats(
map: &LruHashMap<EbpfAddrPortV4, EbpfFlowStats>,
key: &EbpfAddrPortV4,
map: &LruHashMap<AddrPortV4, FlowStats>,
key: &AddrPortV4,
event: &IPv4Event,
now: u64,
) {
unsafe {
if let Some(status) = map.get_ptr_mut(key) {
(*status)[0] += event.len as u64;
(*status)[1] += 1;
(*status)[2] = now;
(*status).bytes += event.len as u64;
(*status).packets += 1;
(*status).last_seen = now;
} else {
let new_stats = [event.len as u64, 1, now];
let new_stats = FlowStats {
bytes: event.len as u64,
packets: 1,
last_seen: now
};
let _ = map.insert(key, &new_stats, 0);
}
}
@ -92,18 +96,22 @@ unsafe fn ipv4_update_flow_stats(
#[inline(always)]
unsafe fn ipv6_update_flow_status(
map: &LruHashMap<EbpfAddrPortV6, EbpfFlowStats>,
key: &EbpfAddrPortV6,
map: &LruHashMap<AddrPortV6, FlowStats>,
key: &AddrPortV6,
event: &IPv6Event,
now: u64,
) {
unsafe {
if let Some(status) = map.get_ptr_mut(key) {
(*status)[0] += event.len as u64;
(*status)[1] += 1;
(*status)[2] = now;
(*status).bytes += event.len as u64;
(*status).packets += 1;
(*status).last_seen = now;
} else {
let new_stats = [event.len as u64, 1, now];
let new_stats = FlowStats {
bytes: event.len as u64,
packets: 1,
last_seen: now
};
let _ = map.insert(key, &new_stats, 0);
}
}

View File

@ -0,0 +1,9 @@
use aya_ebpf::macros::map;
use aya_ebpf::maps::RingBuf;
#[map]
static PACKET_RING: RingBuf = RingBuf::with_byte_size(4 * 1024 * 1024, 0);
pub fn transmission() {
}

View File

@ -3,14 +3,17 @@
mod action;
mod utils;
use action::{access_control, defence, statistics, service};
use aya_ebpf::macros::{map, xdp};
use aya_ebpf::maps::{PerCpuArray, ProgramArray};
use aya_ebpf::{bindings::xdp_action, programs::XdpContext};
use crate::action::{access_control, service, statistics};
use crate::utils::parsing;
use aya_ebpf::{
bindings::xdp_action,
macros::{map, xdp},
maps::{PerCpuArray, ProgramArray},
programs::XdpContext,
};
use aya_log_ebpf::error;
use net_guardia_common::model::event::Event;
use network_types::eth::EtherType;
use utils::parsing;
#[map]
static PROGRAM_ARRAY: ProgramArray = ProgramArray::with_max_entries(8, 0);
@ -116,47 +119,15 @@ unsafe fn try_service(ctx: XdpContext) -> Result<u32, ()> {
}
}
// #[xdp]
// pub fn defence(ctx: XdpContext) -> u32 {
// match unsafe { try_defence(ctx) } {
// Ok(ret) => ret,
// Err(_) => xdp_action::XDP_PASS,
// }
// }
//
// unsafe fn try_defence(ctx: XdpContext) -> Result<u32, ()> {
// let ptr = PARSED_PACKET.get_ptr(0).ok_or(())?;
// let parsed_packet = ptr.read();
// match parsed_packet.eth_type {
// EtherType::Ipv4 => {
// let event = parsed_packet.into_ipv4_event();
// if defence::ipv4_is_attack(&event) {
// return Ok(xdp_action::XDP_DROP);
// }
// }
// EtherType::Ipv6 => {
// let event = parsed_packet.into_ipv6_event();
// if defence::ipv6_is_attack(&event) {
// return Ok(xdp_action::XDP_DROP);
// }
// }
// _ => Err(())?
// }
// if PROGRAM_ARRAY.tail_call(&ctx, 3).is_err() {
// error!(&ctx, "Tail call failed");
// }
// Err(())
// }
#[xdp]
pub fn sampling(ctx: XdpContext) -> u32 {
match unsafe { try_sampling(ctx) } {
pub fn transmission(ctx: XdpContext) -> u32 {
match unsafe { try_transmission(ctx) } {
Ok(ret) => ret,
Err(_) => xdp_action::XDP_PASS,
}
}
unsafe fn try_sampling(ctx: XdpContext) -> Result<u32, ()> {
unsafe fn try_transmission(ctx: XdpContext) -> Result<u32, ()> {
if unsafe { PROGRAM_ARRAY.tail_call(&ctx, 4).is_err() } {
error!(&ctx, "Tail call failed");
}

View File

@ -0,0 +1,12 @@
use network_types::eth::EthHdr;
use network_types::ip::{Ipv4Hdr, Ipv6Hdr};
use network_types::tcp::TcpHdr;
use network_types::udp::UdpHdr;
pub const ETHER_HEADER_OFFSET: usize = size_of::<EthHdr>();
pub const IPV4_HEADER_OFFSET: usize = ETHER_HEADER_OFFSET + size_of::<Ipv4Hdr>();
pub const IPV6_HEADER_OFFSET: usize = ETHER_HEADER_OFFSET + size_of::<Ipv6Hdr>();
pub const IPV4_TCP_HEADER_OFFSET: usize = IPV4_HEADER_OFFSET + size_of::<TcpHdr>();
pub const IPV6_TCP_HEADER_OFFSET: usize = IPV6_HEADER_OFFSET + size_of::<TcpHdr>();
pub const IPV4_UDP_HEADER_OFFSET: usize = IPV4_HEADER_OFFSET + size_of::<UdpHdr>();
pub const IPV6_UDP_HEADER_OFFSET: usize = IPV6_HEADER_OFFSET + size_of::<UdpHdr>();

View File

@ -1 +1,2 @@
pub mod parsing;
mod define;

View File

@ -9,9 +9,9 @@ net-guardia-common = { path = "../net-guardia-common", features = ["user"] }
anyhow = { workspace = true, default-features = true }
aya = { workspace = true }
aya-log = { workspace = true }
tokio = { workspace = true, features = ["macros", "rt", "rt-multi-thread", "net", "signal", "sync", "time", "fs"] }
tokio = { workspace = true, features = ["full", "macros"] }
libc = { workspace = true }
serde = { version = "1.0.215", features = ["derive"] }
serde = { workspace = true }
toml = "0.8.19"
tracing = "0.1.41"
thiserror = "2.0.3"

View File

@ -1,9 +0,0 @@
pub struct Defence {
}
impl Defence {
pub async fn initialize() -> anyhow::Result<()> {
Ok(())
}
}

View File

@ -1,20 +1,14 @@
use crate::core::control::access_control::AccessControl;
use crate::core::control::defence::Defence;
use crate::core::control::sampling::Sampling;
use crate::core::control::service::Service;
pub mod access_control;
pub mod defence;
pub mod service;
pub mod sampling;
pub struct Control;
impl Control {
pub async fn initialize() -> anyhow::Result<()> {
AccessControl::initialize().await?;
Defence::initialize().await?;
Sampling::initialize().await?;
Service::initialize().await
}
}

View File

@ -1,9 +0,0 @@
pub struct Sampling {
}
impl Sampling {
pub async fn initialize() -> anyhow::Result<()> {
Ok(())
}
}

View File

@ -1,25 +1,25 @@
use crate::core::system::System;
use crate::model::http_method::HttpMethod;
use crate::utils::log_entry::ebpf::EbpfEntry;
use crate::utils::log_entry::system::SystemEntry;
use aya::maps::{Array as AyaArray, HashMap as AyaHashMap, MapData};
use net_guardia_common::model::http_method::EbpfHttpMethod;
use net_guardia_common::model::ip_address::{EbpfAddrPortV4, EbpfAddrPortV6, IPv4, IPv6};
use net_guardia_common::model::http_method::{HttpMethod, HttpMethodBitmap};
use net_guardia_common::model::ip_address::{AddrPortV4, AddrPortV6, IPv4, IPv6};
use net_guardia_common::model::placeholder::PlaceHolder;
use std::collections::HashMap as StdHashMap;
use std::net::{Ipv4Addr, Ipv6Addr, SocketAddrV4, SocketAddrV6};
use std::sync::OnceLock;
use tokio::sync::{RwLock, RwLockReadGuard, RwLockWriteGuard};
use tracing::{error, info};
use crate::model::ip_address::IntoNative;
static SERVICE: OnceLock<RwLock<Service>> = OnceLock::new();
pub struct Service {
ipv4_http_service: AyaHashMap<MapData, EbpfAddrPortV4, EbpfHttpMethod>,
ipv6_http_service: AyaHashMap<MapData, EbpfAddrPortV6, EbpfHttpMethod>,
ipv4_http_service: AyaHashMap<MapData, AddrPortV4, HttpMethodBitmap>,
ipv6_http_service: AyaHashMap<MapData, AddrPortV6, HttpMethodBitmap>,
ssh_white_list_enable: AyaArray<MapData, PlaceHolder>,
ipv4_ssh_service: AyaHashMap<MapData, EbpfAddrPortV4, PlaceHolder>,
ipv6_ssh_service: AyaHashMap<MapData, EbpfAddrPortV6, PlaceHolder>,
ipv4_ssh_service: AyaHashMap<MapData, AddrPortV4, PlaceHolder>,
ipv6_ssh_service: AyaHashMap<MapData, AddrPortV6, PlaceHolder>,
ipv4_ssh_white_list: AyaHashMap<MapData, IPv4, PlaceHolder>,
ipv6_ssh_white_list: AyaHashMap<MapData, IPv6, PlaceHolder>,
ipv4_ssh_black_list: AyaHashMap<MapData, IPv4, PlaceHolder>,
@ -83,11 +83,11 @@ impl Service {
.iter()
.filter_map(Result::ok)
.map(|(key, value)| {
let address = Ipv4Addr::from(key[0]);
let port = key[1] as u16;
let address = Ipv4Addr::from(key.ip);
let port = key.port;
(
SocketAddrV4::new(address, port),
HttpMethod::convert_from_ebpf(value),
HttpMethod::convert_from_bitmap(value),
)
})
.collect()
@ -100,11 +100,11 @@ impl Service {
.iter()
.filter_map(Result::ok)
.map(|(key, value)| {
let address = Ipv6Addr::from(key[0]);
let port = key[1] as u16;
let address = Ipv6Addr::from(key.ip);
let port = key.port;
(
SocketAddrV6::new(address, port, 0, 0),
HttpMethod::convert_from_ebpf(value),
HttpMethod::convert_from_bitmap(value),
)
})
.collect()
@ -116,8 +116,8 @@ impl Service {
) -> anyhow::Result<()> {
let ip: u32 = (*address.ip()).into();
let port = address.port();
let addr_port = [ip, port as u32];
let ebpf_method = HttpMethod::convert_to_ebpf(http_method);
let addr_port = AddrPortV4::new(ip, port);
let ebpf_method = HttpMethod::convert_to_bitmap(http_method);
let mut service = Service::instance_mut().await;
service
.ipv4_http_service
@ -132,8 +132,8 @@ impl Service {
) -> anyhow::Result<()> {
let ip: u128 = (*address.ip()).into();
let port = address.port();
let addr_port = [ip, port as u128];
let ebpf_method = HttpMethod::convert_to_ebpf(http_method);
let addr_port = AddrPortV6::new(ip, port);
let ebpf_method = HttpMethod::convert_to_bitmap(http_method);
let mut service = Service::instance_mut().await;
service
.ipv6_http_service
@ -148,10 +148,10 @@ impl Service {
) -> anyhow::Result<()> {
let ip: u32 = (*address.ip()).into();
let port = address.port();
let addr_port = [ip, port as u32];
let addr_port = AddrPortV4::new(ip, port);
let mut service = Service::instance_mut().await;
if let Ok(current_http_method) = service.ipv4_http_service.get(&addr_port, 0) {
let mut http_method = HttpMethod::convert_from_ebpf(current_http_method);
let mut http_method = HttpMethod::convert_from_bitmap(current_http_method);
http_method.retain(|method| !removed_http_method.contains(method));
if http_method.is_empty() {
service
@ -159,7 +159,7 @@ impl Service {
.remove(&addr_port)
.map_err(|_| EbpfEntry::MapOperationError)?;
} else {
let new_http_method = HttpMethod::convert_to_ebpf(http_method);
let new_http_method = HttpMethod::convert_to_bitmap(http_method);
service
.ipv4_http_service
.insert(&addr_port, new_http_method, 0)
@ -177,10 +177,10 @@ impl Service {
) -> anyhow::Result<()> {
let ip: u128 = (*address.ip()).into();
let port = address.port();
let addr_port = [ip, port as u128];
let addr_port = AddrPortV6::new(ip, port);
let mut service = Service::instance_mut().await;
if let Ok(current_http_method) = service.ipv6_http_service.get(&addr_port, 0) {
let mut http_method = HttpMethod::convert_from_ebpf(current_http_method);
let mut http_method = HttpMethod::convert_from_bitmap(current_http_method);
http_method.retain(|method| !removed_http_method.contains(method));
if http_method.is_empty() {
service
@ -188,7 +188,7 @@ impl Service {
.remove(&addr_port)
.map_err(|_| EbpfEntry::MapOperationError)?;
} else {
let new_http_method = HttpMethod::convert_to_ebpf(http_method);
let new_http_method = HttpMethod::convert_to_bitmap(http_method);
service
.ipv6_http_service
.insert(&addr_port, new_http_method, 0)
@ -238,7 +238,7 @@ impl Service {
.ipv4_ssh_service
.keys()
.filter_map(Result::ok)
.map(|key| SocketAddrV4::new(Ipv4Addr::from(key[0]), key[1] as u16))
.map(|key| key.into_native())
.collect()
}
@ -248,14 +248,14 @@ impl Service {
.ipv6_ssh_service
.keys()
.filter_map(Result::ok)
.map(|key| SocketAddrV6::new(Ipv6Addr::from(key[0]), key[1] as u16, 0, 0))
.map(|key| key.into_native())
.collect()
}
pub async fn add_ipv4_ssh_service(address: SocketAddrV4) -> anyhow::Result<()> {
let ip: u32 = (*address.ip()).into();
let port = address.port();
let addr_port = [ip, port as u32];
let addr_port = AddrPortV4::new(ip, port);
let mut service = Service::instance_mut().await;
service
.ipv4_ssh_service
@ -267,7 +267,7 @@ impl Service {
pub async fn add_ipv6_ssh_service(address: SocketAddrV6) -> anyhow::Result<()> {
let ip: u128 = (*address.ip()).into();
let port = address.port();
let addr_port = [ip, port as u128];
let addr_port = AddrPortV6::new(ip, port);
let mut service = Service::instance_mut().await;
service
.ipv6_ssh_service
@ -279,7 +279,7 @@ impl Service {
pub async fn remove_ipv4_ssh_service(address: SocketAddrV4) -> anyhow::Result<()> {
let ip: u32 = (*address.ip()).into();
let port = address.port();
let addr_port = [ip, port as u32];
let addr_port = AddrPortV4::new(ip, port);
let mut service = Service::instance_mut().await;
service
.ipv4_ssh_service
@ -291,7 +291,7 @@ impl Service {
pub async fn remove_ipv6_ssh_service(address: SocketAddrV6) -> anyhow::Result<()> {
let ip: u128 = (*address.ip()).into();
let port = address.port();
let addr_port = [ip, port as u128];
let addr_port = AddrPortV6::new(ip, port);
let mut service = Service::instance_mut().await;
service
.ipv6_ssh_service

View File

@ -1,13 +1,12 @@
use crate::core::system::System;
use crate::model::direction::{Direction, FlowDirection};
use crate::model::flow_stats::FlowStats;
use crate::model::ip_address::IntoNative;
use crate::model::time_type::TimeType;
use crate::utils::log_entry::system::SystemEntry;
use aya::maps::{HashMap as AyaHashMap, MapData};
use aya::Pod;
use net_guardia_common::model::flow_stats::EbpfFlowStats;
use net_guardia_common::model::ip_address::{EbpfAddrPortV4, EbpfAddrPortV6};
use net_guardia_common::model::flow_stats::FlowStats;
use net_guardia_common::model::ip_address::{AddrPortV4, AddrPortV6};
use std::collections::HashMap as StdHashMap;
use std::net::{SocketAddrV4, SocketAddrV6};
use std::sync::OnceLock;
@ -18,8 +17,8 @@ static STATISTICS: OnceLock<RwLock<Statistics>> = OnceLock::new();
pub struct Statistics {
terminate: bool,
ipv4_maps: StdHashMap<(Direction, FlowDirection, TimeType), FlowMap<EbpfAddrPortV4>>,
ipv6_maps: StdHashMap<(Direction, FlowDirection, TimeType), FlowMap<EbpfAddrPortV6>>,
ipv4_maps: StdHashMap<(Direction, FlowDirection, TimeType), FlowMap<AddrPortV4>>,
ipv6_maps: StdHashMap<(Direction, FlowDirection, TimeType), FlowMap<AddrPortV6>>,
}
impl Statistics {
@ -197,7 +196,7 @@ impl Statistics {
}
struct FlowMap<T> {
map: AyaHashMap<MapData, T, EbpfFlowStats>,
map: AyaHashMap<MapData, T, FlowStats>,
}
impl<T: IntoNative + Pod> FlowMap<T> {
@ -216,7 +215,7 @@ impl<T: IntoNative + Pod> FlowMap<T> {
.filter_map(|result| {
result
.ok()
.and_then(|(key, stats)| (now - stats[2] - boot_time > window).then_some(key))
.and_then(|(key, stats)| (now - stats.last_seen - boot_time > window).then_some(key))
})
.collect();
expired_keys.iter().for_each(|key| {

View File

@ -1,25 +0,0 @@
use serde::Serialize;
use net_guardia_common::model::flow_stats::EbpfFlowStats;
#[derive(Serialize, Debug, Clone)]
pub struct FlowStats {
pub bytes: u64,
pub packets: u64,
pub last_seen: u64,
}
impl From<EbpfFlowStats> for FlowStats {
fn from(ebpf_flow_status: EbpfFlowStats) -> Self {
FlowStats {
bytes: ebpf_flow_status[0],
packets: ebpf_flow_status[1],
last_seen: ebpf_flow_status[2],
}
}
}
impl From<FlowStats> for EbpfFlowStats {
fn from(flow_status: FlowStats) -> Self {
[flow_status.bytes, flow_status.packets, flow_status.last_seen]
}
}

View File

@ -1,49 +0,0 @@
use serde::{Deserialize, Serialize};
use net_guardia_common::model::http_method::EbpfHttpMethod;
#[derive(Serialize, Deserialize, Debug, Copy, Clone, Eq, PartialEq)]
pub enum HttpMethod {
GET = 0b0000_0000_0000_0001,
POST = 0b0000_0000_0000_0010,
PUT = 0b0000_0000_0000_0100,
DELETE = 0b0000_0000_0000_1000,
HEAD = 0b0000_0000_0001_0000,
OPTIONS = 0b0000_0000_0010_0000,
PATCH = 0b0000_0000_0100_0000,
TRACE = 0b0000_0000_1000_0000,
CONNECT = 0b0000_0001_0000_0000,
}
impl HttpMethod {
pub fn convert_from_ebpf(ebpf_http_methods: EbpfHttpMethod) -> Vec<HttpMethod> {
let value = ebpf_http_methods as u16;
let mut http_methods = Vec::new();
let all_methods = [
HttpMethod::GET,
HttpMethod::POST,
HttpMethod::PUT,
HttpMethod::DELETE,
HttpMethod::HEAD,
HttpMethod::OPTIONS,
HttpMethod::PATCH,
HttpMethod::TRACE,
HttpMethod::CONNECT,
];
for method in all_methods {
if value & (method as u16) != 0 {
http_methods.push(method);
}
}
http_methods
}
pub fn convert_to_ebpf(http_methods: Vec<HttpMethod>) -> EbpfHttpMethod {
let mut ebpf_http_method = 0_u16;
for http_method in http_methods {
ebpf_http_method |= http_method as u16;
}
ebpf_http_method
}
}

View File

@ -1,4 +1,4 @@
use net_guardia_common::model::ip_address::{IPv4, IPv6, EbpfAddrPortV4, EbpfAddrPortV6};
use net_guardia_common::model::ip_address::*;
use std::hash::Hash;
use std::net::{Ipv4Addr, Ipv6Addr, SocketAddrV4, SocketAddrV6};
@ -23,18 +23,18 @@ impl IntoNative for IPv6 {
}
}
impl IntoNative for EbpfAddrPortV4 {
impl IntoNative for AddrPortV4 {
type Native = SocketAddrV4;
fn into_native(self) -> Self::Native {
SocketAddrV4::new(Ipv4Addr::from(self[0]), self[1] as u16)
SocketAddrV4::new(Ipv4Addr::from(self.ip), self.port)
}
}
impl IntoNative for EbpfAddrPortV6 {
impl IntoNative for AddrPortV6 {
type Native = SocketAddrV6;
fn into_native(self) -> Self::Native {
SocketAddrV6::new(Ipv6Addr::from(self[0]), self[1] as u16, 0, 0)
SocketAddrV6::new(Ipv6Addr::from(self.ip), self.port, 0, 0)
}
}

View File

@ -1,8 +1,6 @@
pub mod alert;
pub mod config;
pub mod direction;
pub mod flow_stats;
pub mod http_method;
pub mod ip_address;
pub mod list_type;
pub mod time_type;

View File

@ -1,5 +1,4 @@
pub mod log_entry;
pub mod definition;
pub mod ip_address;
pub mod logging;
pub mod static_files;

View File

@ -1,7 +1,7 @@
use crate::core::control::service::Service;
use crate::model::http_method::HttpMethod;
use actix_web::{delete, get, post, put, web, HttpResponse, Responder, Scope};
use std::net::{Ipv4Addr, Ipv6Addr, SocketAddrV4, SocketAddrV6};
use net_guardia_common::model::http_method::HttpMethod;
pub fn initialize() -> Scope {
web::scope("/service")