Skip to content

Commit

Permalink
Merge pull request #181 from dave-tucker/multimap
Browse files Browse the repository at this point in the history
aya: Support multiple maps in map sections
  • Loading branch information
alessandrod committed Apr 30, 2022
2 parents 2e494a1 + f357be7 commit 5472ac0
Show file tree
Hide file tree
Showing 5 changed files with 209 additions and 5 deletions.
103 changes: 99 additions & 4 deletions aya/src/obj/mod.rs
Expand Up @@ -519,6 +519,36 @@ impl Object {
Ok(())
}

fn parse_map_section(
&mut self,
section: &Section,
symbols: Vec<Symbol>,
) -> Result<(), ParseError> {
if symbols.is_empty() {
return Err(ParseError::NoSymbolsInMapSection {});
}
for (i, sym) in symbols.iter().enumerate() {
let start = sym.address as usize;
let end = start + sym.size as usize;
let data = &section.data[start..end];
let name = sym
.name
.as_ref()
.ok_or(ParseError::MapSymbolNameNotFound { i })?;
let def = parse_map_def(name, data)?;
self.maps.insert(
name.to_string(),
Map {
section_index: section.index.0,
def,
data: Vec::new(),
kind: MapKind::Other,
},
);
}
Ok(())
}

fn parse_section(&mut self, mut section: Section) -> Result<(), BpfError> {
let mut parts = section.name.rsplitn(2, '/').collect::<Vec<_>>();
parts.reverse();
Expand All @@ -542,9 +572,19 @@ impl Object {
BpfSectionKind::Btf => self.parse_btf(&section)?,
BpfSectionKind::BtfExt => self.parse_btf_ext(&section)?,
BpfSectionKind::Maps => {
let name = section.name.splitn(2, '/').last().unwrap();
self.maps
.insert(name.to_string(), parse_map(&section, name)?);
let symbols: Vec<Symbol> = self
.symbols_by_index
.values()
.filter(|s| {
if let Some(idx) = s.section_index {
idx == section.index.0
} else {
false
}
})
.cloned()
.collect();
self.parse_map_section(&section, symbols)?
}
BpfSectionKind::Program => {
let program = self.parse_program(&section)?;
Expand Down Expand Up @@ -625,6 +665,12 @@ pub enum ParseError {

#[error("map for section with index {index} not found")]
MapNotFound { index: usize },

#[error("the map number {i} in the `maps` section doesn't have a symbol name")]
MapSymbolNameNotFound { i: usize },

#[error("no symbols found for the maps included in the maps section")]
NoSymbolsInMapSection {},
}

#[derive(Debug)]
Expand Down Expand Up @@ -873,6 +919,22 @@ mod tests {
}
}

fn fake_sym(obj: &mut Object, section_index: usize, address: u64, name: &str, size: u64) {
let idx = obj.symbols_by_index.len();
obj.symbols_by_index.insert(
idx + 1,
Symbol {
index: idx + 1,
section_index: Some(section_index),
name: Some(name.to_string()),
address,
size,
is_definition: false,
kind: SymbolKind::Data,
},
);
}

fn bytes_of<T>(val: &T) -> &[u8] {
// Safety: This is for testing only
unsafe { crate::util::bytes_of(val) }
Expand Down Expand Up @@ -1113,7 +1175,7 @@ mod tests {
#[test]
fn test_parse_section_map() {
let mut obj = fake_obj();

fake_sym(&mut obj, 0, 0, "foo", mem::size_of::<bpf_map_def>() as u64);
assert_matches!(
obj.parse_section(fake_section(
BpfSectionKind::Maps,
Expand All @@ -1132,6 +1194,39 @@ mod tests {
assert!(obj.maps.get("foo").is_some());
}

#[test]
fn test_parse_section_multiple_maps() {
let mut obj = fake_obj();
fake_sym(&mut obj, 0, 0, "foo", mem::size_of::<bpf_map_def>() as u64);
fake_sym(&mut obj, 0, 28, "bar", mem::size_of::<bpf_map_def>() as u64);
fake_sym(&mut obj, 0, 60, "baz", mem::size_of::<bpf_map_def>() as u64);
let def = &bpf_map_def {
map_type: 1,
key_size: 2,
value_size: 3,
max_entries: 4,
map_flags: 5,
..Default::default()
};
let map_data = bytes_of(def).to_vec();
let mut buf = vec![];
buf.extend(&map_data);
buf.extend(&map_data);
// throw in some padding
buf.extend(&[0, 0, 0, 0]);
buf.extend(&map_data);
assert_matches!(
obj.parse_section(fake_section(BpfSectionKind::Maps, "maps", buf.as_slice(),)),
Ok(())
);
assert!(obj.maps.get("foo").is_some());
assert!(obj.maps.get("bar").is_some());
assert!(obj.maps.get("baz").is_some());
for (_, m) in &obj.maps {
assert_eq!(&m.def, def);
}
}

#[test]
fn test_parse_section_data() {
let mut obj = fake_obj();
Expand Down
2 changes: 1 addition & 1 deletion test/README.md
Expand Up @@ -7,7 +7,7 @@ common usage behaviours work on real Linux distros

This assumes you have a working Rust and Go toolchain on the host machine

1. `rustup toolchain add x86_64-unknown-linux-musl`
1. `rustup target add x86_64-unknown-linux-musl`
1. Install [`rtf`](https://github.com/linuxkit/rtf): `go install github.com/linuxkit/rtf@latest`
1. Install rust-script: `cargo install rust-script`
1. Install `qemu` and `cloud-init-utils` package - or any package that provides `cloud-localds`
Expand Down
47 changes: 47 additions & 0 deletions test/cases/010_load/010_multiple_maps/multimap.bpf.c
@@ -0,0 +1,47 @@
#include <linux/bpf.h>
#include <bpf/bpf_helpers.h>

const int XDP_ACTION_MAX = (XDP_TX + 1);

struct datarec {
__u64 rx_packets;
};

// stats keyed by XDP Action
struct bpf_map_def SEC("maps") xdp_stats_map = {
.type = BPF_MAP_TYPE_ARRAY,
.key_size = sizeof(__u32),
.value_size = sizeof(struct datarec),
.max_entries = XDP_ACTION_MAX,
};

// tracks number of times called
struct bpf_map_def SEC("maps") prog_stats_map = {
.type = BPF_MAP_TYPE_ARRAY,
.key_size = sizeof(__u32),
.value_size = sizeof(__u64),
.max_entries = 1,
};

SEC("xdp/stats")
int xdp_stats(struct xdp_md *ctx)
{
__u64 *stats;
struct datarec *rec;
__u32 key = XDP_PASS;
__u32 k1 = 0;

stats = bpf_map_lookup_elem(&prog_stats_map, &k1);
if (!stats)
return XDP_ABORTED;
__sync_fetch_and_add(stats, 1);

rec = bpf_map_lookup_elem(&xdp_stats_map, &key);
if (!rec)
return XDP_ABORTED;
__sync_fetch_and_add(&rec->rx_packets, 1);

return XDP_PASS;
}

char _license[] SEC("license") = "GPL";
33 changes: 33 additions & 0 deletions test/cases/010_load/010_multiple_maps/multimap.rs
@@ -0,0 +1,33 @@
//! ```cargo
//! [dependencies]
//! log = "0.4"
//! simplelog = "0.11"
//! aya = { path = "../../../../aya" }
//! ```

use aya::{
Bpf,
programs::{Xdp, XdpFlags},
};
use log::info;
use std::convert::TryInto;

use simplelog::{ColorChoice, ConfigBuilder, LevelFilter, TermLogger, TerminalMode};

fn main() {
TermLogger::init(
LevelFilter::Debug,
ConfigBuilder::new()
.set_target_level(LevelFilter::Error)
.set_location_level(LevelFilter::Error)
.build(),
TerminalMode::Mixed,
ColorChoice::Auto,
).unwrap();
info!("Loading XDP program");
let mut bpf = Bpf::load_file("multimap.o").unwrap();
let pass: &mut Xdp = bpf.program_mut("stats").unwrap().try_into().unwrap();
pass.load().unwrap();
pass.attach("eth0", XdpFlags::default()).unwrap();
info!("Success...");
}
29 changes: 29 additions & 0 deletions test/cases/010_load/010_multiple_maps/test.sh
@@ -0,0 +1,29 @@
#!/bin/sh
# SUMMARY: Check that a program with multiple maps in the maps section loads
# LABELS:

set -e

# Source libraries. Uncomment if needed/defined
#. "${RT_LIB}"
. "${RT_PROJECT_ROOT}/_lib/lib.sh"

NAME=multimap

clean_up() {
rm -rf ${NAME}.o ${NAME}
exec_vm rm -f ${NAME}.o ${NAME}
}

trap clean_up EXIT

# Test code goes here
compile_c_ebpf "$(pwd)/${NAME}.bpf.c"
compile_user "$(pwd)/${NAME}.rs"

scp_vm ${NAME}.o
scp_vm ${NAME}

exec_vm sudo ./${NAME}

exit 0

0 comments on commit 5472ac0

Please sign in to comment.