mirror of https://github.com/aya-rs/aya
Add xdp example from book to aya repo
Adding the source for the xdp example from the bookfor easier reference. Signed-off-by: Nitish Malhotra <nitish.malhotra@gmail.com>pull/91/head
parent
dd7e1de348
commit
8bd8233c96
@ -0,0 +1,2 @@
|
|||||||
|
[workspace]
|
||||||
|
members = ["myapp", "myapp-common", "xtask"]
|
@ -0,0 +1,28 @@
|
|||||||
|
# myapp
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
1. Install a rust stable toolchain: `rustup install stable`
|
||||||
|
1. Install a rust nightly toolchain: `rustup install nightly`
|
||||||
|
1. Install bpf-linker: `cargo install bpf-linker`
|
||||||
|
|
||||||
|
## Build eBPF
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cargo xtask build-ebpf
|
||||||
|
```
|
||||||
|
|
||||||
|
To perform a release build you can use the `--release` flag.
|
||||||
|
You may also change the target architecture with the `--target` flag
|
||||||
|
|
||||||
|
## Build Userspace
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cargo build
|
||||||
|
```
|
||||||
|
|
||||||
|
## Run
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo target/debug/myapp --path target/bpfel-unknown-none/debug/myapp
|
||||||
|
```
|
@ -0,0 +1,14 @@
|
|||||||
|
[package]
|
||||||
|
name = "myapp-common"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = []
|
||||||
|
userspace = [ "aya" ]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
aya = { git = "https://github.com/aya-rs/aya", branch="main", optional=true }
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
path = "src/lib.rs"
|
@ -0,0 +1,10 @@
|
|||||||
|
#![no_std]
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct PacketLog {
|
||||||
|
pub ipv4_address: u32,
|
||||||
|
pub action: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "user")]
|
||||||
|
unsafe impl aya::Pod for PacketLog {}
|
@ -0,0 +1,6 @@
|
|||||||
|
[build]
|
||||||
|
target-dir = "../target"
|
||||||
|
target = "bpfel-unknown-none"
|
||||||
|
|
||||||
|
[unstable]
|
||||||
|
build-std = ["core"]
|
@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"rust-analyzer.cargo.target": "bpfel-unknown-none",
|
||||||
|
"rust-analyzer.checkOnSave.allTargets": false
|
||||||
|
}
|
@ -0,0 +1,25 @@
|
|||||||
|
[package]
|
||||||
|
name = "myapp-ebpf"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
aya-bpf = { git = "http://github.com/aya-rs/aya", branch = "main" }
|
||||||
|
myapp-common = { path = "../myapp-common" }
|
||||||
|
memoffset = "0.6.4"
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "myapp"
|
||||||
|
path = "src/main.rs"
|
||||||
|
|
||||||
|
[profile.dev]
|
||||||
|
panic = "abort"
|
||||||
|
debug = 1
|
||||||
|
opt-level = 2
|
||||||
|
overflow-checks = false
|
||||||
|
|
||||||
|
[profile.release]
|
||||||
|
panic = "abort"
|
||||||
|
|
||||||
|
[workspace]
|
||||||
|
members = []
|
@ -0,0 +1,2 @@
|
|||||||
|
[toolchain]
|
||||||
|
channel="nightly"
|
@ -0,0 +1,189 @@
|
|||||||
|
/* automatically generated by rust-bindgen 0.59.1 */
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Copy, Clone, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
|
||||||
|
pub struct __BindgenBitfieldUnit<Storage> {
|
||||||
|
storage: Storage,
|
||||||
|
}
|
||||||
|
impl<Storage> __BindgenBitfieldUnit<Storage> {
|
||||||
|
#[inline]
|
||||||
|
pub const fn new(storage: Storage) -> Self {
|
||||||
|
Self { storage }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<Storage> __BindgenBitfieldUnit<Storage>
|
||||||
|
where
|
||||||
|
Storage: AsRef<[u8]> + AsMut<[u8]>,
|
||||||
|
{
|
||||||
|
#[inline]
|
||||||
|
pub fn get_bit(&self, index: usize) -> bool {
|
||||||
|
debug_assert!(index / 8 < self.storage.as_ref().len());
|
||||||
|
let byte_index = index / 8;
|
||||||
|
let byte = self.storage.as_ref()[byte_index];
|
||||||
|
let bit_index = if cfg!(target_endian = "big") {
|
||||||
|
7 - (index % 8)
|
||||||
|
} else {
|
||||||
|
index % 8
|
||||||
|
};
|
||||||
|
let mask = 1 << bit_index;
|
||||||
|
byte & mask == mask
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
pub fn set_bit(&mut self, index: usize, val: bool) {
|
||||||
|
debug_assert!(index / 8 < self.storage.as_ref().len());
|
||||||
|
let byte_index = index / 8;
|
||||||
|
let byte = &mut self.storage.as_mut()[byte_index];
|
||||||
|
let bit_index = if cfg!(target_endian = "big") {
|
||||||
|
7 - (index % 8)
|
||||||
|
} else {
|
||||||
|
index % 8
|
||||||
|
};
|
||||||
|
let mask = 1 << bit_index;
|
||||||
|
if val {
|
||||||
|
*byte |= mask;
|
||||||
|
} else {
|
||||||
|
*byte &= !mask;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
pub fn get(&self, bit_offset: usize, bit_width: u8) -> u64 {
|
||||||
|
debug_assert!(bit_width <= 64);
|
||||||
|
debug_assert!(bit_offset / 8 < self.storage.as_ref().len());
|
||||||
|
debug_assert!((bit_offset + (bit_width as usize)) / 8 <= self.storage.as_ref().len());
|
||||||
|
let mut val = 0;
|
||||||
|
for i in 0..(bit_width as usize) {
|
||||||
|
if self.get_bit(i + bit_offset) {
|
||||||
|
let index = if cfg!(target_endian = "big") {
|
||||||
|
bit_width as usize - 1 - i
|
||||||
|
} else {
|
||||||
|
i
|
||||||
|
};
|
||||||
|
val |= 1 << index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
pub fn set(&mut self, bit_offset: usize, bit_width: u8, val: u64) {
|
||||||
|
debug_assert!(bit_width <= 64);
|
||||||
|
debug_assert!(bit_offset / 8 < self.storage.as_ref().len());
|
||||||
|
debug_assert!((bit_offset + (bit_width as usize)) / 8 <= self.storage.as_ref().len());
|
||||||
|
for i in 0..(bit_width as usize) {
|
||||||
|
let mask = 1 << i;
|
||||||
|
let val_bit_is_set = val & mask == mask;
|
||||||
|
let index = if cfg!(target_endian = "big") {
|
||||||
|
bit_width as usize - 1 - i
|
||||||
|
} else {
|
||||||
|
i
|
||||||
|
};
|
||||||
|
self.set_bit(index + bit_offset, val_bit_is_set);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub type __u8 = ::aya_bpf::cty::c_uchar;
|
||||||
|
pub type __u16 = ::aya_bpf::cty::c_ushort;
|
||||||
|
pub type __u32 = ::aya_bpf::cty::c_uint;
|
||||||
|
pub type __be16 = __u16;
|
||||||
|
pub type __be32 = __u32;
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
pub struct ethhdr {
|
||||||
|
pub h_dest: [::aya_bpf::cty::c_uchar; 6usize],
|
||||||
|
pub h_source: [::aya_bpf::cty::c_uchar; 6usize],
|
||||||
|
pub h_proto: __be16,
|
||||||
|
}
|
||||||
|
pub type __sum16 = __u16;
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
pub struct iphdr {
|
||||||
|
pub _bitfield_align_1: [u8; 0],
|
||||||
|
pub _bitfield_1: __BindgenBitfieldUnit<[u8; 1usize]>,
|
||||||
|
pub tos: __u8,
|
||||||
|
pub tot_len: __be16,
|
||||||
|
pub id: __be16,
|
||||||
|
pub frag_off: __be16,
|
||||||
|
pub ttl: __u8,
|
||||||
|
pub protocol: __u8,
|
||||||
|
pub check: __sum16,
|
||||||
|
pub saddr: __be32,
|
||||||
|
pub daddr: __be32,
|
||||||
|
}
|
||||||
|
impl iphdr {
|
||||||
|
#[inline]
|
||||||
|
pub fn ihl(&self) -> __u8 {
|
||||||
|
unsafe { ::core::mem::transmute(self._bitfield_1.get(0usize, 4u8) as u8) }
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
pub fn set_ihl(&mut self, val: __u8) {
|
||||||
|
unsafe {
|
||||||
|
let val: u8 = ::core::mem::transmute(val);
|
||||||
|
self._bitfield_1.set(0usize, 4u8, val as u64)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
pub fn version(&self) -> __u8 {
|
||||||
|
unsafe { ::core::mem::transmute(self._bitfield_1.get(4usize, 4u8) as u8) }
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
pub fn set_version(&mut self, val: __u8) {
|
||||||
|
unsafe {
|
||||||
|
let val: u8 = ::core::mem::transmute(val);
|
||||||
|
self._bitfield_1.set(4usize, 4u8, val as u64)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
pub fn new_bitfield_1(ihl: __u8, version: __u8) -> __BindgenBitfieldUnit<[u8; 1usize]> {
|
||||||
|
let mut __bindgen_bitfield_unit: __BindgenBitfieldUnit<[u8; 1usize]> = Default::default();
|
||||||
|
__bindgen_bitfield_unit.set(0usize, 4u8, {
|
||||||
|
let ihl: u8 = unsafe { ::core::mem::transmute(ihl) };
|
||||||
|
ihl as u64
|
||||||
|
});
|
||||||
|
__bindgen_bitfield_unit.set(4usize, 4u8, {
|
||||||
|
let version: u8 = unsafe { ::core::mem::transmute(version) };
|
||||||
|
version as u64
|
||||||
|
});
|
||||||
|
__bindgen_bitfield_unit
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Storage> __BindgenBitfieldUnit<Storage> {}
|
||||||
|
impl ethhdr {
|
||||||
|
pub fn h_dest(&self) -> Option<[::aya_bpf::cty::c_uchar; 6usize]> {
|
||||||
|
unsafe { ::aya_bpf::helpers::bpf_probe_read(&self.h_dest) }.ok()
|
||||||
|
}
|
||||||
|
pub fn h_source(&self) -> Option<[::aya_bpf::cty::c_uchar; 6usize]> {
|
||||||
|
unsafe { ::aya_bpf::helpers::bpf_probe_read(&self.h_source) }.ok()
|
||||||
|
}
|
||||||
|
pub fn h_proto(&self) -> Option<__be16> {
|
||||||
|
unsafe { ::aya_bpf::helpers::bpf_probe_read(&self.h_proto) }.ok()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl iphdr {
|
||||||
|
pub fn tos(&self) -> Option<__u8> {
|
||||||
|
unsafe { ::aya_bpf::helpers::bpf_probe_read(&self.tos) }.ok()
|
||||||
|
}
|
||||||
|
pub fn tot_len(&self) -> Option<__be16> {
|
||||||
|
unsafe { ::aya_bpf::helpers::bpf_probe_read(&self.tot_len) }.ok()
|
||||||
|
}
|
||||||
|
pub fn id(&self) -> Option<__be16> {
|
||||||
|
unsafe { ::aya_bpf::helpers::bpf_probe_read(&self.id) }.ok()
|
||||||
|
}
|
||||||
|
pub fn frag_off(&self) -> Option<__be16> {
|
||||||
|
unsafe { ::aya_bpf::helpers::bpf_probe_read(&self.frag_off) }.ok()
|
||||||
|
}
|
||||||
|
pub fn ttl(&self) -> Option<__u8> {
|
||||||
|
unsafe { ::aya_bpf::helpers::bpf_probe_read(&self.ttl) }.ok()
|
||||||
|
}
|
||||||
|
pub fn protocol(&self) -> Option<__u8> {
|
||||||
|
unsafe { ::aya_bpf::helpers::bpf_probe_read(&self.protocol) }.ok()
|
||||||
|
}
|
||||||
|
pub fn check(&self) -> Option<__sum16> {
|
||||||
|
unsafe { ::aya_bpf::helpers::bpf_probe_read(&self.check) }.ok()
|
||||||
|
}
|
||||||
|
pub fn saddr(&self) -> Option<__be32> {
|
||||||
|
unsafe { ::aya_bpf::helpers::bpf_probe_read(&self.saddr) }.ok()
|
||||||
|
}
|
||||||
|
pub fn daddr(&self) -> Option<__be32> {
|
||||||
|
unsafe { ::aya_bpf::helpers::bpf_probe_read(&self.daddr) }.ok()
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,95 @@
|
|||||||
|
#![no_std]
|
||||||
|
#![no_main]
|
||||||
|
|
||||||
|
use aya_bpf::{
|
||||||
|
bindings::xdp_action,
|
||||||
|
macros::{map, xdp},
|
||||||
|
maps::{HashMap, PerfEventArray},
|
||||||
|
programs::XdpContext,
|
||||||
|
};
|
||||||
|
|
||||||
|
use core::mem;
|
||||||
|
use memoffset::offset_of;
|
||||||
|
use myapp_common::PacketLog;
|
||||||
|
|
||||||
|
// ANCHOR: bindings
|
||||||
|
mod bindings;
|
||||||
|
use bindings::{ethhdr, iphdr};
|
||||||
|
// ANCHOR_END: bindings
|
||||||
|
|
||||||
|
#[panic_handler]
|
||||||
|
fn panic(_info: &core::panic::PanicInfo) -> ! {
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ANCHOR: map
|
||||||
|
#[map(name = "EVENTS")]
|
||||||
|
static mut EVENTS: PerfEventArray<PacketLog> =
|
||||||
|
PerfEventArray::<PacketLog>::with_max_entries(1024, 0);
|
||||||
|
// ANCHOR_END: map
|
||||||
|
|
||||||
|
// ANCHOR: blocklist
|
||||||
|
#[map(name = "BLOCKLIST")]
|
||||||
|
static mut BLOCKLIST: HashMap<u32, u32> = HashMap::<u32, u32>::with_max_entries(1024, 0);
|
||||||
|
// ANCHOR_END: blocklist
|
||||||
|
|
||||||
|
#[xdp(name="myapp")]
|
||||||
|
pub fn xdp_myapp(ctx: XdpContext) -> u32 {
|
||||||
|
match try_xdp_myapp(ctx) {
|
||||||
|
Ok(ret) => ret,
|
||||||
|
Err(_) => xdp_action::XDP_ABORTED,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ANCHOR: ptr_at
|
||||||
|
#[inline(always)]
|
||||||
|
unsafe fn ptr_at<T>(ctx: &XdpContext, offset: usize) -> Result<*const T, ()> {
|
||||||
|
let start = ctx.data();
|
||||||
|
let end = ctx.data_end();
|
||||||
|
let len = mem::size_of::<T>();
|
||||||
|
|
||||||
|
if start + offset + len > end {
|
||||||
|
return Err(());
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok((start + offset) as *const T)
|
||||||
|
}
|
||||||
|
// ANCHOR_END: ptr_at
|
||||||
|
|
||||||
|
// ANCHOR: block_ip
|
||||||
|
fn block_ip(address: u32) -> bool {
|
||||||
|
unsafe { BLOCKLIST.get(&address).is_some() }
|
||||||
|
}
|
||||||
|
// ANCHOR_END: block_ip
|
||||||
|
|
||||||
|
// ANCHOR: try
|
||||||
|
fn try_xdp_myapp(ctx: XdpContext) -> Result<u32, ()> {
|
||||||
|
let h_proto = u16::from_be(unsafe { *ptr_at(&ctx, offset_of!(ethhdr, h_proto))? });
|
||||||
|
if h_proto != ETH_P_IP {
|
||||||
|
return Ok(xdp_action::XDP_PASS);
|
||||||
|
}
|
||||||
|
let dest = u32::from_be(unsafe { *ptr_at(&ctx, ETH_HDR_LEN + offset_of!(iphdr, daddr))? });
|
||||||
|
|
||||||
|
// ANCHOR: action
|
||||||
|
let action = if block_ip(dest) {
|
||||||
|
xdp_action::XDP_DROP
|
||||||
|
} else {
|
||||||
|
xdp_action::XDP_PASS
|
||||||
|
};
|
||||||
|
// ANCHOR_END: action
|
||||||
|
|
||||||
|
let log_entry = PacketLog {
|
||||||
|
ipv4_address: dest,
|
||||||
|
action: action,
|
||||||
|
};
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
EVENTS.output(&ctx, &log_entry, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(action)
|
||||||
|
}
|
||||||
|
// ANCHOR_END: try
|
||||||
|
|
||||||
|
const ETH_P_IP: u16 = 0x0800;
|
||||||
|
const ETH_HDR_LEN: usize = mem::size_of::<ethhdr>();
|
@ -0,0 +1,19 @@
|
|||||||
|
[package]
|
||||||
|
name = "myapp"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2018"
|
||||||
|
publish = false
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
aya = { git = "https://github.com/aya-rs/aya", branch="main", features=["async_tokio"] }
|
||||||
|
myapp-common = { path = "../myapp-common", features=["userspace"] }
|
||||||
|
anyhow = "1.0.42"
|
||||||
|
bytes = "1"
|
||||||
|
tokio = { version = "1.9.0", features = ["full"] }
|
||||||
|
ctrlc = "3.2"
|
||||||
|
|
||||||
|
structopt = { version = "0.3"}
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "myapp"
|
||||||
|
path = "src/main.rs"
|
@ -0,0 +1,68 @@
|
|||||||
|
use aya::{
|
||||||
|
maps::HashMap,
|
||||||
|
maps::perf::AsyncPerfEventArray,
|
||||||
|
programs::{Xdp, XdpFlags},
|
||||||
|
util::online_cpus,
|
||||||
|
Bpf,
|
||||||
|
};
|
||||||
|
use structopt::StructOpt;
|
||||||
|
use bytes::BytesMut;
|
||||||
|
use std::{
|
||||||
|
convert::{TryFrom, TryInto},
|
||||||
|
net::{self, Ipv4Addr},
|
||||||
|
};
|
||||||
|
use tokio::{signal, task};
|
||||||
|
|
||||||
|
use myapp_common::PacketLog;
|
||||||
|
|
||||||
|
#[derive(Debug, StructOpt)]
|
||||||
|
struct Opt {
|
||||||
|
#[structopt(short, long)]
|
||||||
|
path: String,
|
||||||
|
#[structopt(short, long, default_value = "eth0")]
|
||||||
|
iface: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
// ANCHOR: main
|
||||||
|
#[tokio::main]
|
||||||
|
async fn main() -> Result<(), anyhow::Error> {
|
||||||
|
let opt = Opt::from_args();
|
||||||
|
let mut bpf = Bpf::load_file(&opt.path)?;
|
||||||
|
let program: &mut Xdp = bpf.program_mut("myapp")?.try_into()?;
|
||||||
|
program.load()?;
|
||||||
|
program.attach(&opt.iface, XdpFlags::default())?;
|
||||||
|
|
||||||
|
// ANCHOR: block_address
|
||||||
|
let mut blocklist: HashMap<_, u32, u32> = HashMap::try_from(bpf.map_mut("BLOCKLIST")?)?;
|
||||||
|
let block_addr : u32 = Ipv4Addr::new(192, 168, 0, 10).try_into()?;
|
||||||
|
blocklist.insert(block_addr, 0, 0)?;
|
||||||
|
// ANCHOR_END: block_address
|
||||||
|
|
||||||
|
// ANCHOR: map
|
||||||
|
let mut perf_array = AsyncPerfEventArray::try_from(bpf.map_mut("EVENTS")?)?;
|
||||||
|
// ANCHOR_END: map
|
||||||
|
|
||||||
|
for cpu_id in online_cpus()? {
|
||||||
|
let mut buf = perf_array.open(cpu_id, None)?;
|
||||||
|
|
||||||
|
task::spawn(async move {
|
||||||
|
let mut buffers = (0..10)
|
||||||
|
.map(|_| BytesMut::with_capacity(1024))
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let events = buf.read_events(&mut buffers).await.unwrap();
|
||||||
|
for i in 0..events.read {
|
||||||
|
let buf = &mut buffers[i];
|
||||||
|
let ptr = buf.as_ptr() as *const PacketLog;
|
||||||
|
let data = unsafe { ptr.read_unaligned() };
|
||||||
|
let src_addr = net::Ipv4Addr::from(data.ipv4_address);
|
||||||
|
println!("LOG: SRC {}, ACTION {}", src_addr, data.action);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
signal::ctrl_c().await.expect("failed to listen for event");
|
||||||
|
Ok::<_, anyhow::Error>(())
|
||||||
|
}
|
||||||
|
// ANCHOR_END: main
|
@ -0,0 +1,11 @@
|
|||||||
|
[package]
|
||||||
|
name = "xtask"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
structopt = {version = "0.3", default-features = false }
|
||||||
|
anyhow = "1"
|
||||||
|
aya-gen = { git = "http://github.com/aya-rs/aya", branch = "main" }
|
@ -0,0 +1,62 @@
|
|||||||
|
use std::path::PathBuf;
|
||||||
|
use std::process::Command;
|
||||||
|
|
||||||
|
use structopt::StructOpt;
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
pub enum Architecture {
|
||||||
|
BpfEl,
|
||||||
|
BpfEb,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::str::FromStr for Architecture {
|
||||||
|
type Err = String;
|
||||||
|
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
Ok(match s {
|
||||||
|
"bpfel-unknown-none" => Architecture::BpfEl,
|
||||||
|
"bpfeb-unknown-none" => Architecture::BpfEb,
|
||||||
|
_ => return Err("invalid target".to_owned()),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for Architecture {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
f.write_str(match self {
|
||||||
|
Architecture::BpfEl => "bpfel-unknown-none",
|
||||||
|
Architecture::BpfEb => "bpfeb-unknown-none",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(StructOpt)]
|
||||||
|
pub struct Options {
|
||||||
|
#[structopt(default_value = "bpfel-unknown-none", long)]
|
||||||
|
target: Architecture,
|
||||||
|
#[structopt(long)]
|
||||||
|
release: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn build(opts: Options) -> Result<(), anyhow::Error> {
|
||||||
|
let dir = PathBuf::from("myapp-ebpf");
|
||||||
|
let target = format!("--target={}", opts.target);
|
||||||
|
let mut args = vec![
|
||||||
|
"+nightly",
|
||||||
|
"build",
|
||||||
|
"--verbose",
|
||||||
|
target.as_str(),
|
||||||
|
"-Z",
|
||||||
|
"build-std=core",
|
||||||
|
];
|
||||||
|
if opts.release {
|
||||||
|
args.push("--release")
|
||||||
|
}
|
||||||
|
let status = Command::new("cargo")
|
||||||
|
.current_dir(&dir)
|
||||||
|
.args(&args)
|
||||||
|
.status()
|
||||||
|
.expect("failed to build bpf examples");
|
||||||
|
assert!(status.success());
|
||||||
|
Ok(())
|
||||||
|
}
|
@ -0,0 +1,16 @@
|
|||||||
|
use aya_gen::btf_types;
|
||||||
|
use std::{
|
||||||
|
fs::File,
|
||||||
|
io::Write,
|
||||||
|
path::{Path, PathBuf},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn generate() -> Result<(), anyhow::Error> {
|
||||||
|
let dir = PathBuf::from("myapp-ebpf/src");
|
||||||
|
let names: Vec<&str> = vec!["ethhdr", "iphdr"];
|
||||||
|
let bindings = btf_types::generate(Path::new("/sys/kernel/btf/vmlinux"), &names, true)?;
|
||||||
|
// Write the bindings to the $OUT_DIR/bindings.rs file.
|
||||||
|
let mut out = File::create(dir.join("bindings.rs"))?;
|
||||||
|
write!(out, "{}", bindings)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
@ -0,0 +1,32 @@
|
|||||||
|
mod build_ebpf;
|
||||||
|
mod codegen;
|
||||||
|
|
||||||
|
use std::process::exit;
|
||||||
|
|
||||||
|
use structopt::StructOpt;
|
||||||
|
#[derive(StructOpt)]
|
||||||
|
pub struct Options {
|
||||||
|
#[structopt(subcommand)]
|
||||||
|
command: Command,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(StructOpt)]
|
||||||
|
enum Command {
|
||||||
|
BuildEbpf(build_ebpf::Options),
|
||||||
|
Codegen,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let opts = Options::from_args();
|
||||||
|
|
||||||
|
use Command::*;
|
||||||
|
let ret = match opts.command {
|
||||||
|
BuildEbpf(opts) => build_ebpf::build(opts),
|
||||||
|
Codegen => codegen::generate(),
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Err(e) = ret {
|
||||||
|
eprintln!("{:#}", e);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue