Skip to content

Commit

Permalink
xdp-log: Do not mention PerfEventArray
Browse files Browse the repository at this point in the history
The xdp-log example is using aya-log for logging the data.

PerfEventArray is described in aya-rs#93 instead.

Signed-off-by: Michal Rostecki <[email protected]>
  • Loading branch information
vadorovsky committed Jan 28, 2023
1 parent f001417 commit de7cdd2
Show file tree
Hide file tree
Showing 5 changed files with 106 additions and 189 deletions.
184 changes: 0 additions & 184 deletions docs/book/start/logging-packets.md

This file was deleted.

100 changes: 100 additions & 0 deletions docs/book/start/parsing-packets.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
# Parsing Packets

In the previous chapter, our XDP application ran until Ctrl-C was hit and permitted
all the traffic. Each time a packet was received, the BPF program created a logged
the string "received a packet" for each packet received. In this chapter we're
going to show how to parse packets.

While we could go all out and extract data all the way up to L7, we'll constrain
our example to L3, and to make things easier, IPv4 only.

!!! example "Source Code"

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

## Using Network Types

To get useful data to log, we first need some useful data structures to
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 [network-types crate](https://crates.io/crates/network-types) provides
networking structs, including `EthHdr` and `Ipv4Hdr` which we are ging to use
in our program.

Let's add it to our eBPF crate by adding a dependency on `network-types` in our
`xdp-log-ebpf/Cargo.toml`:

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

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

## 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.

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.8"` 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 log it with macros coming from aya-log. Those macros are
sending the log message and all the logged data to the user-space program, which then does the
actual logging.

The resulting code looks like this:

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

1. Create our map
2. Here's `ptr_at`, which gives ensures packet access is bounds checked
3. Using `ptr_at` to read our ethernet header
4. Logging the IP address and port

Don't forget to rebuild your eBPF program!

## User-space Component

Our user-space code doesn't really differ from the previous chapter, but for the
reference, here's the code:

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

1. Initialize `BpfLogger` to receive and process log messages and data from eBPF.
2. Name was not defined in `xdp-log-ebpf/src/main.rs`, so use `xdp`

## Running the program

As before, the interface can be overwritten by providing the interface name as a parameter, for example, `RUST_LOG=info cargo xtask run -- --iface wlp2s0`.

```console
$ RUST_LOG=info cargo xtask run
[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
```
6 changes: 3 additions & 3 deletions examples/xdp-log/xdp-log-ebpf/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,11 @@ use aya_log_ebpf::info;
use core::mem;
use network_types::{
eth::{EthHdr, EtherType},
ip::{Ipv4Hdr, IpProto},
ip::{IpProto, Ipv4Hdr},
tcp::TcpHdr,
udp::UdpHdr,
};


#[panic_handler]
fn panic(_info: &core::panic::PanicInfo) -> ! {
unsafe { core::hint::unreachable_unchecked() }
Expand Down Expand Up @@ -40,7 +39,7 @@ unsafe fn ptr_at<T>(ctx: &XdpContext, offset: usize) -> Result<*const T, ()> {
}

fn try_xdp_firewall(ctx: XdpContext) -> Result<u32, ()> {
let ethhdr: *const EthHdr = unsafe { ptr_at(&ctx, 0)? };
let ethhdr: *const EthHdr = unsafe { ptr_at(&ctx, 0)? }; // (3)
match unsafe { (*ethhdr).ether_type } {
EtherType::Ipv4 => {}
_ => return Ok(xdp_action::XDP_PASS),
Expand All @@ -63,6 +62,7 @@ fn try_xdp_firewall(ctx: XdpContext) -> Result<u32, ()> {
_ => return Err(()),
};

// (4)
info!(
&ctx,
"SRC IP: {:ipv4}, SRC PORT: {}", source_addr, source_port
Expand Down
3 changes: 2 additions & 1 deletion examples/xdp-log/xdp-log/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,12 @@ async fn main() -> Result<(), anyhow::Error> {
let mut bpf = Bpf::load(include_bytes_aligned!(
"../../target/bpfel-unknown-none/release/xdp-log"
))?;
// (1)
if let Err(e) = BpfLogger::init(&mut bpf) {
// This can happen if you remove all log statements from your eBPF program.
warn!("failed to initialize eBPF logger: {}", e);
}
// (1)
// (2)
let program: &mut Xdp = bpf.program_mut("xdp").unwrap().try_into()?;
program.load()?;
program.attach(&opt.iface, XdpFlags::default())
Expand Down
2 changes: 1 addition & 1 deletion mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ nav:
- A Simple XDP Program:
- book/start/index.md
- Hello XDP!: book/start/hello-xdp.md
- Logging Packets: book/start/logging-packets.md
- Parsing Packets: book/start/parsing-packets.md
- Dropping Packets: book/start/dropping-packets.md
- Working With Aya:
- book/aya/index.md
Expand Down

0 comments on commit de7cdd2

Please sign in to comment.