Ver código fonte

src/helpers.rs: add API documentation and doc tests

Quentin Monnet 8 anos atrás
pai
commit
9bc10e3d60
1 arquivos alterados com 102 adições e 6 exclusões
  1. 102 6
      src/helpers.rs

+ 102 - 6
src/helpers.rs

@@ -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;