pub struct EbpfVmRaw<'a> { /* private fields */ }
Expand description
A virtual machine to run eBPF program. This kind of VM is used for programs expecting to work directly on the memory area representing packet data.
Examples
let prog = &[
0x71, 0x11, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, // ldxb r1[0x04], r1
0x07, 0x01, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, // add r1, 0x22
0xbf, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r0, r1
0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit
];
let mem = &mut [
0xaa, 0xbb, 0x11, 0x22, 0xcc, 0xdd
];
// Instantiate a VM.
let vm = rbpf::EbpfVmRaw::new(Some(prog)).unwrap();
// Provide only a reference to the packet data.
let res = vm.execute_program(mem).unwrap();
assert_eq!(res, 0x22cc);
Implementations
sourceimpl<'a> EbpfVmRaw<'a>
impl<'a> EbpfVmRaw<'a>
sourcepub fn new(prog: Option<&'a [u8]>) -> Result<EbpfVmRaw<'a>, Error>
pub fn new(prog: Option<&'a [u8]>) -> Result<EbpfVmRaw<'a>, Error>
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.
Examples
let prog = &[
0x71, 0x11, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, // ldxb r1[0x04], r1
0x07, 0x01, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, // add r1, 0x22
0xbf, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r0, r1
0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit
];
// Instantiate a VM.
let vm = rbpf::EbpfVmRaw::new(Some(prog)).unwrap();
sourcepub fn set_program(&mut self, prog: &'a [u8]) -> Result<(), Error>
pub fn set_program(&mut self, prog: &'a [u8]) -> Result<(), Error>
Load a new eBPF program into the virtual machine instance.
Examples
let prog1 = &[
0xb7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r0, 0
0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit
];
let prog2 = &[
0x71, 0x11, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, // ldxb r1[0x04], r1
0x07, 0x01, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, // add r1, 0x22
0xbf, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r0, r1
0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit
];
let mem = &mut [
0xaa, 0xbb, 0x11, 0x22, 0xcc, 0x27,
];
let mut vm = rbpf::EbpfVmRaw::new(Some(prog1)).unwrap();
vm.set_program(prog2);
let res = vm.execute_program(mem).unwrap();
assert_eq!(res, 0x22cc);
sourcepub fn set_verifier(&mut self, verifier: Verifier) -> Result<(), Error>
pub fn set_verifier(&mut self, verifier: Verifier) -> Result<(), Error>
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.
Examples
use std::io::{Error, ErrorKind};
use rbpf::ebpf;
// Define a simple verifier function.
fn verifier(prog: &[u8]) -> Result<(), Error> {
let last_insn = ebpf::get_insn(prog, (prog.len() / ebpf::INSN_SIZE) - 1);
if last_insn.opc != ebpf::EXIT {
return Err(Error::new(ErrorKind::Other,
"[Verifier] Error: program does not end with “EXIT” instruction"));
}
Ok(())
}
let prog1 = &[
0xb7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r0, 0
0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit
];
// Instantiate a VM.
let mut vm = rbpf::EbpfVmMbuff::new(Some(prog1)).unwrap();
// Change the verifier.
vm.set_verifier(verifier).unwrap();
sourcepub fn register_helper(
&mut self,
key: u32,
function: fn(_: u64, _: u64, _: u64, _: u64, _: u64) -> u64
) -> Result<(), Error>
pub fn register_helper(
&mut self,
key: u32,
function: fn(_: u64, _: u64, _: u64, _: u64, _: u64) -> u64
) -> Result<(), Error>
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).
Examples
use rbpf::helpers;
let prog = &[
0x79, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ldxdw r1, r1[0x00]
0xb7, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r2, 0
0xb7, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r3, 0
0xb7, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r4, 0
0xb7, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r5, 0
0x85, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, // call helper with key 1
0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit
];
let mem = &mut [
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01
];
// Instantiate a VM.
let mut vm = rbpf::EbpfVmRaw::new(Some(prog)).unwrap();
// Register a helper. This helper will store the result of the square root of r1 into r0.
vm.register_helper(1, helpers::sqrti);
let res = vm.execute_program(mem).unwrap();
assert_eq!(res, 0x10000000);
sourcepub fn execute_program(&self, mem: &'a mut [u8]) -> Result<u64, Error>
pub fn execute_program(&self, mem: &'a mut [u8]) -> Result<u64, Error>
Execute the program loaded, with the given packet data.
Examples
let prog = &[
0x71, 0x11, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, // ldxb r1[0x04], r1
0x07, 0x01, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, // add r1, 0x22
0xbf, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r0, r1
0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit
];
let mem = &mut [
0xaa, 0xbb, 0x11, 0x22, 0xcc, 0x27
];
let mut vm = rbpf::EbpfVmRaw::new(Some(prog)).unwrap();
let res = vm.execute_program(mem).unwrap();
assert_eq!(res, 0x22cc);
sourcepub fn jit_compile(&mut self) -> Result<(), Error>
pub fn jit_compile(&mut self) -> Result<(), Error>
JIT-compile the loaded program. No argument required for this.
If using helper functions, be sure to register them into the VM before calling this function.
Examples
let prog = &[
0x71, 0x11, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, // ldxb r1[0x04], r1
0x07, 0x01, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, // add r1, 0x22
0xbf, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r0, r1
0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit
];
let mut vm = rbpf::EbpfVmRaw::new(Some(prog)).unwrap();
vm.jit_compile();
sourcepub unsafe fn execute_program_jit(&self, mem: &'a mut [u8]) -> Result<u64, Error>
pub unsafe fn execute_program_jit(&self, mem: &'a mut [u8]) -> Result<u64, Error>
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.
Examples
let prog = &[
0x71, 0x11, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, // ldxb r1[0x04], r1
0x07, 0x01, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, // add r1, 0x22
0xbf, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r0, r1
0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit
];
let mem = &mut [
0xaa, 0xbb, 0x11, 0x22, 0xcc, 0x27
];
let mut vm = rbpf::EbpfVmRaw::new(Some(prog)).unwrap();
vm.jit_compile();
unsafe {
let res = vm.execute_program_jit(mem).unwrap();
assert_eq!(res, 0x22cc);
}