Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Doc: improve README #40

Merged
merged 14 commits into from
Feb 24, 2023
199 changes: 15 additions & 184 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,16 @@

[中文文档](README_zh.md)

`Wasm-bpf` is a WebAssembly eBPF library, toolchain and runtime powered by [CO-RE](https://facebookmicrosites.github.io/bpf/blog/2020/02/19/bpf-portability-and-co-re.html)(Compile Once – Run Everywhere) [libbpf](https://github.com/libbpf/libbpf) and [WAMR](https://github.com/bytecodealliance/wasm-micro-runtime). It can help you build almost every eBPF programs or usecases to `Wasm`.
`Wasm-bpf` is a WebAssembly eBPF library, toolchain and runtime powered by [CO-RE](https://facebookmicrosites.github.io/bpf/blog/2020/02/19/bpf-portability-and-co-re.html)(Compile Once – Run Everywhere) [libbpf](https://github.com/libbpf/libbpf). It can help you build almost every eBPF programs or usecases to `Wasm` with nearly zero modification, and run them cross platforms with Wasm sandbox.

## Features

- **`General purpose`**: provide most abilities from eBPF to Wasm, `polling` from the ring buffer or perf buffer, bidirectional communications between `kernel` eBPF and `userspace` Wasm using `maps`, dynamically `loading`, `attaching` or `detaching`, etc. Supports a large number of eBPF program types and map types.
- **`High performance`**: No `serialization` overhead for complex data types, using `shared memory` to avoid copy overhead between host and Wasm.
- **`Easy to use`**: provide a similar developing experience as the [libbpf-bootstrap](https://github.com/libbpf/libbpf-bootstrap), `auto generate` the Wasm-eBPF skeleton headers and type definitions for bindings.
- **`Ultralightweight`**: the sample runtime has only `1.5 MB` in binary size. Compiled Wasm module would be only `~90K`. With the same toolchain, you can easily build your own Wasm-eBPF runtime in any languages and platforms!
- **`Easy to use`**: provide a similar developing experience as the [libbpf-bootstrap](https://github.com/libbpf/libbpf-bootstrap), `auto generate` the Wasm-eBPF skeleton headers and type definitions for bindings. Write your eBPF programs in `C/C++`, `Rust`, `Go` and compile to Wasm.
- **`Ultralightweight`**: the miminal runtime has only `1.5 MB` in binary size. Compiled Wasm module would be only `~90K`. With the same toolchain, you can easily build your own Wasm-eBPF runtime in any languages and platforms!

See the [examples](examples) directory for examples of eBPF programs written in C, Rust and compiled to Wasm, covering the use cases from `tracing`, `networking` to `security`.
See the [examples](examples) directory for examples of eBPF programs written in C, Rust, Go and compiled to Wasm, covering the use cases from `tracing`, `networking` to `security`.

For tools to distribute Wasm-eBPF programs in [`OCI`](https://opencontainers.org/) images, please refer to [eunomia-bpf](https://github.com/eunomia-bpf/eunomia-bpf) repo.

Expand All @@ -26,12 +26,12 @@ For tools to distribute Wasm-eBPF programs in [`OCI`](https://opencontainers.org
The wasm-bpf runtime require two parts: `the host side`(Outside the Wasm runtime) and the `Wasm guest side`(Inside the Wasm runtime).

- host side: A simple runtime implementation example
- see [src](runtime/cpp/src) and [include](runtime/cpp/include) directories, which would be a sample runtime built on the top of [libbpf](https://github.com/libbpf/libbpf) and [WAMR](https://github.com/bytecodealliance/wasm-micro-runtime).
- see [runtime/cpp](runtime/cpp), which would be a sample runtime in `C++` built on the top of [libbpf](https://github.com/libbpf/libbpf) and [WAMR](https://github.com/bytecodealliance/wasm-micro-runtime). Another more complex runtime implement in `Rust` is [runtime/rust](runtime/rust), based on [Wasmtime](https://github.com/bytecodealliance/wasmtime).
- You can easily build your own Wasm-eBPF runtime in `any` languages, `any` eBPF libraries and `any` Wasm runtimes with the same System interface.
- wasm side: toolchains and libraries
- a [`libbpf-wasm`](wasm-sdk/c/libbpf-wasm.h) header only library to provide libbpf APIs for Wasm guest `C/C++` code.
- a [`bpftool`](https://github.com/eunomia-bpf/bpftool/tree/wasm-bpftool) tool to generate the Wasm-eBPF `skeleton` headers, and `C struct definitions` for passing data between the host and Wasm guest without serialization.
- More languages support(`Rust`, `Go`, etc) is on the way.
- `Rust`, `Go` and other language support is similar to the `C/C++` support.

For details compile process, please refer to the [examples/bootstrap/README.md](examples/bootstrap/README.md). The figure below shows the overall interaction between the eBPF and Wasm runtimes:

Expand All @@ -43,199 +43,30 @@ We have proposed a new WASI issue [wasi-bpf](https://github.com/WebAssembly/WASI

## 🔨 Examples

See the [examples](examples) directory for examples of eBPF programs written in C, Rust and compiled to WASM.
See the [examples](examples) directory for examples of eBPF programs written in C, Rust, Go and compiled to WASM.

- [bootstrap](examples/bootstrap) and [runqlat](examples/runqlat) `tracing examples`
- [rust-bootstrap](examples/rust-bootstrap) `tracing examples`
- [lsm](examples/lsm) `security example`
- [bootstrap](examples/bootstrap) and [rust-bootstrap](examples/rust-bootstrap) `tracing examples`
- [runqlat](examples/runqlat) `tracing examples`
- [go-execve](examples/go-execve) `tracing examples`
- [lsm](examples/lsm) and `security example`
- [sockfilter](examples/sockfilter) `networking example`
- [sockops](examples/sockops) `networking example`
yunwei37 marked this conversation as resolved.
Show resolved Hide resolved

### C example: [Bootstrap](examples/bootstrap)

`bootstrap` is an example of a simple (but realistic) BPF application. It
tracks process starts (`exec()` family of syscalls, to be precise) and exits
and emits data about filename, PID and parent PID, as well as exit status and
duration of the process life. With `-d <min-duration-ms>` you can specify
minimum duration of the process to log. In such mode process start
(technically, `exec()`) events are not output (see example output below).

`bootstrap` was created in the similar spirit as
[libbpf-tools](https://github.com/iovisor/bcc/tree/master/libbpf-tools) from
BCC package, but is designed to be more stand-alone and with simpler Makefile
to simplify adoption to user's particular needs. It demonstrates the use of
typical BPF features:

- cooperating BPF programs (tracepoint handlers for process `exec` and `exit`
events, in this particular case);
- BPF map for maintaining the state;
- BPF ring buffer for sending data to user-space;
- global variables for application behavior parameterization.
- it utilizes BPF CO-RE and vmlinux.h to read extra process information from
kernel's `struct task_struct`.

Here's an example output:

```console
$ sudo ./wasm-bpf bootstrap.wasm -h
BPF bootstrap demo application.

It traces process start and exits and shows associated
information (filename, process duration, PID and PPID, etc).

USAGE: ./bootstrap [-d <min-duration-ms>] -v
$ sudo ./wasm-bpf bootstrap.wasm
TIME EVENT COMM PID PPID FILENAME/EXIT CODE
18:57:58 EXEC sed 74911 74910 /usr/bin/sed
18:57:58 EXIT sed 74911 74910 [0] (2ms)
18:57:58 EXIT cat 74912 74910 [0] (0ms)
18:57:58 EXEC cat 74913 74910 /usr/bin/cat
18:57:59 EXIT cat 74913 74910 [0] (0ms)
18:57:59 EXEC cat 74914 74910 /usr/bin/cat
18:57:59 EXIT cat 74914 74910 [0] (0ms)
18:57:59 EXEC cat 74915 74910 /usr/bin/cat
18:57:59 EXIT cat 74915 74910 [0] (1ms)
18:57:59 EXEC sleep 74916 74910 /usr/bin/sleep
```

See [examples/bootstrap](examples/bootstrap) for more details.

### Rust example: [Bootstrap](examples/rust-bootstrap)

similar to C bootstrap, but written in Rust.

See [examples/rust-bootstrap](examples/rust-bootstrap) for more details.

### C example: [runqlat](examples/runqlat)

This program summarizes scheduler run queue latency as a histogram, showing
how long tasks spent waiting their turn to run on-CPU.

This program summarizes scheduler run queue latency as a histogram, showing
how long tasks spent waiting their turn to run on-CPU.

```console
$ sudo ./wasm-bpf runqlat.wasm -h
Summarize run queue (scheduler) latency as a histogram.

USAGE: runqlat [--help] [interval] [count]

EXAMPLES:
runqlat # summarize run queue latency as a histogram
runqlat 1 10 # print 1 second summaries, 10 times
$ sudo ./wasm-bpf runqlat.wasm 1

Tracing run queue latency... Hit Ctrl-C to end.

usecs : count distribution
0 -> 1 : 72 |***************************** |
2 -> 3 : 93 |************************************* |
4 -> 7 : 98 |****************************************|
8 -> 15 : 96 |*************************************** |
16 -> 31 : 38 |*************** |
32 -> 63 : 4 |* |
64 -> 127 : 5 |** |
128 -> 255 : 6 |** |
256 -> 511 : 0 | |
512 -> 1023 : 0 | |
1024 -> 2047 : 0 | |
2048 -> 4095 : 1 | |
```

`runqlat` is also an example of a simple (but realistic) BPF application. It
would show a more complex example of BPF program, which contains more than
one file, and directly access the kernel maps from the user space instead of
polling the kernel ring buffer.

The runtime would use shared memory to access the kernel maps, and the kernel
would update the maps in the shared memory, so the wasm code can access the
eBPF maps directly, without any serialization or copy overhead between userspace
host and Wasm runtime.

You can use the `bpf_map_update_elem` API to update the kernel maps from the user
space, for example:

```c
cg_map_fd = bpf_map__fd(obj->maps.cgroup_map);
....
bpf_map_update_elem(cg_map_fd, &idx, &cgfd, BPF_ANY);
```

So the kernel eBPF can be config by wasm side or recieve the messages from
userspace wasm runtime when it is running.

See [examples/runqlat](examples/runqlat) for more details.

### C example: lsm-rmdir

`lsm-rmdir` hook in dir remove and check the permission to remove a directory. If dir
name with `can_not_rm` will raise Operation not permitted.

See [examples/lsm](examples/lsm) for more details.

### C example: Socket filter

sockfilter is an example of monitoring packet and dealing with __sk_buff structure.

See [examples/sockfilter](examples/sockfilter) for more details.

### C example: Sockops

`sockops` add the pid int tcp option in syn packet.

See [examples/sockops](examples/sockops) for more details.

## build the runtime
yunwei37 marked this conversation as resolved.
Show resolved Hide resolved

The dependencies are libbpf and wasm-micro-runtime only, they are
registered as git submodules.

```sh
git submodule update --init --recursive
```

### Install Dependencies

You will need `clang`, `libelf` and `zlib` to build the examples,
package names may vary across distros.

On Ubuntu/Debian, you need:

```shell
apt install clang libelf1 libelf-dev zlib1g-dev
```

On CentOS/Fedora, you need:

```shell
dnf install clang elfutils-libelf elfutils-libelf-devel zlib-devel
```

### Build runtime as a executable tool

Run `make` to build the runtime, which will be placed in the `build`
directory. `cmake` is required to build the runtime.

```sh
make build
```

### Build runtime as a library
We have two types of runtime samples:

```sh
make build-lib
```
- A C/C++ runtime example, which is a minimal runtime based on WAMR. see [runtime/cpp](../runtime/cpp) for more details.
- A Rust runtime example, which is a more complex runtime based on Wasmtime. see [runtime/rust](../runtime/rust) for more details.

You may refer to [CI](.github/workflows/c-cpp.yml) for more details on how
to build and run the examples.
The runtime can be built as a library or a standalone executable. see [docs/build.md](docs/build.md) to build the runtimes.

## LICENSE

MIT
yunwei37 marked this conversation as resolved.
Show resolved Hide resolved

## 🔗 Links

- GitHub Repository: https://github.com/eunomia-bpf/wasm-bpf
- eunomia-bpf project: simplify and enhance eBPF with CO-RE and WebAssembly https://github.com/eunomia-bpf/eunomia-bpf
- documents and blogs: https://eunomia-bpf.github.io/blog/ebpf-wasm.html
- CO-RE (Compile Once – Run Everywhere): https://facebookmicrosites.github.io/bpf/blog/2020/02/19/bpf-portability-and-co-re.html
Expand Down
125 changes: 3 additions & 122 deletions README_zh.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,138 +25,19 @@

## 示例

请转到 [examples](examples) 文件夹去查看使用 C, Rust 编写并编译到 Wasm 的 eBPF-Wasm 程序的示例。
请转到 [examples](examples) 文件夹去查看使用 C, Rust, Go 编写并编译到 Wasm 的 eBPF-Wasm 程序的示例。

- [bootstrap](examples/bootstrap) and [runqlat](examples/runqlat) `追踪`
- [lsm](examples/lsm) `安全`
- [sockfilter](examples/sockfilter) `网络`

### C 示例: Bootstrap

`bootstrap` 是个简单但很现实的 eBPF 程序的示例。 这个示例可以跟踪进程的启动 (更精确地来说,是 `exec()` 那些系统调用) 和退出,然后输出进程的文件名、PID、父进程PID之类的数据,以及进程的退出状态和存活时间。使用 `-d <最小周期(毫秒>` 来限制要输出的进程的最小存活时间。 在这种模式下,进程启动事件不会被输出(科学一点,`exec()`,具体见下面的示例)。

`bootstrap` 是使用和来自 BCC 里的
[libbpf-tools](https://github.com/iovisor/bcc/tree/master/libbpf-tools) 类似的思路来开发的。 但是为了让用户的修改容易一些, `bootstrap` 更独立,并且使用了更简单的 Makefile 。 `bootstrap` 演示了典型的 eBPF 用途:

- 多个 BPF 程序协同工作 (在这里是进程 `exec(启动)` 和 `exit(退出)` 的事件处理函数);
- 用 BPF map 来维护状态;
- 用 BPF 环形缓冲区来向用户态发送数据;
- 使用全局变量来修改程序行为。
- `bootstrap` 使用了 BPF 的 CO-RE 特性以及 `vmlinux.h` 来从内核的 `struct task_struct` 来读取额外的进程信息。

来看一个样例输出:

```console
$ sudo sudo ./wasm-bpf bootstrap.wasm -h
BPF bootstrap demo application.

It traces process start and exits and shows associated
information (filename, process duration, PID and PPID, etc).

USAGE: ./bootstrap [-d <min-duration-ms>] -v
$ sudo ./wasm-bpf bootstrap.wasm
TIME EVENT COMM PID PPID FILENAME/EXIT CODE
18:57:58 EXEC sed 74911 74910 /usr/bin/sed
18:57:58 EXIT sed 74911 74910 [0] (2ms)
18:57:58 EXIT cat 74912 74910 [0] (0ms)
18:57:58 EXEC cat 74913 74910 /usr/bin/cat
18:57:59 EXIT cat 74913 74910 [0] (0ms)
18:57:59 EXEC cat 74914 74910 /usr/bin/cat
18:57:59 EXIT cat 74914 74910 [0] (0ms)
18:57:59 EXEC cat 74915 74910 /usr/bin/cat
18:57:59 EXIT cat 74915 74910 [0] (1ms)
18:57:59 EXEC sleep 74916 74910 /usr/bin/sleep
```

原始的 C 代码来自 [libbpf-bootstrap](https://github.com/libbpf/libbpf-bootstrap)。

### Rust 示例: [Bootstrap](examples/rust-bootstrap)

类似 C bootstrap, 但是是 Rust 写的。

See [examples/rust-bootstrap](examples/rust-bootstrap) for more details.


### C 示例: runqlat

这个程序通过直方图展示调度器运行队列延迟,给我们展现了任务等了多久才能轮到 CPU 用。

```console
$ sudo ./wasm-bpf runqlat.wasm -h
Summarize run queue (scheduler) latency as a histogram.

USAGE: runqlat [--help] [interval] [count]

EXAMPLES:
runqlat 1 10 # print 1 second summaries, 10 times
$ sudo ./wasm-bpf runqlat.wasm 1

Tracing run queue latency... Hit Ctrl-C to end.

usecs : count distribution
0 -> 1 : 72 |***************************** |
2 -> 3 : 93 |************************************* |
4 -> 7 : 98 |****************************************|
8 -> 15 : 96 |*************************************** |
16 -> 31 : 38 |*************** |
32 -> 63 : 4 |* |
64 -> 127 : 5 |** |
128 -> 255 : 6 |** |
256 -> 511 : 0 | |
512 -> 1023 : 0 | |
1024 -> 2047 : 0 | |
2048 -> 4095 : 1 | |
```

`runqlat` 也是个简单但有实际意义的 BPF 程序的例子。这个例子稍微复杂一些,它有超过一个文件,并且能直接读内核 map 而不是从内核的环形缓冲区获取数据。

运行时将会使用共享内存来访问内核 map,同时内核将会更新在共享内存中的 map ,所以 wasm 代码可以直接访问 eBPF map,而不需要面对用户态主机侧程序和 Wasm 运行时之间的额外拷贝开销。

可以使用 `bpf_map_update_elem` 在用户态程序内更新内核的 eBPF map,比如:

```c
cg_map_fd = bpf_map__fd(obj->maps.cgroup_map);
....
bpf_map_update_elem(cg_map_fd, &idx, &cgfd, BPF_ANY);
```

所以内核的 eBPF 程序可以从 Wasm 侧的程序获取配置,或者在运行的时候接收消息。

## 构建运行时

依赖只有 git submodule 里面的 libbpf 和 wasm-micro-runtime

```sh
git submodule update --init --recursive
```

## 安装依赖

构建示例需要用到 `clang`, `libelf` 和 `zlib` 。包名在不同的发行版间可能不同。

在 Ubuntu/Debian, 需要:

```shell
apt install clang libelf1 libelf-dev zlib1g-dev
```

在 CentOS/Fedora, 需要:

```shell
dnf install clang elfutils-libelf elfutils-libelf-devel zlib-devel
```

运行 `make` 来构建这些示例。 构建结果会被放在 `build` 文件夹里。 构建运行时需要用到 `cmake`。

```sh
make build
```

可以查阅 [CI](.github/workflows/c-cpp.yml) 来详细了解如何编译运行这些示例。
请参考 [docs/build.md](docs/build.md)。

## Wasm-bpf 总览

![wasi-bpf](test/asserts/wasm-bpf-no-bcc.png)
![wasi-bpf](docs/wasm-bpf-no-bcc.png)

Wasm 模块可以同时加载和控制多个 eBPF 程序, 并且能够调用或者控制(通过[组件模型](https://github.com/WebAssembly/component-model))其他语言编写的 Wasm 模块来处理数据。

Expand Down
Loading