README.md
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