{"id":13578476,"url":"https://github.com/qmonnet/rbpf","last_synced_at":"2025-05-13T22:04:32.843Z","repository":{"id":43309087,"uuid":"78370639","full_name":"qmonnet/rbpf","owner":"qmonnet","description":"Rust virtual machine and JIT compiler for eBPF programs","archived":false,"fork":false,"pushed_at":"2025-05-10T00:15:34.000Z","size":952,"stargazers_count":995,"open_issues_count":11,"forks_count":277,"subscribers_count":25,"default_branch":"main","last_synced_at":"2025-05-10T00:25:14.438Z","etag":null,"topics":["assembler","bpf","ebpf","ebpf-programs","interpreter","jit-compiler","packet-filtering","rust"],"latest_commit_sha":null,"homepage":null,"language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/qmonnet.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE-APACHE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2017-01-08T21:09:07.000Z","updated_at":"2025-05-09T23:15:17.000Z","dependencies_parsed_at":"2024-09-29T08:00:55.174Z","dependency_job_id":"5ee52e57-c4ae-4c95-a999-fa1d5aab1908","html_url":"https://github.com/qmonnet/rbpf","commit_stats":{"total_commits":201,"total_committers":12,"mean_commits":16.75,"dds":0.6417910447761195,"last_synced_commit":"e2653a45ddc4f001a5c45efbce8146daffd225f5"},"previous_names":[],"tags_count":4,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/qmonnet%2Frbpf","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/qmonnet%2Frbpf/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/qmonnet%2Frbpf/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/qmonnet%2Frbpf/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/qmonnet","download_url":"https://codeload.github.com/qmonnet/rbpf/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254036816,"owners_count":22003653,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["assembler","bpf","ebpf","ebpf-programs","interpreter","jit-compiler","packet-filtering","rust"],"created_at":"2024-08-01T15:01:30.979Z","updated_at":"2025-05-13T22:04:32.801Z","avatar_url":"https://github.com/qmonnet.png","language":"Rust","readme":"# rbpf\n\n\u003cpicture\u003e\n  \u003csource media=\"(prefers-color-scheme: dark)\" srcset=\"misc/rbpf_256_border.png\"\u003e\n  \u003cimg src=\"misc/rbpf_256.png\"\u003e\n\u003c/picture\u003e\n\nRust (user-space) virtual machine for eBPF\n\n[![Build Status](https://github.com/qmonnet/rbpf/actions/workflows/test.yaml/badge.svg)](https://github.com/qmonnet/rbpf/actions/workflows/test.yaml)\n[![Build status](https://ci.appveyor.com/api/projects/status/ia74coeuhxtrcvsk/branch/main?svg=true)](https://ci.appveyor.com/project/qmonnet/rbpf/branch/main)\n[![Coverage Status](https://coveralls.io/repos/github/qmonnet/rbpf/badge.svg?branch=main)](https://coveralls.io/github/qmonnet/rbpf?branch=main)\n[![Crates.io](https://img.shields.io/crates/v/rbpf.svg)](https://crates.io/crates/rbpf)\n\n* [Description](#description)\n* [Link to the crate](#link-to-the-crate)\n* [API](#api)\n* [Example uses](#example-uses)\n* [Building eBPF programs](#building-ebpf-programs)\n* [Build Features](#build-features)\n* [Feedback welcome!](#feedback-welcome)\n* [Questions / Answers](#questions--answers)\n* [Caveats](#caveats)\n* [_To do_ list](#to-do-list)\n* [License](#license)\n* [Inspired by](#inspired-by)\n* [Other resources](#other-resources)\n\n## Description\n\nThis crate contains a virtual machine for eBPF program execution. BPF, as in\n_Berkeley Packet Filter_, is an assembly-like language initially developed for\nBSD systems, in order to filter packets in the kernel with tools such as\ntcpdump so as to avoid useless copies to user-space. It was ported to Linux,\nwhere it evolved into eBPF (_extended_ BPF), a faster version with more\nfeatures. While BPF programs are originally intended to run in the kernel, the\nvirtual machine of this crate enables running it in user-space applications;\nit contains an interpreter, an x86_64 JIT-compiler for eBPF programs, as well as\na disassembler.\n\nIt is based on Rich Lane's [uBPF software](https://github.com/iovisor/ubpf/),\nwhich does nearly the same, but is written in C.\n\nThe crate is supposed to compile and run on Linux, MacOS X, and Windows,\nalthough the JIT-compiler does not work with Windows at this time.\n\n## Link to the crate\n\nThis crate is available from [crates.io](https://crates.io/crates/rbpf), so it\nshould work out of the box by adding it as a dependency in your `Cargo.toml`\nfile:\n\n```toml\n[dependencies]\nrbpf = \"0.3.0\"\n```\n\nYou can also use the development version from this GitHub repository. This\nshould be as simple as putting this inside your `Cargo.toml`:\n\n```toml\n[dependencies]\nrbpf = { git = \"https://github.com/qmonnet/rbpf\" }\n```\n\nOf course, if you prefer, you can clone it locally, possibly hack the crate,\nand then indicate the path of your local version in `Cargo.toml`:\n\n```toml\n[dependencies]\nrbpf = { path = \"path/to/rbpf\" }\n```\n\nThen indicate in your source code that you want to use the crate:\n\n```rust,ignore\nextern crate rbpf;\n```\n\n## API\n\nThe API is pretty well documented inside the source code. You should also be\nable to access [an online version of the documentation from\nhere](https://docs.rs/rbpf/), automatically generated from the\n[crates.io](https://crates.io/crates/rbpf) version (may not be up-to-date with\nthe main branch). [Examples](../../tree/main/examples) and [unit\ntests](../../tree/main/tests) should also prove helpful. Here is a summary of\nhow to use the crate.\n\nHere are the steps to follow to run an eBPF program with rbpf:\n\n1. Create a virtual machine. There are several kinds of machines, we will come\n   back on this later. When creating the VM, pass the eBPF program as an\n   argument to the constructor.\n2. If you want to use some helper functions, register them into the virtual\n   machine.\n3. If you want a JIT-compiled program, compile it.\n4. Execute your program: either run the interpreter or call the JIT-compiled\n   function.\n\neBPF has been initially designed to filter packets (now it has some other hooks\nin the Linux kernel, such as kprobes, but this is not covered by rbpf). As a\nconsequence, most of the load and store instructions of the program are\nperformed on a memory area representing the packet data. However, in the Linux\nkernel, the eBPF program does not immediately access this data area: initially,\nit has access to a C `struct sk_buff` instead, which is a buffer containing\nmetadata about the packet—including memory addresses of the beginning and of\nthe end of the packet data area. So the program first loads those pointers from\nthe `sk_buff`, and then can access the packet data.\n\nThis behavior can be replicated with rbpf, but it is not mandatory. For this\nreason, we have several structs representing different kinds of virtual\nmachines:\n\n* `struct EbpfVmMbuffer` mimics the kernel. When the program is run, the\n  address provided to its first eBPF register will be the address of a metadata\n  buffer provided by the user, and that is expected to contain pointers to the\n  start and the end of the packet data memory area.\n\n* `struct EbpfVmFixedMbuff` has one purpose: enabling the execution of programs\n  created to be compatible with the kernel, while saving the effort to manually\n  handle the metadata buffer for the user. In fact, this struct has a static\n  internal buffer that is passed to the program. The user has to indicate the\n  offset values at which the eBPF program expects to find the start and the end\n  of packet data in the buffer. On calling the function that runs the program\n  (JITted or not), the struct automatically updates the addresses in this\n  static buffer, at the appointed offsets, for the start and the end of the\n  packet data the program is called upon.\n\n* `struct EbpfVmRaw` is for programs that want to run directly on packet data.\n  No metadata buffer is involved, the eBPF program directly receives the\n  address of the packet data in its first register. This is the behavior of\n  uBPF.\n\n* `struct EbpfVmNoData` does not take any data. The eBPF program takes no\n  argument whatsoever and its return value is deterministic. Not so sure there\n  is a valid use case for that, but if nothing else, this is very useful for\n  unit tests.\n\nAll these structs implement the same public functions:\n\n```rust,ignore\n// called with EbpfVmMbuff:: prefix\npub fn new(prog: \u0026'a [u8]) -\u003e Result\u003cEbpfVmMbuff\u003c'a\u003e, Error\u003e\n\n// called with EbpfVmFixedMbuff:: prefix\npub fn new(prog: \u0026'a [u8],\n           data_offset: usize,\n           data_end_offset: usize) -\u003e Result\u003cEbpfVmFixedMbuff\u003c'a\u003e, Error\u003e\n\n// called with EbpfVmRaw:: prefix\npub fn new(prog: \u0026'a [u8]) -\u003e Result\u003cEbpfVmRaw\u003c'a\u003e, Error\u003e\n\n// called with EbpfVmNoData:: prefix\npub fn new(prog: \u0026'a [u8]) -\u003e Result\u003cEbpfVmNoData\u003c'a\u003e, Error\u003e\n```\n\nThis is used to create a new instance of a VM. The return type is dependent of\nthe struct from which the function is called. For instance,\n`rbpf::EbpfVmRaw::new(Some(my_program))` would return an instance of `struct\nrbpf::EbpfVmRaw` (wrapped in a `Result`). When a program is loaded, it is\nchecked with a very simple verifier (nothing close to the one for Linux\nkernel). Users are also able to replace it with a custom verifier.\n\nFor `struct EbpfVmFixedMbuff`, two additional arguments must be passed to the\nconstructor: `data_offset` and `data_end_offset`. They are the offset (byte\nnumber) at which the pointers to the beginning and to the end, respectively, of\nthe memory area of packet data are to be stored in the internal metadata buffer\neach time the program is executed. Other structs do not use this mechanism and\ndo not need those offsets.\n\n```rust,ignore\n// for struct EbpfVmMbuff, struct EbpfVmRaw and struct EbpfVmRawData\npub fn set_program(\u0026mut self, prog: \u0026'a [u8]) -\u003e Result\u003c(), Error\u003e\n\n// for struct EbpfVmFixedMbuff\npub fn set_program(\u0026mut self, prog: \u0026'a [u8],\n                data_offset: usize,\n                data_end_offset: usize) -\u003e Result\u003c(), Error\u003e\n```\n\nYou can use for example `my_vm.set_program(my_program);` to change the loaded\nprogram after the VM instance creation. This program is checked with the\nverifier attached to the VM. The verifying function of the VM can be changed at\nany moment.\n\n```rust,ignore\npub type Verifier = fn(prog: \u0026[u8]) -\u003e Result\u003c(), Error\u003e;\n\npub fn set_verifier(\u0026mut self,\n                    verifier: Verifier) -\u003e Result\u003c(), Error\u003e\n```\n\nNote that if a program has already been loaded into the VM, setting a new\nverifier also immediately runs it on the loaded program. However, the verifier\nis not run if no program has been loaded (if `None` was passed to the `new()`\nmethod when creating the VM).\n\n```rust,ignore\npub type Helper = fn (u64, u64, u64, u64, u64) -\u003e u64;\n\npub fn register_helper(\u0026mut self,\n                       key: u32,\n                       function: Helper) -\u003e Result\u003c(), Error\u003e\n```\n\nThis function is used to register a helper function. The VM stores its\nregisters in a hashmap, so the key can be any `u32` value you want. It may be\nuseful for programs that should be compatible with the Linux kernel and\ntherefore must use specific helper numbers.\n\n```rust,ignore\npub fn register_allowed_memory(\u0026mut self,, addr: \u0026[u64]) -\u003e ()\n```\n\nThis function adds a list of memory addresses that the eBPF program is allowed\nto load and store. Multiple calls to this function will append the addresses to\nan internal HashSet. At the moment rbpf only validates memory accesses when\nusing the interpreter. This function is useful when using kernel helpers which\nreturn pointers to objects stored in eBPF maps.\n\n```rust,ignore\n// for struct EbpfVmMbuff\npub fn execute_program(\u0026self,\n                 mem: \u0026'a mut [u8],\n                 mbuff: \u0026'a mut [u8]) -\u003e Result\u003c(u64), Error\u003e\n\n// for struct EbpfVmFixedMbuff and struct EbpfVmRaw\npub fn execute_program(\u0026self,\n                 mem: \u0026'a mut [u8]) -\u003e Result\u003c(u64), Error\u003e\n\n// for struct EbpfVmNoData\npub fn execute_program(\u0026self) -\u003e Result\u003c(u64), Error\u003e\n```\n\nInterprets the loaded program. The function takes a reference to the packet\ndata and the metadata buffer, or only to the packet data, or nothing at all,\ndepending on the kind of the VM used. The value returned is the result of the\neBPF program.\n\n```rust,ignore\npub fn jit_compile(\u0026mut self) -\u003e Result\u003c(), Error\u003e\n```\n\nJIT-compile the loaded program, for x86_64 architecture. If the program is to\nuse helper functions, they must be registered into the VM before this function\nis called. The generated assembly function is internally stored in the VM.\n\n```rust,ignore\n// for struct EbpfVmMbuff\npub unsafe fn execute_program_jit(\u0026self, mem: \u0026'a mut [u8],\n                            mbuff: \u0026'a mut [u8]) -\u003e Result\u003c(u64), Error\u003e\n\n// for struct EbpfVmFixedMbuff and struct EbpfVmRaw\npub unsafe fn execute_program_jit(\u0026self, mem: \u0026'a mut [u8]) -\u003e Result\u003c(u64), Error\u003e\n\n// for struct EbpfVmNoData\npub unsafe fn execute_program_jit(\u0026self) -\u003e Result\u003c(u64), Error\u003e\n```\n\nCalls the JIT-compiled program. The arguments to provide are the same as for\n`execute_program()`, again depending on the kind of VM that is used. The result of\nthe JIT-compiled program should be the same as with the interpreter, but it\nshould run faster. Note that if errors occur during the program execution, the\nJIT-compiled version does not handle it as well as the interpreter, and the\nprogram may crash. For this reason, the functions are marked as `unsafe`.\n\n## Example uses\n\n### Simple example\n\nThis comes from the unit test `test_vm_add`.\n\n```rust\nextern crate rbpf;\n\nfn main() {\n\n    // This is the eBPF program, in the form of bytecode instructions.\n    let prog = \u0026[\n        0xb4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov32 r0, 0\n        0xb4, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, // mov32 r1, 2\n        0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, // add32 r0, 1\n        0x0c, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // add32 r0, r1\n        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00  // exit\n    ];\n\n    // Instantiate a struct EbpfVmNoData. This is an eBPF VM for programs that\n    // takes no packet data in argument.\n    // The eBPF program is passed to the constructor.\n    let vm = rbpf::EbpfVmNoData::new(Some(prog)).unwrap();\n\n    // Execute (interpret) the program. No argument required for this VM.\n    assert_eq!(vm.execute_program().unwrap(), 0x3);\n}\n```\n\n### With JIT, on packet data\n\nThis comes from the unit test `test_jit_ldxh`.\n\n```rust\nextern crate rbpf;\n\nfn main() {\n    let prog = \u0026[\n        0x71, 0x10, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, // ldxh r0, [r1+2]\n        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00  // exit\n    ];\n\n    // Let's use some data.\n    let mem = \u0026mut [\n        0xaa, 0xbb, 0x11, 0xcc, 0xdd\n    ];\n\n    // This is an eBPF VM for programs reading from a given memory area (it\n    // directly reads from packet data)\n    let mut vm = rbpf::EbpfVmRaw::new(Some(prog)).unwrap();\n\n    #[cfg(any(windows, not(feature = \"std\")))] {\n        assert_eq!(vm.execute_program(mem).unwrap(), 0x11);\n    }\n    #[cfg(all(not(windows), feature = \"std\"))] {\n        // This time we JIT-compile the program.\n        vm.jit_compile().unwrap();\n\n        // Then we execute it. For this kind of VM, a reference to the packet\n        // data must be passed to the function that executes the program.\n        unsafe { assert_eq!(vm.execute_program_jit(mem).unwrap(), 0x11); }\n    }\n}\n```\n### Using a metadata buffer\n\nThis comes from the unit test `test_jit_mbuff` and derives from the unit test\n`test_jit_ldxh`.\n\n```rust\nextern crate rbpf;\n\nfn main() {\n    let prog = \u0026[\n        // Load mem from mbuff at offset 8 into R1\n        0x79, 0x11, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00,\n        // ldhx r1[2], r0\n        0x69, 0x10, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,\n        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00\n    ];\n    let mem = \u0026mut [\n        0xaa, 0xbb, 0x11, 0x22, 0xcc, 0xdd\n    ];\n\n    // Just for the example we create our metadata buffer from scratch, and\n    // we store the pointers to packet data start and end in it.\n    let mut mbuff = \u0026mut [0u8; 32];\n    unsafe {\n        let mut data     = mbuff.as_ptr().offset(8)  as *mut u64;\n        let mut data_end = mbuff.as_ptr().offset(24) as *mut u64;\n        *data     = mem.as_ptr() as u64;\n        *data_end = mem.as_ptr() as u64 + mem.len() as u64;\n    }\n\n    // This eBPF VM is for program that use a metadata buffer.\n    let mut vm = rbpf::EbpfVmMbuff::new(Some(prog)).unwrap();\n\n    #[cfg(any(windows, not(feature = \"std\")))] {\n        assert_eq!(vm.execute_program(mem, mbuff).unwrap(), 0x2211);\n    }\n    #[cfg(all(not(windows), feature = \"std\"))] {\n        // Here again we JIT-compile the program.\n        vm.jit_compile().unwrap();\n\n        // Here we must provide both a reference to the packet data, and to the\n        // metadata buffer we use.\n        unsafe {\n            assert_eq!(vm.execute_program_jit(mem, mbuff).unwrap(), 0x2211);\n        }\n    }\n}\n```\n\n### Loading code from an object file; and using a virtual metadata buffer\n\nThis comes from unit test `test_vm_block_port`.\n\nThis example requires the following additional crates, you may have to add them\nto your `Cargo.toml` file.\n\n```toml\n[dependencies]\nrbpf = \"0.3.0\"\nelf = \"0.0.10\"\n```\n\nIt also uses a kind of VM that uses an internal buffer used to simulate the\n`sk_buff` used by eBPF programs in the kernel, without having to manually\ncreate a new buffer for each packet. It may be useful for programs compiled for\nthe kernel and that assumes the data they receive is a `sk_buff` pointing to\nthe packet data start and end addresses. So here we just provide the offsets at\nwhich the eBPF program expects to find those pointers, and the VM handles the\nbuffer update so that we only have to provide a reference to the packet data\nfor each run of the program.\n\n```rust\nextern crate elf;\nuse std::path::PathBuf;\n\nextern crate rbpf;\nuse rbpf::helpers;\n\nfn main() {\n    // Load a program from an ELF file, e.g. compiled from C to eBPF with\n    // clang/LLVM. Some minor modification to the bytecode may be required.\n    let filename = \"examples/load_elf__block_a_port.o\";\n\n    let path = PathBuf::from(filename);\n    let file = match elf::File::open_path(\u0026path) {\n        Ok(f) =\u003e f,\n        Err(e) =\u003e panic!(\"Error: {:?}\", e),\n    };\n\n    // Here we assume the eBPF program is in the ELF section called\n    // \".classifier\".\n    let text_scn = match file.get_section(\".classifier\") {\n        Some(s) =\u003e s,\n        None =\u003e panic!(\"Failed to look up .classifier section\"),\n    };\n\n    let prog = \u0026text_scn.data;\n\n    // This is our data: a real packet, starting with Ethernet header\n    let packet = \u0026mut [\n        0x01, 0x23, 0x45, 0x67, 0x89, 0xab,\n        0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54,\n        0x08, 0x00,             // ethertype\n        0x45, 0x00, 0x00, 0x3b, // start ip_hdr\n        0xa6, 0xab, 0x40, 0x00,\n        0x40, 0x06, 0x96, 0x0f,\n        0x7f, 0x00, 0x00, 0x01,\n        0x7f, 0x00, 0x00, 0x01,\n        0x99, 0x99, 0xc6, 0xcc, // start tcp_hdr\n        0xd1, 0xe5, 0xc4, 0x9d,\n        0xd4, 0x30, 0xb5, 0xd2,\n        0x80, 0x18, 0x01, 0x56,\n        0xfe, 0x2f, 0x00, 0x00,\n        0x01, 0x01, 0x08, 0x0a, // start data\n        0x00, 0x23, 0x75, 0x89,\n        0x00, 0x23, 0x63, 0x2d,\n        0x71, 0x64, 0x66, 0x73,\n        0x64, 0x66, 0x0a\n    ];\n\n    // This is an eBPF VM for programs using a virtual metadata buffer, similar\n    // to the sk_buff that eBPF programs use with tc and in Linux kernel.\n    // We must provide the offsets at which the pointers to packet data start\n    // and end must be stored: these are the offsets at which the program will\n    // load the packet data from the metadata buffer.\n    let mut vm = rbpf::EbpfVmFixedMbuff::new(Some(prog), 0x40, 0x50).unwrap();\n\n    // We register a helper function, that can be called by the program, into\n    // the VM. The `bpf_trace_printf` is only available when we have access to\n    // the standard library.\n    #[cfg(feature = \"std\")] {\n        vm.register_helper(helpers::BPF_TRACE_PRINTK_IDX,\n                           helpers::bpf_trace_printf).unwrap();\n    }\n\n    // This kind of VM takes a reference to the packet data, but does not need\n    // any reference to the metadata buffer: a fixed buffer is handled\n    // internally by the VM.\n    let res = vm.execute_program(packet).unwrap();\n    println!(\"Program returned: {:?} ({:#x})\", res, res);\n}\n```\n\n## Building eBPF programs\n\nBesides passing the raw hexadecimal codes for building eBPF programs, two other\nmethods are available.\n\n### Assembler\n\nThe first method consists in using the assembler provided by the crate.\n\n```rust\nextern crate rbpf;\nuse rbpf::assembler::assemble;\n\nlet prog = assemble(\"add64 r1, 0x605\n                     mov64 r2, 0x32\n                     mov64 r1, r0\n                     be16 r0\n                     neg64 r2\n                     exit\").unwrap();\n\n#[cfg(feature = \"std\")] {\n    println!(\"{:?}\", prog);\n}\n```\n\nThe above snippet will produce:\n\n```rust,ignore\nOk([0x07, 0x01, 0x00, 0x00, 0x05, 0x06, 0x00, 0x00,\n    0xb7, 0x02, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00,\n    0xbf, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0xdc, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,\n    0x87, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00])\n```\n\nConversely, a disassembler is also available to dump instruction names from\nbytecode in a human-friendly format.\n\n```rust\nextern crate rbpf;\nuse rbpf::disassembler::disassemble;\n\nlet prog = \u0026[\n    0x07, 0x01, 0x00, 0x00, 0x05, 0x06, 0x00, 0x00,\n    0xb7, 0x02, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00,\n    0xbf, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0xdc, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,\n    0x87, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00\n];\n\ndisassemble(prog);\n```\n\nThis will produce the following output:\n\n```txt\nadd64 r1, 0x605\nmov64 r2, 0x32\nmov64 r1, r0\nbe16 r0\nneg64 r2\nexit\n```\n\nPlease refer to [source code](src/assembler.rs) and [tests](tests/assembler.rs)\nfor the syntax and the list of instruction names.\n\n### Building API\n\nThe other way to build programs is to chain commands from the instruction\nbuilder API. It looks less like assembly, maybe more like high-level functions.\nWhat's sure is that the result is more verbose, but if you prefer to build\nprograms this way, it works just as well. If we take again the same sample as\nabove, it would be constructed as follows.\n\n```rust\nextern crate rbpf;\nuse rbpf::insn_builder::*;\n\nlet mut program = BpfCode::new();\nprogram.add(Source::Imm, Arch::X64).set_dst(1).set_imm(0x605).push()\n       .mov(Source::Imm, Arch::X64).set_dst(2).set_imm(0x32).push()\n       .mov(Source::Reg, Arch::X64).set_src(0).set_dst(1).push()\n       .swap_bytes(Endian::Big).set_dst(0).set_imm(0x10).push()\n       .negate(Arch::X64).set_dst(2).push()\n       .exit().push();\n```\n\nAgain, please refer to [the source and related tests](src/insn_builder.rs) to\nget more information and examples on how to use it.\n\n## Build features\n\n### `no_std`\n\nThe `rbpf` crate has a Cargo feature named \"std\" that is enabled by default. To\nuse `rbpf` in `no_std` environments this feature needs to be disabled. To do\nthis, you need to modify your dependency on `rbpf` in Cargo.toml to disable the\nenabled-by-default features.\n\n```toml\n[dependencies]\nrbpf = { version = \"0.3.0\", default-features = false }\n```\n\nNote that when using this crate in `no_std` environments, the `jit` module\nisn't available. This is because it depends on functions provided by `libc`\n(`libc::posix_memalign()`, `libc::mprotect()`) which aren't available on\n`no_std`.\n\nThe `assembler` module is available, albeit with reduced debugging features. It\ndepends on the `combine` crate providing parser combinators. Under `no_std`\nthis crate only provides simple parsers which generate less descriptive error\nmessages.\n\n## Feedback welcome!\n\nThis is the author's first try at writing Rust code. He learned a lot in the\nprocess, but there remains a feeling that this crate has a kind of C-ish style\nin some places instead of the Rusty look the author would like it to have. So\nfeedback (or PRs) are welcome, including about ways you might see to take\nbetter advantage of Rust features.\n\nNote that the project expects new commits to be covered by the\n[Developer's Certificate of Origin](https://wiki.linuxfoundation.org/dco).\nWhen contributing Pull Requests, please sign off your commits accordingly.\n\n## Questions / Answers\n\n### Why implementing an eBPF virtual machine in Rust?\n\nAs of this writing, there is no particular use case for this crate at the best\nof the author's knowledge. The author happens to work with BPF on Linux and to\nknow how uBPF works, and he wanted to learn and experiment with Rust—no more\nthan that.\n\n### What are the differences with uBPF?\n\nOther than the language, obviously? Well, there are some differences:\n\n* Some constants, such as the maximum length for programs or the length for the\n  stack, differs between uBPF and rbpf. The latter uses the same values as the\n  Linux kernel, while uBPF has its own values.\n\n* When an error occurs while a program is run by uBPF, the function running the\n  program silently returns the maximum value as an error code, while rbpf\n  returns Rust type `Error`.\n\n* The registration of helper functions, that can be called from within an eBPF\n  program, is not handled in the same way.\n\n* The distinct structs permitting to run program either on packet data, or with\n  a metadata buffer (simulated or not) is a specificity of rbpf.\n\n* As for performance: theoretically the JITted programs are expected to run at\n  the same speed, while the C interpreter of uBPF should go slightly faster\n  than rbpf. But this has not been asserted yet. Benchmarking both programs\n  would be an interesting thing to do.\n\n### Can I use it with the “classic” BPF (a.k.a cBPF) version?\n\nNo. This crate only works with extended BPF (eBPF) programs. For cBPF programs,\nsuch as used by tcpdump (as of this writing) for example, you may be interested\nin the [bpfjit crate](https://crates.io/crates/bpfjit) written by Alexander\nPolakov instead.\n\n### What functionalities are implemented?\n\nRunning and JIT-compiling eBPF programs work. There is also a mechanism to\nregister user-defined helper functions. The eBPF implementation of the Linux\nkernel comes with [some additional\nfeatures](https://github.com/iovisor/bcc/blob/master/docs/kernel-versions.md):\na high number of helpers, several kinds of maps, tail calls.\n\n* Additional helpers should be easy to add, but very few of the existing Linux\n  helpers have been replicated in rbpf so far.\n\n* Tail calls (“long jumps” from an eBPF program into another) are not\n  implemented. This is probably not trivial to design and implement.\n\n* The interaction with maps is done through the use of specific helpers, so\n  this should not be difficult to add. The maps themselves can reuse the maps\n  in the kernel (if on Linux), to communicate with in-kernel eBPF programs for\n  instance; or they can be handled in user space. Rust has arrays and hashmaps,\n  so their implementation should be pretty straightforward (and may be added to\n  rbpf in the future).\n\n### What about program validation?\n\nThe ”verifier” of this crate is very short and has nothing to do with the\nkernel verifier, which means that it accepts programs that may not be safe. On\nthe other hand, you probably do not run this in a kernel here, so it will not\ncrash your system. Implementing a verifier similar to the one in the kernel is\nnot trivial, and we cannot “copy” it since it is under GPL license.\n\n### What about safety then?\n\nRust has a strong emphasis on safety. Yet to have the eBPF VM work, some\n`unsafe` blocks of code are used. The VM, taken as an eBPF interpreter, can\nreturn an error but should not crash. Please file an issue otherwise.\n\nAs for the JIT-compiler, it is a different story, since runtime memory checks\nare more complicated to implement in assembly. It _will_ crash if your\nJIT-compiled program tries to perform unauthorized memory accesses. Usually, it\ncould be a good idea to test your program with the interpreter first.\n\nOh, and if your program has infinite loops, even with the interpreter, you're\non your own.\n\n## Caveats\n\n* This crate is **under development** and the API may be subject to change.\n\n* The JIT compiler produces an unsafe program: memory access are not tested at\n  runtime (yet). Use with caution.\n\n* A small number of eBPF instructions have not been implemented yet. This\n  should not be a problem for the majority of eBPF programs.\n\n* Beware of turnips. Turnips are disgusting.\n\n## _To do_ list\n\n* Implement some traits (`Clone`, `Drop`, `Debug` are good candidates).\n* Provide built-in support for user-space array and hash BPF maps.\n* Improve safety of JIT-compiled programs with runtime memory checks.\n* Add helpers (some of those supported in the kernel, such as checksum update,\n  could be helpful).\n* Improve verifier. Could we find a way to directly support programs compiled\n  with clang?\n* Maybe one day, tail calls?\n* JIT-compilers for other architectures?\n* …\n\n## License\n\nFollowing the effort of the Rust language project itself in order to ease\nintegration with other projects, the rbpf crate is distributed under the terms\nof both the MIT license and the Apache License (Version 2.0).\n\nSee\n[LICENSE-APACHE](https://github.com/qmonnet/rbpf/blob/main/LICENSE-APACHE)\nand [LICENSE-MIT](https://github.com/qmonnet/rbpf/blob/main/LICENSE-MIT) for\ndetails.\n\n## Inspired by\n\n* [uBPF](https://github.com/iovisor/ubpf), a C user-space implementation of an\n  eBPF virtual machine, with a JIT-compiler and disassembler (and also\n  including the assembler from the human-readable form of the instructions,\n  such as in `mov r0, 0x1337`), by Rich Lane for Big Switch Networks (2015)\n\n* [_Building a simple JIT in\n  Rust_](https://www.sophiajt.com/building-a-simple-jit-in-rust),\n  by Sophia Turner (2015)\n\n* [bpfjit](https://github.com/polachok/bpfjit) (also [on\n  crates.io](https://crates.io/crates/bpfjit)), a Rust crate exporting the cBPF\n  JIT compiler from FreeBSD 10 tree to Rust, by Alexander Polakov (2016)\n\n## Other resources\n\n* Cilium project documentation about BPF: [_BPF and XDP Reference\n  Guide_](http://docs.cilium.io/en/latest/bpf/)\n\n* [Kernel documentation about BPF](https://docs.kernel.org/bpf/)\n\n* [_Dive into BPF: a list of reading\n  material_](https://qmonnet.github.io/whirl-offload/2016/09/01/dive-into-bpf),\n  a blog article listing documentation for BPF and related technologies (2016)\n\n* [The Rust programming language](https://www.rust-lang.org)\n","funding_links":[],"categories":["eBPF Workflow: Tools and Utilities","Rust","eBPF 工作流：工具和实用程序"],"sub_categories":["User Space eBPF","用户空间 eBPF"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fqmonnet%2Frbpf","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fqmonnet%2Frbpf","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fqmonnet%2Frbpf/lists"}