2 个版本
0.1.1 | 2024 年 4 月 9 日 |
---|---|
0.1.0 | 2024 年 3 月 13 日 |
#498 在 解析器实现 中
用于 falco_plugin
515KB
10K SLoC
Falco 事件
此软件包提供与 Falco 事件一起工作的支持。
事件可以以多种形式出现
- 原始字节数据缓冲区,来自插件 API 或外部源,使用与 Falco 库 ringbuffer 方案兼容的数据格式
- 原始事件,包含有关事件的某些元数据,但所有参数都仅作为一系列字节数据缓冲区提供
- 解析事件,将原始字段反序列化为 Rust 数据类型(可以是特定事件的类型,或包含所有已知事件类型的通用枚举)
自动生成的事件类型
字段类型
由于解析的事件具有强类型,我们需要为事件模式中存在的每个字段定义类型。这些类型在 fields::types
下以 C API 使用的名称提供。
有关可用特定类型的详细信息,请参阅 fields::types
。
自动生成的枚举和位标志
事件类型中的一些字段定义为 PT_FLAGS
或 PT_ENUMFLAGS
。这些在 Rust SDK 中作为枚举(PT_ENUMFLAGS
)或在 bitflags
软件包生成的结构中(PT_FLAGS
)提供。
所有这些类型都位于 fields::event_flags
模块中。
自动生成的动态值类型
一些事件字段根据例如系统调用参数采取不同的类型。这些在 Falco 事件表中编码为 PT_DYN
类型,并在 fields::dynamic_params
中作为 Rust 枚举提供。
在抽象层次结构中上下移动
字节数据切片到原始事件
要从 &[u8]
读取一个事件到 events::RawEvent,使用 events::RawEvent::from。它对切片进行了一些基本的一致性检查,但 不会 验证例如所有事件参数是否存在以及事件是否被截断。
还存在 events::RawEvent::from_ptr,如果所有你有的只是一个原始指针,但有两个不安全的原因
- 它解引用了一个原始指针,这已经足够不安全了
- 它根据事件头部确定要访问的内存长度
此方法从指针创建一个切片(基于发现的长度)并将其传递给 events::RawEvent::from。
原始事件到类型化事件
根据您的用例,您可以使用两种方法进一步细化事件类型。
如果您期望一个特定类型(或少数几个类型)的事件,您可以匹配 events::RawEvent::event_type 并使用适当的泛型类型调用 events::RawEvent::load,例如
# use falco_event::events::RawEvent;
# let event = RawEvent {
# metadata: Default::default(),
# len: 0,
# event_type: 0,
# nparams: 0,
# payload: &[],
# };
use falco_event::events::types::EventType;
use falco_event::events::types;
use falco_event::num_traits::FromPrimitive;
match EventType::from_u16(event.event_type) {
Some(EventType::SYSCALL_OPENAT2_E) => {
let openat2_e_event = event.load::<types::PPME_SYSCALL_OPENAT2_E>()?;
// openat2_e_event is Event<types::PPME_SYSCALL_OPENAT2_E>
// ...
}
Some(EventType::SYSCALL_OPENAT2_X) => {
let openat2_x_event = event.load::<types::PPME_SYSCALL_OPENAT2_X>()?;
// openat2_x_event is Event<types::PPME_SYSCALL_OPENAT2_X>
// ...
}
_ => (),
}
# Result::<(), anyhow::Error>::Ok(())
注意:events::RawEvent::load 也对事件类型进行内部验证,因此您也可以使用 if-let 链
# use falco_event::events::RawEvent;
# let event = RawEvent {
# metadata: Default::default(),
# len: 0,
# event_type: 0,
# nparams: 0,
# payload: &[],
# };
use falco_event::events::types::EventType;
use falco_event::events::types;
if let Ok(openat2_e_event) = event.load::<types::PPME_SYSCALL_OPENAT2_E>() {
// openat2_e_event is Event<types::PPME_SYSCALL_OPENAT2_E>
// ...
} else if let Ok(openat2_x_event) = event.load::<types::PPME_SYSCALL_OPENAT2_X>() {
// openat2_x_event is Event<types::PPME_SYSCALL_OPENAT2_X>
// ...
}
另一方面,如果您不期望任何特定的事件类型,但仍希望将其作为强类型化结构体,您可以使用 events::RawEvent::load_any,它返回一个 Event<AnyEvent>
,其中 events::types::AnyEvent 是一个大型枚举,包含所有已知的事件类型。
请注意,在这种情况下,可用的方法非常有限。从现实的角度来看,您只能期望一个 std::fmt::Debug 实现,尽管这可能会随时间而改变。您仍然可以匹配每个单独的变体并访问其字段,但请注意,显式匹配可能更受欢迎:您不必为构建不感兴趣的事件的类型安全表示支付成本。
事件(原始或类型化)到字节数组
有一个特质(events::EventToBytes),它将事件的序列化形式写入到写入器(即实现了 std::io::Write 的类型,例如 Vec<u8>
)。
依赖项
~0.7–1.5MB
~30K SLoC