1# rbpf 2 3<picture> 4 <source media="(prefers-color-scheme: dark)" srcset="misc/rbpf_256_border.png"> 5 <img src="misc/rbpf_256.png"> 6</picture> 7 8Rust (user-space) virtual machine for eBPF 9 10[![Build Status](https://github.com/qmonnet/rbpf/actions/workflows/test.yaml/badge.svg)](https://github.com/qmonnet/rbpf/actions/workflows/test.yaml) 11[![Build status](https://ci.appveyor.com/api/projects/status/ia74coeuhxtrcvsk/branch/main?svg=true)](https://ci.appveyor.com/project/qmonnet/rbpf/branch/main) 12[![Coverage Status](https://coveralls.io/repos/github/qmonnet/rbpf/badge.svg?branch=main)](https://coveralls.io/github/qmonnet/rbpf?branch=main) 13[![Crates.io](https://img.shields.io/crates/v/rbpf.svg)](https://crates.io/crates/rbpf) 14 15* [Description](#description) 16* [Link to the crate](#link-to-the-crate) 17* [API](#api) 18* [Example uses](#example-uses) 19* [Building eBPF programs](#building-ebpf-programs) 20* [Build Features](#build-features) 21* [Feedback welcome!](#feedback-welcome) 22* [Questions / Answers](#questions--answers) 23* [Caveats](#caveats) 24* [_To do_ list](#to-do-list) 25* [License](#license) 26* [Inspired by](#inspired-by) 27* [Other resources](#other-resources) 28 29## Description 30 31This crate contains a virtual machine for eBPF program execution. BPF, as in 32_Berkeley Packet Filter_, is an assembly-like language initially developed for 33BSD systems, in order to filter packets in the kernel with tools such as 34tcpdump so as to avoid useless copies to user-space. It was ported to Linux, 35where it evolved into eBPF (_extended_ BPF), a faster version with more 36features. While BPF programs are originally intended to run in the kernel, the 37virtual machine of this crate enables running it in user-space applications; 38it contains an interpreter, an x86_64 JIT-compiler for eBPF programs, as well as 39a disassembler. 40 41It is based on Rich Lane's [uBPF software](https://github.com/iovisor/ubpf/), 42which does nearly the same, but is written in C. 43 44The crate is supposed to compile and run on Linux, MacOS X, and Windows, 45although the JIT-compiler does not work with Windows at this time. 46 47## Link to the crate 48 49This crate is available from [crates.io](https://crates.io/crates/rbpf), so it 50should work out of the box by adding it as a dependency in your `Cargo.toml` 51file: 52 53```toml 54[dependencies] 55rbpf = "0.2.0" 56``` 57 58You can also use the development version from this GitHub repository. This 59should be as simple as putting this inside your `Cargo.toml`: 60 61```toml 62[dependencies] 63rbpf = { git = "https://github.com/qmonnet/rbpf" } 64``` 65 66Of course, if you prefer, you can clone it locally, possibly hack the crate, 67and then indicate the path of your local version in `Cargo.toml`: 68 69```toml 70[dependencies] 71rbpf = { path = "path/to/rbpf" } 72``` 73 74Then indicate in your source code that you want to use the crate: 75 76```rust,ignore 77extern crate rbpf; 78``` 79 80## API 81 82The API is pretty well documented inside the source code. You should also be 83able to access [an online version of the documentation from 84here](https://docs.rs/rbpf/), automatically generated from the 85[crates.io](https://crates.io/crates/rbpf) version (may not be up-to-date with 86the main branch). [Examples](../../tree/main/examples) and [unit 87tests](../../tree/main/tests) should also prove helpful. Here is a summary of 88how to use the crate. 89 90Here are the steps to follow to run an eBPF program with rbpf: 91 921. Create a virtual machine. There are several kinds of machines, we will come 93 back on this later. When creating the VM, pass the eBPF program as an 94 argument to the constructor. 952. If you want to use some helper functions, register them into the virtual 96 machine. 973. If you want a JIT-compiled program, compile it. 984. Execute your program: either run the interpreter or call the JIT-compiled 99 function. 100 101eBPF has been initially designed to filter packets (now it has some other hooks 102in the Linux kernel, such as kprobes, but this is not covered by rbpf). As a 103consequence, most of the load and store instructions of the program are 104performed on a memory area representing the packet data. However, in the Linux 105kernel, the eBPF program does not immediately access this data area: initially, 106it has access to a C `struct sk_buff` instead, which is a buffer containing 107metadata about the packet—including memory addresses of the beginning and of 108the end of the packet data area. So the program first loads those pointers from 109the `sk_buff`, and then can access the packet data. 110 111This behavior can be replicated with rbpf, but it is not mandatory. For this 112reason, we have several structs representing different kinds of virtual 113machines: 114 115* `struct EbpfVmMbuffer` mimics the kernel. When the program is run, the 116 address provided to its first eBPF register will be the address of a metadata 117 buffer provided by the user, and that is expected to contain pointers to the 118 start and the end of the packet data memory area. 119 120* `struct EbpfVmFixedMbuff` has one purpose: enabling the execution of programs 121 created to be compatible with the kernel, while saving the effort to manually 122 handle the metadata buffer for the user. In fact, this struct has a static 123 internal buffer that is passed to the program. The user has to indicate the 124 offset values at which the eBPF program expects to find the start and the end 125 of packet data in the buffer. On calling the function that runs the program 126 (JITted or not), the struct automatically updates the addresses in this 127 static buffer, at the appointed offsets, for the start and the end of the 128 packet data the program is called upon. 129 130* `struct EbpfVmRaw` is for programs that want to run directly on packet data. 131 No metadata buffer is involved, the eBPF program directly receives the 132 address of the packet data in its first register. This is the behavior of 133 uBPF. 134 135* `struct EbpfVmNoData` does not take any data. The eBPF program takes no 136 argument whatsoever and its return value is deterministic. Not so sure there 137 is a valid use case for that, but if nothing else, this is very useful for 138 unit tests. 139 140All these structs implement the same public functions: 141 142```rust,ignore 143// called with EbpfVmMbuff:: prefix 144pub fn new(prog: &'a [u8]) -> Result<EbpfVmMbuff<'a>, Error> 145 146// called with EbpfVmFixedMbuff:: prefix 147pub fn new(prog: &'a [u8], 148 data_offset: usize, 149 data_end_offset: usize) -> Result<EbpfVmFixedMbuff<'a>, Error> 150 151// called with EbpfVmRaw:: prefix 152pub fn new(prog: &'a [u8]) -> Result<EbpfVmRaw<'a>, Error> 153 154// called with EbpfVmNoData:: prefix 155pub fn new(prog: &'a [u8]) -> Result<EbpfVmNoData<'a>, Error> 156``` 157 158This is used to create a new instance of a VM. The return type is dependent of 159the struct from which the function is called. For instance, 160`rbpf::EbpfVmRaw::new(Some(my_program))` would return an instance of `struct 161rbpf::EbpfVmRaw` (wrapped in a `Result`). When a program is loaded, it is 162checked with a very simple verifier (nothing close to the one for Linux 163kernel). Users are also able to replace it with a custom verifier. 164 165For `struct EbpfVmFixedMbuff`, two additional arguments must be passed to the 166constructor: `data_offset` and `data_end_offset`. They are the offset (byte 167number) at which the pointers to the beginning and to the end, respectively, of 168the memory area of packet data are to be stored in the internal metadata buffer 169each time the program is executed. Other structs do not use this mechanism and 170do not need those offsets. 171 172```rust,ignore 173// for struct EbpfVmMbuff, struct EbpfVmRaw and struct EbpfVmRawData 174pub fn set_program(&mut self, prog: &'a [u8]) -> Result<(), Error> 175 176// for struct EbpfVmFixedMbuff 177pub fn set_program(&mut self, prog: &'a [u8], 178 data_offset: usize, 179 data_end_offset: usize) -> Result<(), Error> 180``` 181 182You can use for example `my_vm.set_program(my_program);` to change the loaded 183program after the VM instance creation. This program is checked with the 184verifier attached to the VM. The verifying function of the VM can be changed at 185any moment. 186 187```rust,ignore 188pub type Verifier = fn(prog: &[u8]) -> Result<(), Error>; 189 190pub fn set_verifier(&mut self, 191 verifier: Verifier) -> Result<(), Error> 192``` 193 194Note that if a program has already been loaded into the VM, setting a new 195verifier also immediately runs it on the loaded program. However, the verifier 196is not run if no program has been loaded (if `None` was passed to the `new()` 197method when creating the VM). 198 199```rust,ignore 200pub type Helper = fn (u64, u64, u64, u64, u64) -> u64; 201 202pub fn register_helper(&mut self, 203 key: u32, 204 function: Helper) -> Result<(), Error> 205``` 206 207This function is used to register a helper function. The VM stores its 208registers in a hashmap, so the key can be any `u32` value you want. It may be 209useful for programs that should be compatible with the Linux kernel and 210therefore must use specific helper numbers. 211 212```rust,ignore 213// for struct EbpfVmMbuff 214pub fn execute_program(&self, 215 mem: &'a mut [u8], 216 mbuff: &'a mut [u8]) -> Result<(u64), Error> 217 218// for struct EbpfVmFixedMbuff and struct EbpfVmRaw 219pub fn execute_program(&self, 220 mem: &'a mut [u8]) -> Result<(u64), Error> 221 222// for struct EbpfVmNoData 223pub fn execute_program(&self) -> Result<(u64), Error> 224``` 225 226Interprets the loaded program. The function takes a reference to the packet 227data and the metadata buffer, or only to the packet data, or nothing at all, 228depending on the kind of the VM used. The value returned is the result of the 229eBPF program. 230 231```rust,ignore 232pub fn jit_compile(&mut self) -> Result<(), Error> 233``` 234 235JIT-compile the loaded program, for x86_64 architecture. If the program is to 236use helper functions, they must be registered into the VM before this function 237is called. The generated assembly function is internally stored in the VM. 238 239```rust,ignore 240// for struct EbpfVmMbuff 241pub unsafe fn execute_program_jit(&self, mem: &'a mut [u8], 242 mbuff: &'a mut [u8]) -> Result<(u64), Error> 243 244// for struct EbpfVmFixedMbuff and struct EbpfVmRaw 245pub unsafe fn execute_program_jit(&self, mem: &'a mut [u8]) -> Result<(u64), Error> 246 247// for struct EbpfVmNoData 248pub unsafe fn execute_program_jit(&self) -> Result<(u64), Error> 249``` 250 251Calls the JIT-compiled program. The arguments to provide are the same as for 252`execute_program()`, again depending on the kind of VM that is used. The result of 253the JIT-compiled program should be the same as with the interpreter, but it 254should run faster. Note that if errors occur during the program execution, the 255JIT-compiled version does not handle it as well as the interpreter, and the 256program may crash. For this reason, the functions are marked as `unsafe`. 257 258## Example uses 259 260### Simple example 261 262This comes from the unit test `test_vm_add`. 263 264```rust 265extern crate rbpf; 266 267fn main() { 268 269 // This is the eBPF program, in the form of bytecode instructions. 270 let prog = &[ 271 0xb4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov32 r0, 0 272 0xb4, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, // mov32 r1, 2 273 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, // add32 r0, 1 274 0x0c, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // add32 r0, r1 275 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit 276 ]; 277 278 // Instantiate a struct EbpfVmNoData. This is an eBPF VM for programs that 279 // takes no packet data in argument. 280 // The eBPF program is passed to the constructor. 281 let vm = rbpf::EbpfVmNoData::new(Some(prog)).unwrap(); 282 283 // Execute (interpret) the program. No argument required for this VM. 284 assert_eq!(vm.execute_program().unwrap(), 0x3); 285} 286``` 287 288### With JIT, on packet data 289 290This comes from the unit test `test_jit_ldxh`. 291 292```rust 293extern crate rbpf; 294 295fn main() { 296 let prog = &[ 297 0x71, 0x10, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, // ldxh r0, [r1+2] 298 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit 299 ]; 300 301 // Let's use some data. 302 let mem = &mut [ 303 0xaa, 0xbb, 0x11, 0xcc, 0xdd 304 ]; 305 306 // This is an eBPF VM for programs reading from a given memory area (it 307 // directly reads from packet data) 308 let mut vm = rbpf::EbpfVmRaw::new(Some(prog)).unwrap(); 309 310 #[cfg(any(windows, not(feature = "std")))] { 311 assert_eq!(vm.execute_program(mem).unwrap(), 0x11); 312 } 313 #[cfg(all(not(windows), feature = "std"))] { 314 // This time we JIT-compile the program. 315 vm.jit_compile().unwrap(); 316 317 // Then we execute it. For this kind of VM, a reference to the packet 318 // data must be passed to the function that executes the program. 319 unsafe { assert_eq!(vm.execute_program_jit(mem).unwrap(), 0x11); } 320 } 321} 322``` 323### Using a metadata buffer 324 325This comes from the unit test `test_jit_mbuff` and derives from the unit test 326`test_jit_ldxh`. 327 328```rust 329extern crate rbpf; 330 331fn main() { 332 let prog = &[ 333 // Load mem from mbuff at offset 8 into R1 334 0x79, 0x11, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 335 // ldhx r1[2], r0 336 0x69, 0x10, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 337 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 338 ]; 339 let mem = &mut [ 340 0xaa, 0xbb, 0x11, 0x22, 0xcc, 0xdd 341 ]; 342 343 // Just for the example we create our metadata buffer from scratch, and 344 // we store the pointers to packet data start and end in it. 345 let mut mbuff = &mut [0u8; 32]; 346 unsafe { 347 let mut data = mbuff.as_ptr().offset(8) as *mut u64; 348 let mut data_end = mbuff.as_ptr().offset(24) as *mut u64; 349 *data = mem.as_ptr() as u64; 350 *data_end = mem.as_ptr() as u64 + mem.len() as u64; 351 } 352 353 // This eBPF VM is for program that use a metadata buffer. 354 let mut vm = rbpf::EbpfVmMbuff::new(Some(prog)).unwrap(); 355 356 #[cfg(any(windows, not(feature = "std")))] { 357 assert_eq!(vm.execute_program(mem, mbuff).unwrap(), 0x2211); 358 } 359 #[cfg(all(not(windows), feature = "std"))] { 360 // Here again we JIT-compile the program. 361 vm.jit_compile().unwrap(); 362 363 // Here we must provide both a reference to the packet data, and to the 364 // metadata buffer we use. 365 unsafe { 366 assert_eq!(vm.execute_program_jit(mem, mbuff).unwrap(), 0x2211); 367 } 368 } 369} 370``` 371 372### Loading code from an object file; and using a virtual metadata buffer 373 374This comes from unit test `test_vm_block_port`. 375 376This example requires the following additional crates, you may have to add them 377to your `Cargo.toml` file. 378 379```toml 380[dependencies] 381rbpf = "0.2.0" 382elf = "0.0.10" 383``` 384 385It also uses a kind of VM that uses an internal buffer used to simulate the 386`sk_buff` used by eBPF programs in the kernel, without having to manually 387create a new buffer for each packet. It may be useful for programs compiled for 388the kernel and that assumes the data they receive is a `sk_buff` pointing to 389the packet data start and end addresses. So here we just provide the offsets at 390which the eBPF program expects to find those pointers, and the VM handles the 391buffer update so that we only have to provide a reference to the packet data 392for each run of the program. 393 394```rust 395extern crate elf; 396use std::path::PathBuf; 397 398extern crate rbpf; 399use rbpf::helpers; 400 401fn main() { 402 // Load a program from an ELF file, e.g. compiled from C to eBPF with 403 // clang/LLVM. Some minor modification to the bytecode may be required. 404 let filename = "examples/load_elf__block_a_port.elf"; 405 406 let path = PathBuf::from(filename); 407 let file = match elf::File::open_path(&path) { 408 Ok(f) => f, 409 Err(e) => panic!("Error: {:?}", e), 410 }; 411 412 // Here we assume the eBPF program is in the ELF section called 413 // ".classifier". 414 let text_scn = match file.get_section(".classifier") { 415 Some(s) => s, 416 None => panic!("Failed to look up .classifier section"), 417 }; 418 419 let prog = &text_scn.data; 420 421 // This is our data: a real packet, starting with Ethernet header 422 let packet = &mut [ 423 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 424 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 425 0x08, 0x00, // ethertype 426 0x45, 0x00, 0x00, 0x3b, // start ip_hdr 427 0xa6, 0xab, 0x40, 0x00, 428 0x40, 0x06, 0x96, 0x0f, 429 0x7f, 0x00, 0x00, 0x01, 430 0x7f, 0x00, 0x00, 0x01, 431 0x99, 0x99, 0xc6, 0xcc, // start tcp_hdr 432 0xd1, 0xe5, 0xc4, 0x9d, 433 0xd4, 0x30, 0xb5, 0xd2, 434 0x80, 0x18, 0x01, 0x56, 435 0xfe, 0x2f, 0x00, 0x00, 436 0x01, 0x01, 0x08, 0x0a, // start data 437 0x00, 0x23, 0x75, 0x89, 438 0x00, 0x23, 0x63, 0x2d, 439 0x71, 0x64, 0x66, 0x73, 440 0x64, 0x66, 0x0a 441 ]; 442 443 // This is an eBPF VM for programs using a virtual metadata buffer, similar 444 // to the sk_buff that eBPF programs use with tc and in Linux kernel. 445 // We must provide the offsets at which the pointers to packet data start 446 // and end must be stored: these are the offsets at which the program will 447 // load the packet data from the metadata buffer. 448 let mut vm = rbpf::EbpfVmFixedMbuff::new(Some(prog), 0x40, 0x50).unwrap(); 449 450 // We register a helper function, that can be called by the program, into 451 // the VM. The `bpf_trace_printf` is only available when we have access to 452 // the standard library. 453 #[cfg(feature = "std")] { 454 vm.register_helper(helpers::BPF_TRACE_PRINTK_IDX, 455 helpers::bpf_trace_printf).unwrap(); 456 } 457 458 // This kind of VM takes a reference to the packet data, but does not need 459 // any reference to the metadata buffer: a fixed buffer is handled 460 // internally by the VM. 461 let res = vm.execute_program(packet).unwrap(); 462 println!("Program returned: {:?} ({:#x})", res, res); 463} 464``` 465 466## Building eBPF programs 467 468Besides passing the raw hexadecimal codes for building eBPF programs, two other 469methods are available. 470 471### Assembler 472 473The first method consists in using the assembler provided by the crate. 474 475```rust 476extern crate rbpf; 477use rbpf::assembler::assemble; 478 479let prog = assemble("add64 r1, 0x605 480 mov64 r2, 0x32 481 mov64 r1, r0 482 be16 r0 483 neg64 r2 484 exit").unwrap(); 485 486#[cfg(feature = "std")] { 487 println!("{:?}", prog); 488} 489``` 490 491The above snippet will produce: 492 493```rust,ignore 494Ok([0x07, 0x01, 0x00, 0x00, 0x05, 0x06, 0x00, 0x00, 495 0xb7, 0x02, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 496 0xbf, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 497 0xdc, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 498 0x87, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 499 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]) 500``` 501 502Conversely, a disassembler is also available to dump instruction names from 503bytecode in a human-friendly format. 504 505```rust 506extern crate rbpf; 507use rbpf::disassembler::disassemble; 508 509let prog = &[ 510 0x07, 0x01, 0x00, 0x00, 0x05, 0x06, 0x00, 0x00, 511 0xb7, 0x02, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 512 0xbf, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 513 0xdc, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 514 0x87, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 515 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 516]; 517 518disassemble(prog); 519``` 520 521This will produce the following output: 522 523```txt 524add64 r1, 0x605 525mov64 r2, 0x32 526mov64 r1, r0 527be16 r0 528neg64 r2 529exit 530``` 531 532Please refer to [source code](src/assembler.rs) and [tests](tests/assembler.rs) 533for the syntax and the list of instruction names. 534 535### Building API 536 537The other way to build programs is to chain commands from the instruction 538builder API. It looks less like assembly, maybe more like high-level functions. 539What's sure is that the result is more verbose, but if you prefer to build 540programs this way, it works just as well. If we take again the same sample as 541above, it would be constructed as follows. 542 543```rust 544extern crate rbpf; 545use rbpf::insn_builder::*; 546 547let mut program = BpfCode::new(); 548program.add(Source::Imm, Arch::X64).set_dst(1).set_imm(0x605).push() 549 .mov(Source::Imm, Arch::X64).set_dst(2).set_imm(0x32).push() 550 .mov(Source::Reg, Arch::X64).set_src(0).set_dst(1).push() 551 .swap_bytes(Endian::Big).set_dst(0).set_imm(0x10).push() 552 .negate(Arch::X64).set_dst(2).push() 553 .exit().push(); 554``` 555 556Again, please refer to [the source and related tests](src/insn_builder.rs) to 557get more information and examples on how to use it. 558 559## Build features 560 561### `no_std` 562 563The `rbpf` crate has a Cargo feature named "std" that is enabled by default. To 564use `rbpf` in `no_std` environments this feature needs to be disabled. To do 565this, you need to modify your dependency on `rbpf` in Cargo.toml to disable the 566enabled-by-default features. 567 568```toml 569[dependencies] 570rbpf = { version = "1.0", default-features = false } 571``` 572 573Note that when using this crate in `no_std` environments, the `jit` module 574isn't available. This is because it depends on functions provided by `libc` 575(`libc::posix_memalign()`, `libc::mprotect()`) which aren't available on 576`no_std`. 577 578The `assembler` module is available, albeit with reduced debugging features. It 579depends on the `combine` crate providing parser combinators. Under `no_std` 580this crate only provides simple parsers which generate less descriptive error 581messages. 582 583## Feedback welcome! 584 585This is the author's first try at writing Rust code. He learned a lot in the 586process, but there remains a feeling that this crate has a kind of C-ish style 587in some places instead of the Rusty look the author would like it to have. So 588feedback (or PRs) are welcome, including about ways you might see to take 589better advantage of Rust features. 590 591Note that the project expects new commits to be covered by the 592[Developer's Certificate of Origin](https://wiki.linuxfoundation.org/dco). 593When contributing Pull Requests, please sign off your commits accordingly. 594 595## Questions / Answers 596 597### Why implementing an eBPF virtual machine in Rust? 598 599As of this writing, there is no particular use case for this crate at the best 600of the author's knowledge. The author happens to work with BPF on Linux and to 601know how uBPF works, and he wanted to learn and experiment with Rust—no more 602than that. 603 604### What are the differences with uBPF? 605 606Other than the language, obviously? Well, there are some differences: 607 608* Some constants, such as the maximum length for programs or the length for the 609 stack, differs between uBPF and rbpf. The latter uses the same values as the 610 Linux kernel, while uBPF has its own values. 611 612* When an error occurs while a program is run by uBPF, the function running the 613 program silently returns the maximum value as an error code, while rbpf 614 returns Rust type `Error`. 615 616* The registration of helper functions, that can be called from within an eBPF 617 program, is not handled in the same way. 618 619* The distinct structs permitting to run program either on packet data, or with 620 a metadata buffer (simulated or not) is a specificity of rbpf. 621 622* As for performance: theoretically the JITted programs are expected to run at 623 the same speed, while the C interpreter of uBPF should go slightly faster 624 than rbpf. But this has not been asserted yet. Benchmarking both programs 625 would be an interesting thing to do. 626 627### Can I use it with the “classic” BPF (a.k.a cBPF) version? 628 629No. This crate only works with extended BPF (eBPF) programs. For cBPF programs, 630such as used by tcpdump (as of this writing) for example, you may be interested 631in the [bpfjit crate](https://crates.io/crates/bpfjit) written by Alexander 632Polakov instead. 633 634### What functionalities are implemented? 635 636Running and JIT-compiling eBPF programs work. There is also a mechanism to 637register user-defined helper functions. The eBPF implementation of the Linux 638kernel comes with [some additional 639features](https://github.com/iovisor/bcc/blob/master/docs/kernel-versions.md): 640a high number of helpers, several kinds of maps, tail calls. 641 642* Additional helpers should be easy to add, but very few of the existing Linux 643 helpers have been replicated in rbpf so far. 644 645* Tail calls (“long jumps” from an eBPF program into another) are not 646 implemented. This is probably not trivial to design and implement. 647 648* The interaction with maps is done through the use of specific helpers, so 649 this should not be difficult to add. The maps themselves can reuse the maps 650 in the kernel (if on Linux), to communicate with in-kernel eBPF programs for 651 instance; or they can be handled in user space. Rust has arrays and hashmaps, 652 so their implementation should be pretty straightforward (and may be added to 653 rbpf in the future). 654 655### What about program validation? 656 657The ”verifier” of this crate is very short and has nothing to do with the 658kernel verifier, which means that it accepts programs that may not be safe. On 659the other hand, you probably do not run this in a kernel here, so it will not 660crash your system. Implementing a verifier similar to the one in the kernel is 661not trivial, and we cannot “copy” it since it is under GPL license. 662 663### What about safety then? 664 665Rust has a strong emphasis on safety. Yet to have the eBPF VM work, some 666`unsafe` blocks of code are used. The VM, taken as an eBPF interpreter, can 667return an error but should not crash. Please file an issue otherwise. 668 669As for the JIT-compiler, it is a different story, since runtime memory checks 670are more complicated to implement in assembly. It _will_ crash if your 671JIT-compiled program tries to perform unauthorized memory accesses. Usually, it 672could be a good idea to test your program with the interpreter first. 673 674Oh, and if your program has infinite loops, even with the interpreter, you're 675on your own. 676 677## Caveats 678 679* This crate is **under development** and the API may be subject to change. 680 681* The JIT compiler produces an unsafe program: memory access are not tested at 682 runtime (yet). Use with caution. 683 684* A small number of eBPF instructions have not been implemented yet. This 685 should not be a problem for the majority of eBPF programs. 686 687* Beware of turnips. Turnips are disgusting. 688 689## _To do_ list 690 691* Implement some traits (`Clone`, `Drop`, `Debug` are good candidates). 692* Provide built-in support for user-space array and hash BPF maps. 693* Improve safety of JIT-compiled programs with runtime memory checks. 694* Add helpers (some of those supported in the kernel, such as checksum update, 695 could be helpful). 696* Improve verifier. Could we find a way to directly support programs compiled 697 with clang? 698* Maybe one day, tail calls? 699* JIT-compilers for other architectures? 700* … 701 702## License 703 704Following the effort of the Rust language project itself in order to ease 705integration with other projects, the rbpf crate is distributed under the terms 706of both the MIT license and the Apache License (Version 2.0). 707 708See 709[LICENSE-APACHE](https://github.com/qmonnet/rbpf/blob/main/LICENSE-APACHE) 710and [LICENSE-MIT](https://github.com/qmonnet/rbpf/blob/main/LICENSE-MIT) for 711details. 712 713## Version 714[The last commit](https://github.com/qmonnet/rbpf/commit/fe7021b07b08a43b836743a77796d07ce1f4902e) 715 716 717## Inspired by 718 719* [uBPF](https://github.com/iovisor/ubpf), a C user-space implementation of an 720 eBPF virtual machine, with a JIT-compiler and disassembler (and also 721 including the assembler from the human-readable form of the instructions, 722 such as in `mov r0, 0x1337`), by Rich Lane for Big Switch Networks (2015) 723 724* [_Building a simple JIT in 725 Rust_](https://www.sophiajt.com/building-a-simple-jit-in-rust), 726 by Sophia Turner (2015) 727 728* [bpfjit](https://github.com/polachok/bpfjit) (also [on 729 crates.io](https://crates.io/crates/bpfjit)), a Rust crate exporting the cBPF 730 JIT compiler from FreeBSD 10 tree to Rust, by Alexander Polakov (2016) 731 732## Other resources 733 734* Cilium project documentation about BPF: [_BPF and XDP Reference 735 Guide_](http://docs.cilium.io/en/latest/bpf/) 736 737* [Kernel documentation about BPF](https://docs.kernel.org/bpf/) 738 739* [_Dive into BPF: a list of reading 740 material_](https://qmonnet.github.io/whirl-offload/2016/09/01/dive-into-bpf), 741 a blog article listing documentation for BPF and related technologies (2016) 742 743* [The Rust programming language](https://www.rust-lang.org) 744