Skip to content

Commit

Permalink
Merge pull request aya-rs#72 from vadorovsky/xdp-docs-update
Browse files Browse the repository at this point in the history
examples/{tc,xdp}*: Use network-types crate instead of aya-tool bindings
  • Loading branch information
vadorovsky authored Jan 13, 2023
2 parents fdc71fe + cbc37cd commit b5dbd06
Show file tree
Hide file tree
Showing 90 changed files with 338 additions and 957 deletions.
28 changes: 14 additions & 14 deletions docs/book/start/dropping-packets.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ we're going to extend it to allow the dropping of traffic.

!!! example "Source Code"

Full code for the example in this chapter is available [here](https://github.com/aya-rs/book/tree/main/examples/myapp-03)
Full code for the example in this chapter is available [here](https://github.com/aya-rs/book/tree/main/examples/xdp-drop)

## Design

Expand All @@ -31,8 +31,8 @@ keep this logic in a function called `block_ip`.

Here's what the code looks like now:

```rust linenums="1" title="myapp-ebpf/src/main.rs"
--8<-- "examples/myapp-03/myapp-ebpf/src/main.rs"
```rust linenums="1" title="xdp-drop-ebpf/src/main.rs"
--8<-- "examples/xdp-drop/xdp-drop-ebpf/src/main.rs"
```

1. Create our map
Expand Down Expand Up @@ -60,8 +60,8 @@ traffic originating from `1.1.1.1` in this example.

Here's how the userspace code looks:

```rust linenums="1" title="myapp/src/main.rs"
--8<-- "examples/myapp-03/myapp/src/main.rs"
```rust linenums="1" title="xdp-drop/src/main.rs"
--8<-- "examples/xdp-drop/xdp-drop/src/main.rs"
```

1. Get a reference to the map
Expand All @@ -71,13 +71,13 @@ Here's how the userspace code looks:

```console
$ RUST_LOG=info cargo xtask run
[2022-10-04T12:46:05Z INFO myapp] LOG: SRC 1.1.1.1, ACTION 1
[2022-10-04T12:46:05Z INFO myapp] LOG: SRC 192.168.1.21, ACTION 2
[2022-10-04T12:46:05Z INFO myapp] LOG: SRC 192.168.1.21, ACTION 2
[2022-10-04T12:46:05Z INFO myapp] LOG: SRC 18.168.253.132, ACTION 2
[2022-10-04T12:46:05Z INFO myapp] LOG: SRC 1.1.1.1, ACTION 1
[2022-10-04T12:46:05Z INFO myapp] LOG: SRC 18.168.253.132, ACTION 2
[2022-10-04T12:46:05Z INFO myapp] LOG: SRC 18.168.253.132, ACTION 2
[2022-10-04T12:46:05Z INFO myapp] LOG: SRC 1.1.1.1, ACTION 1
[2022-10-04T12:46:05Z INFO myapp] LOG: SRC 140.82.121.6, ACTION 2
[2022-10-04T12:46:05Z INFO xdp_drop] SRC: 1.1.1.1, ACTION: 1
[2022-10-04T12:46:05Z INFO xdp_drop] SRC: 192.168.1.21, ACTION: 2
[2022-10-04T12:46:05Z INFO xdp_drop] SRC: 192.168.1.21, ACTION: 2
[2022-10-04T12:46:05Z INFO xdp_drop] SRC: 18.168.253.132, ACTION: 2
[2022-10-04T12:46:05Z INFO xdp_drop] SRC: 1.1.1.1, ACTION: 1
[2022-10-04T12:46:05Z INFO xdp_drop] SRC: 18.168.253.132, ACTION: 2
[2022-10-04T12:46:05Z INFO xdp_drop] SRC: 18.168.253.132, ACTION: 2
[2022-10-04T12:46:05Z INFO xdp_drop] SRC: 1.1.1.1, ACTION: 1
[2022-10-04T12:46:05Z INFO xdp_drop] SRC: 140.82.121.6, ACTION: 2
```
49 changes: 24 additions & 25 deletions docs/book/start/hello-xdp.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

!!! example "Source Code"

Full code for the example in this chapter is availble [here](https://github.com/aya-rs/book/tree/main/examples/myapp-01)
Full code for the example in this chapter is available [here](https://github.com/aya-rs/book/tree/main/examples/xdp-hello)

## Example Project

Expand All @@ -16,10 +16,10 @@ XDP (eXpress Data Path) programs permit our eBPF program to make decisions about

We must first write the eBPF component of our program.
This is a minimal generated XDP program that permits all traffic.
The logic for this program is located in `myapp-ebpf/src/main.rs` and currently looks like this:
The logic for this program is located in `xdp-hello-ebpf/src/main.rs` and currently looks like this:

```rust linenums="1" title="myapp-ebpf/src/main.rs"
--8<-- "examples/myapp-01/myapp-ebpf/src/main.rs"
```rust linenums="1" title="xdp-hello-ebpf/src/main.rs"
--8<-- "examples/xdp-hello/xdp-hello-ebpf/src/main.rs"
```

1. `#![no_std]` is required since we cannot use the standard library.
Expand All @@ -37,9 +37,9 @@ Now we can compile this using `cargo xtask build-ebpf`.
Let's take a look at the compiled eBPF program:

```console
$ llvm-objdump -S target/bpfel-unknown-none/debug/myapp
$ llvm-objdump -S target/bpfel-unknown-none/debug/xdp-hello

target/bpfel-unknown-none/debug/myapp: file format elf64-bpf
target/bpfel-unknown-none/debug/xdp-hello: file format elf64-bpf

Disassembly of section .text:

Expand Down Expand Up @@ -74,9 +74,9 @@ Disassembly of section .text:
0000000000000090 <LBB2_3>:
18: 95 00 00 00 00 00 00 00 exit

Disassembly of section xdp/myapp:
Disassembly of section xdp/xdp_hello:

0000000000000000 <myapp>:
0000000000000000 <xdp_hello>:
0: bf 16 00 00 00 00 00 00 r6 = r1
1: b7 07 00 00 00 00 00 00 r7 = 0
2: 63 7a fc ff 00 00 00 00 *(u32 *)(r10 - 4) = r7
Expand All @@ -93,7 +93,7 @@ Disassembly of section xdp/myapp:
```

The output was trimmed for brevity.
We can see an `xdp/myapp` section here.
We can see an `xdp/xdp_hello` section here.
And in `<LBB0_2>`, `r0 = 2` sets register `0` to `2`, which is the value of the `XDP_PASS` action.
`exit` ends the program.

Expand All @@ -102,14 +102,14 @@ Simple!
## User-space Component

Now our eBPF program is complete and compiled, we need a user-space program to load it and attach it to a trace point.
Fortunately, we have a generated program ready in `myapp/src/main.rs` which is going to do that for us.
Fortunately, we have a generated program ready in `xdp-hello/src/main.rs` which is going to do that for us.

### Starting Out

Let's look at the details of our generated user-space application:

```rust linenums="1" title="myapp/src/main.rs"
--8<-- "examples/myapp-01/myapp/src/main.rs"
```rust linenums="1" title="xdp-hello/src/main.rs"
--8<-- "examples/xdp-hello/xdp-hello/src/main.rs"
```

1. `tokio` is the async library we're using, which provides our [Ctrl-C handler](https://docs.rs/tokio/latest/tokio/signal/fn.ctrl_c.html). It will come in useful later as we expand the functionality of the initial program:
Expand All @@ -130,10 +130,10 @@ $ cargo xtask run -- -h
:
Finished dev [optimized] target(s) in 0.90s
Finished dev [unoptimized + debuginfo] target(s) in 0.60s
myapp
xdp-hello

USAGE:
myapp [OPTIONS]
xdp-hello [OPTIONS]

OPTIONS:
-h, --help Print help information
Expand All @@ -148,13 +148,12 @@ OPTIONS:

```console
$ RUST_LOG=info cargo xtask run
17:51:57 [INFO] myapp: [myapp/src/main.rs:48] Waiting for Ctrl-C...
17:51:57 [INFO] myapp: [src/main.rs:20] received a packet
17:51:57 [INFO] myapp: [src/main.rs:20] received a packet
17:51:57 [INFO] myapp: [src/main.rs:20] received a packet
:
17:51:58 [INFO] myapp: [src/main.rs:20] received a packet
^C17:51:58 [INFO] myapp: [myapp/src/main.rs:50] Exiting...
[2022-12-21T18:03:09Z INFO xdp_hello] Waiting for Ctrl-C...
[2022-12-21T18:03:11Z INFO xdp_hello] received a packet
[2022-12-21T18:03:11Z INFO xdp_hello] received a packet
[2022-12-21T18:03:11Z INFO xdp_hello] received a packet
[2022-12-21T18:03:11Z INFO xdp_hello] received a packet
^C[2022-12-21T18:03:11Z INFO xdp_hello] Exiting...
```

So everytime a packet was received on the interface, a log was printed!
Expand All @@ -169,13 +168,13 @@ So everytime a packet was received on the interface, a log was printed!
The program runs until CTRL+C is pressed and then exits.
On exit, Aya takes care of detaching the program for us.

If you issue the `sudo bpftool prog list` command when `myapp` is running you can verify that it is loaded:
If you issue the `sudo bpftool prog list` command when `xdp_hello` is running you can verify that it is loaded:

```console
958: xdp name myapp tag 0137ce4fce70b467 gpl
958: xdp name xdp_hello tag 0137ce4fce70b467 gpl
loaded_at 2022-06-23T13:55:28-0400 uid 0
xlated 2016B jited 1138B memlock 4096B map_ids 275,274,273
pids myapp(131677)
pids xdp-hello(131677)
```

Running the command again once `myapp` has exited will show that the program is no longer running.
Running the command again once `xdp_hello` has exited will show that the program is no longer running.
87 changes: 31 additions & 56 deletions docs/book/start/logging-packets.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ Let's expand this program to log the traffic that is being permitted in the user

!!! example "Source Code"

Full code for the example in this chapter is availble [here](https://github.com/aya-rs/book/tree/main/examples/myapp-02)
Full code for the example in this chapter is available [here](https://github.com/aya-rs/book/tree/main/examples/xdp-log)

## Getting Data to User-Space

Expand All @@ -17,8 +17,8 @@ To get data from kernel-space to user-space we use an eBPF map. There are numero
While we could go all out and extract data all the way up to L7, we'll constrain our firewall to L3, and to make things easier, IPv4 only.
The data structure that we'll need to send information to user-space will need to hold an IPv4 address and an action for Permit/Deny, we'll encode both as a `u32`.

```rust linenums="1" title="myapp-common/src/lib.rs"
--8<-- "examples/myapp-02/myapp-common/src/lib.rs"
```rust linenums="1" title="xdp-log-common/src/lib.rs"
--8<-- "examples/xdp-log/xdp-log-common/src/lib.rs"
```

1. We implement the `aya::Pod` trait for our struct since it is Plain Old Data as can be safely converted to a byte-slice and back.
Expand Down Expand Up @@ -87,73 +87,50 @@ The data structure that we'll need to send information to user-space will need t

## Writing Data

### Generating Bindings To vmlinux.h
### Using Kernel Network Types

To get useful data to add to our maps, we first need some useful data structures to populate with data from the `XdpContext`.
To get useful data to add to our maps, we first need some useful data structures
to populate with data from the `XdpContext`.
We want to log the Source IP Address of incoming traffic, so we'll need to:

1. Read the Ethernet Header to determine if this is an IPv4 Packet
1. Read the Source IP Address from the IPv4 Header

The two structs in the kernel for this are `ethhdr` from `uapi/linux/if_ether.h` and `iphdr` from `uapi/linux/ip.h`.
If I were to use bindgen to generate Rust bindings for those headers, I'd be tied to the kernel version of the system that I'm developing on.
This is where `aya-tool` comes in to play. It can easily generate bindings for using the BTF information in `/sys/kernel/btf/vmlinux`.
The two structs in the kernel for this are `ethhdr` from `uapi/linux/if_ether.h`
and `iphdr` from `uapi/linux/ip.h`. Rust equivalents of those structures (`EthHdr`
and `Ipv4Hdr`) are provided by the [network-types crate](https://crates.io/crates/network-types).

First, we must make sure that `bindgen` is installed.
```sh
cargo install bindgen-cli
```

Once the bindings are generated and checked in to our repository they shouldn't need to be regenerated again unless we need to add a new struct.

Lets use `xtask` to automate this so we can easily reproduce this file in future.

We'll add the following code

=== "xtask/src/codegen.rs"

```rust linenums="1"
--8<-- "examples/myapp-02/xtask/src/codegen.rs"
```
Let's add it to our eBPF crate by adding a dependency on `network-types` in our
`xdp-log-ebpf/Cargo.toml`:

=== "xtask/Cargo.toml"
=== "xdp-log-ebpf/Cargo.toml"

```toml linenums="1"
--8<-- "examples/myapp-02/xtask/Cargo.toml"
--8<-- "examples/xdp-log/xdp-log-ebpf/Cargo.toml"
```

=== "xtask/src/main.rs"

```rust linenums="1"
--8<-- "examples/myapp-02/xtask/src/main.rs"
```

Once we've generated our file using `cargo xtask codegen` from the root of the project.
We can access these by including `mod bindings` from our eBPF code.

### Getting Packet Data From The Context And Into the Map

The `XdpContext` contains two fields, `data` and `data_end`.
`data` is a pointer to the start of the data in kernel memory and `data_end`, a pointer to the end of the data in kernel memory. In order to access this data and ensure that the eBPF verifier is happy, we'll introduce a helper function called `ptr_at`. This function will ensure that before we access any data, we check that it's contained between `data` and `data_end`. It is marked as `unsafe` because when calling the function, you must ensure that there is a valid `T` at that location or there will be undefined behaviour.
`data` is a pointer to the start of the data in kernel memory and `data_end`, a
pointer to the end of the data in kernel memory. In order to access this data
and ensure that the eBPF verifier is happy, we'll introduce a helper function
called `ptr_at`. This function will ensure that before we access any data, we
check that it's contained between `data` and `data_end`. It is marked as `unsafe`
because when calling the function, you must ensure that there is a valid `T` at
that location or there will be undefined behaviour.

With our helper function in place, we can:

1. Read the Ethertype field to check if we have an IPv4 packet.
1. Read the IPv4 Source Address from the IP header

To do this efficiently we'll add a dependency on `memoffset = "0.6"` in our `myapp-ebpf/Cargo.toml`

!!! tip "Reading Fields Using `offset_of!`"

As there is limited stack space, it's more memory efficient to use the `offset_of!` macro to read
a single field from a struct, rather than reading the whole struct and accessing the field by name.

Once we have our IPv4 source address, we can create a `PacketLog` struct and output this to our `PerfEventArray`

The resulting code looks like this:

```rust linenums="1" title="myapp-ebpf/src/main.rs"
--8<-- "examples/myapp-02/myapp-ebpf/src/main.rs"
```rust linenums="1" title="xdp-log-ebpf/src/main.rs"
--8<-- "examples/xdp-log/xdp-log-ebpf/src/main.rs"
```

1. Create our map
Expand All @@ -168,16 +145,16 @@ Don't forget to rebuild your eBPF program!
In order to read from the `AsyncPerfEventArray`, we have to call `AsyncPerfEventArray::open()` for each online CPU, then we have to poll the file descriptor for events.
While this is do-able using `PerfEventArray` and `mio` or `epoll`, the code is much less easy to follow. Instead, we'll use `tokio`, which was added to our template for us.

We'll need to add a dependency on `bytes = "1"` to `myapp/Cargo.toml` since this will make it easier
We'll need to add a dependency on `bytes = "1"` to `xdp-log/Cargo.toml` since this will make it easier
to deal with the chunks of bytes yielded by the `AsyncPerfEventArray`.

Here's the code:

```rust linenums="1" title="myapp/src/main.rs"
--8<-- "examples/myapp-02/myapp/src/main.rs"
```rust linenums="1" title="xdp-log/src/main.rs"
--8<-- "examples/xdp-log/xdp-log/src/main.rs"
```

1. Name was not defined in `myapp-ebpf/src/main.rs`, so use `xdp` instead of `myapp`
1. Name was not defined in `xdp-log-ebpf/src/main.rs`, so use `xdp`
2. Define our map
3. Call `open()` for each online CPU
4. Spawn a `tokio::task`
Expand All @@ -192,11 +169,9 @@ As before, the interface can be overwritten by providing the interface name as a

```console
$ RUST_LOG=info cargo xtask run
[2022-10-04T12:46:05Z INFO myapp] LOG: SRC 192.168.1.205, ACTION 2
[2022-10-04T12:46:05Z INFO myapp] LOG: SRC 192.168.1.21, ACTION 2
[2022-10-04T12:46:05Z INFO myapp] LOG: SRC 192.168.1.21, ACTION 2
[2022-10-04T12:46:05Z INFO myapp] LOG: SRC 18.168.253.132, ACTION 2
[2022-10-04T12:46:05Z INFO myapp] LOG: SRC 18.168.253.132, ACTION 2
[2022-10-04T12:46:05Z INFO myapp] LOG: SRC 18.168.253.132, ACTION 2
[2022-10-04T12:46:05Z INFO myapp] LOG: SRC 140.82.121.6, ACTION 2
[2022-12-22T11:32:21Z INFO xdp_log] SRC IP: 172.52.22.104, SRC PORT: 443
[2022-12-22T11:32:21Z INFO xdp_log] SRC IP: 172.52.22.104, SRC PORT: 443
[2022-12-22T11:32:21Z INFO xdp_log] SRC IP: 172.52.22.104, SRC PORT: 443
[2022-12-22T11:32:21Z INFO xdp_log] SRC IP: 172.52.22.104, SRC PORT: 443
[2022-12-22T11:32:21Z INFO xdp_log] SRC IP: 234.130.159.162, SRC PORT: 443
```
3 changes: 0 additions & 3 deletions examples/myapp-01/.vim/coc-settings.json

This file was deleted.

3 changes: 0 additions & 3 deletions examples/myapp-01/.vscode/settings.json

This file was deleted.

2 changes: 0 additions & 2 deletions examples/myapp-01/Cargo.toml

This file was deleted.

3 changes: 0 additions & 3 deletions examples/myapp-02/.vim/coc-settings.json

This file was deleted.

3 changes: 0 additions & 3 deletions examples/myapp-02/.vscode/settings.json

This file was deleted.

2 changes: 0 additions & 2 deletions examples/myapp-02/Cargo.toml

This file was deleted.

Loading

0 comments on commit b5dbd06

Please sign in to comment.