xref: /DragonOS/kernel/crates/rbpf/README.md (revision 7b0ef10895108a0de5ff5ef3d2f93f40cf2e33a5)
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