|
@@ -8,18 +8,63 @@
|
|
|
// copied, modified, or distributed except according to those terms.
|
|
|
|
|
|
|
|
|
+//! This module implements some built-in helpers that can be called from within an eBPF program.
|
|
|
+//! These helpers may originate from several places:
|
|
|
+//!
|
|
|
+//! * Some of them mimic the helpers available in the Linux kernel.
|
|
|
+//! * Some of them were proposed as example helpers in uBPF and they were adapted here.
|
|
|
+//! * Other helpers may be specific to rbpf.
|
|
|
+//!
|
|
|
+//! The prototype for helpers is always the same: five `u64` as arguments, and a `u64` as a return
|
|
|
+//! value. Hence some helpers have unused arguments, or return a 0 value in all cases, in order to
|
|
|
+//! respect this convention.
|
|
|
+
|
|
|
use std::u64;
|
|
|
|
|
|
// Helpers associated to kernel helpers
|
|
|
// See also linux/include/uapi/linux/bpf.h in Linux kernel sources.
|
|
|
|
|
|
// bpf_trace_printk()
|
|
|
-// No side effect: just print arg3, arg4 and arg5 to standard output.
|
|
|
+
|
|
|
+/// Index of helper `bpf_trace_printk()`, equivalent to `bpf_trace_printf()`, in Linux kernel, see
|
|
|
+/// <https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/tree/include/uapi/linux/bpf.h>.
|
|
|
pub const BPF_TRACE_PRINTF_IDX: u32 = 6;
|
|
|
|
|
|
+/// Prints its **last three** arguments to standard output. The **first two** arguments are
|
|
|
+/// **unused**. Returns 0.
|
|
|
+///
|
|
|
+/// By ignoring the first two arguments, it creates a helper that will have a behavior similar to
|
|
|
+/// the one of the equivalent helper `bpf_trace_printk()` from Linux kernel.
|
|
|
+///
|
|
|
+/// # Examples
|
|
|
+///
|
|
|
+/// ```
|
|
|
+/// use rbpf::helpers;
|
|
|
+///
|
|
|
+/// helpers::bpf_trace_printf(1, 15, 32, 0, 0);
|
|
|
+/// ```
|
|
|
+///
|
|
|
+/// This will print `bpf_trace_printf: 0x1, 0xf, 0x20`.
|
|
|
+///
|
|
|
+/// The eBPF code produced would be nearly the same as when compiling the following code from C to
|
|
|
+/// eBPF with clang:
|
|
|
+///
|
|
|
+/// ```c
|
|
|
+/// #include <linux/bpf.h>
|
|
|
+/// #include "path/to/linux/samples/bpf/bpf_helpers.h"
|
|
|
+///
|
|
|
+/// int main(struct __sk_buff *skb)
|
|
|
+/// {
|
|
|
+/// char *fmt = "bpf_trace_printk %llx %llx %llx\n";
|
|
|
+/// return bpf_trace_printk(fmt, sizeof(fmt), 1, 15, 32);
|
|
|
+/// }
|
|
|
+/// ```
|
|
|
+///
|
|
|
+/// This would equally print the three numbers in `/sys/kernel/debug/tracing` file each time the
|
|
|
+/// program is run.
|
|
|
#[allow(dead_code)]
|
|
|
#[allow(unused_variables)]
|
|
|
-pub fn bpf_trace_printf (arg1: u64, arg2: u64, arg3: u64, arg4: u64, arg5: u64) -> u64 {
|
|
|
+pub fn bpf_trace_printf (unused1: u64, unused2: u64, arg3: u64, arg4: u64, arg5: u64) -> u64 {
|
|
|
println!("bpf_trace_printf: {:#x}, {:#x}, {:#x}", arg3, arg4, arg5);
|
|
|
0
|
|
|
}
|
|
@@ -27,6 +72,17 @@ pub fn bpf_trace_printf (arg1: u64, arg2: u64, arg3: u64, arg4: u64, arg5: u64)
|
|
|
|
|
|
// Helpers coming from uBPF <https://github.com/iovisor/ubpf/blob/master/vm/test.c>
|
|
|
|
|
|
+/// The idea is to assemble five bytes into a single `u64`. For compatibility with the helpers API,
|
|
|
+/// each argument must be a `u64`.
|
|
|
+///
|
|
|
+/// # Examples
|
|
|
+///
|
|
|
+/// ```
|
|
|
+/// use rbpf::helpers;
|
|
|
+///
|
|
|
+/// let gathered = helpers::gather_bytes(0x11, 0x22, 0x33, 0x44, 0x55);
|
|
|
+/// assert_eq!(gathered, 0x1122334455);
|
|
|
+/// ```
|
|
|
pub fn gather_bytes (arg1: u64, arg2: u64, arg3: u64, arg4: u64, arg5: u64) -> u64 {
|
|
|
arg1.wrapping_shl(32) |
|
|
|
arg2.wrapping_shl(24) |
|
|
@@ -35,8 +91,25 @@ pub fn gather_bytes (arg1: u64, arg2: u64, arg3: u64, arg4: u64, arg5: u64) -> u
|
|
|
arg5
|
|
|
}
|
|
|
|
|
|
+/// Same as `void *memfrob(void *s, size_t n);` in `string.h` in C. See the GNU manual page (in
|
|
|
+/// section 3) for `memfrob`. The memory is directly modified, and the helper returns 0 in all
|
|
|
+/// cases. Arguments 3 to 5 are unused.
|
|
|
+///
|
|
|
+/// # Examples
|
|
|
+///
|
|
|
+/// ```
|
|
|
+/// use rbpf::helpers;
|
|
|
+///
|
|
|
+/// let val: u64 = 0x112233;
|
|
|
+/// let val_ptr = &val as *const u64;
|
|
|
+///
|
|
|
+/// helpers::memfrob(val_ptr as u64, 8, 0, 0, 0);
|
|
|
+/// assert_eq!(val, 0x2a2a2a2a2a3b0819);
|
|
|
+/// helpers::memfrob(val_ptr as u64, 8, 0, 0, 0);
|
|
|
+/// assert_eq!(val, 0x112233);
|
|
|
+/// ```
|
|
|
#[allow(unused_variables)]
|
|
|
-pub fn memfrob (ptr: u64, len: u64, arg3: u64, arg4: u64, arg5: u64) -> u64 {
|
|
|
+pub fn memfrob (ptr: u64, len: u64, unused3: u64, unused4: u64, unused5: u64) -> u64 {
|
|
|
for i in 0..len {
|
|
|
unsafe {
|
|
|
let mut p = (ptr + i) as *mut u8;
|
|
@@ -68,16 +141,39 @@ pub fn memfrob (ptr: u64, len: u64, arg3: u64, arg4: u64, arg5: u64) -> u64 {
|
|
|
// 0
|
|
|
// }
|
|
|
|
|
|
+/// Compute and return the square root of argument 1, cast as a float. Arguments 2 to 5 are
|
|
|
+/// unused.
|
|
|
+///
|
|
|
+/// # Examples
|
|
|
+///
|
|
|
+/// ```
|
|
|
+/// use rbpf::helpers;
|
|
|
+///
|
|
|
+/// let x = helpers::sqrti(9, 0, 0, 0, 0);
|
|
|
+/// assert_eq!(x, 3);
|
|
|
+/// ```
|
|
|
#[allow(dead_code)]
|
|
|
#[allow(unused_variables)]
|
|
|
-pub fn sqrti (arg1: u64, arg2: u64, arg3: u64, arg4: u64, arg5: u64) -> u64 {
|
|
|
- // Warning: untested
|
|
|
+pub fn sqrti (arg1: u64, unused2: u64, unused3: u64, unused4: u64, unused5: u64) -> u64 {
|
|
|
(arg1 as f64).sqrt() as u64
|
|
|
}
|
|
|
|
|
|
+/// C-like `strcmp`, return 0 if the strings are equal, and a non-null value otherwise.
|
|
|
+///
|
|
|
+/// # Examples
|
|
|
+///
|
|
|
+/// ```
|
|
|
+/// use rbpf::helpers;
|
|
|
+///
|
|
|
+/// let foo = "This is a string.".as_ptr() as u64;
|
|
|
+/// let bar = "This is another sting.".as_ptr() as u64;
|
|
|
+///
|
|
|
+/// assert!(helpers::strcmp(foo, foo, 0, 0, 0) == 0);
|
|
|
+/// assert!(helpers::strcmp(foo, bar, 0, 0, 0) != 0);
|
|
|
+/// ```
|
|
|
#[allow(dead_code)]
|
|
|
#[allow(unused_variables)]
|
|
|
-pub fn strcmp (arg1: u64, arg2: u64, arg3: u64, arg4: u64, arg5: u64) -> u64 {
|
|
|
+pub fn strcmp (arg1: u64, arg2: u64, arg3: u64, unused4: u64, unused5: u64) -> u64 {
|
|
|
// C-like strcmp, maybe shorter than converting the bytes to string and comparing?
|
|
|
if arg1 == 0 || arg2 == 0 {
|
|
|
return u64::MAX;
|