examples: Add an XDP example

Signed-off-by: Dave Tucker <dave@dtucker.co.uk>
pull/33/head
Dave Tucker 4 years ago
parent 7251b9bc53
commit 79343c445a

@ -21,8 +21,14 @@ jobs:
- uses: actions/checkout@v2
- uses: Swatinem/rust-cache@v1
- name: Install bpf-linker
run: cargo install bpf-linker
- name: Build
run: cargo build --verbose
- name: Build examples
run: cargo xtask examples
- name: Run tests
run: RUST_BACKTRACE=full cargo test --verbose

@ -74,6 +74,19 @@ let cgroup = File::open("/sys/fs/cgroup/unified")?;
ingress.attach(cgroup, CgroupSkbAttachType::Ingress)?;
```
### Examples
There are some examples of how to use Aya to write BPF programs and userspace program in the `examples` directory. These can be compiled by running `cargo xtask examples`.
The BPF examples require you to have a rust nightly toolchain installed.
`cargo xtask examples`
# Run
## XDP
`sudo ./examples/target/debug/xdp_userspace ./examples/target/bpfel-unknown-none/release/xdp wlp2s0`
## Community
Join [the conversation on Discord][chat-url] to discuss anything related to aya.

@ -0,0 +1,2 @@
[workspace]
members = ["user", "bpf"]

@ -0,0 +1,17 @@
aya-examples
============
# Prerequisites
1. Install a rust nightly toolchain: `rustup install nightly`
1. Install bpf-linker: `cargo install bpf-linker`
# Build
From the project root directory, run `cargo xtask examples`
# Run
## XDP
`sudo ./examples/target/debug/xdp_userspace wlp2s0`

@ -0,0 +1,20 @@
[package]
name = "bpf"
version = "0.1.0"
edition = "2018"
publish = false
[features]
default = []
user = [ "aya" ]
[dependencies]
aya-bpf = { path = "../../bpf/aya-bpf" }
aya = { path = "../../aya", optional=true }
[lib]
path = "src/lib.rs"
[[bin]]
name = "xdp"
path = "src/xdp/main.rs"

@ -0,0 +1,3 @@
#![no_std]
pub mod xdp;

@ -0,0 +1,38 @@
#![no_std]
#![no_main]
use aya_bpf::bindings::xdp_action;
use aya_bpf::cty::c_long;
use aya_bpf::macros::{map, xdp};
use aya_bpf::maps::Array;
use aya_bpf::programs::XdpContext;
use bpf::xdp::XdpData;
#[panic_handler]
fn panic(_info: &core::panic::PanicInfo) -> ! {
unreachable!()
}
#[map]
static mut xdp_stats_map : Array<XdpData> = Array::with_max_entries(1,0);
#[xdp(name = "xdp_stats")]
pub fn xdp_stats(ctx: XdpContext) -> u32 {
match unsafe { try_xdp_stats(ctx) } {
Ok(ret) => ret,
Err(_) => xdp_action::XDP_DROP,
}
}
unsafe fn try_xdp_stats(_ctx: XdpContext) -> Result<u32, c_long> {
let data = match xdp_stats_map.get(0) {
Some(data) => data,
None => return Err(0)
};
let xdp_data = XdpData{
packet_count: data.packet_count + 1,
};
xdp_stats_map.set(0, &xdp_data, 0)?;
Ok(xdp_action::XDP_PASS)
}

@ -0,0 +1,8 @@
#[derive(Copy, Clone)]
#[repr(C)]
pub struct XdpData {
pub packet_count : u32,
}
#[cfg(feature = "user")]
unsafe impl aya::Pod for XdpData {}

@ -0,0 +1,14 @@
[package]
name = "user"
version = "0.1.0"
edition = "2018"
publish = false
[dependencies]
aya = { path = "../../aya" }
bpf = { path = "../bpf", features=["user"] }
anyhow = "1.0.42"
[[bin]]
name = "xdp_userspace"
path = "src/xdp.rs"

@ -0,0 +1,53 @@
use aya::Bpf;
use aya::maps::{Array, MapRefMut};
use aya::programs::{Xdp, XdpFlags};
use std::{
convert::TryFrom,
convert::TryInto,
env,
fs,
thread,
time::Duration,
};
use bpf::xdp::XdpData;
fn main() {
if let Err(e) = try_main() {
eprintln!("error: {:#}", e);
}
}
fn try_main() -> Result<(), anyhow::Error> {
let path = match env::args().nth(1) {
Some(iface) => iface,
None => panic!("not path provided"),
};
let iface = match env::args().nth(2) {
Some(iface) => iface,
None => "eth0".to_string(),
};
let data = fs::read(path)?;
let mut bpf = Bpf::load(
&data,
None)?;
// get the `xdp_stats` program compiled into `xdp`.
let probe: &mut Xdp = bpf.program_mut("xdp_stats")?.try_into()?;
// load the program into the kernel
probe.load()?;
// attach to the interface
probe.attach(&iface, XdpFlags::default())?;
let xdp_stats_map : Array::<MapRefMut, XdpData> = Array::try_from(bpf.map_mut("xdp_stats_map")?)?;
for _i in 1..10 {
let data = xdp_stats_map.get(&0, 0)?;
println!("packets received: {}", data.packet_count);
thread::sleep(Duration::from_secs(1));
};
Ok(())
}

@ -0,0 +1,29 @@
use std::path::PathBuf;
use std::process::Command;
use structopt::StructOpt;
#[derive(StructOpt)]
pub struct Options {}
pub fn examples(_opts: Options) -> Result<(), anyhow::Error> {
let dir = PathBuf::from("examples");
// build bpf examples
let status = Command::new("cargo")
.current_dir(&dir)
.args(&["+nightly", "build", "--verbose", "--release", "--package=bpf", "--target=bpfel-unknown-none", "-Z", "build-std=core"])
.status()
.expect("failed to build bpf examples");
assert!(status.success());
// build userspace examples
let status = Command::new("cargo")
.current_dir(&dir)
.args(&["build", "--verbose", "--package=user"])
.status()
.expect("failed to build userspace examples");
assert!(status.success());
Ok(())
}

@ -1,4 +1,5 @@
mod codegen;
mod examples;
use std::process::exit;
@ -12,6 +13,7 @@ pub struct Options {
#[derive(StructOpt)]
enum Command {
Codegen(codegen::Options),
Examples(examples::Options),
}
fn main() {
@ -20,6 +22,7 @@ fn main() {
use Command::*;
let ret = match opts.command {
Codegen(opts) => codegen::codegen(opts),
Examples(opts) => examples::examples(opts),
};
if let Err(e) = ret {

Loading…
Cancel
Save