Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
140 changes: 37 additions & 103 deletions src/base_plugin/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -156,85 +156,32 @@ impl EderaPlugin {
self.with_zone_syscall_evt_ctx(&mut req, |zone_evt| Ok(zone_evt.cpuid as u64))
}

pub fn extract_latency(&mut self, mut req: ExtractRequest<Self>) -> Result<u64> {
let ctx = self.get_or_cache_evt_ctx(&mut req);

let enter_ts = self
.threadstate
.with_zoneinfo(&ctx.decoded_evt.zone_id, |zinfo| {
zinfo
.get_enter_event(&ctx.decoded_evt)
.map(|enter_evt| enter_evt.timestamp)
});
let Some(enter_t) = enter_ts else {
return Ok(0);
};

Ok(ctx.decoded_evt.timestamp - enter_t)
// Latency fields are deprecated in Falco 0.43+ because modern_bpf only captures
// exit events. Like libsinsp, we hardcode to 0 to avoid breaking changes.
// See: libs/userspace/libsinsp/sinsp_filtercheck_event.cpp lines 859-867
pub fn extract_latency(&mut self, _req: ExtractRequest<Self>) -> Result<u64> {
Ok(0)
}

pub fn extract_latency_s(&mut self, mut req: ExtractRequest<Self>) -> Result<u64> {
let ctx = self.get_or_cache_evt_ctx(&mut req);

let enter_ts = self
.threadstate
.with_zoneinfo(&ctx.decoded_evt.zone_id, |zinfo| {
zinfo
.get_enter_event(&ctx.decoded_evt)
.map(|enter_evt| enter_evt.timestamp)
});
let Some(enter_t) = enter_ts else {
return Ok(0);
};

Ok(ctx.decoded_evt.timestamp - enter_t / 1_000_000_000)
// Latency fields are deprecated in Falco 0.43+ because modern_bpf only captures
// exit events. Like libsinsp, we hardcode to 0 to avoid breaking changes.
// See: libs/userspace/libsinsp/sinsp_filtercheck_event.cpp lines 859-867
pub fn extract_latency_s(&mut self, _req: ExtractRequest<Self>) -> Result<u64> {
Ok(0)
}

pub fn extract_latency_ns(&mut self, mut req: ExtractRequest<Self>) -> Result<u64> {
let ctx = self.get_or_cache_evt_ctx(&mut req);

let enter_ts = self
.threadstate
.with_zoneinfo(&ctx.decoded_evt.zone_id, |zinfo| {
zinfo
.get_enter_event(&ctx.decoded_evt)
.map(|enter_evt| enter_evt.timestamp)
});
let Some(enter_t) = enter_ts else {
return Ok(0);
};

Ok(ctx.decoded_evt.timestamp - enter_t % 1_000_000_000)
// Latency fields are deprecated in Falco 0.43+ because modern_bpf only captures
// exit events. Like libsinsp, we hardcode to 0 to avoid breaking changes.
// See: libs/userspace/libsinsp/sinsp_filtercheck_event.cpp lines 859-867
pub fn extract_latency_ns(&mut self, _req: ExtractRequest<Self>) -> Result<u64> {
Ok(0)
}

pub fn extract_latency_human(&mut self, mut req: ExtractRequest<Self>) -> Result<CString> {
let ctx = self.get_or_cache_evt_ctx(&mut req);

let enter_ts = self
.threadstate
.with_zoneinfo(&ctx.decoded_evt.zone_id, |zinfo| {
zinfo
.get_enter_event(&ctx.decoded_evt)
.map(|enter_evt| enter_evt.timestamp)
});

let Some(enter_t) = enter_ts else {
return Ok(CString::new("0s")?);
};

let latency_ns = ctx.decoded_evt.timestamp - enter_t;

let formatted = if latency_ns < 1_000 {
format!("{}ns", latency_ns)
} else if latency_ns < 1_000_000 {
format!("{:.1}us", latency_ns as f64 / 1_000.0)
} else if latency_ns < 1_000_000_000 {
format!("{:.1}ms", latency_ns as f64 / 1_000_000.0)
} else {
format!("{:.1}s", latency_ns as f64 / 1_000_000_000.0)
};

Ok(CString::new(formatted)?)
// Latency fields are deprecated in Falco 0.43+ because modern_bpf only captures
// exit events. Like libsinsp, we hardcode to "0ns" to avoid breaking changes.
// See: libs/userspace/libsinsp/sinsp_filtercheck_event.cpp lines 869-874
pub fn extract_latency_human(&mut self, _req: ExtractRequest<Self>) -> Result<CString> {
Ok(CString::new("0ns")?)
}

pub fn extract_args(&mut self, mut req: ExtractRequest<Self>) -> Result<CString> {
Expand Down Expand Up @@ -598,9 +545,10 @@ impl EderaPlugin {

let zid = &context.decoded_evt.zone_id;
if matches!(context.fdinfo, Cached::NotFetched) {
context.fdinfo = match self.threadstate.with_zoneinfo(zid, |zinfo| {
zinfo.get_enterexit_event_fdinfo(&context.decoded_evt)
}) {
context.fdinfo = match self
.threadstate
.with_zoneinfo(zid, |zinfo| zinfo.get_event_fdinfo(&context.decoded_evt))
{
Some(t) => Cached::Found(t),
None => Cached::NotFound,
};
Expand Down Expand Up @@ -2836,33 +2784,19 @@ impl EderaPlugin {
}

fn get_fs_path_nameraw(&mut self, event: &ZoneKernelSyscallEvent) -> Result<CString> {
// OPENAT_X is specialcased to get the path from the ENTER event in libsinsp.
// For all others, it's pulled from the exit event.
if event.event_type == event_codes::PPME_SYSCALL_OPENAT_X as u32 {
Ok(self
.threadstate
.with_zoneinfo(&event.zone_id, |zinfo| {
zinfo.get_enter_event(event).and_then(|enter_evt| {
enter_evt
.event_params
.iter()
.find(|param| param.name == "name")
.and_then(|param| CString::new(param.param_pretty.clone()).ok())
})
})
.unwrap_or(CString::new("NA").expect("default value must parse")))
} else {
Ok(Self::get_paths_from_evt_params(event)
.into_iter()
.find_map(|path_type| {
if let EventPathType::Singular(path) = path_type {
CString::new(path).ok()
} else {
None
}
})
.unwrap_or(CString::new("NA").expect("default value must parse")))
}
// With modern_bpf (exit-only events), all paths are in the exit event params.
// The old libsinsp code that retrieved openat paths from enter events is obsolete.
// See: libs/driver/modern_bpf/programs/tail_called/events/syscall_dispatched_events/openat.bpf.c
Ok(Self::get_paths_from_evt_params(event)
.into_iter()
.find_map(|path_type| {
if let EventPathType::Singular(path) = path_type {
CString::new(path).ok()
} else {
None
}
})
.unwrap_or(CString::new("NA").expect("default value must parse")))
}

fn get_fs_path_sourceraw(&mut self, event: &ZoneKernelSyscallEvent) -> Result<CString> {
Expand Down
85 changes: 37 additions & 48 deletions src/parsers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ use crate::proto::generated::protect::control::v1::{
use anyhow::{Result, anyhow};
use libscap_bindings::consts as ppm_consts;
use libscap_bindings::types::{
ppm_event_code as event_codes, ppm_event_flags as event_flags, scap_l4_proto as l4_types,
ppm_event_code as event_codes, ppm_event_flags as event_flags, ppm_param_type as param_type,
scap_l4_proto as l4_types,
};
use log::debug;
use std::ffi::CStr;
use std::fs::File;
use std::io::{BufRead, BufReader};
Expand Down Expand Up @@ -92,7 +92,16 @@ pub fn is_wait(evt: &ZoneKernelSyscallEvent) -> bool {
}

pub fn has_fd(evt: &ZoneKernelSyscallEvent) -> bool {
(evt.event_flags & (event_flags::EF_USES_FD as u32)) != 0
// Check event_flags first
if (evt.event_flags & (event_flags::EF_USES_FD as u32)) != 0 {
return true;
}

// Fallback: check if any parameter is of type PT_FD
// This handles edge cases where event_flags might still not be populated
evt.event_params
.iter()
.any(|param| param.param_type == param_type::PT_FD as u32)
}

pub fn reads_fd(evt: &ZoneKernelSyscallEvent) -> bool {
Expand Down Expand Up @@ -145,12 +154,14 @@ pub fn has_retval(evt: &ZoneKernelSyscallEvent) -> bool {
return false;
}
// The event has a return value if:
// * it is a syscall event.
// * it is a syscall event (or a syscall-like metaevent).
// * it is an exit event.
// * it has at least one parameter. Some exit events are not instrumented, see
// `PPME_SOCKET_GETSOCKNAME_X`

if evt.event_category.contains("SYSCALL") && !is_enter(evt) && !evt.event_params.is_empty() {
if (evt.event_category.contains("SYSCALL") || evt.event_category.contains("EC_METAEVENT"))
&& !is_enter(evt)
&& !evt.event_params.is_empty()
{
return true;
}

Expand All @@ -167,38 +178,33 @@ pub fn get_retval(evt: &ZoneKernelSyscallEvent) -> Option<i64> {
}
}

/// If this event is the kind of syscall event (enter types, some others) that encodes an FD,
/// return the FD number (for table lookup, etc).
/// Note that unlike libsinsp's equivalents, this can be passed an enter or exit event,
/// and will suck out the FDI (if present) in either case.
/// Extract FD number from an event.
///
/// Modern BPF driver only captures exit events, so this extracts FD from exit event parameters.
/// It searches for a parameter with type PT_FD and returns its value.
pub fn get_fdi(event: &ZoneKernelSyscallEvent) -> Option<u64> {
use event_codes::*;

if !has_fd(event) {
return None;
}

let etype = event_codes::from_repr(event.event_type)
.ok_or(anyhow!("could not parse event type"))
.expect("should parse");

// most FDs come in the enter event
let maybe_fd_loc: Option<usize> = if is_enter(event) {
if has_fd(event) {
match etype {
PPME_SYSCALL_MMAP_E | PPME_SYSCALL_MMAP2_E => Some(4),

PPME_SYSCALL_SPLICE_E => Some(1),
_ => {
Some(0) // *most* of the time, the 0th param is the fd
}
}
// For exit events (modern_bpf only captures these), search for the FD parameter
// Special case: sendmmsg and recvmmsg have FD at position 1
let maybe_fd_loc: Option<usize> =
if etype == PPME_SOCKET_SENDMMSG_X || etype == PPME_SOCKET_RECVMMSG_X {
Some(1)
} else {
None
}
// sendmmsg and recvmmsg send all data in the EXIT event, fd included.
} else if (etype == PPME_SOCKET_SENDMMSG_X || etype == PPME_SOCKET_RECVMMSG_X)
&& !event.event_params.is_empty()
{
Some(1)
} else {
None
};
// For other exit events, search for the PT_FD parameter
event
.event_params
.iter()
.position(|param| param.param_type == param_type::PT_FD as u32)
};

if let Some(loc) = maybe_fd_loc {
let res = i64::from_ne_bytes(
Expand Down Expand Up @@ -247,23 +253,6 @@ pub fn hex_char_to_nibble(c: u8) -> Option<u8> {
}
}

pub fn get_enter_event_fd_loc(event: &ZoneKernelSyscallEvent, etype: event_codes) -> Option<u64> {
use event_codes::*;
if !is_enter(event) || !has_fd(event) {
debug!("not an enter event or has no FD {:?}", event);
return None;
}

match etype {
PPME_SYSCALL_MMAP_E | PPME_SYSCALL_MMAP2_E => Some(4),
PPME_SYSCALL_SPLICE_E => Some(1),
_ => {
// For almost all parameters the default position is `0`
Some(0)
}
}
}

// libscap stores the "list of caps" as a bitmask in the driver,
// and libsinsp munges the bitmask on syscall events.
// This naturally requires that the discriminants/ordering of Linux caps are identical
Expand Down
3 changes: 2 additions & 1 deletion src/source_plugin/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,9 @@ impl EderaSourcePluginInstance {
// pull out the timestamp and use it as the scap event's timestamp.
let encoded = evt.encode_length_delimited_to_vec();
let mut wrapped_evt = Self::plugin_event(encoded.as_slice());
// set the wrapped evt TS to the original evt ts
// set the wrapped evt TS and TID to the original evt values
wrapped_evt.metadata.ts = evt.timestamp;
wrapped_evt.metadata.tid = evt.thread_id as i64;
batch.add(wrapped_evt).expect("event should add");
drained_event_count += 1;

Expand Down
Loading
Loading