1*fae6e9adSlinfeng // SPDX-License-Identifier: (Apache-2.0 OR MIT) 2*fae6e9adSlinfeng // Derived from uBPF <https://github.com/iovisor/ubpf> 3*fae6e9adSlinfeng // Copyright 2016 6WIND S.A. <quentin.monnet@6wind.com> 4*fae6e9adSlinfeng // Copyright 2023 Isovalent, Inc. <quentin@isovalent.com> 5*fae6e9adSlinfeng 6*fae6e9adSlinfeng //! Virtual machine and JIT compiler for eBPF programs. 7*fae6e9adSlinfeng #![doc( 8*fae6e9adSlinfeng html_logo_url = "https://raw.githubusercontent.com/qmonnet/rbpf/main/misc/rbpf.png", 9*fae6e9adSlinfeng html_favicon_url = "https://raw.githubusercontent.com/qmonnet/rbpf/main/misc/rbpf.ico" 10*fae6e9adSlinfeng )] 11*fae6e9adSlinfeng #![warn(missing_docs)] 12*fae6e9adSlinfeng // There are unused mut warnings due to unsafe code. 13*fae6e9adSlinfeng #![allow(unused_mut)] 14*fae6e9adSlinfeng // Allows old-style clippy 15*fae6e9adSlinfeng #![allow(renamed_and_removed_lints)] 16*fae6e9adSlinfeng #![cfg_attr( 17*fae6e9adSlinfeng clippy, 18*fae6e9adSlinfeng allow( 19*fae6e9adSlinfeng redundant_field_names, 20*fae6e9adSlinfeng single_match, 21*fae6e9adSlinfeng cast_lossless, 22*fae6e9adSlinfeng doc_markdown, 23*fae6e9adSlinfeng match_same_arms, 24*fae6e9adSlinfeng unreadable_literal 25*fae6e9adSlinfeng ) 26*fae6e9adSlinfeng )] 27*fae6e9adSlinfeng // Configures the crate to be `no_std` when `std` feature is disabled. 28*fae6e9adSlinfeng #![cfg_attr(not(feature = "std"), no_std)] 29*fae6e9adSlinfeng extern crate alloc; 30*fae6e9adSlinfeng use alloc::{collections::BTreeMap, format, vec, vec::Vec}; 31*fae6e9adSlinfeng 32*fae6e9adSlinfeng use byteorder::{ByteOrder, LittleEndian}; 33*fae6e9adSlinfeng 34*fae6e9adSlinfeng type HashMap<K, V> = BTreeMap<K, V>; 35*fae6e9adSlinfeng #[cfg(feature = "cranelift")] 36*fae6e9adSlinfeng type HashSet<T> = alloc::collections::BTreeSet<T>; 37*fae6e9adSlinfeng mod asm_parser; 38*fae6e9adSlinfeng pub mod assembler; 39*fae6e9adSlinfeng #[cfg(feature = "cranelift")] 40*fae6e9adSlinfeng mod cranelift; 41*fae6e9adSlinfeng pub mod disassembler; 42*fae6e9adSlinfeng pub mod ebpf; 43*fae6e9adSlinfeng pub mod helpers; 44*fae6e9adSlinfeng pub mod insn_builder; 45*fae6e9adSlinfeng mod interpreter; 46*fae6e9adSlinfeng #[cfg(all(not(windows), feature = "std"))] 47*fae6e9adSlinfeng mod jit; 48*fae6e9adSlinfeng #[cfg(not(feature = "std"))] 49*fae6e9adSlinfeng mod no_std_error; 50*fae6e9adSlinfeng mod stack; 51*fae6e9adSlinfeng mod verifier; 52*fae6e9adSlinfeng 53*fae6e9adSlinfeng #[cfg(feature = "std")] 54*fae6e9adSlinfeng pub use std::io::{Error, ErrorKind}; 55*fae6e9adSlinfeng 56*fae6e9adSlinfeng /// In no_std we use a custom implementation of the error which acts as a 57*fae6e9adSlinfeng /// replacement for the io Error. 58*fae6e9adSlinfeng #[cfg(not(feature = "std"))] 59*fae6e9adSlinfeng pub use crate::no_std_error::{Error, ErrorKind}; 60*fae6e9adSlinfeng 61*fae6e9adSlinfeng /// eBPF verification function that returns an error if the program does not meet its requirements. 62*fae6e9adSlinfeng /// 63*fae6e9adSlinfeng /// Some examples of things the verifier may reject the program for: 64*fae6e9adSlinfeng /// 65*fae6e9adSlinfeng /// - Program does not terminate. 66*fae6e9adSlinfeng /// - Unknown instructions. 67*fae6e9adSlinfeng /// - Bad formed instruction. 68*fae6e9adSlinfeng /// - Unknown eBPF helper index. 69*fae6e9adSlinfeng pub type Verifier = fn(prog: &[u8]) -> Result<(), Error>; 70*fae6e9adSlinfeng 71*fae6e9adSlinfeng /// eBPF helper function. 72*fae6e9adSlinfeng pub type Helper = fn(u64, u64, u64, u64, u64) -> u64; 73*fae6e9adSlinfeng 74*fae6e9adSlinfeng // A metadata buffer with two offset indications. It can be used in one kind of eBPF VM to simulate 75*fae6e9adSlinfeng // the use of a metadata buffer each time the program is executed, without the user having to 76*fae6e9adSlinfeng // actually handle it. The offsets are used to tell the VM where in the buffer the pointers to 77*fae6e9adSlinfeng // packet data start and end should be stored each time the program is run on a new packet. 78*fae6e9adSlinfeng struct MetaBuff { 79*fae6e9adSlinfeng data_offset: usize, 80*fae6e9adSlinfeng data_end_offset: usize, 81*fae6e9adSlinfeng buffer: Vec<u8>, 82*fae6e9adSlinfeng } 83*fae6e9adSlinfeng 84*fae6e9adSlinfeng /// A virtual machine to run eBPF program. This kind of VM is used for programs expecting to work 85*fae6e9adSlinfeng /// on a metadata buffer containing pointers to packet data. 86*fae6e9adSlinfeng /// 87*fae6e9adSlinfeng /// # Examples 88*fae6e9adSlinfeng /// 89*fae6e9adSlinfeng /// ``` 90*fae6e9adSlinfeng /// let prog = &[ 91*fae6e9adSlinfeng /// 0x79, 0x11, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, // Load mem from mbuff at offset 8 into R1. 92*fae6e9adSlinfeng /// 0x69, 0x10, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, // ldhx r1[2], r0 93*fae6e9adSlinfeng /// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit 94*fae6e9adSlinfeng /// ]; 95*fae6e9adSlinfeng /// let mem = &mut [ 96*fae6e9adSlinfeng /// 0xaa, 0xbb, 0x11, 0x22, 0xcc, 0xdd 97*fae6e9adSlinfeng /// ]; 98*fae6e9adSlinfeng /// 99*fae6e9adSlinfeng /// // Just for the example we create our metadata buffer from scratch, and we store the pointers 100*fae6e9adSlinfeng /// // to packet data start and end in it. 101*fae6e9adSlinfeng /// let mut mbuff = [0u8; 32]; 102*fae6e9adSlinfeng /// unsafe { 103*fae6e9adSlinfeng /// let mut data = mbuff.as_ptr().offset(8) as *mut u64; 104*fae6e9adSlinfeng /// let mut data_end = mbuff.as_ptr().offset(24) as *mut u64; 105*fae6e9adSlinfeng /// *data = mem.as_ptr() as u64; 106*fae6e9adSlinfeng /// *data_end = mem.as_ptr() as u64 + mem.len() as u64; 107*fae6e9adSlinfeng /// } 108*fae6e9adSlinfeng /// 109*fae6e9adSlinfeng /// // Instantiate a VM. 110*fae6e9adSlinfeng /// let mut vm = rbpf::EbpfVmMbuff::new(Some(prog)).unwrap(); 111*fae6e9adSlinfeng /// 112*fae6e9adSlinfeng /// // Provide both a reference to the packet data, and to the metadata buffer. 113*fae6e9adSlinfeng /// let res = vm.execute_program(mem, &mut mbuff).unwrap(); 114*fae6e9adSlinfeng /// assert_eq!(res, 0x2211); 115*fae6e9adSlinfeng /// ``` 116*fae6e9adSlinfeng pub struct EbpfVmMbuff<'a> { 117*fae6e9adSlinfeng prog: Option<&'a [u8]>, 118*fae6e9adSlinfeng verifier: Verifier, 119*fae6e9adSlinfeng #[cfg(all(not(windows), feature = "std"))] 120*fae6e9adSlinfeng jit: Option<jit::JitMemory<'a>>, 121*fae6e9adSlinfeng #[cfg(feature = "cranelift")] 122*fae6e9adSlinfeng cranelift_prog: Option<cranelift::CraneliftProgram>, 123*fae6e9adSlinfeng helpers: HashMap<u32, ebpf::Helper>, 124*fae6e9adSlinfeng } 125*fae6e9adSlinfeng 126*fae6e9adSlinfeng impl<'a> EbpfVmMbuff<'a> { 127*fae6e9adSlinfeng /// Create a new virtual machine instance, and load an eBPF program into that instance. 128*fae6e9adSlinfeng /// When attempting to load the program, it passes through a simple verifier. 129*fae6e9adSlinfeng /// 130*fae6e9adSlinfeng /// # Examples 131*fae6e9adSlinfeng /// 132*fae6e9adSlinfeng /// ``` 133*fae6e9adSlinfeng /// let prog = &[ 134*fae6e9adSlinfeng /// 0x79, 0x11, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, // Load mem from mbuff into R1. 135*fae6e9adSlinfeng /// 0x69, 0x10, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, // ldhx r1[2], r0 136*fae6e9adSlinfeng /// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit 137*fae6e9adSlinfeng /// ]; 138*fae6e9adSlinfeng /// 139*fae6e9adSlinfeng /// // Instantiate a VM. 140*fae6e9adSlinfeng /// let mut vm = rbpf::EbpfVmMbuff::new(Some(prog)).unwrap(); 141*fae6e9adSlinfeng /// ``` new(prog: Option<&'a [u8]>) -> Result<EbpfVmMbuff<'a>, Error>142*fae6e9adSlinfeng pub fn new(prog: Option<&'a [u8]>) -> Result<EbpfVmMbuff<'a>, Error> { 143*fae6e9adSlinfeng if let Some(prog) = prog { 144*fae6e9adSlinfeng verifier::check(prog)?; 145*fae6e9adSlinfeng } 146*fae6e9adSlinfeng 147*fae6e9adSlinfeng Ok(EbpfVmMbuff { 148*fae6e9adSlinfeng prog, 149*fae6e9adSlinfeng verifier: verifier::check, 150*fae6e9adSlinfeng #[cfg(all(not(windows), feature = "std"))] 151*fae6e9adSlinfeng jit: None, 152*fae6e9adSlinfeng #[cfg(feature = "cranelift")] 153*fae6e9adSlinfeng cranelift_prog: None, 154*fae6e9adSlinfeng helpers: HashMap::new(), 155*fae6e9adSlinfeng }) 156*fae6e9adSlinfeng } 157*fae6e9adSlinfeng 158*fae6e9adSlinfeng /// Load a new eBPF program into the virtual machine instance. 159*fae6e9adSlinfeng /// 160*fae6e9adSlinfeng /// # Examples 161*fae6e9adSlinfeng /// 162*fae6e9adSlinfeng /// ``` 163*fae6e9adSlinfeng /// let prog1 = &[ 164*fae6e9adSlinfeng /// 0xb7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r0, 0 165*fae6e9adSlinfeng /// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit 166*fae6e9adSlinfeng /// ]; 167*fae6e9adSlinfeng /// let prog2 = &[ 168*fae6e9adSlinfeng /// 0x79, 0x11, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, // Load mem from mbuff into R1. 169*fae6e9adSlinfeng /// 0x69, 0x10, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, // ldhx r1[2], r0 170*fae6e9adSlinfeng /// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit 171*fae6e9adSlinfeng /// ]; 172*fae6e9adSlinfeng /// 173*fae6e9adSlinfeng /// // Instantiate a VM. 174*fae6e9adSlinfeng /// let mut vm = rbpf::EbpfVmMbuff::new(Some(prog1)).unwrap(); 175*fae6e9adSlinfeng /// vm.set_program(prog2).unwrap(); 176*fae6e9adSlinfeng /// ``` set_program(&mut self, prog: &'a [u8]) -> Result<(), Error>177*fae6e9adSlinfeng pub fn set_program(&mut self, prog: &'a [u8]) -> Result<(), Error> { 178*fae6e9adSlinfeng (self.verifier)(prog)?; 179*fae6e9adSlinfeng self.prog = Some(prog); 180*fae6e9adSlinfeng Ok(()) 181*fae6e9adSlinfeng } 182*fae6e9adSlinfeng 183*fae6e9adSlinfeng /// Set a new verifier function. The function should return an `Error` if the program should be 184*fae6e9adSlinfeng /// rejected by the virtual machine. If a program has been loaded to the VM already, the 185*fae6e9adSlinfeng /// verifier is immediately run. 186*fae6e9adSlinfeng /// 187*fae6e9adSlinfeng /// # Examples 188*fae6e9adSlinfeng /// 189*fae6e9adSlinfeng /// ``` 190*fae6e9adSlinfeng /// use rbpf::{Error, ErrorKind}; 191*fae6e9adSlinfeng /// use rbpf::ebpf; 192*fae6e9adSlinfeng /// 193*fae6e9adSlinfeng /// // Define a simple verifier function. 194*fae6e9adSlinfeng /// fn verifier(prog: &[u8]) -> Result<(), Error> { 195*fae6e9adSlinfeng /// let last_insn = ebpf::get_insn(prog, (prog.len() / ebpf::INSN_SIZE) - 1); 196*fae6e9adSlinfeng /// if last_insn.opc != ebpf::EXIT { 197*fae6e9adSlinfeng /// return Err(Error::new(ErrorKind::Other, 198*fae6e9adSlinfeng /// "[Verifier] Error: program does not end with “EXIT” instruction")); 199*fae6e9adSlinfeng /// } 200*fae6e9adSlinfeng /// Ok(()) 201*fae6e9adSlinfeng /// } 202*fae6e9adSlinfeng /// 203*fae6e9adSlinfeng /// let prog1 = &[ 204*fae6e9adSlinfeng /// 0xb7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r0, 0 205*fae6e9adSlinfeng /// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit 206*fae6e9adSlinfeng /// ]; 207*fae6e9adSlinfeng /// 208*fae6e9adSlinfeng /// // Instantiate a VM. 209*fae6e9adSlinfeng /// let mut vm = rbpf::EbpfVmMbuff::new(Some(prog1)).unwrap(); 210*fae6e9adSlinfeng /// // Change the verifier. 211*fae6e9adSlinfeng /// vm.set_verifier(verifier).unwrap(); 212*fae6e9adSlinfeng /// ``` set_verifier(&mut self, verifier: Verifier) -> Result<(), Error>213*fae6e9adSlinfeng pub fn set_verifier(&mut self, verifier: Verifier) -> Result<(), Error> { 214*fae6e9adSlinfeng if let Some(prog) = self.prog { 215*fae6e9adSlinfeng verifier(prog)?; 216*fae6e9adSlinfeng } 217*fae6e9adSlinfeng self.verifier = verifier; 218*fae6e9adSlinfeng Ok(()) 219*fae6e9adSlinfeng } 220*fae6e9adSlinfeng 221*fae6e9adSlinfeng /// Register a built-in or user-defined helper function in order to use it later from within 222*fae6e9adSlinfeng /// the eBPF program. The helper is registered into a hashmap, so the `key` can be any `u32`. 223*fae6e9adSlinfeng /// 224*fae6e9adSlinfeng /// If using JIT-compiled eBPF programs, be sure to register all helpers before compiling the 225*fae6e9adSlinfeng /// program. You should be able to change registered helpers after compiling, but not to add 226*fae6e9adSlinfeng /// new ones (i.e. with new keys). 227*fae6e9adSlinfeng /// 228*fae6e9adSlinfeng /// # Examples 229*fae6e9adSlinfeng /// 230*fae6e9adSlinfeng /// ``` 231*fae6e9adSlinfeng /// use rbpf::helpers; 232*fae6e9adSlinfeng /// 233*fae6e9adSlinfeng /// // This program was compiled with clang, from a C program containing the following single 234*fae6e9adSlinfeng /// // instruction: `return bpf_trace_printk("foo %c %c %c\n", 10, 1, 2, 3);` 235*fae6e9adSlinfeng /// let prog = &[ 236*fae6e9adSlinfeng /// 0x18, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // load 0 as u64 into r1 (That would be 237*fae6e9adSlinfeng /// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // replaced by tc by the address of 238*fae6e9adSlinfeng /// // the format string, in the .map 239*fae6e9adSlinfeng /// // section of the ELF file). 240*fae6e9adSlinfeng /// 0xb7, 0x02, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, // mov r2, 10 241*fae6e9adSlinfeng /// 0xb7, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, // mov r3, 1 242*fae6e9adSlinfeng /// 0xb7, 0x04, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, // mov r4, 2 243*fae6e9adSlinfeng /// 0xb7, 0x05, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, // mov r5, 3 244*fae6e9adSlinfeng /// 0x85, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, // call helper with key 6 245*fae6e9adSlinfeng /// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit 246*fae6e9adSlinfeng /// ]; 247*fae6e9adSlinfeng /// 248*fae6e9adSlinfeng /// // Instantiate a VM. 249*fae6e9adSlinfeng /// let mut vm = rbpf::EbpfVmMbuff::new(Some(prog)).unwrap(); 250*fae6e9adSlinfeng /// 251*fae6e9adSlinfeng /// // Register a helper. 252*fae6e9adSlinfeng /// // On running the program this helper will print the content of registers r3, r4 and r5 to 253*fae6e9adSlinfeng /// // standard output. 254*fae6e9adSlinfeng /// # #[cfg(feature = "std")] 255*fae6e9adSlinfeng /// vm.register_helper(6, helpers::bpf_trace_printf).unwrap(); 256*fae6e9adSlinfeng /// ``` register_helper(&mut self, key: u32, function: Helper) -> Result<(), Error>257*fae6e9adSlinfeng pub fn register_helper(&mut self, key: u32, function: Helper) -> Result<(), Error> { 258*fae6e9adSlinfeng self.helpers.insert(key, function); 259*fae6e9adSlinfeng Ok(()) 260*fae6e9adSlinfeng } 261*fae6e9adSlinfeng 262*fae6e9adSlinfeng /// Execute the program loaded, with the given packet data and metadata buffer. 263*fae6e9adSlinfeng /// 264*fae6e9adSlinfeng /// If the program is made to be compatible with Linux kernel, it is expected to load the 265*fae6e9adSlinfeng /// address of the beginning and of the end of the memory area used for packet data from the 266*fae6e9adSlinfeng /// metadata buffer, at some appointed offsets. It is up to the user to ensure that these 267*fae6e9adSlinfeng /// pointers are correctly stored in the buffer. 268*fae6e9adSlinfeng /// 269*fae6e9adSlinfeng /// # Examples 270*fae6e9adSlinfeng /// 271*fae6e9adSlinfeng /// ``` 272*fae6e9adSlinfeng /// let prog = &[ 273*fae6e9adSlinfeng /// 0x79, 0x11, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, // Load mem from mbuff into R1. 274*fae6e9adSlinfeng /// 0x69, 0x10, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, // ldhx r1[2], r0 275*fae6e9adSlinfeng /// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit 276*fae6e9adSlinfeng /// ]; 277*fae6e9adSlinfeng /// let mem = &mut [ 278*fae6e9adSlinfeng /// 0xaa, 0xbb, 0x11, 0x22, 0xcc, 0xdd 279*fae6e9adSlinfeng /// ]; 280*fae6e9adSlinfeng /// 281*fae6e9adSlinfeng /// // Just for the example we create our metadata buffer from scratch, and we store the 282*fae6e9adSlinfeng /// // pointers to packet data start and end in it. 283*fae6e9adSlinfeng /// let mut mbuff = [0u8; 32]; 284*fae6e9adSlinfeng /// unsafe { 285*fae6e9adSlinfeng /// let mut data = mbuff.as_ptr().offset(8) as *mut u64; 286*fae6e9adSlinfeng /// let mut data_end = mbuff.as_ptr().offset(24) as *mut u64; 287*fae6e9adSlinfeng /// *data = mem.as_ptr() as u64; 288*fae6e9adSlinfeng /// *data_end = mem.as_ptr() as u64 + mem.len() as u64; 289*fae6e9adSlinfeng /// } 290*fae6e9adSlinfeng /// 291*fae6e9adSlinfeng /// // Instantiate a VM. 292*fae6e9adSlinfeng /// let mut vm = rbpf::EbpfVmMbuff::new(Some(prog)).unwrap(); 293*fae6e9adSlinfeng /// 294*fae6e9adSlinfeng /// // Provide both a reference to the packet data, and to the metadata buffer. 295*fae6e9adSlinfeng /// let res = vm.execute_program(mem, &mut mbuff).unwrap(); 296*fae6e9adSlinfeng /// assert_eq!(res, 0x2211); 297*fae6e9adSlinfeng /// ``` execute_program(&self, mem: &[u8], mbuff: &[u8]) -> Result<u64, Error>298*fae6e9adSlinfeng pub fn execute_program(&self, mem: &[u8], mbuff: &[u8]) -> Result<u64, Error> { 299*fae6e9adSlinfeng interpreter::execute_program(self.prog, mem, mbuff, &self.helpers) 300*fae6e9adSlinfeng } 301*fae6e9adSlinfeng 302*fae6e9adSlinfeng /// JIT-compile the loaded program. No argument required for this. 303*fae6e9adSlinfeng /// 304*fae6e9adSlinfeng /// If using helper functions, be sure to register them into the VM before calling this 305*fae6e9adSlinfeng /// function. 306*fae6e9adSlinfeng /// 307*fae6e9adSlinfeng /// # Examples 308*fae6e9adSlinfeng /// 309*fae6e9adSlinfeng /// ``` 310*fae6e9adSlinfeng /// let prog = &[ 311*fae6e9adSlinfeng /// 0x79, 0x11, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, // Load mem from mbuff into R1. 312*fae6e9adSlinfeng /// 0x69, 0x10, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, // ldhx r1[2], r0 313*fae6e9adSlinfeng /// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit 314*fae6e9adSlinfeng /// ]; 315*fae6e9adSlinfeng /// 316*fae6e9adSlinfeng /// // Instantiate a VM. 317*fae6e9adSlinfeng /// let mut vm = rbpf::EbpfVmMbuff::new(Some(prog)).unwrap(); 318*fae6e9adSlinfeng /// 319*fae6e9adSlinfeng /// vm.jit_compile(); 320*fae6e9adSlinfeng /// ``` 321*fae6e9adSlinfeng #[cfg(all(not(windows), feature = "std"))] jit_compile(&mut self) -> Result<(), Error>322*fae6e9adSlinfeng pub fn jit_compile(&mut self) -> Result<(), Error> { 323*fae6e9adSlinfeng let prog = match self.prog { 324*fae6e9adSlinfeng Some(prog) => prog, 325*fae6e9adSlinfeng None => Err(Error::new( 326*fae6e9adSlinfeng ErrorKind::Other, 327*fae6e9adSlinfeng "Error: No program set, call prog_set() to load one", 328*fae6e9adSlinfeng ))?, 329*fae6e9adSlinfeng }; 330*fae6e9adSlinfeng self.jit = Some(jit::JitMemory::new(prog, &self.helpers, true, false)?); 331*fae6e9adSlinfeng Ok(()) 332*fae6e9adSlinfeng } 333*fae6e9adSlinfeng 334*fae6e9adSlinfeng /// Execute the previously JIT-compiled program, with the given packet data and metadata 335*fae6e9adSlinfeng /// buffer, in a manner very similar to `execute_program()`. 336*fae6e9adSlinfeng /// 337*fae6e9adSlinfeng /// If the program is made to be compatible with Linux kernel, it is expected to load the 338*fae6e9adSlinfeng /// address of the beginning and of the end of the memory area used for packet data from the 339*fae6e9adSlinfeng /// metadata buffer, at some appointed offsets. It is up to the user to ensure that these 340*fae6e9adSlinfeng /// pointers are correctly stored in the buffer. 341*fae6e9adSlinfeng /// 342*fae6e9adSlinfeng /// # Safety 343*fae6e9adSlinfeng /// 344*fae6e9adSlinfeng /// **WARNING:** JIT-compiled assembly code is not safe, in particular there is no runtime 345*fae6e9adSlinfeng /// check for memory access; so if the eBPF program attempts erroneous accesses, this may end 346*fae6e9adSlinfeng /// very bad (program may segfault). It may be wise to check that the program works with the 347*fae6e9adSlinfeng /// interpreter before running the JIT-compiled version of it. 348*fae6e9adSlinfeng /// 349*fae6e9adSlinfeng /// For this reason the function should be called from within an `unsafe` bloc. 350*fae6e9adSlinfeng /// 351*fae6e9adSlinfeng /// # Examples 352*fae6e9adSlinfeng /// 353*fae6e9adSlinfeng /// ``` 354*fae6e9adSlinfeng /// let prog = &[ 355*fae6e9adSlinfeng /// 0x79, 0x11, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, // Load mem from mbuff into r1. 356*fae6e9adSlinfeng /// 0x69, 0x10, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, // ldhx r1[2], r0 357*fae6e9adSlinfeng /// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit 358*fae6e9adSlinfeng /// ]; 359*fae6e9adSlinfeng /// let mem = &mut [ 360*fae6e9adSlinfeng /// 0xaa, 0xbb, 0x11, 0x22, 0xcc, 0xdd 361*fae6e9adSlinfeng /// ]; 362*fae6e9adSlinfeng /// 363*fae6e9adSlinfeng /// // Just for the example we create our metadata buffer from scratch, and we store the 364*fae6e9adSlinfeng /// // pointers to packet data start and end in it. 365*fae6e9adSlinfeng /// let mut mbuff = [0u8; 32]; 366*fae6e9adSlinfeng /// unsafe { 367*fae6e9adSlinfeng /// let mut data = mbuff.as_ptr().offset(8) as *mut u64; 368*fae6e9adSlinfeng /// let mut data_end = mbuff.as_ptr().offset(24) as *mut u64; 369*fae6e9adSlinfeng /// *data = mem.as_ptr() as u64; 370*fae6e9adSlinfeng /// *data_end = mem.as_ptr() as u64 + mem.len() as u64; 371*fae6e9adSlinfeng /// } 372*fae6e9adSlinfeng /// 373*fae6e9adSlinfeng /// // Instantiate a VM. 374*fae6e9adSlinfeng /// let mut vm = rbpf::EbpfVmMbuff::new(Some(prog)).unwrap(); 375*fae6e9adSlinfeng /// 376*fae6e9adSlinfeng /// # #[cfg(all(not(windows), feature = "std"))] 377*fae6e9adSlinfeng /// vm.jit_compile(); 378*fae6e9adSlinfeng /// 379*fae6e9adSlinfeng /// // Provide both a reference to the packet data, and to the metadata buffer. 380*fae6e9adSlinfeng /// # #[cfg(all(not(windows), feature = "std"))] 381*fae6e9adSlinfeng /// unsafe { 382*fae6e9adSlinfeng /// let res = vm.execute_program_jit(mem, &mut mbuff).unwrap(); 383*fae6e9adSlinfeng /// assert_eq!(res, 0x2211); 384*fae6e9adSlinfeng /// } 385*fae6e9adSlinfeng /// ``` 386*fae6e9adSlinfeng #[cfg(all(not(windows), feature = "std"))] execute_program_jit( &self, mem: &mut [u8], mbuff: &'a mut [u8], ) -> Result<u64, Error>387*fae6e9adSlinfeng pub unsafe fn execute_program_jit( 388*fae6e9adSlinfeng &self, 389*fae6e9adSlinfeng mem: &mut [u8], 390*fae6e9adSlinfeng mbuff: &'a mut [u8], 391*fae6e9adSlinfeng ) -> Result<u64, Error> { 392*fae6e9adSlinfeng // If packet data is empty, do not send the address of an empty slice; send a null pointer 393*fae6e9adSlinfeng // as first argument instead, as this is uBPF's behavior (empty packet should not happen 394*fae6e9adSlinfeng // in the kernel; anyway the verifier would prevent the use of uninitialized registers). 395*fae6e9adSlinfeng // See `mul_loop` test. 396*fae6e9adSlinfeng let mem_ptr = match mem.len() { 397*fae6e9adSlinfeng 0 => std::ptr::null_mut(), 398*fae6e9adSlinfeng _ => mem.as_ptr() as *mut u8, 399*fae6e9adSlinfeng }; 400*fae6e9adSlinfeng // The last two arguments are not used in this function. They would be used if there was a 401*fae6e9adSlinfeng // need to indicate to the JIT at which offset in the mbuff mem_ptr and mem_ptr + mem.len() 402*fae6e9adSlinfeng // should be stored; this is what happens with struct EbpfVmFixedMbuff. 403*fae6e9adSlinfeng match &self.jit { 404*fae6e9adSlinfeng Some(jit) => Ok(jit.get_prog()( 405*fae6e9adSlinfeng mbuff.as_ptr() as *mut u8, 406*fae6e9adSlinfeng mbuff.len(), 407*fae6e9adSlinfeng mem_ptr, 408*fae6e9adSlinfeng mem.len(), 409*fae6e9adSlinfeng 0, 410*fae6e9adSlinfeng 0, 411*fae6e9adSlinfeng )), 412*fae6e9adSlinfeng None => Err(Error::new( 413*fae6e9adSlinfeng ErrorKind::Other, 414*fae6e9adSlinfeng "Error: program has not been JIT-compiled", 415*fae6e9adSlinfeng )), 416*fae6e9adSlinfeng } 417*fae6e9adSlinfeng } 418*fae6e9adSlinfeng 419*fae6e9adSlinfeng /// Compile the loaded program using the Cranelift JIT. 420*fae6e9adSlinfeng /// 421*fae6e9adSlinfeng /// If using helper functions, be sure to register them into the VM before calling this 422*fae6e9adSlinfeng /// function. 423*fae6e9adSlinfeng /// 424*fae6e9adSlinfeng /// # Examples 425*fae6e9adSlinfeng /// 426*fae6e9adSlinfeng /// ``` 427*fae6e9adSlinfeng /// let prog = &[ 428*fae6e9adSlinfeng /// 0x79, 0x11, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, // Load mem from mbuff into R1. 429*fae6e9adSlinfeng /// 0x69, 0x10, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, // ldhx r1[2], r0 430*fae6e9adSlinfeng /// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit 431*fae6e9adSlinfeng /// ]; 432*fae6e9adSlinfeng /// 433*fae6e9adSlinfeng /// // Instantiate a VM. 434*fae6e9adSlinfeng /// let mut vm = rbpf::EbpfVmMbuff::new(Some(prog)).unwrap(); 435*fae6e9adSlinfeng /// 436*fae6e9adSlinfeng /// vm.cranelift_compile(); 437*fae6e9adSlinfeng /// ``` 438*fae6e9adSlinfeng #[cfg(feature = "cranelift")] cranelift_compile(&mut self) -> Result<(), Error>439*fae6e9adSlinfeng pub fn cranelift_compile(&mut self) -> Result<(), Error> { 440*fae6e9adSlinfeng use crate::cranelift::CraneliftCompiler; 441*fae6e9adSlinfeng 442*fae6e9adSlinfeng let prog = match self.prog { 443*fae6e9adSlinfeng Some(prog) => prog, 444*fae6e9adSlinfeng None => Err(Error::new( 445*fae6e9adSlinfeng ErrorKind::Other, 446*fae6e9adSlinfeng "Error: No program set, call prog_set() to load one", 447*fae6e9adSlinfeng ))?, 448*fae6e9adSlinfeng }; 449*fae6e9adSlinfeng 450*fae6e9adSlinfeng let mut compiler = CraneliftCompiler::new(self.helpers.clone()); 451*fae6e9adSlinfeng let program = compiler.compile_function(prog)?; 452*fae6e9adSlinfeng 453*fae6e9adSlinfeng self.cranelift_prog = Some(program); 454*fae6e9adSlinfeng Ok(()) 455*fae6e9adSlinfeng } 456*fae6e9adSlinfeng 457*fae6e9adSlinfeng /// Execute the previously compiled program, with the given packet data and metadata 458*fae6e9adSlinfeng /// buffer, in a manner very similar to `execute_program()`. 459*fae6e9adSlinfeng /// 460*fae6e9adSlinfeng /// If the program is made to be compatible with Linux kernel, it is expected to load the 461*fae6e9adSlinfeng /// address of the beginning and of the end of the memory area used for packet data from the 462*fae6e9adSlinfeng /// metadata buffer, at some appointed offsets. It is up to the user to ensure that these 463*fae6e9adSlinfeng /// pointers are correctly stored in the buffer. 464*fae6e9adSlinfeng /// 465*fae6e9adSlinfeng /// 466*fae6e9adSlinfeng /// # Examples 467*fae6e9adSlinfeng /// 468*fae6e9adSlinfeng /// ``` 469*fae6e9adSlinfeng /// let prog = &[ 470*fae6e9adSlinfeng /// 0x79, 0x11, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, // Load mem from mbuff into r1. 471*fae6e9adSlinfeng /// 0x69, 0x10, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, // ldhx r1[2], r0 472*fae6e9adSlinfeng /// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit 473*fae6e9adSlinfeng /// ]; 474*fae6e9adSlinfeng /// let mem = &mut [ 475*fae6e9adSlinfeng /// 0xaa, 0xbb, 0x11, 0x22, 0xcc, 0xdd 476*fae6e9adSlinfeng /// ]; 477*fae6e9adSlinfeng /// 478*fae6e9adSlinfeng /// // Just for the example we create our metadata buffer from scratch, and we store the 479*fae6e9adSlinfeng /// // pointers to packet data start and end in it. 480*fae6e9adSlinfeng /// let mut mbuff = [0u8; 32]; 481*fae6e9adSlinfeng /// unsafe { 482*fae6e9adSlinfeng /// let mut data = mbuff.as_ptr().offset(8) as *mut u64; 483*fae6e9adSlinfeng /// let mut data_end = mbuff.as_ptr().offset(24) as *mut u64; 484*fae6e9adSlinfeng /// *data = mem.as_ptr() as u64; 485*fae6e9adSlinfeng /// *data_end = mem.as_ptr() as u64 + mem.len() as u64; 486*fae6e9adSlinfeng /// } 487*fae6e9adSlinfeng /// 488*fae6e9adSlinfeng /// // Instantiate a VM. 489*fae6e9adSlinfeng /// let mut vm = rbpf::EbpfVmMbuff::new(Some(prog)).unwrap(); 490*fae6e9adSlinfeng /// 491*fae6e9adSlinfeng /// vm.cranelift_compile(); 492*fae6e9adSlinfeng /// 493*fae6e9adSlinfeng /// // Provide both a reference to the packet data, and to the metadata buffer. 494*fae6e9adSlinfeng /// let res = vm.execute_program_cranelift(mem, &mut mbuff).unwrap(); 495*fae6e9adSlinfeng /// assert_eq!(res, 0x2211); 496*fae6e9adSlinfeng /// ``` 497*fae6e9adSlinfeng #[cfg(feature = "cranelift")] execute_program_cranelift( &self, mem: &mut [u8], mbuff: &'a mut [u8], ) -> Result<u64, Error>498*fae6e9adSlinfeng pub fn execute_program_cranelift( 499*fae6e9adSlinfeng &self, 500*fae6e9adSlinfeng mem: &mut [u8], 501*fae6e9adSlinfeng mbuff: &'a mut [u8], 502*fae6e9adSlinfeng ) -> Result<u64, Error> { 503*fae6e9adSlinfeng // If packet data is empty, do not send the address of an empty slice; send a null pointer 504*fae6e9adSlinfeng // as first argument instead, as this is uBPF's behavior (empty packet should not happen 505*fae6e9adSlinfeng // in the kernel; anyway the verifier would prevent the use of uninitialized registers). 506*fae6e9adSlinfeng // See `mul_loop` test. 507*fae6e9adSlinfeng let mem_ptr = match mem.len() { 508*fae6e9adSlinfeng 0 => core::ptr::null_mut(), 509*fae6e9adSlinfeng _ => mem.as_ptr() as *mut u8, 510*fae6e9adSlinfeng }; 511*fae6e9adSlinfeng 512*fae6e9adSlinfeng // The last two arguments are not used in this function. They would be used if there was a 513*fae6e9adSlinfeng // need to indicate to the JIT at which offset in the mbuff mem_ptr and mem_ptr + mem.len() 514*fae6e9adSlinfeng // should be stored; this is what happens with struct EbpfVmFixedMbuff. 515*fae6e9adSlinfeng match &self.cranelift_prog { 516*fae6e9adSlinfeng Some(prog) => { 517*fae6e9adSlinfeng Ok(prog.execute(mem_ptr, mem.len(), mbuff.as_ptr() as *mut u8, mbuff.len())) 518*fae6e9adSlinfeng } 519*fae6e9adSlinfeng None => Err(Error::new( 520*fae6e9adSlinfeng ErrorKind::Other, 521*fae6e9adSlinfeng "Error: program has not been compiled with cranelift", 522*fae6e9adSlinfeng )), 523*fae6e9adSlinfeng } 524*fae6e9adSlinfeng } 525*fae6e9adSlinfeng } 526*fae6e9adSlinfeng 527*fae6e9adSlinfeng /// A virtual machine to run eBPF program. This kind of VM is used for programs expecting to work 528*fae6e9adSlinfeng /// on a metadata buffer containing pointers to packet data, but it internally handles the buffer 529*fae6e9adSlinfeng /// so as to save the effort to manually handle the metadata buffer for the user. 530*fae6e9adSlinfeng /// 531*fae6e9adSlinfeng /// This struct implements a static internal buffer that is passed to the program. The user has to 532*fae6e9adSlinfeng /// indicate the offset values at which the eBPF program expects to find the start and the end of 533*fae6e9adSlinfeng /// packet data in the buffer. On calling the `execute_program()` or `execute_program_jit()` functions, the 534*fae6e9adSlinfeng /// struct automatically updates the addresses in this static buffer, at the appointed offsets, for 535*fae6e9adSlinfeng /// the start and the end of the packet data the program is called upon. 536*fae6e9adSlinfeng /// 537*fae6e9adSlinfeng /// # Examples 538*fae6e9adSlinfeng /// 539*fae6e9adSlinfeng /// This was compiled with clang from the following program, in C: 540*fae6e9adSlinfeng /// 541*fae6e9adSlinfeng /// ```c 542*fae6e9adSlinfeng /// #include <linux/bpf.h> 543*fae6e9adSlinfeng /// #include "path/to/linux/samples/bpf/bpf_helpers.h" 544*fae6e9adSlinfeng /// 545*fae6e9adSlinfeng /// SEC(".classifier") 546*fae6e9adSlinfeng /// int classifier(struct __sk_buff *skb) 547*fae6e9adSlinfeng /// { 548*fae6e9adSlinfeng /// void *data = (void *)(long)skb->data; 549*fae6e9adSlinfeng /// void *data_end = (void *)(long)skb->data_end; 550*fae6e9adSlinfeng /// 551*fae6e9adSlinfeng /// // Check program is long enough. 552*fae6e9adSlinfeng /// if (data + 5 > data_end) 553*fae6e9adSlinfeng /// return 0; 554*fae6e9adSlinfeng /// 555*fae6e9adSlinfeng /// return *((char *)data + 5); 556*fae6e9adSlinfeng /// } 557*fae6e9adSlinfeng /// ``` 558*fae6e9adSlinfeng /// 559*fae6e9adSlinfeng /// Some small modifications have been brought to have it work, see comments. 560*fae6e9adSlinfeng /// 561*fae6e9adSlinfeng /// ``` 562*fae6e9adSlinfeng /// let prog = &[ 563*fae6e9adSlinfeng /// 0xb7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r0, 0 564*fae6e9adSlinfeng /// // Here opcode 0x61 had to be replace by 0x79 so as to load a 8-bytes long address. 565*fae6e9adSlinfeng /// // Also, offset 0x4c had to be replace with e.g. 0x40 so as to prevent the two pointers 566*fae6e9adSlinfeng /// // from overlapping in the buffer. 567*fae6e9adSlinfeng /// 0x79, 0x12, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, // load pointer to mem from r1[0x40] to r2 568*fae6e9adSlinfeng /// 0x07, 0x02, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, // add r2, 5 569*fae6e9adSlinfeng /// // Here opcode 0x61 had to be replace by 0x79 so as to load a 8-bytes long address. 570*fae6e9adSlinfeng /// 0x79, 0x11, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, // load ptr to mem_end from r1[0x50] to r1 571*fae6e9adSlinfeng /// 0x2d, 0x12, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, // if r2 > r1 skip 3 instructions 572*fae6e9adSlinfeng /// 0x71, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // load r2 (= *(mem + 5)) into r0 573*fae6e9adSlinfeng /// 0x67, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, // r0 >>= 56 574*fae6e9adSlinfeng /// 0xc7, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, // r0 <<= 56 (arsh) extend byte sign to u64 575*fae6e9adSlinfeng /// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit 576*fae6e9adSlinfeng /// ]; 577*fae6e9adSlinfeng /// let mem1 = &mut [ 578*fae6e9adSlinfeng /// 0xaa, 0xbb, 0x11, 0x22, 0xcc, 0xdd 579*fae6e9adSlinfeng /// ]; 580*fae6e9adSlinfeng /// let mem2 = &mut [ 581*fae6e9adSlinfeng /// 0xaa, 0xbb, 0x11, 0x22, 0xcc, 0x27 582*fae6e9adSlinfeng /// ]; 583*fae6e9adSlinfeng /// 584*fae6e9adSlinfeng /// // Instantiate a VM. Note that we provide the start and end offsets for mem pointers. 585*fae6e9adSlinfeng /// let mut vm = rbpf::EbpfVmFixedMbuff::new(Some(prog), 0x40, 0x50).unwrap(); 586*fae6e9adSlinfeng /// 587*fae6e9adSlinfeng /// // Provide only a reference to the packet data. We do not manage the metadata buffer. 588*fae6e9adSlinfeng /// let res = vm.execute_program(mem1).unwrap(); 589*fae6e9adSlinfeng /// assert_eq!(res, 0xffffffffffffffdd); 590*fae6e9adSlinfeng /// 591*fae6e9adSlinfeng /// let res = vm.execute_program(mem2).unwrap(); 592*fae6e9adSlinfeng /// assert_eq!(res, 0x27); 593*fae6e9adSlinfeng /// ``` 594*fae6e9adSlinfeng pub struct EbpfVmFixedMbuff<'a> { 595*fae6e9adSlinfeng parent: EbpfVmMbuff<'a>, 596*fae6e9adSlinfeng mbuff: MetaBuff, 597*fae6e9adSlinfeng } 598*fae6e9adSlinfeng 599*fae6e9adSlinfeng impl<'a> EbpfVmFixedMbuff<'a> { 600*fae6e9adSlinfeng /// Create a new virtual machine instance, and load an eBPF program into that instance. 601*fae6e9adSlinfeng /// When attempting to load the program, it passes through a simple verifier. 602*fae6e9adSlinfeng /// 603*fae6e9adSlinfeng /// # Examples 604*fae6e9adSlinfeng /// 605*fae6e9adSlinfeng /// ``` 606*fae6e9adSlinfeng /// let prog = &[ 607*fae6e9adSlinfeng /// 0xb7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r0, 0 608*fae6e9adSlinfeng /// 0x79, 0x12, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, // load mem from r1[0x40] to r2 609*fae6e9adSlinfeng /// 0x07, 0x02, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, // add r2, 5 610*fae6e9adSlinfeng /// 0x79, 0x11, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, // load mem_end from r1[0x50] to r1 611*fae6e9adSlinfeng /// 0x2d, 0x12, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, // if r2 > r1 skip 3 instructions 612*fae6e9adSlinfeng /// 0x71, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // load r2 (= *(mem + 5)) into r0 613*fae6e9adSlinfeng /// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit 614*fae6e9adSlinfeng /// ]; 615*fae6e9adSlinfeng /// 616*fae6e9adSlinfeng /// // Instantiate a VM. Note that we provide the start and end offsets for mem pointers. 617*fae6e9adSlinfeng /// let mut vm = rbpf::EbpfVmFixedMbuff::new(Some(prog), 0x40, 0x50).unwrap(); 618*fae6e9adSlinfeng /// ``` new( prog: Option<&'a [u8]>, data_offset: usize, data_end_offset: usize, ) -> Result<EbpfVmFixedMbuff<'a>, Error>619*fae6e9adSlinfeng pub fn new( 620*fae6e9adSlinfeng prog: Option<&'a [u8]>, 621*fae6e9adSlinfeng data_offset: usize, 622*fae6e9adSlinfeng data_end_offset: usize, 623*fae6e9adSlinfeng ) -> Result<EbpfVmFixedMbuff<'a>, Error> { 624*fae6e9adSlinfeng let parent = EbpfVmMbuff::new(prog)?; 625*fae6e9adSlinfeng let get_buff_len = |x: usize, y: usize| if x >= y { x + 8 } else { y + 8 }; 626*fae6e9adSlinfeng let buffer = vec![0u8; get_buff_len(data_offset, data_end_offset)]; 627*fae6e9adSlinfeng let mbuff = MetaBuff { 628*fae6e9adSlinfeng data_offset, 629*fae6e9adSlinfeng data_end_offset, 630*fae6e9adSlinfeng buffer, 631*fae6e9adSlinfeng }; 632*fae6e9adSlinfeng Ok(EbpfVmFixedMbuff { parent, mbuff }) 633*fae6e9adSlinfeng } 634*fae6e9adSlinfeng 635*fae6e9adSlinfeng /// Load a new eBPF program into the virtual machine instance. 636*fae6e9adSlinfeng /// 637*fae6e9adSlinfeng /// At the same time, load new offsets for storing pointers to start and end of packet data in 638*fae6e9adSlinfeng /// the internal metadata buffer. 639*fae6e9adSlinfeng /// 640*fae6e9adSlinfeng /// # Examples 641*fae6e9adSlinfeng /// 642*fae6e9adSlinfeng /// ``` 643*fae6e9adSlinfeng /// let prog1 = &[ 644*fae6e9adSlinfeng /// 0xb7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r0, 0 645*fae6e9adSlinfeng /// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit 646*fae6e9adSlinfeng /// ]; 647*fae6e9adSlinfeng /// let prog2 = &[ 648*fae6e9adSlinfeng /// 0xb7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r0, 0 649*fae6e9adSlinfeng /// 0x79, 0x12, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, // load mem from r1[0x40] to r2 650*fae6e9adSlinfeng /// 0x07, 0x02, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, // add r2, 5 651*fae6e9adSlinfeng /// 0x79, 0x11, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, // load mem_end from r1[0x50] to r1 652*fae6e9adSlinfeng /// 0x2d, 0x12, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, // if r2 > r1 skip 3 instructions 653*fae6e9adSlinfeng /// 0x71, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // load r2 (= *(mem + 5)) into r0 654*fae6e9adSlinfeng /// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit 655*fae6e9adSlinfeng /// ]; 656*fae6e9adSlinfeng /// 657*fae6e9adSlinfeng /// let mem = &mut [ 658*fae6e9adSlinfeng /// 0xaa, 0xbb, 0x11, 0x22, 0xcc, 0x27, 659*fae6e9adSlinfeng /// ]; 660*fae6e9adSlinfeng /// 661*fae6e9adSlinfeng /// let mut vm = rbpf::EbpfVmFixedMbuff::new(Some(prog1), 0, 0).unwrap(); 662*fae6e9adSlinfeng /// vm.set_program(prog2, 0x40, 0x50); 663*fae6e9adSlinfeng /// 664*fae6e9adSlinfeng /// let res = vm.execute_program(mem).unwrap(); 665*fae6e9adSlinfeng /// assert_eq!(res, 0x27); 666*fae6e9adSlinfeng /// ``` set_program( &mut self, prog: &'a [u8], data_offset: usize, data_end_offset: usize, ) -> Result<(), Error>667*fae6e9adSlinfeng pub fn set_program( 668*fae6e9adSlinfeng &mut self, 669*fae6e9adSlinfeng prog: &'a [u8], 670*fae6e9adSlinfeng data_offset: usize, 671*fae6e9adSlinfeng data_end_offset: usize, 672*fae6e9adSlinfeng ) -> Result<(), Error> { 673*fae6e9adSlinfeng let get_buff_len = |x: usize, y: usize| if x >= y { x + 8 } else { y + 8 }; 674*fae6e9adSlinfeng let buffer = vec![0u8; get_buff_len(data_offset, data_end_offset)]; 675*fae6e9adSlinfeng self.mbuff.buffer = buffer; 676*fae6e9adSlinfeng self.mbuff.data_offset = data_offset; 677*fae6e9adSlinfeng self.mbuff.data_end_offset = data_end_offset; 678*fae6e9adSlinfeng self.parent.set_program(prog)?; 679*fae6e9adSlinfeng Ok(()) 680*fae6e9adSlinfeng } 681*fae6e9adSlinfeng 682*fae6e9adSlinfeng /// Set a new verifier function. The function should return an `Error` if the program should be 683*fae6e9adSlinfeng /// rejected by the virtual machine. If a program has been loaded to the VM already, the 684*fae6e9adSlinfeng /// verifier is immediately run. 685*fae6e9adSlinfeng /// 686*fae6e9adSlinfeng /// # Examples 687*fae6e9adSlinfeng /// 688*fae6e9adSlinfeng /// ``` 689*fae6e9adSlinfeng /// use rbpf::{Error, ErrorKind}; 690*fae6e9adSlinfeng /// use rbpf::ebpf; 691*fae6e9adSlinfeng /// 692*fae6e9adSlinfeng /// // Define a simple verifier function. 693*fae6e9adSlinfeng /// fn verifier(prog: &[u8]) -> Result<(), Error> { 694*fae6e9adSlinfeng /// let last_insn = ebpf::get_insn(prog, (prog.len() / ebpf::INSN_SIZE) - 1); 695*fae6e9adSlinfeng /// if last_insn.opc != ebpf::EXIT { 696*fae6e9adSlinfeng /// return Err(Error::new(ErrorKind::Other, 697*fae6e9adSlinfeng /// "[Verifier] Error: program does not end with “EXIT” instruction")); 698*fae6e9adSlinfeng /// } 699*fae6e9adSlinfeng /// Ok(()) 700*fae6e9adSlinfeng /// } 701*fae6e9adSlinfeng /// 702*fae6e9adSlinfeng /// let prog1 = &[ 703*fae6e9adSlinfeng /// 0xb7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r0, 0 704*fae6e9adSlinfeng /// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit 705*fae6e9adSlinfeng /// ]; 706*fae6e9adSlinfeng /// 707*fae6e9adSlinfeng /// // Instantiate a VM. 708*fae6e9adSlinfeng /// let mut vm = rbpf::EbpfVmMbuff::new(Some(prog1)).unwrap(); 709*fae6e9adSlinfeng /// // Change the verifier. 710*fae6e9adSlinfeng /// vm.set_verifier(verifier).unwrap(); 711*fae6e9adSlinfeng /// ``` set_verifier(&mut self, verifier: Verifier) -> Result<(), Error>712*fae6e9adSlinfeng pub fn set_verifier(&mut self, verifier: Verifier) -> Result<(), Error> { 713*fae6e9adSlinfeng self.parent.set_verifier(verifier) 714*fae6e9adSlinfeng } 715*fae6e9adSlinfeng 716*fae6e9adSlinfeng /// Register a built-in or user-defined helper function in order to use it later from within 717*fae6e9adSlinfeng /// the eBPF program. The helper is registered into a hashmap, so the `key` can be any `u32`. 718*fae6e9adSlinfeng /// 719*fae6e9adSlinfeng /// If using JIT-compiled eBPF programs, be sure to register all helpers before compiling the 720*fae6e9adSlinfeng /// program. You should be able to change registered helpers after compiling, but not to add 721*fae6e9adSlinfeng /// new ones (i.e. with new keys). 722*fae6e9adSlinfeng /// 723*fae6e9adSlinfeng /// # Examples 724*fae6e9adSlinfeng /// 725*fae6e9adSlinfeng /// ``` 726*fae6e9adSlinfeng /// #[cfg(feature = "std")] { 727*fae6e9adSlinfeng /// use rbpf::helpers; 728*fae6e9adSlinfeng /// 729*fae6e9adSlinfeng /// // This program was compiled with clang, from a C program containing the following single 730*fae6e9adSlinfeng /// // instruction: `return bpf_trace_printk("foo %c %c %c\n", 10, 1, 2, 3);` 731*fae6e9adSlinfeng /// let prog = &[ 732*fae6e9adSlinfeng /// 0xb7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r0, 0 733*fae6e9adSlinfeng /// 0x79, 0x12, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, // load mem from r1[0x40] to r2 734*fae6e9adSlinfeng /// 0x07, 0x02, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, // add r2, 5 735*fae6e9adSlinfeng /// 0x79, 0x11, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, // load mem_end from r1[0x50] to r1 736*fae6e9adSlinfeng /// 0x2d, 0x12, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, // if r2 > r1 skip 6 instructions 737*fae6e9adSlinfeng /// 0x71, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // load r2 (= *(mem + 5)) into r1 738*fae6e9adSlinfeng /// 0xb7, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r2, 0 739*fae6e9adSlinfeng /// 0xb7, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r3, 0 740*fae6e9adSlinfeng /// 0xb7, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r4, 0 741*fae6e9adSlinfeng /// 0xb7, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r5, 0 742*fae6e9adSlinfeng /// 0x85, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, // call helper with key 1 743*fae6e9adSlinfeng /// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit 744*fae6e9adSlinfeng /// ]; 745*fae6e9adSlinfeng /// 746*fae6e9adSlinfeng /// let mem = &mut [ 747*fae6e9adSlinfeng /// 0xaa, 0xbb, 0x11, 0x22, 0xcc, 0x09, 748*fae6e9adSlinfeng /// ]; 749*fae6e9adSlinfeng /// 750*fae6e9adSlinfeng /// // Instantiate a VM. 751*fae6e9adSlinfeng /// let mut vm = rbpf::EbpfVmFixedMbuff::new(Some(prog), 0x40, 0x50).unwrap(); 752*fae6e9adSlinfeng /// 753*fae6e9adSlinfeng /// // Register a helper. This helper will store the result of the square root of r1 into r0. 754*fae6e9adSlinfeng /// vm.register_helper(1, helpers::sqrti); 755*fae6e9adSlinfeng /// 756*fae6e9adSlinfeng /// let res = vm.execute_program(mem).unwrap(); 757*fae6e9adSlinfeng /// assert_eq!(res, 3); 758*fae6e9adSlinfeng /// } 759*fae6e9adSlinfeng /// ``` register_helper( &mut self, key: u32, function: fn(u64, u64, u64, u64, u64) -> u64, ) -> Result<(), Error>760*fae6e9adSlinfeng pub fn register_helper( 761*fae6e9adSlinfeng &mut self, 762*fae6e9adSlinfeng key: u32, 763*fae6e9adSlinfeng function: fn(u64, u64, u64, u64, u64) -> u64, 764*fae6e9adSlinfeng ) -> Result<(), Error> { 765*fae6e9adSlinfeng self.parent.register_helper(key, function) 766*fae6e9adSlinfeng } 767*fae6e9adSlinfeng 768*fae6e9adSlinfeng /// Execute the program loaded, with the given packet data. 769*fae6e9adSlinfeng /// 770*fae6e9adSlinfeng /// If the program is made to be compatible with Linux kernel, it is expected to load the 771*fae6e9adSlinfeng /// address of the beginning and of the end of the memory area used for packet data from some 772*fae6e9adSlinfeng /// metadata buffer, which in the case of this VM is handled internally. The offsets at which 773*fae6e9adSlinfeng /// the addresses should be placed should have be set at the creation of the VM. 774*fae6e9adSlinfeng /// 775*fae6e9adSlinfeng /// # Examples 776*fae6e9adSlinfeng /// 777*fae6e9adSlinfeng /// ``` 778*fae6e9adSlinfeng /// let prog = &[ 779*fae6e9adSlinfeng /// 0xb7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r0, 0 780*fae6e9adSlinfeng /// 0x79, 0x12, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, // load mem from r1[0x40] to r2 781*fae6e9adSlinfeng /// 0x07, 0x02, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, // add r2, 5 782*fae6e9adSlinfeng /// 0x79, 0x11, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, // load mem_end from r1[0x50] to r1 783*fae6e9adSlinfeng /// 0x2d, 0x12, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, // if r2 > r1 skip 3 instructions 784*fae6e9adSlinfeng /// 0x71, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // load r2 (= *(mem + 5)) into r0 785*fae6e9adSlinfeng /// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit 786*fae6e9adSlinfeng /// ]; 787*fae6e9adSlinfeng /// let mem = &mut [ 788*fae6e9adSlinfeng /// 0xaa, 0xbb, 0x11, 0x22, 0xcc, 0xdd 789*fae6e9adSlinfeng /// ]; 790*fae6e9adSlinfeng /// 791*fae6e9adSlinfeng /// // Instantiate a VM. Note that we provide the start and end offsets for mem pointers. 792*fae6e9adSlinfeng /// let mut vm = rbpf::EbpfVmFixedMbuff::new(Some(prog), 0x40, 0x50).unwrap(); 793*fae6e9adSlinfeng /// 794*fae6e9adSlinfeng /// // Provide only a reference to the packet data. We do not manage the metadata buffer. 795*fae6e9adSlinfeng /// let res = vm.execute_program(mem).unwrap(); 796*fae6e9adSlinfeng /// assert_eq!(res, 0xdd); 797*fae6e9adSlinfeng /// ``` execute_program(&mut self, mem: &'a mut [u8]) -> Result<u64, Error>798*fae6e9adSlinfeng pub fn execute_program(&mut self, mem: &'a mut [u8]) -> Result<u64, Error> { 799*fae6e9adSlinfeng let l = self.mbuff.buffer.len(); 800*fae6e9adSlinfeng // Can this ever happen? Probably not, should be ensured at mbuff creation. 801*fae6e9adSlinfeng if self.mbuff.data_offset + 8 > l || self.mbuff.data_end_offset + 8 > l { 802*fae6e9adSlinfeng Err(Error::new(ErrorKind::Other, format!("Error: buffer too small ({:?}), cannot use data_offset {:?} and data_end_offset {:?}", 803*fae6e9adSlinfeng l, self.mbuff.data_offset, self.mbuff.data_end_offset)))?; 804*fae6e9adSlinfeng } 805*fae6e9adSlinfeng LittleEndian::write_u64( 806*fae6e9adSlinfeng &mut self.mbuff.buffer[(self.mbuff.data_offset)..], 807*fae6e9adSlinfeng mem.as_ptr() as u64, 808*fae6e9adSlinfeng ); 809*fae6e9adSlinfeng LittleEndian::write_u64( 810*fae6e9adSlinfeng &mut self.mbuff.buffer[(self.mbuff.data_end_offset)..], 811*fae6e9adSlinfeng mem.as_ptr() as u64 + mem.len() as u64, 812*fae6e9adSlinfeng ); 813*fae6e9adSlinfeng self.parent.execute_program(mem, &self.mbuff.buffer) 814*fae6e9adSlinfeng } 815*fae6e9adSlinfeng 816*fae6e9adSlinfeng /// JIT-compile the loaded program. No argument required for this. 817*fae6e9adSlinfeng /// 818*fae6e9adSlinfeng /// If using helper functions, be sure to register them into the VM before calling this 819*fae6e9adSlinfeng /// function. 820*fae6e9adSlinfeng /// 821*fae6e9adSlinfeng /// # Examples 822*fae6e9adSlinfeng /// 823*fae6e9adSlinfeng /// ``` 824*fae6e9adSlinfeng /// let prog = &[ 825*fae6e9adSlinfeng /// 0xb7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r0, 0 826*fae6e9adSlinfeng /// 0x79, 0x12, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, // load mem from r1[0x40] to r2 827*fae6e9adSlinfeng /// 0x07, 0x02, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, // add r2, 5 828*fae6e9adSlinfeng /// 0x79, 0x11, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, // load mem_end from r1[0x50] to r1 829*fae6e9adSlinfeng /// 0x2d, 0x12, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, // if r2 > r1 skip 3 instructions 830*fae6e9adSlinfeng /// 0x71, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // load r2 (= *(mem + 5)) into r0 831*fae6e9adSlinfeng /// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit 832*fae6e9adSlinfeng /// ]; 833*fae6e9adSlinfeng /// 834*fae6e9adSlinfeng /// // Instantiate a VM. Note that we provide the start and end offsets for mem pointers. 835*fae6e9adSlinfeng /// let mut vm = rbpf::EbpfVmFixedMbuff::new(Some(prog), 0x40, 0x50).unwrap(); 836*fae6e9adSlinfeng /// 837*fae6e9adSlinfeng /// vm.jit_compile(); 838*fae6e9adSlinfeng /// ``` 839*fae6e9adSlinfeng #[cfg(all(not(windows), feature = "std"))] jit_compile(&mut self) -> Result<(), Error>840*fae6e9adSlinfeng pub fn jit_compile(&mut self) -> Result<(), Error> { 841*fae6e9adSlinfeng let prog = match self.parent.prog { 842*fae6e9adSlinfeng Some(prog) => prog, 843*fae6e9adSlinfeng None => Err(Error::new( 844*fae6e9adSlinfeng ErrorKind::Other, 845*fae6e9adSlinfeng "Error: No program set, call prog_set() to load one", 846*fae6e9adSlinfeng ))?, 847*fae6e9adSlinfeng }; 848*fae6e9adSlinfeng self.parent.jit = Some(jit::JitMemory::new(prog, &self.parent.helpers, true, true)?); 849*fae6e9adSlinfeng Ok(()) 850*fae6e9adSlinfeng } 851*fae6e9adSlinfeng 852*fae6e9adSlinfeng /// Execute the previously JIT-compiled program, with the given packet data, in a manner very 853*fae6e9adSlinfeng /// similar to `execute_program()`. 854*fae6e9adSlinfeng /// 855*fae6e9adSlinfeng /// If the program is made to be compatible with Linux kernel, it is expected to load the 856*fae6e9adSlinfeng /// address of the beginning and of the end of the memory area used for packet data from some 857*fae6e9adSlinfeng /// metadata buffer, which in the case of this VM is handled internally. The offsets at which 858*fae6e9adSlinfeng /// the addresses should be placed should have be set at the creation of the VM. 859*fae6e9adSlinfeng /// 860*fae6e9adSlinfeng /// # Safety 861*fae6e9adSlinfeng /// 862*fae6e9adSlinfeng /// **WARNING:** JIT-compiled assembly code is not safe, in particular there is no runtime 863*fae6e9adSlinfeng /// check for memory access; so if the eBPF program attempts erroneous accesses, this may end 864*fae6e9adSlinfeng /// very bad (program may segfault). It may be wise to check that the program works with the 865*fae6e9adSlinfeng /// interpreter before running the JIT-compiled version of it. 866*fae6e9adSlinfeng /// 867*fae6e9adSlinfeng /// For this reason the function should be called from within an `unsafe` bloc. 868*fae6e9adSlinfeng /// 869*fae6e9adSlinfeng /// # Examples 870*fae6e9adSlinfeng /// 871*fae6e9adSlinfeng /// ``` 872*fae6e9adSlinfeng /// let prog = &[ 873*fae6e9adSlinfeng /// 0xb7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r0, 0 874*fae6e9adSlinfeng /// 0x79, 0x12, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, // load mem from r1[0x40] to r2 875*fae6e9adSlinfeng /// 0x07, 0x02, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, // add r2, 5 876*fae6e9adSlinfeng /// 0x79, 0x11, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, // load mem_end from r1[0x50] to r1 877*fae6e9adSlinfeng /// 0x2d, 0x12, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, // if r2 > r1 skip 3 instructions 878*fae6e9adSlinfeng /// 0x71, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // load r2 (= *(mem + 5)) into r0 879*fae6e9adSlinfeng /// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit 880*fae6e9adSlinfeng /// ]; 881*fae6e9adSlinfeng /// let mem = &mut [ 882*fae6e9adSlinfeng /// 0xaa, 0xbb, 0x11, 0x22, 0xcc, 0xdd 883*fae6e9adSlinfeng /// ]; 884*fae6e9adSlinfeng /// 885*fae6e9adSlinfeng /// // Instantiate a VM. Note that we provide the start and end offsets for mem pointers. 886*fae6e9adSlinfeng /// let mut vm = rbpf::EbpfVmFixedMbuff::new(Some(prog), 0x40, 0x50).unwrap(); 887*fae6e9adSlinfeng /// 888*fae6e9adSlinfeng /// # #[cfg(all(not(windows), feature = "std"))] 889*fae6e9adSlinfeng /// vm.jit_compile(); 890*fae6e9adSlinfeng /// 891*fae6e9adSlinfeng /// // Provide only a reference to the packet data. We do not manage the metadata buffer. 892*fae6e9adSlinfeng /// # #[cfg(all(not(windows), feature = "std"))] 893*fae6e9adSlinfeng /// unsafe { 894*fae6e9adSlinfeng /// let res = vm.execute_program_jit(mem).unwrap(); 895*fae6e9adSlinfeng /// assert_eq!(res, 0xdd); 896*fae6e9adSlinfeng /// } 897*fae6e9adSlinfeng /// ``` 898*fae6e9adSlinfeng // This struct redefines the `execute_program_jit()` function, in order to pass the offsets 899*fae6e9adSlinfeng // associated with the fixed mbuff. 900*fae6e9adSlinfeng #[cfg(all(not(windows), feature = "std"))] execute_program_jit(&mut self, mem: &'a mut [u8]) -> Result<u64, Error>901*fae6e9adSlinfeng pub unsafe fn execute_program_jit(&mut self, mem: &'a mut [u8]) -> Result<u64, Error> { 902*fae6e9adSlinfeng // If packet data is empty, do not send the address of an empty slice; send a null pointer 903*fae6e9adSlinfeng // as first argument instead, as this is uBPF's behavior (empty packet should not happen 904*fae6e9adSlinfeng // in the kernel; anyway the verifier would prevent the use of uninitialized registers). 905*fae6e9adSlinfeng // See `mul_loop` test. 906*fae6e9adSlinfeng let mem_ptr = match mem.len() { 907*fae6e9adSlinfeng 0 => core::ptr::null_mut(), 908*fae6e9adSlinfeng _ => mem.as_ptr() as *mut u8, 909*fae6e9adSlinfeng }; 910*fae6e9adSlinfeng 911*fae6e9adSlinfeng match &self.parent.jit { 912*fae6e9adSlinfeng Some(jit) => Ok(jit.get_prog()( 913*fae6e9adSlinfeng self.mbuff.buffer.as_ptr() as *mut u8, 914*fae6e9adSlinfeng self.mbuff.buffer.len(), 915*fae6e9adSlinfeng mem_ptr, 916*fae6e9adSlinfeng mem.len(), 917*fae6e9adSlinfeng self.mbuff.data_offset, 918*fae6e9adSlinfeng self.mbuff.data_end_offset, 919*fae6e9adSlinfeng )), 920*fae6e9adSlinfeng None => Err(Error::new( 921*fae6e9adSlinfeng ErrorKind::Other, 922*fae6e9adSlinfeng "Error: program has not been JIT-compiled", 923*fae6e9adSlinfeng )), 924*fae6e9adSlinfeng } 925*fae6e9adSlinfeng } 926*fae6e9adSlinfeng 927*fae6e9adSlinfeng /// Compile the loaded program using the Cranelift JIT. 928*fae6e9adSlinfeng /// 929*fae6e9adSlinfeng /// If using helper functions, be sure to register them into the VM before calling this 930*fae6e9adSlinfeng /// function. 931*fae6e9adSlinfeng /// 932*fae6e9adSlinfeng /// # Examples 933*fae6e9adSlinfeng /// 934*fae6e9adSlinfeng /// ``` 935*fae6e9adSlinfeng /// let prog = &[ 936*fae6e9adSlinfeng /// 0xb7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r0, 0 937*fae6e9adSlinfeng /// 0x79, 0x12, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, // load mem from r1[0x40] to r2 938*fae6e9adSlinfeng /// 0x07, 0x02, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, // add r2, 5 939*fae6e9adSlinfeng /// 0x79, 0x11, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, // load mem_end from r1[0x50] to r1 940*fae6e9adSlinfeng /// 0x2d, 0x12, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, // if r2 > r1 skip 3 instructions 941*fae6e9adSlinfeng /// 0x71, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // load r2 (= *(mem + 5)) into r0 942*fae6e9adSlinfeng /// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit 943*fae6e9adSlinfeng /// ]; 944*fae6e9adSlinfeng /// 945*fae6e9adSlinfeng /// // Instantiate a VM. Note that we provide the start and end offsets for mem pointers. 946*fae6e9adSlinfeng /// let mut vm = rbpf::EbpfVmFixedMbuff::new(Some(prog), 0x40, 0x50).unwrap(); 947*fae6e9adSlinfeng /// 948*fae6e9adSlinfeng /// vm.cranelift_compile(); 949*fae6e9adSlinfeng /// ``` 950*fae6e9adSlinfeng #[cfg(feature = "cranelift")] cranelift_compile(&mut self) -> Result<(), Error>951*fae6e9adSlinfeng pub fn cranelift_compile(&mut self) -> Result<(), Error> { 952*fae6e9adSlinfeng use crate::cranelift::CraneliftCompiler; 953*fae6e9adSlinfeng 954*fae6e9adSlinfeng let prog = match self.parent.prog { 955*fae6e9adSlinfeng Some(prog) => prog, 956*fae6e9adSlinfeng None => Err(Error::new( 957*fae6e9adSlinfeng ErrorKind::Other, 958*fae6e9adSlinfeng "Error: No program set, call prog_set() to load one", 959*fae6e9adSlinfeng ))?, 960*fae6e9adSlinfeng }; 961*fae6e9adSlinfeng 962*fae6e9adSlinfeng let mut compiler = CraneliftCompiler::new(self.parent.helpers.clone()); 963*fae6e9adSlinfeng let program = compiler.compile_function(prog)?; 964*fae6e9adSlinfeng 965*fae6e9adSlinfeng self.parent.cranelift_prog = Some(program); 966*fae6e9adSlinfeng Ok(()) 967*fae6e9adSlinfeng } 968*fae6e9adSlinfeng 969*fae6e9adSlinfeng /// Execute the previously compiled program, with the given packet data and metadata 970*fae6e9adSlinfeng /// buffer, in a manner very similar to `execute_program()`. 971*fae6e9adSlinfeng /// 972*fae6e9adSlinfeng /// If the program is made to be compatible with Linux kernel, it is expected to load the 973*fae6e9adSlinfeng /// address of the beginning and of the end of the memory area used for packet data from some 974*fae6e9adSlinfeng /// metadata buffer, which in the case of this VM is handled internally. The offsets at which 975*fae6e9adSlinfeng /// the addresses should be placed should have be set at the creation of the VM. 976*fae6e9adSlinfeng /// 977*fae6e9adSlinfeng /// # Examples 978*fae6e9adSlinfeng /// 979*fae6e9adSlinfeng /// ``` 980*fae6e9adSlinfeng /// let prog = &[ 981*fae6e9adSlinfeng /// 0xb7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r0, 0 982*fae6e9adSlinfeng /// 0x79, 0x12, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, // load mem from r1[0x40] to r2 983*fae6e9adSlinfeng /// 0x07, 0x02, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, // add r2, 5 984*fae6e9adSlinfeng /// 0x79, 0x11, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, // load mem_end from r1[0x50] to r1 985*fae6e9adSlinfeng /// 0x2d, 0x12, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, // if r2 > r1 skip 3 instructions 986*fae6e9adSlinfeng /// 0x71, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // load r2 (= *(mem + 5)) into r0 987*fae6e9adSlinfeng /// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit 988*fae6e9adSlinfeng /// ]; 989*fae6e9adSlinfeng /// let mem = &mut [ 990*fae6e9adSlinfeng /// 0xaa, 0xbb, 0x11, 0x22, 0xcc, 0xdd 991*fae6e9adSlinfeng /// ]; 992*fae6e9adSlinfeng /// 993*fae6e9adSlinfeng /// // Instantiate a VM. Note that we provide the start and end offsets for mem pointers. 994*fae6e9adSlinfeng /// let mut vm = rbpf::EbpfVmFixedMbuff::new(Some(prog), 0x40, 0x50).unwrap(); 995*fae6e9adSlinfeng /// 996*fae6e9adSlinfeng /// vm.cranelift_compile(); 997*fae6e9adSlinfeng /// 998*fae6e9adSlinfeng /// // Provide only a reference to the packet data. We do not manage the metadata buffer. 999*fae6e9adSlinfeng /// let res = vm.execute_program_cranelift(mem).unwrap(); 1000*fae6e9adSlinfeng /// assert_eq!(res, 0xdd); 1001*fae6e9adSlinfeng /// ``` 1002*fae6e9adSlinfeng #[cfg(feature = "cranelift")] execute_program_cranelift(&mut self, mem: &'a mut [u8]) -> Result<u64, Error>1003*fae6e9adSlinfeng pub fn execute_program_cranelift(&mut self, mem: &'a mut [u8]) -> Result<u64, Error> { 1004*fae6e9adSlinfeng // If packet data is empty, do not send the address of an empty slice; send a null pointer 1005*fae6e9adSlinfeng // as first argument instead, as this is uBPF's behavior (empty packet should not happen 1006*fae6e9adSlinfeng // in the kernel; anyway the verifier would prevent the use of uninitialized registers). 1007*fae6e9adSlinfeng // See `mul_loop` test. 1008*fae6e9adSlinfeng let mem_ptr = match mem.len() { 1009*fae6e9adSlinfeng 0 => core::ptr::null_mut(), 1010*fae6e9adSlinfeng _ => mem.as_ptr() as *mut u8, 1011*fae6e9adSlinfeng }; 1012*fae6e9adSlinfeng 1013*fae6e9adSlinfeng let l = self.mbuff.buffer.len(); 1014*fae6e9adSlinfeng // Can this ever happen? Probably not, should be ensured at mbuff creation. 1015*fae6e9adSlinfeng if self.mbuff.data_offset + 8 > l || self.mbuff.data_end_offset + 8 > l { 1016*fae6e9adSlinfeng Err(Error::new(ErrorKind::Other, format!("Error: buffer too small ({:?}), cannot use data_offset {:?} and data_end_offset {:?}", 1017*fae6e9adSlinfeng l, self.mbuff.data_offset, self.mbuff.data_end_offset)))?; 1018*fae6e9adSlinfeng } 1019*fae6e9adSlinfeng LittleEndian::write_u64( 1020*fae6e9adSlinfeng &mut self.mbuff.buffer[(self.mbuff.data_offset)..], 1021*fae6e9adSlinfeng mem.as_ptr() as u64, 1022*fae6e9adSlinfeng ); 1023*fae6e9adSlinfeng LittleEndian::write_u64( 1024*fae6e9adSlinfeng &mut self.mbuff.buffer[(self.mbuff.data_end_offset)..], 1025*fae6e9adSlinfeng mem.as_ptr() as u64 + mem.len() as u64, 1026*fae6e9adSlinfeng ); 1027*fae6e9adSlinfeng 1028*fae6e9adSlinfeng match &self.parent.cranelift_prog { 1029*fae6e9adSlinfeng Some(prog) => Ok(prog.execute( 1030*fae6e9adSlinfeng mem_ptr, 1031*fae6e9adSlinfeng mem.len(), 1032*fae6e9adSlinfeng self.mbuff.buffer.as_ptr() as *mut u8, 1033*fae6e9adSlinfeng self.mbuff.buffer.len(), 1034*fae6e9adSlinfeng )), 1035*fae6e9adSlinfeng None => Err(Error::new( 1036*fae6e9adSlinfeng ErrorKind::Other, 1037*fae6e9adSlinfeng "Error: program has not been compiled with cranelift", 1038*fae6e9adSlinfeng )), 1039*fae6e9adSlinfeng } 1040*fae6e9adSlinfeng } 1041*fae6e9adSlinfeng } 1042*fae6e9adSlinfeng 1043*fae6e9adSlinfeng /// A virtual machine to run eBPF program. This kind of VM is used for programs expecting to work 1044*fae6e9adSlinfeng /// directly on the memory area representing packet data. 1045*fae6e9adSlinfeng /// 1046*fae6e9adSlinfeng /// # Examples 1047*fae6e9adSlinfeng /// 1048*fae6e9adSlinfeng /// ``` 1049*fae6e9adSlinfeng /// let prog = &[ 1050*fae6e9adSlinfeng /// 0x71, 0x11, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, // ldxb r1[0x04], r1 1051*fae6e9adSlinfeng /// 0x07, 0x01, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, // add r1, 0x22 1052*fae6e9adSlinfeng /// 0xbf, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r0, r1 1053*fae6e9adSlinfeng /// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit 1054*fae6e9adSlinfeng /// ]; 1055*fae6e9adSlinfeng /// let mem = &mut [ 1056*fae6e9adSlinfeng /// 0xaa, 0xbb, 0x11, 0x22, 0xcc, 0xdd 1057*fae6e9adSlinfeng /// ]; 1058*fae6e9adSlinfeng /// 1059*fae6e9adSlinfeng /// // Instantiate a VM. 1060*fae6e9adSlinfeng /// let vm = rbpf::EbpfVmRaw::new(Some(prog)).unwrap(); 1061*fae6e9adSlinfeng /// 1062*fae6e9adSlinfeng /// // Provide only a reference to the packet data. 1063*fae6e9adSlinfeng /// let res = vm.execute_program(mem).unwrap(); 1064*fae6e9adSlinfeng /// assert_eq!(res, 0x22cc); 1065*fae6e9adSlinfeng /// ``` 1066*fae6e9adSlinfeng pub struct EbpfVmRaw<'a> { 1067*fae6e9adSlinfeng parent: EbpfVmMbuff<'a>, 1068*fae6e9adSlinfeng } 1069*fae6e9adSlinfeng 1070*fae6e9adSlinfeng impl<'a> EbpfVmRaw<'a> { 1071*fae6e9adSlinfeng /// Create a new virtual machine instance, and load an eBPF program into that instance. 1072*fae6e9adSlinfeng /// When attempting to load the program, it passes through a simple verifier. 1073*fae6e9adSlinfeng /// 1074*fae6e9adSlinfeng /// # Examples 1075*fae6e9adSlinfeng /// 1076*fae6e9adSlinfeng /// ``` 1077*fae6e9adSlinfeng /// let prog = &[ 1078*fae6e9adSlinfeng /// 0x71, 0x11, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, // ldxb r1[0x04], r1 1079*fae6e9adSlinfeng /// 0x07, 0x01, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, // add r1, 0x22 1080*fae6e9adSlinfeng /// 0xbf, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r0, r1 1081*fae6e9adSlinfeng /// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit 1082*fae6e9adSlinfeng /// ]; 1083*fae6e9adSlinfeng /// 1084*fae6e9adSlinfeng /// // Instantiate a VM. 1085*fae6e9adSlinfeng /// let vm = rbpf::EbpfVmRaw::new(Some(prog)).unwrap(); 1086*fae6e9adSlinfeng /// ``` new(prog: Option<&'a [u8]>) -> Result<EbpfVmRaw<'a>, Error>1087*fae6e9adSlinfeng pub fn new(prog: Option<&'a [u8]>) -> Result<EbpfVmRaw<'a>, Error> { 1088*fae6e9adSlinfeng let parent = EbpfVmMbuff::new(prog)?; 1089*fae6e9adSlinfeng Ok(EbpfVmRaw { parent }) 1090*fae6e9adSlinfeng } 1091*fae6e9adSlinfeng 1092*fae6e9adSlinfeng /// Load a new eBPF program into the virtual machine instance. 1093*fae6e9adSlinfeng /// 1094*fae6e9adSlinfeng /// # Examples 1095*fae6e9adSlinfeng /// 1096*fae6e9adSlinfeng /// ``` 1097*fae6e9adSlinfeng /// let prog1 = &[ 1098*fae6e9adSlinfeng /// 0xb7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r0, 0 1099*fae6e9adSlinfeng /// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit 1100*fae6e9adSlinfeng /// ]; 1101*fae6e9adSlinfeng /// let prog2 = &[ 1102*fae6e9adSlinfeng /// 0x71, 0x11, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, // ldxb r1[0x04], r1 1103*fae6e9adSlinfeng /// 0x07, 0x01, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, // add r1, 0x22 1104*fae6e9adSlinfeng /// 0xbf, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r0, r1 1105*fae6e9adSlinfeng /// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit 1106*fae6e9adSlinfeng /// ]; 1107*fae6e9adSlinfeng /// 1108*fae6e9adSlinfeng /// let mem = &mut [ 1109*fae6e9adSlinfeng /// 0xaa, 0xbb, 0x11, 0x22, 0xcc, 0x27, 1110*fae6e9adSlinfeng /// ]; 1111*fae6e9adSlinfeng /// 1112*fae6e9adSlinfeng /// let mut vm = rbpf::EbpfVmRaw::new(Some(prog1)).unwrap(); 1113*fae6e9adSlinfeng /// vm.set_program(prog2); 1114*fae6e9adSlinfeng /// 1115*fae6e9adSlinfeng /// let res = vm.execute_program(mem).unwrap(); 1116*fae6e9adSlinfeng /// assert_eq!(res, 0x22cc); 1117*fae6e9adSlinfeng /// ``` set_program(&mut self, prog: &'a [u8]) -> Result<(), Error>1118*fae6e9adSlinfeng pub fn set_program(&mut self, prog: &'a [u8]) -> Result<(), Error> { 1119*fae6e9adSlinfeng self.parent.set_program(prog)?; 1120*fae6e9adSlinfeng Ok(()) 1121*fae6e9adSlinfeng } 1122*fae6e9adSlinfeng 1123*fae6e9adSlinfeng /// Set a new verifier function. The function should return an `Error` if the program should be 1124*fae6e9adSlinfeng /// rejected by the virtual machine. If a program has been loaded to the VM already, the 1125*fae6e9adSlinfeng /// verifier is immediately run. 1126*fae6e9adSlinfeng /// 1127*fae6e9adSlinfeng /// # Examples 1128*fae6e9adSlinfeng /// 1129*fae6e9adSlinfeng /// ``` 1130*fae6e9adSlinfeng /// use rbpf::{Error, ErrorKind}; 1131*fae6e9adSlinfeng /// use rbpf::ebpf; 1132*fae6e9adSlinfeng /// 1133*fae6e9adSlinfeng /// // Define a simple verifier function. 1134*fae6e9adSlinfeng /// fn verifier(prog: &[u8]) -> Result<(), Error> { 1135*fae6e9adSlinfeng /// let last_insn = ebpf::get_insn(prog, (prog.len() / ebpf::INSN_SIZE) - 1); 1136*fae6e9adSlinfeng /// if last_insn.opc != ebpf::EXIT { 1137*fae6e9adSlinfeng /// return Err(Error::new(ErrorKind::Other, 1138*fae6e9adSlinfeng /// "[Verifier] Error: program does not end with “EXIT” instruction")); 1139*fae6e9adSlinfeng /// } 1140*fae6e9adSlinfeng /// Ok(()) 1141*fae6e9adSlinfeng /// } 1142*fae6e9adSlinfeng /// 1143*fae6e9adSlinfeng /// let prog1 = &[ 1144*fae6e9adSlinfeng /// 0xb7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r0, 0 1145*fae6e9adSlinfeng /// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit 1146*fae6e9adSlinfeng /// ]; 1147*fae6e9adSlinfeng /// 1148*fae6e9adSlinfeng /// // Instantiate a VM. 1149*fae6e9adSlinfeng /// let mut vm = rbpf::EbpfVmMbuff::new(Some(prog1)).unwrap(); 1150*fae6e9adSlinfeng /// // Change the verifier. 1151*fae6e9adSlinfeng /// vm.set_verifier(verifier).unwrap(); 1152*fae6e9adSlinfeng /// ``` set_verifier(&mut self, verifier: Verifier) -> Result<(), Error>1153*fae6e9adSlinfeng pub fn set_verifier(&mut self, verifier: Verifier) -> Result<(), Error> { 1154*fae6e9adSlinfeng self.parent.set_verifier(verifier) 1155*fae6e9adSlinfeng } 1156*fae6e9adSlinfeng 1157*fae6e9adSlinfeng /// Register a built-in or user-defined helper function in order to use it later from within 1158*fae6e9adSlinfeng /// the eBPF program. The helper is registered into a hashmap, so the `key` can be any `u32`. 1159*fae6e9adSlinfeng /// 1160*fae6e9adSlinfeng /// If using JIT-compiled eBPF programs, be sure to register all helpers before compiling the 1161*fae6e9adSlinfeng /// program. You should be able to change registered helpers after compiling, but not to add 1162*fae6e9adSlinfeng /// new ones (i.e. with new keys). 1163*fae6e9adSlinfeng /// 1164*fae6e9adSlinfeng /// # Examples 1165*fae6e9adSlinfeng /// 1166*fae6e9adSlinfeng /// ``` 1167*fae6e9adSlinfeng /// #[cfg(feature = "std")] { 1168*fae6e9adSlinfeng /// use rbpf::helpers; 1169*fae6e9adSlinfeng /// 1170*fae6e9adSlinfeng /// let prog = &[ 1171*fae6e9adSlinfeng /// 0x79, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ldxdw r1, r1[0x00] 1172*fae6e9adSlinfeng /// 0xb7, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r2, 0 1173*fae6e9adSlinfeng /// 0xb7, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r3, 0 1174*fae6e9adSlinfeng /// 0xb7, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r4, 0 1175*fae6e9adSlinfeng /// 0xb7, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r5, 0 1176*fae6e9adSlinfeng /// 0x85, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, // call helper with key 1 1177*fae6e9adSlinfeng /// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit 1178*fae6e9adSlinfeng /// ]; 1179*fae6e9adSlinfeng /// 1180*fae6e9adSlinfeng /// let mem = &mut [ 1181*fae6e9adSlinfeng /// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 1182*fae6e9adSlinfeng /// ]; 1183*fae6e9adSlinfeng /// 1184*fae6e9adSlinfeng /// // Instantiate a VM. 1185*fae6e9adSlinfeng /// let mut vm = rbpf::EbpfVmRaw::new(Some(prog)).unwrap(); 1186*fae6e9adSlinfeng /// 1187*fae6e9adSlinfeng /// // Register a helper. This helper will store the result of the square root of r1 into r0. 1188*fae6e9adSlinfeng /// vm.register_helper(1, helpers::sqrti); 1189*fae6e9adSlinfeng /// 1190*fae6e9adSlinfeng /// let res = vm.execute_program(mem).unwrap(); 1191*fae6e9adSlinfeng /// assert_eq!(res, 0x10000000); 1192*fae6e9adSlinfeng /// } 1193*fae6e9adSlinfeng /// ``` register_helper( &mut self, key: u32, function: fn(u64, u64, u64, u64, u64) -> u64, ) -> Result<(), Error>1194*fae6e9adSlinfeng pub fn register_helper( 1195*fae6e9adSlinfeng &mut self, 1196*fae6e9adSlinfeng key: u32, 1197*fae6e9adSlinfeng function: fn(u64, u64, u64, u64, u64) -> u64, 1198*fae6e9adSlinfeng ) -> Result<(), Error> { 1199*fae6e9adSlinfeng self.parent.register_helper(key, function) 1200*fae6e9adSlinfeng } 1201*fae6e9adSlinfeng 1202*fae6e9adSlinfeng /// Register a set of built-in or user-defined helper functions in order to use them later from 1203*fae6e9adSlinfeng /// within the eBPF program. The helpers are registered into a hashmap, so the `key` can be any 1204*fae6e9adSlinfeng /// `u32`. 1205*fae6e9adSlinfeng #[allow(clippy::type_complexity)] register_helper_set( &mut self, helpers: &HashMap<u32, fn(u64, u64, u64, u64, u64) -> u64>, ) -> Result<(), Error>1206*fae6e9adSlinfeng pub fn register_helper_set( 1207*fae6e9adSlinfeng &mut self, 1208*fae6e9adSlinfeng helpers: &HashMap<u32, fn(u64, u64, u64, u64, u64) -> u64>, 1209*fae6e9adSlinfeng ) -> Result<(), Error> { 1210*fae6e9adSlinfeng for (key, function) in helpers { 1211*fae6e9adSlinfeng self.parent.register_helper(*key, *function)?; 1212*fae6e9adSlinfeng } 1213*fae6e9adSlinfeng Ok(()) 1214*fae6e9adSlinfeng } 1215*fae6e9adSlinfeng 1216*fae6e9adSlinfeng /// Execute the program loaded, with the given packet data. 1217*fae6e9adSlinfeng /// 1218*fae6e9adSlinfeng /// # Examples 1219*fae6e9adSlinfeng /// 1220*fae6e9adSlinfeng /// ``` 1221*fae6e9adSlinfeng /// let prog = &[ 1222*fae6e9adSlinfeng /// 0x71, 0x11, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, // ldxb r1[0x04], r1 1223*fae6e9adSlinfeng /// 0x07, 0x01, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, // add r1, 0x22 1224*fae6e9adSlinfeng /// 0xbf, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r0, r1 1225*fae6e9adSlinfeng /// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit 1226*fae6e9adSlinfeng /// ]; 1227*fae6e9adSlinfeng /// 1228*fae6e9adSlinfeng /// let mem = &mut [ 1229*fae6e9adSlinfeng /// 0xaa, 0xbb, 0x11, 0x22, 0xcc, 0x27 1230*fae6e9adSlinfeng /// ]; 1231*fae6e9adSlinfeng /// 1232*fae6e9adSlinfeng /// let mut vm = rbpf::EbpfVmRaw::new(Some(prog)).unwrap(); 1233*fae6e9adSlinfeng /// 1234*fae6e9adSlinfeng /// let res = vm.execute_program(mem).unwrap(); 1235*fae6e9adSlinfeng /// assert_eq!(res, 0x22cc); 1236*fae6e9adSlinfeng /// ``` execute_program(&self, mem: &'a mut [u8]) -> Result<u64, Error>1237*fae6e9adSlinfeng pub fn execute_program(&self, mem: &'a mut [u8]) -> Result<u64, Error> { 1238*fae6e9adSlinfeng self.parent.execute_program(mem, &[]) 1239*fae6e9adSlinfeng } 1240*fae6e9adSlinfeng 1241*fae6e9adSlinfeng /// JIT-compile the loaded program. No argument required for this. 1242*fae6e9adSlinfeng /// 1243*fae6e9adSlinfeng /// If using helper functions, be sure to register them into the VM before calling this 1244*fae6e9adSlinfeng /// function. 1245*fae6e9adSlinfeng /// 1246*fae6e9adSlinfeng /// # Examples 1247*fae6e9adSlinfeng /// 1248*fae6e9adSlinfeng /// ``` 1249*fae6e9adSlinfeng /// let prog = &[ 1250*fae6e9adSlinfeng /// 0x71, 0x11, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, // ldxb r1[0x04], r1 1251*fae6e9adSlinfeng /// 0x07, 0x01, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, // add r1, 0x22 1252*fae6e9adSlinfeng /// 0xbf, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r0, r1 1253*fae6e9adSlinfeng /// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit 1254*fae6e9adSlinfeng /// ]; 1255*fae6e9adSlinfeng /// 1256*fae6e9adSlinfeng /// let mut vm = rbpf::EbpfVmRaw::new(Some(prog)).unwrap(); 1257*fae6e9adSlinfeng /// 1258*fae6e9adSlinfeng /// vm.jit_compile(); 1259*fae6e9adSlinfeng /// ``` 1260*fae6e9adSlinfeng #[cfg(all(not(windows), feature = "std"))] jit_compile(&mut self) -> Result<(), Error>1261*fae6e9adSlinfeng pub fn jit_compile(&mut self) -> Result<(), Error> { 1262*fae6e9adSlinfeng let prog = match self.parent.prog { 1263*fae6e9adSlinfeng Some(prog) => prog, 1264*fae6e9adSlinfeng None => Err(Error::new( 1265*fae6e9adSlinfeng ErrorKind::Other, 1266*fae6e9adSlinfeng "Error: No program set, call prog_set() to load one", 1267*fae6e9adSlinfeng ))?, 1268*fae6e9adSlinfeng }; 1269*fae6e9adSlinfeng self.parent.jit = Some(jit::JitMemory::new( 1270*fae6e9adSlinfeng prog, 1271*fae6e9adSlinfeng &self.parent.helpers, 1272*fae6e9adSlinfeng false, 1273*fae6e9adSlinfeng false, 1274*fae6e9adSlinfeng )?); 1275*fae6e9adSlinfeng Ok(()) 1276*fae6e9adSlinfeng } 1277*fae6e9adSlinfeng 1278*fae6e9adSlinfeng /// Execute the previously JIT-compiled program, with the given packet data, in a manner very 1279*fae6e9adSlinfeng /// similar to `execute_program()`. 1280*fae6e9adSlinfeng /// 1281*fae6e9adSlinfeng /// # Safety 1282*fae6e9adSlinfeng /// 1283*fae6e9adSlinfeng /// **WARNING:** JIT-compiled assembly code is not safe, in particular there is no runtime 1284*fae6e9adSlinfeng /// check for memory access; so if the eBPF program attempts erroneous accesses, this may end 1285*fae6e9adSlinfeng /// very bad (program may segfault). It may be wise to check that the program works with the 1286*fae6e9adSlinfeng /// interpreter before running the JIT-compiled version of it. 1287*fae6e9adSlinfeng /// 1288*fae6e9adSlinfeng /// For this reason the function should be called from within an `unsafe` bloc. 1289*fae6e9adSlinfeng /// 1290*fae6e9adSlinfeng /// # Examples 1291*fae6e9adSlinfeng /// 1292*fae6e9adSlinfeng /// ``` 1293*fae6e9adSlinfeng /// let prog = &[ 1294*fae6e9adSlinfeng /// 0x71, 0x11, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, // ldxb r1[0x04], r1 1295*fae6e9adSlinfeng /// 0x07, 0x01, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, // add r1, 0x22 1296*fae6e9adSlinfeng /// 0xbf, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r0, r1 1297*fae6e9adSlinfeng /// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit 1298*fae6e9adSlinfeng /// ]; 1299*fae6e9adSlinfeng /// 1300*fae6e9adSlinfeng /// let mem = &mut [ 1301*fae6e9adSlinfeng /// 0xaa, 0xbb, 0x11, 0x22, 0xcc, 0x27 1302*fae6e9adSlinfeng /// ]; 1303*fae6e9adSlinfeng /// 1304*fae6e9adSlinfeng /// let mut vm = rbpf::EbpfVmRaw::new(Some(prog)).unwrap(); 1305*fae6e9adSlinfeng /// 1306*fae6e9adSlinfeng /// # #[cfg(all(not(windows), feature = "std"))] 1307*fae6e9adSlinfeng /// vm.jit_compile(); 1308*fae6e9adSlinfeng /// 1309*fae6e9adSlinfeng /// # #[cfg(all(not(windows), feature = "std"))] 1310*fae6e9adSlinfeng /// unsafe { 1311*fae6e9adSlinfeng /// let res = vm.execute_program_jit(mem).unwrap(); 1312*fae6e9adSlinfeng /// assert_eq!(res, 0x22cc); 1313*fae6e9adSlinfeng /// } 1314*fae6e9adSlinfeng /// ``` 1315*fae6e9adSlinfeng #[cfg(all(not(windows), feature = "std"))] execute_program_jit(&self, mem: &'a mut [u8]) -> Result<u64, Error>1316*fae6e9adSlinfeng pub unsafe fn execute_program_jit(&self, mem: &'a mut [u8]) -> Result<u64, Error> { 1317*fae6e9adSlinfeng let mut mbuff = vec![]; 1318*fae6e9adSlinfeng self.parent.execute_program_jit(mem, &mut mbuff) 1319*fae6e9adSlinfeng } 1320*fae6e9adSlinfeng 1321*fae6e9adSlinfeng /// Compile the loaded program using the Cranelift JIT. 1322*fae6e9adSlinfeng /// 1323*fae6e9adSlinfeng /// If using helper functions, be sure to register them into the VM before calling this 1324*fae6e9adSlinfeng /// function. 1325*fae6e9adSlinfeng /// 1326*fae6e9adSlinfeng /// # Examples 1327*fae6e9adSlinfeng /// 1328*fae6e9adSlinfeng /// ``` 1329*fae6e9adSlinfeng /// let prog = &[ 1330*fae6e9adSlinfeng /// 0x71, 0x11, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, // ldxb r1[0x04], r1 1331*fae6e9adSlinfeng /// 0x07, 0x01, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, // add r1, 0x22 1332*fae6e9adSlinfeng /// 0xbf, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r0, r1 1333*fae6e9adSlinfeng /// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit 1334*fae6e9adSlinfeng /// ]; 1335*fae6e9adSlinfeng /// 1336*fae6e9adSlinfeng /// let mut vm = rbpf::EbpfVmRaw::new(Some(prog)).unwrap(); 1337*fae6e9adSlinfeng /// 1338*fae6e9adSlinfeng /// vm.cranelift_compile(); 1339*fae6e9adSlinfeng /// ``` 1340*fae6e9adSlinfeng #[cfg(feature = "cranelift")] cranelift_compile(&mut self) -> Result<(), Error>1341*fae6e9adSlinfeng pub fn cranelift_compile(&mut self) -> Result<(), Error> { 1342*fae6e9adSlinfeng use crate::cranelift::CraneliftCompiler; 1343*fae6e9adSlinfeng 1344*fae6e9adSlinfeng let prog = match self.parent.prog { 1345*fae6e9adSlinfeng Some(prog) => prog, 1346*fae6e9adSlinfeng None => Err(Error::new( 1347*fae6e9adSlinfeng ErrorKind::Other, 1348*fae6e9adSlinfeng "Error: No program set, call prog_set() to load one", 1349*fae6e9adSlinfeng ))?, 1350*fae6e9adSlinfeng }; 1351*fae6e9adSlinfeng 1352*fae6e9adSlinfeng let mut compiler = CraneliftCompiler::new(self.parent.helpers.clone()); 1353*fae6e9adSlinfeng let program = compiler.compile_function(prog)?; 1354*fae6e9adSlinfeng 1355*fae6e9adSlinfeng self.parent.cranelift_prog = Some(program); 1356*fae6e9adSlinfeng Ok(()) 1357*fae6e9adSlinfeng } 1358*fae6e9adSlinfeng 1359*fae6e9adSlinfeng /// Execute the previously compiled program, with the given packet data, in a manner very 1360*fae6e9adSlinfeng /// similar to `execute_program()`. 1361*fae6e9adSlinfeng /// 1362*fae6e9adSlinfeng /// # Examples 1363*fae6e9adSlinfeng /// 1364*fae6e9adSlinfeng /// ``` 1365*fae6e9adSlinfeng /// let prog = &[ 1366*fae6e9adSlinfeng /// 0x71, 0x11, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, // ldxb r1[0x04], r1 1367*fae6e9adSlinfeng /// 0x07, 0x01, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, // add r1, 0x22 1368*fae6e9adSlinfeng /// 0xbf, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r0, r1 1369*fae6e9adSlinfeng /// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit 1370*fae6e9adSlinfeng /// ]; 1371*fae6e9adSlinfeng /// 1372*fae6e9adSlinfeng /// let mem = &mut [ 1373*fae6e9adSlinfeng /// 0xaa, 0xbb, 0x11, 0x22, 0xcc, 0x27 1374*fae6e9adSlinfeng /// ]; 1375*fae6e9adSlinfeng /// 1376*fae6e9adSlinfeng /// let mut vm = rbpf::EbpfVmRaw::new(Some(prog)).unwrap(); 1377*fae6e9adSlinfeng /// 1378*fae6e9adSlinfeng /// vm.cranelift_compile(); 1379*fae6e9adSlinfeng /// 1380*fae6e9adSlinfeng /// let res = vm.execute_program_cranelift(mem).unwrap(); 1381*fae6e9adSlinfeng /// assert_eq!(res, 0x22cc); 1382*fae6e9adSlinfeng /// ``` 1383*fae6e9adSlinfeng #[cfg(feature = "cranelift")] execute_program_cranelift(&self, mem: &'a mut [u8]) -> Result<u64, Error>1384*fae6e9adSlinfeng pub fn execute_program_cranelift(&self, mem: &'a mut [u8]) -> Result<u64, Error> { 1385*fae6e9adSlinfeng let mut mbuff = vec![]; 1386*fae6e9adSlinfeng self.parent.execute_program_cranelift(mem, &mut mbuff) 1387*fae6e9adSlinfeng } 1388*fae6e9adSlinfeng } 1389*fae6e9adSlinfeng 1390*fae6e9adSlinfeng /// A virtual machine to run eBPF program. This kind of VM is used for programs that do not work 1391*fae6e9adSlinfeng /// with any memory area—no metadata buffer, no packet data either. 1392*fae6e9adSlinfeng /// 1393*fae6e9adSlinfeng /// # Examples 1394*fae6e9adSlinfeng /// 1395*fae6e9adSlinfeng /// ``` 1396*fae6e9adSlinfeng /// let prog = &[ 1397*fae6e9adSlinfeng /// 0xb7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r0, 0 1398*fae6e9adSlinfeng /// 0xb7, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, // mov r1, 1 1399*fae6e9adSlinfeng /// 0xb7, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, // mov r2, 2 1400*fae6e9adSlinfeng /// 0xb7, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, // mov r3, 3 1401*fae6e9adSlinfeng /// 0xb7, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, // mov r4, 4 1402*fae6e9adSlinfeng /// 0xb7, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, // mov r5, 5 1403*fae6e9adSlinfeng /// 0xb7, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, // mov r6, 6 1404*fae6e9adSlinfeng /// 0xb7, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, // mov r7, 7 1405*fae6e9adSlinfeng /// 0xb7, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, // mov r8, 8 1406*fae6e9adSlinfeng /// 0x4f, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // or r0, r5 1407*fae6e9adSlinfeng /// 0x47, 0x00, 0x00, 0x00, 0xa0, 0x00, 0x00, 0x00, // or r0, 0xa0 1408*fae6e9adSlinfeng /// 0x57, 0x00, 0x00, 0x00, 0xa3, 0x00, 0x00, 0x00, // and r0, 0xa3 1409*fae6e9adSlinfeng /// 0xb7, 0x09, 0x00, 0x00, 0x91, 0x00, 0x00, 0x00, // mov r9, 0x91 1410*fae6e9adSlinfeng /// 0x5f, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // and r0, r9 1411*fae6e9adSlinfeng /// 0x67, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, // lsh r0, 32 1412*fae6e9adSlinfeng /// 0x67, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, // lsh r0, 22 1413*fae6e9adSlinfeng /// 0x6f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // lsh r0, r8 1414*fae6e9adSlinfeng /// 0x77, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, // rsh r0, 32 1415*fae6e9adSlinfeng /// 0x77, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, // rsh r0, 19 1416*fae6e9adSlinfeng /// 0x7f, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // rsh r0, r7 1417*fae6e9adSlinfeng /// 0xa7, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, // xor r0, 0x03 1418*fae6e9adSlinfeng /// 0xaf, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // xor r0, r2 1419*fae6e9adSlinfeng /// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit 1420*fae6e9adSlinfeng /// ]; 1421*fae6e9adSlinfeng /// 1422*fae6e9adSlinfeng /// // Instantiate a VM. 1423*fae6e9adSlinfeng /// let vm = rbpf::EbpfVmNoData::new(Some(prog)).unwrap(); 1424*fae6e9adSlinfeng /// 1425*fae6e9adSlinfeng /// // Provide only a reference to the packet data. 1426*fae6e9adSlinfeng /// let res = vm.execute_program().unwrap(); 1427*fae6e9adSlinfeng /// assert_eq!(res, 0x11); 1428*fae6e9adSlinfeng /// ``` 1429*fae6e9adSlinfeng pub struct EbpfVmNoData<'a> { 1430*fae6e9adSlinfeng parent: EbpfVmRaw<'a>, 1431*fae6e9adSlinfeng } 1432*fae6e9adSlinfeng 1433*fae6e9adSlinfeng impl<'a> EbpfVmNoData<'a> { 1434*fae6e9adSlinfeng /// Create a new virtual machine instance, and load an eBPF program into that instance. 1435*fae6e9adSlinfeng /// When attempting to load the program, it passes through a simple verifier. 1436*fae6e9adSlinfeng /// 1437*fae6e9adSlinfeng /// # Examples 1438*fae6e9adSlinfeng /// 1439*fae6e9adSlinfeng /// ``` 1440*fae6e9adSlinfeng /// let prog = &[ 1441*fae6e9adSlinfeng /// 0xb7, 0x00, 0x00, 0x00, 0x11, 0x22, 0x00, 0x00, // mov r0, 0x2211 1442*fae6e9adSlinfeng /// 0xdc, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, // be16 r0 1443*fae6e9adSlinfeng /// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit 1444*fae6e9adSlinfeng /// ]; 1445*fae6e9adSlinfeng /// 1446*fae6e9adSlinfeng /// // Instantiate a VM. 1447*fae6e9adSlinfeng /// let vm = rbpf::EbpfVmNoData::new(Some(prog)); 1448*fae6e9adSlinfeng /// ``` new(prog: Option<&'a [u8]>) -> Result<EbpfVmNoData<'a>, Error>1449*fae6e9adSlinfeng pub fn new(prog: Option<&'a [u8]>) -> Result<EbpfVmNoData<'a>, Error> { 1450*fae6e9adSlinfeng let parent = EbpfVmRaw::new(prog)?; 1451*fae6e9adSlinfeng Ok(EbpfVmNoData { parent }) 1452*fae6e9adSlinfeng } 1453*fae6e9adSlinfeng 1454*fae6e9adSlinfeng /// Load a new eBPF program into the virtual machine instance. 1455*fae6e9adSlinfeng /// 1456*fae6e9adSlinfeng /// # Examples 1457*fae6e9adSlinfeng /// 1458*fae6e9adSlinfeng /// ``` 1459*fae6e9adSlinfeng /// let prog1 = &[ 1460*fae6e9adSlinfeng /// 0xb7, 0x00, 0x00, 0x00, 0x11, 0x22, 0x00, 0x00, // mov r0, 0x2211 1461*fae6e9adSlinfeng /// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit 1462*fae6e9adSlinfeng /// ]; 1463*fae6e9adSlinfeng /// let prog2 = &[ 1464*fae6e9adSlinfeng /// 0xb7, 0x00, 0x00, 0x00, 0x11, 0x22, 0x00, 0x00, // mov r0, 0x2211 1465*fae6e9adSlinfeng /// 0xdc, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, // be16 r0 1466*fae6e9adSlinfeng /// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit 1467*fae6e9adSlinfeng /// ]; 1468*fae6e9adSlinfeng /// 1469*fae6e9adSlinfeng /// let mut vm = rbpf::EbpfVmNoData::new(Some(prog1)).unwrap(); 1470*fae6e9adSlinfeng /// 1471*fae6e9adSlinfeng /// let res = vm.execute_program().unwrap(); 1472*fae6e9adSlinfeng /// assert_eq!(res, 0x2211); 1473*fae6e9adSlinfeng /// 1474*fae6e9adSlinfeng /// vm.set_program(prog2); 1475*fae6e9adSlinfeng /// 1476*fae6e9adSlinfeng /// let res = vm.execute_program().unwrap(); 1477*fae6e9adSlinfeng /// assert_eq!(res, 0x1122); 1478*fae6e9adSlinfeng /// ``` set_program(&mut self, prog: &'a [u8]) -> Result<(), Error>1479*fae6e9adSlinfeng pub fn set_program(&mut self, prog: &'a [u8]) -> Result<(), Error> { 1480*fae6e9adSlinfeng self.parent.set_program(prog)?; 1481*fae6e9adSlinfeng Ok(()) 1482*fae6e9adSlinfeng } 1483*fae6e9adSlinfeng 1484*fae6e9adSlinfeng /// Set a new verifier function. The function should return an `Error` if the program should be 1485*fae6e9adSlinfeng /// rejected by the virtual machine. If a program has been loaded to the VM already, the 1486*fae6e9adSlinfeng /// verifier is immediately run. 1487*fae6e9adSlinfeng /// 1488*fae6e9adSlinfeng /// # Examples 1489*fae6e9adSlinfeng /// 1490*fae6e9adSlinfeng /// ``` 1491*fae6e9adSlinfeng /// use rbpf::{Error, ErrorKind}; 1492*fae6e9adSlinfeng /// use rbpf::ebpf; 1493*fae6e9adSlinfeng /// 1494*fae6e9adSlinfeng /// // Define a simple verifier function. 1495*fae6e9adSlinfeng /// fn verifier(prog: &[u8]) -> Result<(), Error> { 1496*fae6e9adSlinfeng /// let last_insn = ebpf::get_insn(prog, (prog.len() / ebpf::INSN_SIZE) - 1); 1497*fae6e9adSlinfeng /// if last_insn.opc != ebpf::EXIT { 1498*fae6e9adSlinfeng /// return Err(Error::new(ErrorKind::Other, 1499*fae6e9adSlinfeng /// "[Verifier] Error: program does not end with “EXIT” instruction")); 1500*fae6e9adSlinfeng /// } 1501*fae6e9adSlinfeng /// Ok(()) 1502*fae6e9adSlinfeng /// } 1503*fae6e9adSlinfeng /// 1504*fae6e9adSlinfeng /// let prog1 = &[ 1505*fae6e9adSlinfeng /// 0xb7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r0, 0 1506*fae6e9adSlinfeng /// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit 1507*fae6e9adSlinfeng /// ]; 1508*fae6e9adSlinfeng /// 1509*fae6e9adSlinfeng /// // Instantiate a VM. 1510*fae6e9adSlinfeng /// let mut vm = rbpf::EbpfVmMbuff::new(Some(prog1)).unwrap(); 1511*fae6e9adSlinfeng /// // Change the verifier. 1512*fae6e9adSlinfeng /// vm.set_verifier(verifier).unwrap(); 1513*fae6e9adSlinfeng /// ``` set_verifier(&mut self, verifier: Verifier) -> Result<(), Error>1514*fae6e9adSlinfeng pub fn set_verifier(&mut self, verifier: Verifier) -> Result<(), Error> { 1515*fae6e9adSlinfeng self.parent.set_verifier(verifier) 1516*fae6e9adSlinfeng } 1517*fae6e9adSlinfeng 1518*fae6e9adSlinfeng /// Register a built-in or user-defined helper function in order to use it later from within 1519*fae6e9adSlinfeng /// the eBPF program. The helper is registered into a hashmap, so the `key` can be any `u32`. 1520*fae6e9adSlinfeng /// 1521*fae6e9adSlinfeng /// If using JIT-compiled eBPF programs, be sure to register all helpers before compiling the 1522*fae6e9adSlinfeng /// program. You should be able to change registered helpers after compiling, but not to add 1523*fae6e9adSlinfeng /// new ones (i.e. with new keys). 1524*fae6e9adSlinfeng /// 1525*fae6e9adSlinfeng /// # Examples 1526*fae6e9adSlinfeng /// 1527*fae6e9adSlinfeng /// ``` 1528*fae6e9adSlinfeng /// #[cfg(feature = "std")] { 1529*fae6e9adSlinfeng /// use rbpf::helpers; 1530*fae6e9adSlinfeng /// 1531*fae6e9adSlinfeng /// let prog = &[ 1532*fae6e9adSlinfeng /// 0xb7, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, // mov r1, 0x010000000 1533*fae6e9adSlinfeng /// 0xb7, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r2, 0 1534*fae6e9adSlinfeng /// 0xb7, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r3, 0 1535*fae6e9adSlinfeng /// 0xb7, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r4, 0 1536*fae6e9adSlinfeng /// 0xb7, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r5, 0 1537*fae6e9adSlinfeng /// 0x85, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, // call helper with key 1 1538*fae6e9adSlinfeng /// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit 1539*fae6e9adSlinfeng /// ]; 1540*fae6e9adSlinfeng /// 1541*fae6e9adSlinfeng /// let mut vm = rbpf::EbpfVmNoData::new(Some(prog)).unwrap(); 1542*fae6e9adSlinfeng /// 1543*fae6e9adSlinfeng /// // Register a helper. This helper will store the result of the square root of r1 into r0. 1544*fae6e9adSlinfeng /// vm.register_helper(1, helpers::sqrti).unwrap(); 1545*fae6e9adSlinfeng /// 1546*fae6e9adSlinfeng /// let res = vm.execute_program().unwrap(); 1547*fae6e9adSlinfeng /// assert_eq!(res, 0x1000); 1548*fae6e9adSlinfeng /// } 1549*fae6e9adSlinfeng /// ``` register_helper( &mut self, key: u32, function: fn(u64, u64, u64, u64, u64) -> u64, ) -> Result<(), Error>1550*fae6e9adSlinfeng pub fn register_helper( 1551*fae6e9adSlinfeng &mut self, 1552*fae6e9adSlinfeng key: u32, 1553*fae6e9adSlinfeng function: fn(u64, u64, u64, u64, u64) -> u64, 1554*fae6e9adSlinfeng ) -> Result<(), Error> { 1555*fae6e9adSlinfeng self.parent.register_helper(key, function) 1556*fae6e9adSlinfeng } 1557*fae6e9adSlinfeng 1558*fae6e9adSlinfeng /// JIT-compile the loaded program. No argument required for this. 1559*fae6e9adSlinfeng /// 1560*fae6e9adSlinfeng /// If using helper functions, be sure to register them into the VM before calling this 1561*fae6e9adSlinfeng /// function. 1562*fae6e9adSlinfeng /// 1563*fae6e9adSlinfeng /// # Examples 1564*fae6e9adSlinfeng /// 1565*fae6e9adSlinfeng /// ``` 1566*fae6e9adSlinfeng /// let prog = &[ 1567*fae6e9adSlinfeng /// 0xb7, 0x00, 0x00, 0x00, 0x11, 0x22, 0x00, 0x00, // mov r0, 0x2211 1568*fae6e9adSlinfeng /// 0xdc, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, // be16 r0 1569*fae6e9adSlinfeng /// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit 1570*fae6e9adSlinfeng /// ]; 1571*fae6e9adSlinfeng /// 1572*fae6e9adSlinfeng /// let mut vm = rbpf::EbpfVmNoData::new(Some(prog)).unwrap(); 1573*fae6e9adSlinfeng /// 1574*fae6e9adSlinfeng /// 1575*fae6e9adSlinfeng /// vm.jit_compile(); 1576*fae6e9adSlinfeng /// ``` 1577*fae6e9adSlinfeng #[cfg(all(not(windows), feature = "std"))] jit_compile(&mut self) -> Result<(), Error>1578*fae6e9adSlinfeng pub fn jit_compile(&mut self) -> Result<(), Error> { 1579*fae6e9adSlinfeng self.parent.jit_compile() 1580*fae6e9adSlinfeng } 1581*fae6e9adSlinfeng 1582*fae6e9adSlinfeng /// Execute the program loaded, without providing pointers to any memory area whatsoever. 1583*fae6e9adSlinfeng /// 1584*fae6e9adSlinfeng /// # Examples 1585*fae6e9adSlinfeng /// 1586*fae6e9adSlinfeng /// ``` 1587*fae6e9adSlinfeng /// let prog = &[ 1588*fae6e9adSlinfeng /// 0xb7, 0x00, 0x00, 0x00, 0x11, 0x22, 0x00, 0x00, // mov r0, 0x2211 1589*fae6e9adSlinfeng /// 0xdc, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, // be16 r0 1590*fae6e9adSlinfeng /// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit 1591*fae6e9adSlinfeng /// ]; 1592*fae6e9adSlinfeng /// 1593*fae6e9adSlinfeng /// let vm = rbpf::EbpfVmNoData::new(Some(prog)).unwrap(); 1594*fae6e9adSlinfeng /// 1595*fae6e9adSlinfeng /// // For this kind of VM, the `execute_program()` function needs no argument. 1596*fae6e9adSlinfeng /// let res = vm.execute_program().unwrap(); 1597*fae6e9adSlinfeng /// assert_eq!(res, 0x1122); 1598*fae6e9adSlinfeng /// ``` execute_program(&self) -> Result<u64, Error>1599*fae6e9adSlinfeng pub fn execute_program(&self) -> Result<u64, Error> { 1600*fae6e9adSlinfeng self.parent.execute_program(&mut []) 1601*fae6e9adSlinfeng } 1602*fae6e9adSlinfeng 1603*fae6e9adSlinfeng /// Execute the previously JIT-compiled program, without providing pointers to any memory area 1604*fae6e9adSlinfeng /// whatsoever, in a manner very similar to `execute_program()`. 1605*fae6e9adSlinfeng /// 1606*fae6e9adSlinfeng /// # Safety 1607*fae6e9adSlinfeng /// 1608*fae6e9adSlinfeng /// **WARNING:** JIT-compiled assembly code is not safe, in particular there is no runtime 1609*fae6e9adSlinfeng /// check for memory access; so if the eBPF program attempts erroneous accesses, this may end 1610*fae6e9adSlinfeng /// very bad (program may segfault). It may be wise to check that the program works with the 1611*fae6e9adSlinfeng /// interpreter before running the JIT-compiled version of it. 1612*fae6e9adSlinfeng /// 1613*fae6e9adSlinfeng /// For this reason the function should be called from within an `unsafe` bloc. 1614*fae6e9adSlinfeng /// 1615*fae6e9adSlinfeng /// # Examples 1616*fae6e9adSlinfeng /// 1617*fae6e9adSlinfeng /// ``` 1618*fae6e9adSlinfeng /// let prog = &[ 1619*fae6e9adSlinfeng /// 0xb7, 0x00, 0x00, 0x00, 0x11, 0x22, 0x00, 0x00, // mov r0, 0x2211 1620*fae6e9adSlinfeng /// 0xdc, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, // be16 r0 1621*fae6e9adSlinfeng /// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit 1622*fae6e9adSlinfeng /// ]; 1623*fae6e9adSlinfeng /// 1624*fae6e9adSlinfeng /// let mut vm = rbpf::EbpfVmNoData::new(Some(prog)).unwrap(); 1625*fae6e9adSlinfeng /// 1626*fae6e9adSlinfeng /// # #[cfg(all(not(windows), feature = "std"))] 1627*fae6e9adSlinfeng /// vm.jit_compile(); 1628*fae6e9adSlinfeng /// 1629*fae6e9adSlinfeng /// # #[cfg(all(not(windows), feature = "std"))] 1630*fae6e9adSlinfeng /// unsafe { 1631*fae6e9adSlinfeng /// let res = vm.execute_program_jit().unwrap(); 1632*fae6e9adSlinfeng /// assert_eq!(res, 0x1122); 1633*fae6e9adSlinfeng /// } 1634*fae6e9adSlinfeng /// ``` 1635*fae6e9adSlinfeng #[cfg(all(not(windows), feature = "std"))] execute_program_jit(&self) -> Result<u64, Error>1636*fae6e9adSlinfeng pub unsafe fn execute_program_jit(&self) -> Result<u64, Error> { 1637*fae6e9adSlinfeng self.parent.execute_program_jit(&mut []) 1638*fae6e9adSlinfeng } 1639*fae6e9adSlinfeng 1640*fae6e9adSlinfeng /// Compile the loaded program using the Cranelift JIT. 1641*fae6e9adSlinfeng /// 1642*fae6e9adSlinfeng /// If using helper functions, be sure to register them into the VM before calling this 1643*fae6e9adSlinfeng /// function. 1644*fae6e9adSlinfeng /// 1645*fae6e9adSlinfeng /// # Examples 1646*fae6e9adSlinfeng /// 1647*fae6e9adSlinfeng /// ``` 1648*fae6e9adSlinfeng /// let prog = &[ 1649*fae6e9adSlinfeng /// 0xb7, 0x00, 0x00, 0x00, 0x11, 0x22, 0x00, 0x00, // mov r0, 0x2211 1650*fae6e9adSlinfeng /// 0xdc, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, // be16 r0 1651*fae6e9adSlinfeng /// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit 1652*fae6e9adSlinfeng /// ]; 1653*fae6e9adSlinfeng /// 1654*fae6e9adSlinfeng /// let mut vm = rbpf::EbpfVmNoData::new(Some(prog)).unwrap(); 1655*fae6e9adSlinfeng /// 1656*fae6e9adSlinfeng /// 1657*fae6e9adSlinfeng /// vm.cranelift_compile(); 1658*fae6e9adSlinfeng /// ``` 1659*fae6e9adSlinfeng #[cfg(feature = "cranelift")] cranelift_compile(&mut self) -> Result<(), Error>1660*fae6e9adSlinfeng pub fn cranelift_compile(&mut self) -> Result<(), Error> { 1661*fae6e9adSlinfeng self.parent.cranelift_compile() 1662*fae6e9adSlinfeng } 1663*fae6e9adSlinfeng 1664*fae6e9adSlinfeng /// Execute the previously JIT-compiled program, without providing pointers to any memory area 1665*fae6e9adSlinfeng /// whatsoever, in a manner very similar to `execute_program()`. 1666*fae6e9adSlinfeng /// 1667*fae6e9adSlinfeng /// # Examples 1668*fae6e9adSlinfeng /// 1669*fae6e9adSlinfeng /// ``` 1670*fae6e9adSlinfeng /// let prog = &[ 1671*fae6e9adSlinfeng /// 0xb7, 0x00, 0x00, 0x00, 0x11, 0x22, 0x00, 0x00, // mov r0, 0x2211 1672*fae6e9adSlinfeng /// 0xdc, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, // be16 r0 1673*fae6e9adSlinfeng /// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit 1674*fae6e9adSlinfeng /// ]; 1675*fae6e9adSlinfeng /// 1676*fae6e9adSlinfeng /// let mut vm = rbpf::EbpfVmNoData::new(Some(prog)).unwrap(); 1677*fae6e9adSlinfeng /// 1678*fae6e9adSlinfeng /// vm.cranelift_compile(); 1679*fae6e9adSlinfeng /// 1680*fae6e9adSlinfeng /// let res = vm.execute_program_cranelift().unwrap(); 1681*fae6e9adSlinfeng /// assert_eq!(res, 0x1122); 1682*fae6e9adSlinfeng /// ``` 1683*fae6e9adSlinfeng #[cfg(feature = "cranelift")] execute_program_cranelift(&self) -> Result<u64, Error>1684*fae6e9adSlinfeng pub fn execute_program_cranelift(&self) -> Result<u64, Error> { 1685*fae6e9adSlinfeng self.parent.execute_program_cranelift(&mut []) 1686*fae6e9adSlinfeng } 1687*fae6e9adSlinfeng } 1688*fae6e9adSlinfeng 1689*fae6e9adSlinfeng /// EbpfVm with Owned data 1690*fae6e9adSlinfeng pub struct EbpfVmRawOwned { 1691*fae6e9adSlinfeng parent: EbpfVmRaw<'static>, 1692*fae6e9adSlinfeng data_len: usize, 1693*fae6e9adSlinfeng data_cap: usize, 1694*fae6e9adSlinfeng } 1695*fae6e9adSlinfeng 1696*fae6e9adSlinfeng impl EbpfVmRawOwned { 1697*fae6e9adSlinfeng /// Create a new virtual machine instance, and load an eBPF program into that instance. 1698*fae6e9adSlinfeng /// When attempting to load the program, it passes through a simple verifier. new(prog: Option<Vec<u8>>) -> Result<EbpfVmRawOwned, Error>1699*fae6e9adSlinfeng pub fn new(prog: Option<Vec<u8>>) -> Result<EbpfVmRawOwned, Error> { 1700*fae6e9adSlinfeng let (prog, data_len, data_cap) = match prog { 1701*fae6e9adSlinfeng Some(prog) => { 1702*fae6e9adSlinfeng let data_len = prog.len(); 1703*fae6e9adSlinfeng let data_cap = prog.capacity(); 1704*fae6e9adSlinfeng let slice = prog.leak(); 1705*fae6e9adSlinfeng let slice = unsafe { core::slice::from_raw_parts(slice.as_ptr(), data_len) }; 1706*fae6e9adSlinfeng (Some(slice), data_len, data_cap) 1707*fae6e9adSlinfeng } 1708*fae6e9adSlinfeng None => (None, 0, 0), 1709*fae6e9adSlinfeng }; 1710*fae6e9adSlinfeng let parent = EbpfVmRaw::new(prog)?; 1711*fae6e9adSlinfeng Ok(Self { 1712*fae6e9adSlinfeng parent, 1713*fae6e9adSlinfeng data_len, 1714*fae6e9adSlinfeng data_cap, 1715*fae6e9adSlinfeng }) 1716*fae6e9adSlinfeng } 1717*fae6e9adSlinfeng /// Load a new eBPF program into the virtual machine instance set_program(&mut self, prog: Vec<u8>) -> Result<(), Error>1718*fae6e9adSlinfeng pub fn set_program(&mut self, prog: Vec<u8>) -> Result<(), Error> { 1719*fae6e9adSlinfeng self.data_len = prog.len(); 1720*fae6e9adSlinfeng self.data_cap = prog.capacity(); 1721*fae6e9adSlinfeng let slice = prog.leak(); 1722*fae6e9adSlinfeng self.parent.set_program(slice)?; 1723*fae6e9adSlinfeng Ok(()) 1724*fae6e9adSlinfeng } 1725*fae6e9adSlinfeng 1726*fae6e9adSlinfeng /// Set a new verifier function. The function should return an Error if the program should be rejected by the virtual machine. 1727*fae6e9adSlinfeng /// If a program has been loaded to the VM already, the verifier is immediately run. set_verifier(&mut self, verifier: Verifier) -> Result<(), Error>1728*fae6e9adSlinfeng pub fn set_verifier(&mut self, verifier: Verifier) -> Result<(), Error> { 1729*fae6e9adSlinfeng self.parent.set_verifier(verifier) 1730*fae6e9adSlinfeng } 1731*fae6e9adSlinfeng 1732*fae6e9adSlinfeng /// Register a built-in or user-defined helper function in order to use it later from within the eBPF program. 1733*fae6e9adSlinfeng /// The helper is registered into a hashmap, so the key can be any u32. 1734*fae6e9adSlinfeng /// If using JIT-compiled eBPF programs, be sure to register all helpers before compiling the program. 1735*fae6e9adSlinfeng /// You should be able to change registered helpers after compiling, but not to add new ones (i. e. with new keys). register_helper( &mut self, key: u32, function: fn(u64, u64, u64, u64, u64) -> u64, ) -> Result<(), Error>1736*fae6e9adSlinfeng pub fn register_helper( 1737*fae6e9adSlinfeng &mut self, 1738*fae6e9adSlinfeng key: u32, 1739*fae6e9adSlinfeng function: fn(u64, u64, u64, u64, u64) -> u64, 1740*fae6e9adSlinfeng ) -> Result<(), Error> { 1741*fae6e9adSlinfeng self.parent.register_helper(key, function) 1742*fae6e9adSlinfeng } 1743*fae6e9adSlinfeng 1744*fae6e9adSlinfeng /// Register a set of built-in or user-defined helper functions in order to use them later from 1745*fae6e9adSlinfeng /// within the eBPF program. The helpers are registered into a hashmap, so the `key` can be any 1746*fae6e9adSlinfeng /// `u32`. 1747*fae6e9adSlinfeng #[allow(clippy::type_complexity)] register_helper_set( &mut self, helpers: &HashMap<u32, fn(u64, u64, u64, u64, u64) -> u64>, ) -> Result<(), Error>1748*fae6e9adSlinfeng pub fn register_helper_set( 1749*fae6e9adSlinfeng &mut self, 1750*fae6e9adSlinfeng helpers: &HashMap<u32, fn(u64, u64, u64, u64, u64) -> u64>, 1751*fae6e9adSlinfeng ) -> Result<(), Error> { 1752*fae6e9adSlinfeng for (key, function) in helpers { 1753*fae6e9adSlinfeng self.parent.register_helper(*key, *function)?; 1754*fae6e9adSlinfeng } 1755*fae6e9adSlinfeng Ok(()) 1756*fae6e9adSlinfeng } 1757*fae6e9adSlinfeng 1758*fae6e9adSlinfeng /// Execute the previously JIT-compiled program, with the given packet data, in a manner very similar to execute_program(). 1759*fae6e9adSlinfeng /// 1760*fae6e9adSlinfeng /// Safety 1761*fae6e9adSlinfeng /// 1762*fae6e9adSlinfeng /// **WARNING:** JIT-compiled assembly code is not safe, in particular there is no runtime check for memory access; 1763*fae6e9adSlinfeng /// so if the eBPF program attempts erroneous accesses, this may end very bad (program may segfault). 1764*fae6e9adSlinfeng /// It may be wise to check that the program works with the interpreter before running the JIT-compiled version of it. 1765*fae6e9adSlinfeng /// 1766*fae6e9adSlinfeng /// For this reason the function should be called from within an unsafe bloc. execute_program(&self, mem: &mut [u8]) -> Result<u64, Error>1767*fae6e9adSlinfeng pub fn execute_program(&self, mem: &mut [u8]) -> Result<u64, Error> { 1768*fae6e9adSlinfeng self.parent.execute_program(mem) 1769*fae6e9adSlinfeng } 1770*fae6e9adSlinfeng } 1771*fae6e9adSlinfeng 1772*fae6e9adSlinfeng impl Drop for EbpfVmRawOwned { drop(&mut self)1773*fae6e9adSlinfeng fn drop(&mut self) { 1774*fae6e9adSlinfeng match self.parent.parent.prog { 1775*fae6e9adSlinfeng Some(prog) => unsafe { 1776*fae6e9adSlinfeng let ptr = prog.as_ptr(); 1777*fae6e9adSlinfeng let _prog = Vec::from_raw_parts(ptr as *mut u8, self.data_len, self.data_cap); 1778*fae6e9adSlinfeng }, 1779*fae6e9adSlinfeng None => {} 1780*fae6e9adSlinfeng }; 1781*fae6e9adSlinfeng } 1782*fae6e9adSlinfeng } 1783