Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add ebpf support #912

Merged
merged 11 commits into from
Oct 19, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
2 changes: 1 addition & 1 deletion .github/workflows/cache-toolchain.yml
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,6 @@ jobs:

rustup target add x86_64-unknown-linux-musl --toolchain nightly-2024-07-23-x86_64-unknown-linux-gnu
rustup component add rust-src --toolchain nightly-2024-07-23-x86_64-unknown-linux-gnu

cargo install bpf-linker
Godones marked this conversation as resolved.
Show resolved Hide resolved


2 changes: 2 additions & 0 deletions kernel/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ paste = "=1.0.14"
slabmalloc = { path = "crates/rust-slabmalloc" }
log = "0.4.21"
kprobe = { path = "crates/kprobe" }
rbpf = { path = "crates/rbpf" , default-features = false }
printf-compat = { version = "0.1.1", default-features = false }

# target为x86_64时,使用下面的依赖
[target.'cfg(target_arch = "x86_64")'.dependencies]
Expand Down
164 changes: 44 additions & 120 deletions kernel/crates/kprobe/src/arch/mod.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
use alloc::collections::BTreeMap;
use alloc::boxed::Box;
use alloc::string::String;
use alloc::sync::Arc;
use alloc::vec::Vec;
use core::{any::Any, fmt::Debug};

#[cfg(target_arch = "loongarch64")]
Expand Down Expand Up @@ -76,7 +75,13 @@ pub struct KprobeBuilder {
pre_handler: ProbeHandler,
post_handler: ProbeHandler,
fault_handler: Option<ProbeHandler>,
event_callback: Option<Box<dyn CallBackFunc>>,
probe_point: Option<Arc<KprobePoint>>,
enable: bool,
}

pub trait EventCallback: Send {
fn call(&self, trap_frame: &dyn ProbeArgs);
}

impl KprobeBuilder {
Expand All @@ -86,15 +91,18 @@ impl KprobeBuilder {
offset: usize,
pre_handler: fn(&dyn ProbeArgs),
post_handler: fn(&dyn ProbeArgs),
enable: bool,
) -> Self {
KprobeBuilder {
symbol,
symbol_addr,
offset,
pre_handler: ProbeHandler::new(pre_handler),
post_handler: ProbeHandler::new(post_handler),
event_callback: None,
fault_handler: None,
probe_point: None,
enable,
}
}

Expand All @@ -108,6 +116,11 @@ impl KprobeBuilder {
self
}

pub fn with_event_callback(mut self, event_callback: Box<dyn CallBackFunc>) -> Self {
self.event_callback = Some(event_callback);
self
}

/// 获取探测点的地址
///
/// 探测点的地址 == break指令的地址
Expand All @@ -123,6 +136,12 @@ pub struct KprobeBasic {
pre_handler: ProbeHandler,
post_handler: ProbeHandler,
fault_handler: ProbeHandler,
event_callback: Option<Box<dyn CallBackFunc>>,
enable: bool,
}

pub trait CallBackFunc: Send + Sync {
fn call(&self, trap_frame: &dyn ProbeArgs);
}

impl Debug for KprobeBasic {
Expand All @@ -148,6 +167,27 @@ impl KprobeBasic {
self.fault_handler.call(trap_frame);
}

pub fn call_event_callback(&self, trap_frame: &dyn ProbeArgs) {
if let Some(ref call_back) = self.event_callback {
call_back.call(trap_frame);
}
}

pub fn update_event_callback(&mut self, callback: Box<dyn CallBackFunc>) {
self.event_callback = Some(callback);
}

pub fn disable(&mut self) {
self.enable = false;
}

pub fn enable(&mut self) {
self.enable = true;
}

pub fn is_enabled(&self) -> bool {
self.enable
}
/// 返回探测点的函数名称
pub fn symbol(&self) -> Option<&str> {
self.symbol.as_deref()
Expand All @@ -163,125 +203,9 @@ impl From<KprobeBuilder> for KprobeBasic {
offset: value.offset,
pre_handler: value.pre_handler,
post_handler: value.post_handler,
event_callback: value.event_callback,
fault_handler,
}
}
}

/// 管理所有的kprobe探测点
#[derive(Debug, Default)]
pub struct KprobeManager {
break_list: BTreeMap<usize, Vec<Arc<Kprobe>>>,
debug_list: BTreeMap<usize, Vec<Arc<Kprobe>>>,
}

impl KprobeManager {
pub const fn new() -> Self {
KprobeManager {
break_list: BTreeMap::new(),
debug_list: BTreeMap::new(),
}
}
/// # 插入一个kprobe
///
/// ## 参数
/// - `kprobe`: kprobe的实例
pub fn insert_kprobe(&mut self, kprobe: Arc<Kprobe>) {
let probe_point = kprobe.probe_point();
self.insert_break_point(probe_point.break_address(), kprobe.clone());
self.insert_debug_point(probe_point.debug_address(), kprobe);
}

/// # 向break_list中插入一个kprobe
///
/// ## 参数
/// - `address`: kprobe的地址, 由`KprobePoint::break_address()`或者`KprobeBuilder::probe_addr()`返回
/// - `kprobe`: kprobe的实例
fn insert_break_point(&mut self, address: usize, kprobe: Arc<Kprobe>) {
let list = self.break_list.entry(address).or_default();
list.push(kprobe);
}

/// # 向debug_list中插入一个kprobe
///
/// ## 参数
/// - `address`: kprobe的单步执行地址,由`KprobePoint::debug_address()`返回
/// - `kprobe`: kprobe的实例
fn insert_debug_point(&mut self, address: usize, kprobe: Arc<Kprobe>) {
let list = self.debug_list.entry(address).or_default();
list.push(kprobe);
}

pub fn get_break_list(&self, address: usize) -> Option<&Vec<Arc<Kprobe>>> {
self.break_list.get(&address)
}

pub fn get_debug_list(&self, address: usize) -> Option<&Vec<Arc<Kprobe>>> {
self.debug_list.get(&address)
}

/// # 返回一个地址上注册的kprobe数量
///
/// ## 参数
/// - `address`: kprobe的地址, 由`KprobePoint::break_address()`或者`KprobeBuilder::probe_addr()`返回
pub fn kprobe_num(&self, address: usize) -> usize {
self.break_list_len(address)
}

#[inline]
fn break_list_len(&self, address: usize) -> usize {
self.break_list
.get(&address)
.map(|list| list.len())
.unwrap_or(0)
}
#[inline]
fn debug_list_len(&self, address: usize) -> usize {
self.debug_list
.get(&address)
.map(|list| list.len())
.unwrap_or(0)
}

/// # 移除一个kprobe
///
/// ## 参数
/// - `kprobe`: kprobe的实例
pub fn remove_kprobe(&mut self, kprobe: &Arc<Kprobe>) {
let probe_point = kprobe.probe_point();
self.remove_one_break(probe_point.break_address(), kprobe);
self.remove_one_debug(probe_point.debug_address(), kprobe);
}

/// # 从break_list中移除一个kprobe
///
/// 如果没有其他kprobe注册在这个地址上,则删除列表
///
/// ## 参数
/// - `address`: kprobe的地址, 由`KprobePoint::break_address()`或者`KprobeBuilder::probe_addr()`返回
/// - `kprobe`: kprobe的实例
fn remove_one_break(&mut self, address: usize, kprobe: &Arc<Kprobe>) {
if let Some(list) = self.break_list.get_mut(&address) {
list.retain(|x| !Arc::ptr_eq(x, kprobe));
}
if self.break_list_len(address) == 0 {
self.break_list.remove(&address);
}
}

/// # 从debug_list中移除一个kprobe
///
/// 如果没有其他kprobe注册在这个地址上,则删除列表
///
/// ## 参数
/// - `address`: kprobe的单步执行地址,由`KprobePoint::debug_address()`返回
/// - `kprobe`: kprobe的实例
fn remove_one_debug(&mut self, address: usize, kprobe: &Arc<Kprobe>) {
if let Some(list) = self.debug_list.get_mut(&address) {
list.retain(|x| !Arc::ptr_eq(x, kprobe));
}
if self.debug_list_len(address) == 0 {
self.debug_list.remove(&address);
enable: value.enable,
}
}
}
2 changes: 1 addition & 1 deletion kernel/crates/rbpf/src/disassembler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -801,7 +801,7 @@ pub fn disassemble(prog: &[u8]) {
#[cfg(not(feature = "std"))]
{
for insn in to_insn_vec(prog) {
info!("{}", insn.desc);
log::info!("{}", insn.desc);
}
}
}
1 change: 1 addition & 0 deletions kernel/crates/rbpf/src/interpreter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ use crate::{
};

#[cfg(not(feature = "user"))]
#[allow(unused)]
fn check_mem(
addr: u64,
len: usize,
Expand Down
103 changes: 98 additions & 5 deletions kernel/crates/rbpf/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,7 @@
// Configures the crate to be `no_std` when `std` feature is disabled.
#![cfg_attr(not(feature = "std"), no_std)]
extern crate alloc;

use alloc::{collections::BTreeMap, format, vec, vec::Vec};
use core::ptr;

use byteorder::{ByteOrder, LittleEndian};

Expand Down Expand Up @@ -509,7 +507,7 @@ impl<'a> EbpfVmMbuff<'a> {
// in the kernel; anyway the verifier would prevent the use of uninitialized registers).
// See `mul_loop` test.
let mem_ptr = match mem.len() {
0 => ptr::null_mut(),
0 => core::ptr::null_mut(),
_ => mem.as_ptr() as *mut u8,
};

Expand Down Expand Up @@ -908,7 +906,7 @@ impl<'a> EbpfVmFixedMbuff<'a> {
// in the kernel; anyway the verifier would prevent the use of uninitialized registers).
// See `mul_loop` test.
let mem_ptr = match mem.len() {
0 => ptr::null_mut(),
0 => core::ptr::null_mut(),
_ => mem.as_ptr() as *mut u8,
};

Expand Down Expand Up @@ -1010,7 +1008,7 @@ impl<'a> EbpfVmFixedMbuff<'a> {
// in the kernel; anyway the verifier would prevent the use of uninitialized registers).
// See `mul_loop` test.
let mem_ptr = match mem.len() {
0 => ptr::null_mut(),
0 => core::ptr::null_mut(),
_ => mem.as_ptr() as *mut u8,
};

Expand Down Expand Up @@ -1689,3 +1687,98 @@ impl<'a> EbpfVmNoData<'a> {
self.parent.execute_program_cranelift(&mut [])
}
}

/// EbpfVm with Owned data
pub struct EbpfVmRawOwned {
parent: EbpfVmRaw<'static>,
data_len: usize,
data_cap: usize,
}

impl EbpfVmRawOwned {
/// Create a new virtual machine instance, and load an eBPF program into that instance.
/// When attempting to load the program, it passes through a simple verifier.
pub fn new(prog: Option<Vec<u8>>) -> Result<EbpfVmRawOwned, Error> {
let (prog, data_len, data_cap) = match prog {
Some(prog) => {
let data_len = prog.len();
let data_cap = prog.capacity();
let slice = prog.leak();
let slice = unsafe { core::slice::from_raw_parts(slice.as_ptr(), data_len) };
(Some(slice), data_len, data_cap)
}
None => (None, 0, 0),
};
let parent = EbpfVmRaw::new(prog)?;
Ok(Self {
parent,
data_len,
data_cap,
})
}
/// Load a new eBPF program into the virtual machine instance
pub fn set_program(&mut self, prog: Vec<u8>) -> Result<(), Error> {
self.data_len = prog.len();
self.data_cap = prog.capacity();
let slice = prog.leak();
self.parent.set_program(slice)?;
Ok(())
}

/// Set a new verifier function. The function should return an Error if the program should be rejected by the virtual machine.
/// If a program has been loaded to the VM already, the verifier is immediately run.
pub fn set_verifier(&mut self, verifier: Verifier) -> Result<(), Error> {
self.parent.set_verifier(verifier)
}

/// Register a built-in or user-defined helper function in order to use it later from within the eBPF program.
/// The helper is registered into a hashmap, so the key can be any u32.
/// If using JIT-compiled eBPF programs, be sure to register all helpers before compiling the program.
/// You should be able to change registered helpers after compiling, but not to add new ones (i. e. with new keys).
pub fn register_helper(
&mut self,
key: u32,
function: fn(u64, u64, u64, u64, u64) -> u64,
) -> Result<(), Error> {
self.parent.register_helper(key, function)
}

/// Register a set of built-in or user-defined helper functions in order to use them later from
/// within the eBPF program. The helpers are registered into a hashmap, so the `key` can be any
/// `u32`.
#[allow(clippy::type_complexity)]
pub fn register_helper_set(
&mut self,
helpers: &HashMap<u32, fn(u64, u64, u64, u64, u64) -> u64>,
) -> Result<(), Error> {
for (key, function) in helpers {
self.parent.register_helper(*key, *function)?;
}
Ok(())
}

/// Execute the previously JIT-compiled program, with the given packet data, in a manner very similar to execute_program().
///
/// Safety
///
/// **WARNING:** JIT-compiled assembly code is not safe, in particular there is no runtime check for memory access;
/// so if the eBPF program attempts erroneous accesses, this may end very bad (program may segfault).
/// It may be wise to check that the program works with the interpreter before running the JIT-compiled version of it.
///
/// For this reason the function should be called from within an unsafe bloc.
pub fn execute_program(&self, mem: &mut [u8]) -> Result<u64, Error> {
self.parent.execute_program(mem)
}
}

impl Drop for EbpfVmRawOwned {
fn drop(&mut self) {
match self.parent.parent.prog {
Some(prog) => unsafe {
let ptr = prog.as_ptr();
let _prog = Vec::from_raw_parts(ptr as *mut u8, self.data_len, self.data_cap);
},
None => {}
};
}
}
Loading
Loading