From b29a061bce99d06971dc977ddc075cbf653971d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alessandro=C2=A0Decina?= Date: Sun, 19 Sep 2021 22:32:22 +0000 Subject: [PATCH 01/41] Initial commit --- aya-log/.gitignore | 13 ++ aya-log/Cargo.toml | 2 + aya-log/aya-log-common/Cargo.toml | 14 ++ aya-log/aya-log-common/src/lib.rs | 44 ++++++ aya-log/aya-log-ebpf/.cargo/config.toml | 6 + aya-log/aya-log-ebpf/Cargo.toml | 24 +++ aya-log/aya-log-ebpf/src/lib.rs | 170 ++++++++++++++++++++ aya-log/aya-log-ebpf/src/macros.rs | 201 +++++++++++++++++++++++ aya-log/aya-log/Cargo.toml | 16 ++ aya-log/aya-log/src/lib.rs | 202 ++++++++++++++++++++++++ 10 files changed, 692 insertions(+) create mode 100644 aya-log/.gitignore create mode 100644 aya-log/Cargo.toml create mode 100644 aya-log/aya-log-common/Cargo.toml create mode 100644 aya-log/aya-log-common/src/lib.rs create mode 100644 aya-log/aya-log-ebpf/.cargo/config.toml create mode 100644 aya-log/aya-log-ebpf/Cargo.toml create mode 100644 aya-log/aya-log-ebpf/src/lib.rs create mode 100644 aya-log/aya-log-ebpf/src/macros.rs create mode 100644 aya-log/aya-log/Cargo.toml create mode 100644 aya-log/aya-log/src/lib.rs diff --git a/aya-log/.gitignore b/aya-log/.gitignore new file mode 100644 index 00000000..54f741e1 --- /dev/null +++ b/aya-log/.gitignore @@ -0,0 +1,13 @@ +### https://raw.github.com/github/gitignore/master/Rust.gitignore + +# Generated by Cargo +# will have compiled files and executables +debug/ +target/ + +# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries +# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html +Cargo.lock + +# These are backup files generated by rustfmt +**/*.rs.bk diff --git a/aya-log/Cargo.toml b/aya-log/Cargo.toml new file mode 100644 index 00000000..a8e5e117 --- /dev/null +++ b/aya-log/Cargo.toml @@ -0,0 +1,2 @@ +[workspace] +members = ["aya-log", "aya-log-common", "xtask"] diff --git a/aya-log/aya-log-common/Cargo.toml b/aya-log/aya-log-common/Cargo.toml new file mode 100644 index 00000000..ff6a3944 --- /dev/null +++ b/aya-log/aya-log-common/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "aya-log-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" \ No newline at end of file diff --git a/aya-log/aya-log-common/src/lib.rs b/aya-log/aya-log-common/src/lib.rs new file mode 100644 index 00000000..837bdbbf --- /dev/null +++ b/aya-log/aya-log-common/src/lib.rs @@ -0,0 +1,44 @@ +#![no_std] + +pub const LOG_BUF_CAPACITY: usize = 1024; + +pub const LOG_FIELDS: usize = 6; + +#[repr(usize)] +#[derive(Copy, Clone, Eq, PartialEq, Debug, Hash)] +pub enum Level { + /// The "error" level. + /// + /// Designates very serious errors. + Error = 1, + /// The "warn" level. + /// + /// Designates hazardous situations. + Warn, + /// The "info" level. + /// + /// Designates useful information. + Info, + /// The "debug" level. + /// + /// Designates lower priority information. + Debug, + /// The "trace" level. + /// + /// Designates very low priority, often extremely verbose, information. + Trace, +} + +#[repr(usize)] +#[derive(Copy, Clone, Debug)] +pub enum RecordField { + Target = 1, + Level, + Module, + File, + Line, + Log, +} + +#[cfg(feature = "userspace")] +unsafe impl aya::Pod for RecordField {} diff --git a/aya-log/aya-log-ebpf/.cargo/config.toml b/aya-log/aya-log-ebpf/.cargo/config.toml new file mode 100644 index 00000000..5d7e5915 --- /dev/null +++ b/aya-log/aya-log-ebpf/.cargo/config.toml @@ -0,0 +1,6 @@ +[build] +target-dir = "../target" +target = "bpfel-unknown-none" + +[unstable] +build-std = ["core"] \ No newline at end of file diff --git a/aya-log/aya-log-ebpf/Cargo.toml b/aya-log/aya-log-ebpf/Cargo.toml new file mode 100644 index 00000000..c005d22b --- /dev/null +++ b/aya-log/aya-log-ebpf/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name = "aya-log-ebpf" +version = "0.1.0" +edition = "2018" + +[dependencies] +aya-bpf = { git = "https://github.com/aya-rs/aya", branch = "main" } +aya-log-common = { path = "../aya-log-common" } +ufmt = "0.1.0" + +[lib] +path = "src/lib.rs" + +[profile.dev] +panic = "abort" +debug = 1 +opt-level = 2 +overflow-checks = false + +[profile.release] +panic = "abort" + +[workspace] +members = [] \ No newline at end of file diff --git a/aya-log/aya-log-ebpf/src/lib.rs b/aya-log/aya-log-ebpf/src/lib.rs new file mode 100644 index 00000000..2b361e40 --- /dev/null +++ b/aya-log/aya-log-ebpf/src/lib.rs @@ -0,0 +1,170 @@ +#![no_std] + +mod macros; + +use core::{cmp, mem, ptr}; + +use aya_bpf::{ + bindings::{bpf_map_def, bpf_map_type::BPF_MAP_TYPE_PERF_EVENT_ARRAY, BPF_F_CURRENT_CPU}, + cty::c_long, + helpers::bpf_perf_event_output, + macros::map, + maps::PerCpuArray, + BpfContext, +}; +pub use aya_log_common::Level; +use aya_log_common::{RecordField, LOG_BUF_CAPACITY}; + +#[doc(hidden)] +#[repr(C)] +pub struct LogBuf { + pub buf: [u8; LOG_BUF_CAPACITY], +} + +#[doc(hidden)] +#[map] +pub static mut AYA_LOG_BUF: PerCpuArray = PerCpuArray::with_max_entries(1, 0); + +#[doc(hidden)] +#[map] +pub static mut AYA_LOGS: LogEventArray = LogEventArray::with_max_entries(1024, 0); + +#[doc(hidden)] +pub struct LogBufWriter<'a> { + pos: usize, + data: &'a mut [u8], +} + +impl<'a> LogBufWriter<'a> { + pub fn new(data: &mut [u8]) -> LogBufWriter<'_> { + LogBufWriter { + pos: mem::size_of::() + mem::size_of::(), + data, + } + } + + pub fn finish(self) -> usize { + let mut buf = self.data; + unsafe { ptr::write_unaligned(buf.as_mut_ptr() as *mut _, RecordField::Log) }; + buf = &mut buf[mem::size_of::()..]; + + let len = self.pos - mem::size_of::() - mem::size_of::(); + unsafe { ptr::write_unaligned(buf.as_mut_ptr() as *mut _, len) }; + + self.pos + } +} + +impl<'a> ufmt::uWrite for LogBufWriter<'a> { + type Error = (); + + fn write_str(&mut self, s: &str) -> Result<(), Self::Error> { + let bytes = s.as_bytes(); + let len = bytes.len(); + let available = self.data.len() - self.pos; + if available < len { + return Err(()); + } + + self.data[self.pos..self.pos + len].copy_from_slice(bytes); + self.pos += len; + Ok(()) + } +} + +struct TagLenValue<'a> { + tag: RecordField, + value: &'a [u8], +} + +impl<'a> TagLenValue<'a> { + #[inline(always)] + pub(crate) fn new(tag: RecordField, value: &'a [u8]) -> TagLenValue<'a> { + TagLenValue { tag, value } + } + + pub(crate) fn try_write(&self, mut buf: &mut [u8]) -> Result { + let size = mem::size_of::() + mem::size_of::() + self.value.len(); + if buf.len() < size { + return Err(()); + } + + unsafe { ptr::write_unaligned(buf.as_mut_ptr() as *mut _, self.tag) }; + buf = &mut buf[mem::size_of::()..]; + + unsafe { ptr::write_unaligned(buf.as_mut_ptr() as *mut _, self.value.len()) }; + buf = &mut buf[mem::size_of::()..]; + + let len = cmp::min(buf.len(), self.value.len()); + buf[..len].copy_from_slice(&self.value[..len]); + Ok(size) + } +} + +#[doc(hidden)] +pub fn write_record_header( + buf: &mut [u8], + target: &str, + level: Level, + module: &str, + file: &str, + line: u32, +) -> Result { + let mut size = 0; + for attr in [ + TagLenValue::new(RecordField::Target, target.as_bytes()), + TagLenValue::new(RecordField::Level, &(level as usize).to_ne_bytes()), + TagLenValue::new(RecordField::Module, module.as_bytes()), + TagLenValue::new(RecordField::File, file.as_bytes()), + TagLenValue::new(RecordField::Line, &line.to_ne_bytes()), + ] { + size += attr.try_write(&mut buf[size..])?; + } + + Ok(size) +} + +#[doc(hidden)] +#[repr(transparent)] +pub struct LogEventArray { + def: bpf_map_def, +} + +impl LogEventArray { + const fn with_max_entries(max_entries: u32, flags: u32) -> LogEventArray { + LogEventArray { + def: bpf_map_def { + type_: BPF_MAP_TYPE_PERF_EVENT_ARRAY, + key_size: mem::size_of::() as u32, + value_size: mem::size_of::() as u32, + max_entries, + map_flags: flags, + id: 0, + pinning: 0, + }, + } + } + + #[inline(always)] + pub fn output( + &mut self, + ctx: &C, + buf: &mut LogBuf, + len: usize, + ) -> Result<(), c_long> { + let ret = unsafe { + bpf_perf_event_output( + ctx.as_ptr(), + &mut self.def as *mut _ as *mut _, + BPF_F_CURRENT_CPU, + buf.buf.as_mut_ptr() as *mut _, + len as u64, + ) + }; + if ret < 0 { + Err(ret) + } else { + Ok(()) + } + } +} diff --git a/aya-log/aya-log-ebpf/src/macros.rs b/aya-log/aya-log-ebpf/src/macros.rs new file mode 100644 index 00000000..e982f67e --- /dev/null +++ b/aya-log/aya-log-ebpf/src/macros.rs @@ -0,0 +1,201 @@ +/// Logs a message at the error level. +/// +/// # Examples +/// +/// ```no_run +/// # let ctx = (); +/// # let err_code = -1; +/// use aya_log_ebpf::error; +/// +/// error!(&ctx, "Error redirecting packet: {}", err_code); +/// error!(&ctx, target: "ingress", "Error redirecting packet: {}", err_code); +/// ``` +#[macro_export] +macro_rules! error { + ($ctx:expr, target: $target:expr, $($arg:tt)+) => ( + $crate::log!($ctx, target: $target, $crate::Level::Error, $($arg)+) + ); + ($ctx:expr, $($arg:tt)+) => ( + $crate::log!($ctx, $crate::Level::Error, $($arg)+) + ) +} + +/// Logs a message at the warn level. +/// +/// # Examples +/// +/// ``` +/// use aya_log_ebpf::warn; +/// +/// # fn main() { +/// let warn_description = "Invalid Input"; +/// +/// warn!("Warning! {}!", warn_description); +/// warn!(target: "input_events", "App received warning: {}", warn_description); +/// # } +/// ``` +#[macro_export] +macro_rules! warn { + ($ctx:expr, target: $target:expr, $($arg:tt)+) => ( + $crate::log!($ctx, target: $target, $crate::Level::Warn, $($arg)+) + ); + ($ctx:expr, $($arg:tt)+) => ( + $crate::log!($ctx, $crate::Level::Warn, $($arg)+) + ) +} + +/// Logs a message at the info level. +/// +/// # Examples +/// +/// ```edition2018 +/// use log::info; +/// +/// # fn main() { +/// # struct Connection { port: u32, speed: u32 } +/// let conn_info = Connection { port: 40, speed: 3 }; +/// +/// info!("Connected to port {} at {} Mb/s", conn_info.port, conn_info.speed); +/// info!(target: "connection_events", "Successfull connection, port: {}, speed: {}", +/// conn_info.port, conn_info.speed); +/// # } +/// ``` +#[macro_export] +macro_rules! info { + ($ctx:expr, target: $target:expr, $($arg:tt)+) => ( + $crate::log!($ctx, target: $target, $crate::Level::Info, $($arg)+) + ); + ($ctx:expr, $($arg:tt)+) => ( + $crate::log!($ctx, $crate::Level::Info, $($arg)+) + ) +} + +/// Logs a message at the debug level. +/// +/// # Examples +/// +/// ```edition2018 +/// use log::debug; +/// +/// # fn main() { +/// # struct Position { x: i64, y: i64 } +/// let pos = Position { x: 3.234, y: -1223 }; +/// +/// debug!("New position: x: {}, y: {}", pos.x, pos.y); +/// debug!(target: "app_events", "New position: x: {}, y: {}", pos.x, pos.y); +/// # } +/// ``` +#[macro_export] +macro_rules! debug { + ($ctx:expr, target: $target:expr, $($arg:tt)+) => ( + $crate::log!($ctx, target: $target, $crate::Level::Debug, $($arg)+) + ); + ($ctx:expr, $($arg:tt)+) => ( + $crate::log!($ctx, $crate::Level::Debug, $($arg)+) + ) +} + +/// Logs a message at the trace level. +/// +/// # Examples +/// +/// ```edition2018 +/// use log::trace; +/// +/// # fn main() { +/// # struct Position { x: i64, y: i64 } +/// let pos = Position { x: 3234, y: -1223 }; +/// +/// trace!("Position is: x: {}, y: {}", pos.x, pos.y); +/// trace!(target: "app_events", "x is {} and y is {}", +/// if pos.x >= 0 { "positive" } else { "negative" }, +/// if pos.y >= 0 { "positive" } else { "negative" }); +/// # } +/// ``` +#[macro_export] +macro_rules! trace { + ($ctx:expr, target: $target:expr, $($arg:tt)+) => ( + $crate::log!($ctx, target: $target, $crate::Level::Trace, $($arg)+) + ); + ($ctx:expr, $($arg:tt)+) => ( + $crate::log!($ctx, $crate::Level::Trace, $($arg)+) + ) +} + +// /// Determines if a message logged at the specified level in that module will +// /// be logged. +// /// +// /// This can be used to avoid expensive computation of log message arguments if +// /// the message would be ignored anyway. +// /// +// /// # Examples +// /// +// /// ```edition2018 +// /// use log::Level::Debug; +// /// use log::{debug, log_enabled}; +// /// +// /// # fn foo() { +// /// if log_enabled!(Debug) { +// /// let data = expensive_call(); +// /// debug!("expensive debug data: {} {}", data.x, data.y); +// /// } +// /// if log_enabled!(target: "Global", Debug) { +// /// let data = expensive_call(); +// /// debug!(target: "Global", "expensive debug data: {} {}", data.x, data.y); +// /// } +// /// # } +// /// # struct Data { x: u32, y: u32 } +// /// # fn expensive_call() -> Data { Data { x: 0, y: 0 } } +// /// # fn main() {} +// /// ``` +// macro_rules! log_enabled { +// (target: $target:expr, $lvl:expr) => {{ +// let lvl = $lvl; +// lvl <= $crate::STATIC_MAX_LEVEL +// }}; +// ($lvl:expr) => { +// log_enabled!(target: __log_module_path!(), $lvl) +// }; +// } + +/// Log a message at the given level. +/// +/// This macro will generically log with the specified `Level` and `format!` +/// based argument list. +/// +/// # Examples +/// +/// ```edition2018 +/// use log::{log, Level}; +/// +/// # fn main() { +/// let data = (42, "Forty-two"); +/// let private_data = "private"; +/// +/// log!(Level::Error, "Received errors: {}, {}", data.0, data.1); +/// log!(target: "app_events", Level::Warn, "App warning: {}, {}, {}", +/// data.0, data.1, private_data); +/// # } +/// ``` +#[macro_export] +macro_rules! log { + ($ctx:expr, target: $target:expr, $lvl:expr, $($arg:tt)+) => ({ + if let Some(buf) = unsafe { $crate::AYA_LOG_BUF.get_mut(0) } { + if let Ok(header_len) = $crate::write_record_header(&mut buf.buf, module_path!(), $lvl, module_path!(), file!(), line!()) { + if let Ok(message_len) = $crate::write_record_message!(&mut buf.buf[header_len..], $($arg)+) { + let _ = unsafe { $crate::AYA_LOGS.output($ctx, buf, header_len + message_len) }; + }; + } + } + }); + ($ctx:expr, $lvl:expr, $($arg:tt)+) => ($crate::log!($ctx, target: __log_module_path!(), $lvl, $($arg)+)) +} + +#[doc(hidden)] +#[macro_export] +macro_rules! write_record_message { + ($buf:expr, $($arg:tt)+) => {{ + let mut writer = $crate::LogBufWriter::new($buf); + ufmt::uwrite!(writer, $($arg)+).map(|_| writer.finish()) + }} +} diff --git a/aya-log/aya-log/Cargo.toml b/aya-log/aya-log/Cargo.toml new file mode 100644 index 00000000..0e1c5736 --- /dev/null +++ b/aya-log/aya-log/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "aya-log" +version = "0.1.0" +edition = "2018" +publish = false + +[dependencies] +aya = { git = "https://github.com/aya-rs/aya", branch="main", features=["async_tokio"] } +aya-log-common = { path = "../aya-log-common", features=["userspace"] } +thiserror = "1" +log = "0.4" +bytes = "1.1" +tokio = { version = "1.2.0" } + +[lib] +path = "src/lib.rs" diff --git a/aya-log/aya-log/src/lib.rs b/aya-log/aya-log/src/lib.rs new file mode 100644 index 00000000..5a75724c --- /dev/null +++ b/aya-log/aya-log/src/lib.rs @@ -0,0 +1,202 @@ +//! A logging framework for eBPF programs. +//! +//! This is the user space side of the [Aya] logging framework. For the eBPF +//! side, see the `aya-log-ebpf` crate. +//! +//! `aya-log` provides the [BpfLogger] type, which reads log records created by +//! `aya-log-ebpf` and logs them using the [log] crate. Any logger that +//! implements the [Log] trait can be used with this crate. +//! +//! # Example: +//! +//! This example uses the [simplelog] crate to log messages to the terminal. +//! +//! ```no_run +//! # let mut bpf = aya::Bpf::load(&[], None)?; +//! use aya_log::BpfLogger; +//! use simplelog::{ColorChoice, ConfigBuilder, LevelFilter, TermLogger, TerminalMode}; +//! +//! BpfLogger::init( +//! &mut bpf, +//! TermLogger::new( +//! LevelFilter::Trace, +//! ConfigBuilder::new() +//! .set_target_level(LevelFilter::Error) +//! .set_location_level(LevelFilter::Error) +//! .build(), +//! TerminalMode::Mixed, +//! ColorChoice::Auto, +//! ), +//! ).unwrap(); +//! ``` +//! +//! With the following eBPF code: +//! +//! ```no_run +//! # let ctx = (); +//! use aya_log_ebpf::{debug, error, info, trace, warn}; +//! +//! error!(&ctx, "this is an error message 🚨"); +//! warn!(&ctx, "this is a warning message ⚠ī¸"); +//! info!(&ctx, "this is an info message ℹī¸"); +//! debug!(&ctx, "this is a debug message ī¸đŸ"); +//! trace!(&ctx, "this is a trace message 🔍"); +//! ``` +//! Outputs: +//! +//! ```text +//! 21:58:55 [ERROR] xxx: [src/main.rs:35] this is an error message 🚨 +//! 21:58:55 [WARN] xxx: [src/main.rs:36] this is a warning message ⚠ī¸ +//! 21:58:55 [INFO] xxx: [src/main.rs:37] this is an info message ℹī¸ +//! 21:58:55 [DEBUG] (7) xxx: [src/main.rs:38] this is a debug message ī¸đŸ +//! 21:58:55 [TRACE] (7) xxx: [src/main.rs:39] this is a trace message 🔍 +//! ``` +//! +//! [Aya]: https://docs.rs/aya +//! [simplelog]: https://docs.rs/simplelog +//! [Log]: https://docs.rs/log/0.4.14/log/trait.Log.html +//! [log]: https://docs.rs/log +//! +use std::{convert::TryInto, io, mem, ptr, sync::Arc}; + +use aya_log_common::{RecordField, LOG_BUF_CAPACITY, LOG_FIELDS}; +use bytes::BytesMut; +use log::{Level, Log, Record}; +use thiserror::Error; + +use aya::{ + maps::{ + perf::{AsyncPerfEventArray, PerfBufferError}, + MapError, + }, + util::online_cpus, + Bpf, Pod, +}; + +/// Log messages generated by `aya_log_ebpf` using the [log] crate. +/// +/// For more details see the [module level documentation](crate). +pub struct BpfLogger; + +impl BpfLogger { + /// Starts reading log records created with `aya-log-ebpf` and logs them + /// with the given logger. + pub fn init(bpf: &mut Bpf, logger: T) -> Result { + let logger = Arc::new(logger); + let mut logs: AsyncPerfEventArray<_> = bpf.map_mut("AYA_LOGS")?.try_into()?; + + for cpu_id in online_cpus().map_err(Error::InvalidOnlineCpu)? { + let mut buf = logs.open(cpu_id, None)?; + + let log = logger.clone(); + tokio::spawn(async move { + let mut buffers = (0..10) + .map(|_| BytesMut::with_capacity(LOG_BUF_CAPACITY)) + .collect::>(); + + loop { + let events = buf.read_events(&mut buffers).await.unwrap(); + + #[allow(clippy::needless_range_loop)] + for i in 0..events.read { + let buf = &mut buffers[i]; + log_buf(buf, &*log).unwrap(); + } + } + }); + } + + Ok(BpfLogger {}) + } +} + +#[derive(Error, Debug)] +pub enum Error { + #[error("error opening log event array")] + MapError(#[from] MapError), + + #[error("error opening log buffer")] + PerfBufferError(#[from] PerfBufferError), + + #[error("invalid /sys/devices/system/cpu/online format")] + InvalidOnlineCpu(#[source] io::Error), +} + +fn log_buf(mut buf: &[u8], logger: &dyn Log) -> Result<(), ()> { + let mut target = None; + let mut level = Level::Trace; + let mut module = None; + let mut file = None; + let mut line = None; + let mut log = None; + + for _ in 0..LOG_FIELDS { + let (attr, rest) = unsafe { TagLenValue::<'_, RecordField>::try_read(buf)? }; + + match attr.tag { + RecordField::Target => { + target = Some(std::str::from_utf8(attr.value).map_err(|_| ())?); + } + RecordField::Level => { + level = unsafe { ptr::read_unaligned(attr.value.as_ptr() as *const _) } + } + RecordField::Module => { + module = Some(std::str::from_utf8(attr.value).map_err(|_| ())?); + } + RecordField::File => { + file = Some(std::str::from_utf8(attr.value).map_err(|_| ())?); + } + RecordField::Line => { + line = Some(u32::from_ne_bytes(attr.value.try_into().map_err(|_| ())?)); + } + RecordField::Log => { + log = Some(std::str::from_utf8(attr.value).map_err(|_| ())?); + } + } + + buf = rest; + } + + logger.log( + &Record::builder() + .args(format_args!("{}", log.ok_or(())?)) + .target(target.ok_or(())?) + .level(level) + .module_path(module) + .file(file) + .line(line) + .build(), + ); + Ok(()) +} + +struct TagLenValue<'a, T: Pod> { + tag: T, + value: &'a [u8], +} + +impl<'a, T: Pod> TagLenValue<'a, T> { + unsafe fn try_read(mut buf: &'a [u8]) -> Result<(TagLenValue<'a, T>, &'a [u8]), ()> { + if buf.len() < mem::size_of::() + mem::size_of::() { + return Err(()); + } + + let tag = ptr::read_unaligned(buf.as_ptr() as *const T); + buf = &buf[mem::size_of::()..]; + + let len = usize::from_ne_bytes(buf[..mem::size_of::()].try_into().unwrap()); + buf = &buf[mem::size_of::()..]; + + if buf.len() < len { + return Err(()); + } + + Ok(( + TagLenValue { + tag, + value: &buf[..len], + }, + &buf[len..], + )) + } +} From 5df853cfb030c3a37a066b892623546a77c97db2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alessandro=C2=A0Decina?= Date: Mon, 20 Sep 2021 01:27:43 +0000 Subject: [PATCH 02/41] Update readme --- aya-log/README.md | 72 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 aya-log/README.md diff --git a/aya-log/README.md b/aya-log/README.md new file mode 100644 index 00000000..850590ed --- /dev/null +++ b/aya-log/README.md @@ -0,0 +1,72 @@ +# aya-log - a logging library for eBPF programs + +## Overview + +`aya-log` is a logging library for eBPF programs written using [aya]. Think of +it as the [log] crate for eBPF. + +## Installation + +### User space + +Add `aya-log` to `Cargo.toml`: + +```toml +[dependencies] +aya-log = { git = "https://github.com/aya-rs/aya-log", branch = "main" } +``` + +### eBPF side + +Add `aya-log-ebpf` to `Cargo.toml`: + +```toml +[dependencies] +aya-log-ebpf = { git = "https://github.com/aya-rs/aya-log", branch = "main" } +``` + +## Example + +Here's an example that uses `aya-log` in conjunction with the [simplelog] crate +to log eBPF messages to the terminal. + +### User space code + +```rust +use aya_log::BpfLogger; +use simplelog::{ColorChoice, ConfigBuilder, LevelFilter, TermLogger, TerminalMode}; + +BpfLogger::init( + &mut bpf, + TermLogger::new( + LevelFilter::Trace, + ConfigBuilder::new() + .set_target_level(LevelFilter::Error) + .set_location_level(LevelFilter::Error) + .build(), + TerminalMode::Mixed, + ColorChoice::Auto, + ), +).unwrap(); +``` + +### eBPF code + +```rust +use aya_log_ebpf::info; + +fn try_xdp_firewall(ctx: XdpContext) -> Result { + if let Some(port) = tcp_dest_port(&ctx)? { + if block_port(port) { + info!(&ctx, "❌ blocked incoming connection on port: {}", port); + return Ok(XDP_DROP); + } + } + + Ok(XDP_PASS) +} +``` + +[aya]: https://github.com/aya-rs/aya +[log]: https://docs.rs/log +[simplelog]: https://docs.rs/simplelog \ No newline at end of file From 0d7ac3eb3ee58bd4ba10af9c49f7c9ef80e09143 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alessandro=C2=A0Decina?= Date: Tue, 21 Sep 2021 09:33:23 +0000 Subject: [PATCH 03/41] Add ufmt to readme --- aya-log/README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/aya-log/README.md b/aya-log/README.md index 850590ed..186c3898 100644 --- a/aya-log/README.md +++ b/aya-log/README.md @@ -18,11 +18,12 @@ aya-log = { git = "https://github.com/aya-rs/aya-log", branch = "main" } ### eBPF side -Add `aya-log-ebpf` to `Cargo.toml`: +Add `aya-log-ebpf` and `ufmt` to `Cargo.toml`: ```toml [dependencies] aya-log-ebpf = { git = "https://github.com/aya-rs/aya-log", branch = "main" } +ufmt = "0.1" ``` ## Example From 741957f94598d149960a5296b2010a07ffac02e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alessandro=C2=A0Decina?= Date: Sat, 2 Oct 2021 11:02:04 +0000 Subject: [PATCH 04/41] Use aya_log_ebpf::ufmt instead of ::ufmt --- aya-log/README.md | 3 +-- aya-log/aya-log-ebpf/src/lib.rs | 2 ++ aya-log/aya-log-ebpf/src/macros.rs | 3 ++- aya-log/aya-log/src/lib.rs | 1 + 4 files changed, 6 insertions(+), 3 deletions(-) diff --git a/aya-log/README.md b/aya-log/README.md index 186c3898..f8e5b872 100644 --- a/aya-log/README.md +++ b/aya-log/README.md @@ -23,7 +23,6 @@ Add `aya-log-ebpf` and `ufmt` to `Cargo.toml`: ```toml [dependencies] aya-log-ebpf = { git = "https://github.com/aya-rs/aya-log", branch = "main" } -ufmt = "0.1" ``` ## Example @@ -70,4 +69,4 @@ fn try_xdp_firewall(ctx: XdpContext) -> Result { [aya]: https://github.com/aya-rs/aya [log]: https://docs.rs/log -[simplelog]: https://docs.rs/simplelog \ No newline at end of file +[simplelog]: https://docs.rs/simplelog diff --git a/aya-log/aya-log-ebpf/src/lib.rs b/aya-log/aya-log-ebpf/src/lib.rs index 2b361e40..ad0ab8b5 100644 --- a/aya-log/aya-log-ebpf/src/lib.rs +++ b/aya-log/aya-log-ebpf/src/lib.rs @@ -1,5 +1,7 @@ #![no_std] +pub extern crate ufmt; + mod macros; use core::{cmp, mem, ptr}; diff --git a/aya-log/aya-log-ebpf/src/macros.rs b/aya-log/aya-log-ebpf/src/macros.rs index e982f67e..fd56c534 100644 --- a/aya-log/aya-log-ebpf/src/macros.rs +++ b/aya-log/aya-log-ebpf/src/macros.rs @@ -195,7 +195,8 @@ macro_rules! log { #[macro_export] macro_rules! write_record_message { ($buf:expr, $($arg:tt)+) => {{ + use aya_log_ebpf::ufmt; let mut writer = $crate::LogBufWriter::new($buf); - ufmt::uwrite!(writer, $($arg)+).map(|_| writer.finish()) + aya_log_ebpf::ufmt::uwrite!(writer, $($arg)+).map(|_| writer.finish()) }} } diff --git a/aya-log/aya-log/src/lib.rs b/aya-log/aya-log/src/lib.rs index 5a75724c..838fe5a5 100644 --- a/aya-log/aya-log/src/lib.rs +++ b/aya-log/aya-log/src/lib.rs @@ -167,6 +167,7 @@ fn log_buf(mut buf: &[u8], logger: &dyn Log) -> Result<(), ()> { .line(line) .build(), ); + logger.flush(); Ok(()) } From 22d8f86fbb10ec5e71bca750119f93eb5ba171e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alessandro=C2=A0Decina?= Date: Sat, 2 Oct 2021 11:15:10 +0000 Subject: [PATCH 05/41] Use aya_bpf::maps::PerfEventByteArray to output logs --- aya-log/aya-log-ebpf/src/lib.rs | 53 ++---------------------------- aya-log/aya-log-ebpf/src/macros.rs | 2 +- 2 files changed, 3 insertions(+), 52 deletions(-) diff --git a/aya-log/aya-log-ebpf/src/lib.rs b/aya-log/aya-log-ebpf/src/lib.rs index ad0ab8b5..448f0871 100644 --- a/aya-log/aya-log-ebpf/src/lib.rs +++ b/aya-log/aya-log-ebpf/src/lib.rs @@ -7,12 +7,8 @@ mod macros; use core::{cmp, mem, ptr}; use aya_bpf::{ - bindings::{bpf_map_def, bpf_map_type::BPF_MAP_TYPE_PERF_EVENT_ARRAY, BPF_F_CURRENT_CPU}, - cty::c_long, - helpers::bpf_perf_event_output, macros::map, - maps::PerCpuArray, - BpfContext, + maps::{PerCpuArray, PerfEventByteArray}, }; pub use aya_log_common::Level; use aya_log_common::{RecordField, LOG_BUF_CAPACITY}; @@ -29,7 +25,7 @@ pub static mut AYA_LOG_BUF: PerCpuArray = PerCpuArray::with_max_entries( #[doc(hidden)] #[map] -pub static mut AYA_LOGS: LogEventArray = LogEventArray::with_max_entries(1024, 0); +pub static mut AYA_LOGS: PerfEventByteArray = PerfEventByteArray::with_max_entries(1024, 0); #[doc(hidden)] pub struct LogBufWriter<'a> { @@ -125,48 +121,3 @@ pub fn write_record_header( Ok(size) } - -#[doc(hidden)] -#[repr(transparent)] -pub struct LogEventArray { - def: bpf_map_def, -} - -impl LogEventArray { - const fn with_max_entries(max_entries: u32, flags: u32) -> LogEventArray { - LogEventArray { - def: bpf_map_def { - type_: BPF_MAP_TYPE_PERF_EVENT_ARRAY, - key_size: mem::size_of::() as u32, - value_size: mem::size_of::() as u32, - max_entries, - map_flags: flags, - id: 0, - pinning: 0, - }, - } - } - - #[inline(always)] - pub fn output( - &mut self, - ctx: &C, - buf: &mut LogBuf, - len: usize, - ) -> Result<(), c_long> { - let ret = unsafe { - bpf_perf_event_output( - ctx.as_ptr(), - &mut self.def as *mut _ as *mut _, - BPF_F_CURRENT_CPU, - buf.buf.as_mut_ptr() as *mut _, - len as u64, - ) - }; - if ret < 0 { - Err(ret) - } else { - Ok(()) - } - } -} diff --git a/aya-log/aya-log-ebpf/src/macros.rs b/aya-log/aya-log-ebpf/src/macros.rs index fd56c534..f4c420f1 100644 --- a/aya-log/aya-log-ebpf/src/macros.rs +++ b/aya-log/aya-log-ebpf/src/macros.rs @@ -183,7 +183,7 @@ macro_rules! log { if let Some(buf) = unsafe { $crate::AYA_LOG_BUF.get_mut(0) } { if let Ok(header_len) = $crate::write_record_header(&mut buf.buf, module_path!(), $lvl, module_path!(), file!(), line!()) { if let Ok(message_len) = $crate::write_record_message!(&mut buf.buf[header_len..], $($arg)+) { - let _ = unsafe { $crate::AYA_LOGS.output($ctx, buf, header_len + message_len) }; + let _ = unsafe { $crate::AYA_LOGS.output($ctx, &buf.buf[..header_len + message_len], 0) }; }; } } From b14d4bab2fac894d4e47838d7de8a9b63a5ac4c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alessandro=C2=A0Decina?= Date: Fri, 15 Oct 2021 08:09:45 +0000 Subject: [PATCH 06/41] Switch to aya-ufmt --- aya-log/README.md | 2 +- aya-log/aya-log-ebpf/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/aya-log/README.md b/aya-log/README.md index f8e5b872..0cb3b167 100644 --- a/aya-log/README.md +++ b/aya-log/README.md @@ -18,7 +18,7 @@ aya-log = { git = "https://github.com/aya-rs/aya-log", branch = "main" } ### eBPF side -Add `aya-log-ebpf` and `ufmt` to `Cargo.toml`: +Add `aya-log-ebpf` to `Cargo.toml`: ```toml [dependencies] diff --git a/aya-log/aya-log-ebpf/Cargo.toml b/aya-log/aya-log-ebpf/Cargo.toml index c005d22b..5e00a175 100644 --- a/aya-log/aya-log-ebpf/Cargo.toml +++ b/aya-log/aya-log-ebpf/Cargo.toml @@ -6,7 +6,7 @@ edition = "2018" [dependencies] aya-bpf = { git = "https://github.com/aya-rs/aya", branch = "main" } aya-log-common = { path = "../aya-log-common" } -ufmt = "0.1.0" +ufmt = { version = "0.1", package = "aya-ufmt" } [lib] path = "src/lib.rs" From 2ac433449cdea32f10c8fc88218799995946032d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alessandro=C2=A0Decina?= Date: Fri, 15 Oct 2021 08:13:18 +0000 Subject: [PATCH 07/41] Minor tweaks to make the verifier's job easier --- aya-log/aya-log-ebpf/src/lib.rs | 11 +++++++++-- aya-log/aya-log-ebpf/src/macros.rs | 5 ++++- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/aya-log/aya-log-ebpf/src/lib.rs b/aya-log/aya-log-ebpf/src/lib.rs index 448f0871..56ae6746 100644 --- a/aya-log/aya-log-ebpf/src/lib.rs +++ b/aya-log/aya-log-ebpf/src/lib.rs @@ -11,7 +11,8 @@ use aya_bpf::{ maps::{PerCpuArray, PerfEventByteArray}, }; pub use aya_log_common::Level; -use aya_log_common::{RecordField, LOG_BUF_CAPACITY}; +use aya_log_common::RecordField; +pub use aya_log_common::LOG_BUF_CAPACITY; #[doc(hidden)] #[repr(C)] @@ -59,12 +60,18 @@ impl<'a> ufmt::uWrite for LogBufWriter<'a> { fn write_str(&mut self, s: &str) -> Result<(), Self::Error> { let bytes = s.as_bytes(); let len = bytes.len(); + + // this is to make sure the verifier knows about the upper bound + if len > LOG_BUF_CAPACITY { + return Err(()); + } + let available = self.data.len() - self.pos; if available < len { return Err(()); } - self.data[self.pos..self.pos + len].copy_from_slice(bytes); + self.data[self.pos..self.pos + len].copy_from_slice(&bytes[..len]); self.pos += len; Ok(()) } diff --git a/aya-log/aya-log-ebpf/src/macros.rs b/aya-log/aya-log-ebpf/src/macros.rs index f4c420f1..2d2ee666 100644 --- a/aya-log/aya-log-ebpf/src/macros.rs +++ b/aya-log/aya-log-ebpf/src/macros.rs @@ -183,7 +183,10 @@ macro_rules! log { if let Some(buf) = unsafe { $crate::AYA_LOG_BUF.get_mut(0) } { if let Ok(header_len) = $crate::write_record_header(&mut buf.buf, module_path!(), $lvl, module_path!(), file!(), line!()) { if let Ok(message_len) = $crate::write_record_message!(&mut buf.buf[header_len..], $($arg)+) { - let _ = unsafe { $crate::AYA_LOGS.output($ctx, &buf.buf[..header_len + message_len], 0) }; + let record_len = header_len + message_len; + if record_len <= $crate::LOG_BUF_CAPACITY { + let _ = unsafe { $crate::AYA_LOGS.output($ctx, &buf.buf[..header_len + message_len], 0) }; + } }; } } From 9ab9c80183edcb23297a644d0e63f7c1f28cd968 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alessandro=C2=A0Decina?= Date: Fri, 15 Oct 2021 09:03:12 +0000 Subject: [PATCH 08/41] Simplify BpfLogger::init Make BpfLogger::init(bpf) log using the default logger. Add BpfLoger::init_with_logger(bpf, logger) for logging using a custom logger instance. --- aya-log/README.md | 27 +++++++++--------- aya-log/aya-log/src/lib.rs | 58 ++++++++++++++++++++++++++++---------- 2 files changed, 57 insertions(+), 28 deletions(-) diff --git a/aya-log/README.md b/aya-log/README.md index 0cb3b167..5e1245ca 100644 --- a/aya-log/README.md +++ b/aya-log/README.md @@ -33,21 +33,22 @@ to log eBPF messages to the terminal. ### User space code ```rust -use aya_log::BpfLogger; use simplelog::{ColorChoice, ConfigBuilder, LevelFilter, TermLogger, TerminalMode}; +use aya_log::BpfLogger; -BpfLogger::init( - &mut bpf, - TermLogger::new( - LevelFilter::Trace, - ConfigBuilder::new() - .set_target_level(LevelFilter::Error) - .set_location_level(LevelFilter::Error) - .build(), - TerminalMode::Mixed, - ColorChoice::Auto, - ), -).unwrap(); +TermLogger::init( + LevelFilter::Debug, + ConfigBuilder::new() + .set_target_level(LevelFilter::Error) + .set_location_level(LevelFilter::Error) + .build(), + TerminalMode::Mixed, + ColorChoice::Auto, +) +.unwrap(); + +// Will log using the default logger, which is TermLogger in this case +BpfLogger::init(&mut bpf).unwrap(); ``` ### eBPF code diff --git a/aya-log/aya-log/src/lib.rs b/aya-log/aya-log/src/lib.rs index 838fe5a5..d50fd860 100644 --- a/aya-log/aya-log/src/lib.rs +++ b/aya-log/aya-log/src/lib.rs @@ -13,21 +13,23 @@ //! //! ```no_run //! # let mut bpf = aya::Bpf::load(&[], None)?; -//! use aya_log::BpfLogger; //! use simplelog::{ColorChoice, ConfigBuilder, LevelFilter, TermLogger, TerminalMode}; +//! use aya_log::BpfLogger; //! -//! BpfLogger::init( -//! &mut bpf, -//! TermLogger::new( -//! LevelFilter::Trace, -//! ConfigBuilder::new() -//! .set_target_level(LevelFilter::Error) -//! .set_location_level(LevelFilter::Error) -//! .build(), -//! TerminalMode::Mixed, -//! ColorChoice::Auto, -//! ), -//! ).unwrap(); +//! // initialize simplelog::TermLogger as the default logger +//! TermLogger::init( +//! LevelFilter::Debug, +//! ConfigBuilder::new() +//! .set_target_level(LevelFilter::Error) +//! .set_location_level(LevelFilter::Error) +//! .build(), +//! TerminalMode::Mixed, +//! ColorChoice::Auto, +//! ) +//! .unwrap(); +//! +//! // start reading aya-log records and log them using the default logger +//! BpfLogger::init(&mut bpf).unwrap(); //! ``` //! //! With the following eBPF code: @@ -61,7 +63,7 @@ use std::{convert::TryInto, io, mem, ptr, sync::Arc}; use aya_log_common::{RecordField, LOG_BUF_CAPACITY, LOG_FIELDS}; use bytes::BytesMut; -use log::{Level, Log, Record}; +use log::{logger, Level, Log, Record}; use thiserror::Error; use aya::{ @@ -79,9 +81,18 @@ use aya::{ pub struct BpfLogger; impl BpfLogger { + /// Starts reading log records created with `aya-log-ebpf` and logs them + /// with the default logger. See [log::logger]. + pub fn init(bpf: &mut Bpf) -> Result { + BpfLogger::init_with_logger(bpf, DefaultLogger {}) + } + /// Starts reading log records created with `aya-log-ebpf` and logs them /// with the given logger. - pub fn init(bpf: &mut Bpf, logger: T) -> Result { + pub fn init_with_logger( + bpf: &mut Bpf, + logger: T, + ) -> Result { let logger = Arc::new(logger); let mut logs: AsyncPerfEventArray<_> = bpf.map_mut("AYA_LOGS")?.try_into()?; @@ -110,6 +121,23 @@ impl BpfLogger { } } +#[derive(Copy, Clone, Debug)] +struct DefaultLogger; + +impl Log for DefaultLogger { + fn enabled(&self, metadata: &log::Metadata) -> bool { + log::logger().enabled(metadata) + } + + fn log(&self, record: &Record) { + log::logger().log(record) + } + + fn flush(&self) { + log::logger().flush() + } +} + #[derive(Error, Debug)] pub enum Error { #[error("error opening log event array")] From cced3da5c8ff45df0596e25123071a4a761286d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alessandro=C2=A0Decina?= Date: Fri, 15 Oct 2021 09:37:34 +0000 Subject: [PATCH 09/41] Update to aya 0.10.5 --- aya-log/aya-log-common/Cargo.toml | 2 +- aya-log/aya-log/Cargo.toml | 4 ++-- aya-log/aya-log/src/lib.rs | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/aya-log/aya-log-common/Cargo.toml b/aya-log/aya-log-common/Cargo.toml index ff6a3944..62e6d6cd 100644 --- a/aya-log/aya-log-common/Cargo.toml +++ b/aya-log/aya-log-common/Cargo.toml @@ -8,7 +8,7 @@ default = [] userspace = [ "aya" ] [dependencies] -aya = { git = "https://github.com/aya-rs/aya", branch="main", optional=true } +aya = { version = "0.10.5", optional=true } [lib] path = "src/lib.rs" \ No newline at end of file diff --git a/aya-log/aya-log/Cargo.toml b/aya-log/aya-log/Cargo.toml index 0e1c5736..f10aef69 100644 --- a/aya-log/aya-log/Cargo.toml +++ b/aya-log/aya-log/Cargo.toml @@ -5,8 +5,8 @@ edition = "2018" publish = false [dependencies] -aya = { git = "https://github.com/aya-rs/aya", branch="main", features=["async_tokio"] } -aya-log-common = { path = "../aya-log-common", features=["userspace"] } +aya = { version = "0.10.5", features=["async_tokio"] } +aya-log-common = { version = "0.1", path = "../aya-log-common", features=["userspace"] } thiserror = "1" log = "0.4" bytes = "1.1" diff --git a/aya-log/aya-log/src/lib.rs b/aya-log/aya-log/src/lib.rs index d50fd860..6460512c 100644 --- a/aya-log/aya-log/src/lib.rs +++ b/aya-log/aya-log/src/lib.rs @@ -12,7 +12,7 @@ //! This example uses the [simplelog] crate to log messages to the terminal. //! //! ```no_run -//! # let mut bpf = aya::Bpf::load(&[], None)?; +//! # let mut bpf = aya::Bpf::load(&[]).unwrap(); //! use simplelog::{ColorChoice, ConfigBuilder, LevelFilter, TermLogger, TerminalMode}; //! use aya_log::BpfLogger; //! From 6d14a16d8ed54d90e9dfdbdca1fb2caf0a15c017 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alessandro=C2=A0Decina?= Date: Fri, 15 Oct 2021 09:46:39 +0000 Subject: [PATCH 10/41] git add .cargo and xtask --- aya-log/.cargo/config.toml | 2 ++ aya-log/xtask/Cargo.toml | 10 ++++++ aya-log/xtask/src/build_ebpf.rs | 62 +++++++++++++++++++++++++++++++++ aya-log/xtask/src/main.rs | 29 +++++++++++++++ 4 files changed, 103 insertions(+) create mode 100644 aya-log/.cargo/config.toml create mode 100644 aya-log/xtask/Cargo.toml create mode 100644 aya-log/xtask/src/build_ebpf.rs create mode 100644 aya-log/xtask/src/main.rs diff --git a/aya-log/.cargo/config.toml b/aya-log/.cargo/config.toml new file mode 100644 index 00000000..f0ccbc9a --- /dev/null +++ b/aya-log/.cargo/config.toml @@ -0,0 +1,2 @@ +[alias] +xtask = "run --package xtask --" \ No newline at end of file diff --git a/aya-log/xtask/Cargo.toml b/aya-log/xtask/Cargo.toml new file mode 100644 index 00000000..3b50084b --- /dev/null +++ b/aya-log/xtask/Cargo.toml @@ -0,0 +1,10 @@ +[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" \ No newline at end of file diff --git a/aya-log/xtask/src/build_ebpf.rs b/aya-log/xtask/src/build_ebpf.rs new file mode 100644 index 00000000..756801f3 --- /dev/null +++ b/aya-log/xtask/src/build_ebpf.rs @@ -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 { + 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("aya-log-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(()) +} diff --git a/aya-log/xtask/src/main.rs b/aya-log/xtask/src/main.rs new file mode 100644 index 00000000..96308fd3 --- /dev/null +++ b/aya-log/xtask/src/main.rs @@ -0,0 +1,29 @@ +mod build_ebpf; + +use std::process::exit; + +use structopt::StructOpt; +#[derive(StructOpt)] +pub struct Options { + #[structopt(subcommand)] + command: Command, +} + +#[derive(StructOpt)] +enum Command { + BuildEbpf(build_ebpf::Options), +} + +fn main() { + let opts = Options::from_args(); + + use Command::*; + let ret = match opts.command { + BuildEbpf(opts) => build_ebpf::build(opts), + }; + + if let Err(e) = ret { + eprintln!("{:#}", e); + exit(1); + } +} From 29955b22875b865f567079c76aeba70630fa42dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alessandro=C2=A0Decina?= Date: Fri, 15 Oct 2021 09:47:11 +0000 Subject: [PATCH 11/41] (cargo-release) version 0.1.1 --- aya-log/aya-log-common/Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/aya-log/aya-log-common/Cargo.toml b/aya-log/aya-log-common/Cargo.toml index 62e6d6cd..b4b7009b 100644 --- a/aya-log/aya-log-common/Cargo.toml +++ b/aya-log/aya-log-common/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "aya-log-common" -version = "0.1.0" +version = "0.1.1" edition = "2018" [features] @@ -11,4 +11,4 @@ userspace = [ "aya" ] aya = { version = "0.10.5", optional=true } [lib] -path = "src/lib.rs" \ No newline at end of file +path = "src/lib.rs" From 31e71f8db53454ce673bd9891be06fc002af5721 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alessandro=C2=A0Decina?= Date: Fri, 15 Oct 2021 09:47:11 +0000 Subject: [PATCH 12/41] (cargo-release) version 0.1.1 --- aya-log/aya-log/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aya-log/aya-log/Cargo.toml b/aya-log/aya-log/Cargo.toml index f10aef69..7fd5768e 100644 --- a/aya-log/aya-log/Cargo.toml +++ b/aya-log/aya-log/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "aya-log" -version = "0.1.0" +version = "0.1.1" edition = "2018" publish = false From 5e18a715b2d1cf153de96d9775dfea762c684258 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alessandro=C2=A0Decina?= Date: Fri, 15 Oct 2021 09:51:22 +0000 Subject: [PATCH 13/41] Add missing manifest fields --- aya-log/aya-log-common/Cargo.toml | 6 ++++++ aya-log/aya-log/Cargo.toml | 8 +++++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/aya-log/aya-log-common/Cargo.toml b/aya-log/aya-log-common/Cargo.toml index b4b7009b..9d0da97a 100644 --- a/aya-log/aya-log-common/Cargo.toml +++ b/aya-log/aya-log-common/Cargo.toml @@ -1,6 +1,12 @@ [package] name = "aya-log-common" version = "0.1.1" +description = "A logging library for eBPF programs." +keywords = ["ebpf", "bpf", "log", "logging"] +license = "MIT OR Apache-2.0" +authors = ["The Aya Contributors"] +repository = "https://github.com/aya-rs/aya-log" +documentation = "https://docs.rs/aya-log" edition = "2018" [features] diff --git a/aya-log/aya-log/Cargo.toml b/aya-log/aya-log/Cargo.toml index 7fd5768e..47056232 100644 --- a/aya-log/aya-log/Cargo.toml +++ b/aya-log/aya-log/Cargo.toml @@ -1,8 +1,14 @@ [package] name = "aya-log" version = "0.1.1" +description = "A logging library for eBPF programs." +keywords = ["ebpf", "bpf", "log", "logging"] +license = "MIT OR Apache-2.0" +authors = ["The Aya Contributors"] +repository = "https://github.com/aya-rs/aya-log" +readme = "README.md" +documentation = "https://docs.rs/aya-log" edition = "2018" -publish = false [dependencies] aya = { version = "0.10.5", features=["async_tokio"] } From 8bde15dad70016f6fb72a77906da341768d59720 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alessandro=C2=A0Decina?= Date: Fri, 15 Oct 2021 09:55:37 +0000 Subject: [PATCH 14/41] Add copy of README.md inside aya-log/ Needed for crates.io --- aya-log/aya-log/README.md | 73 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 aya-log/aya-log/README.md diff --git a/aya-log/aya-log/README.md b/aya-log/aya-log/README.md new file mode 100644 index 00000000..5e1245ca --- /dev/null +++ b/aya-log/aya-log/README.md @@ -0,0 +1,73 @@ +# aya-log - a logging library for eBPF programs + +## Overview + +`aya-log` is a logging library for eBPF programs written using [aya]. Think of +it as the [log] crate for eBPF. + +## Installation + +### User space + +Add `aya-log` to `Cargo.toml`: + +```toml +[dependencies] +aya-log = { git = "https://github.com/aya-rs/aya-log", branch = "main" } +``` + +### eBPF side + +Add `aya-log-ebpf` to `Cargo.toml`: + +```toml +[dependencies] +aya-log-ebpf = { git = "https://github.com/aya-rs/aya-log", branch = "main" } +``` + +## Example + +Here's an example that uses `aya-log` in conjunction with the [simplelog] crate +to log eBPF messages to the terminal. + +### User space code + +```rust +use simplelog::{ColorChoice, ConfigBuilder, LevelFilter, TermLogger, TerminalMode}; +use aya_log::BpfLogger; + +TermLogger::init( + LevelFilter::Debug, + ConfigBuilder::new() + .set_target_level(LevelFilter::Error) + .set_location_level(LevelFilter::Error) + .build(), + TerminalMode::Mixed, + ColorChoice::Auto, +) +.unwrap(); + +// Will log using the default logger, which is TermLogger in this case +BpfLogger::init(&mut bpf).unwrap(); +``` + +### eBPF code + +```rust +use aya_log_ebpf::info; + +fn try_xdp_firewall(ctx: XdpContext) -> Result { + if let Some(port) = tcp_dest_port(&ctx)? { + if block_port(port) { + info!(&ctx, "❌ blocked incoming connection on port: {}", port); + return Ok(XDP_DROP); + } + } + + Ok(XDP_PASS) +} +``` + +[aya]: https://github.com/aya-rs/aya +[log]: https://docs.rs/log +[simplelog]: https://docs.rs/simplelog From 2800454763f5f0250c46c87f9cfb2e3d1f5f0a7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alessandro=C2=A0Decina?= Date: Fri, 15 Oct 2021 09:57:01 +0000 Subject: [PATCH 15/41] Fix clippy warning --- aya-log/aya-log/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aya-log/aya-log/src/lib.rs b/aya-log/aya-log/src/lib.rs index 6460512c..559a2692 100644 --- a/aya-log/aya-log/src/lib.rs +++ b/aya-log/aya-log/src/lib.rs @@ -63,7 +63,7 @@ use std::{convert::TryInto, io, mem, ptr, sync::Arc}; use aya_log_common::{RecordField, LOG_BUF_CAPACITY, LOG_FIELDS}; use bytes::BytesMut; -use log::{logger, Level, Log, Record}; +use log::{Level, Log, Record}; use thiserror::Error; use aya::{ From 7f8d7057df11f41d0869f7f713d121785934adca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alessandro=C2=A0Decina?= Date: Fri, 15 Oct 2021 10:26:32 +0000 Subject: [PATCH 16/41] ebpf: initialize AYA_LOGS with max_entries=0 This way aya will create one perf buffer for each cpu --- aya-log/aya-log-ebpf/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aya-log/aya-log-ebpf/src/lib.rs b/aya-log/aya-log-ebpf/src/lib.rs index 56ae6746..5d2dd3b3 100644 --- a/aya-log/aya-log-ebpf/src/lib.rs +++ b/aya-log/aya-log-ebpf/src/lib.rs @@ -26,7 +26,7 @@ pub static mut AYA_LOG_BUF: PerCpuArray = PerCpuArray::with_max_entries( #[doc(hidden)] #[map] -pub static mut AYA_LOGS: PerfEventByteArray = PerfEventByteArray::with_max_entries(1024, 0); +pub static mut AYA_LOGS: PerfEventByteArray = PerfEventByteArray::new(0); #[doc(hidden)] pub struct LogBufWriter<'a> { From 9b229d00e110a5b3b610ad567f8d15682c0b78e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alessandro=C2=A0Decina?= Date: Fri, 15 Oct 2021 10:30:21 +0000 Subject: [PATCH 17/41] Don't recompute the record length --- aya-log/aya-log-ebpf/src/macros.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aya-log/aya-log-ebpf/src/macros.rs b/aya-log/aya-log-ebpf/src/macros.rs index 2d2ee666..abc8a586 100644 --- a/aya-log/aya-log-ebpf/src/macros.rs +++ b/aya-log/aya-log-ebpf/src/macros.rs @@ -185,7 +185,7 @@ macro_rules! log { if let Ok(message_len) = $crate::write_record_message!(&mut buf.buf[header_len..], $($arg)+) { let record_len = header_len + message_len; if record_len <= $crate::LOG_BUF_CAPACITY { - let _ = unsafe { $crate::AYA_LOGS.output($ctx, &buf.buf[..header_len + message_len], 0) }; + let _ = unsafe { $crate::AYA_LOGS.output($ctx, &buf.buf[..record_len], 0) }; } }; } From ca1fe7e05f7b52c5e864680abeda29e640617d40 Mon Sep 17 00:00:00 2001 From: Michal Rostecki Date: Mon, 28 Feb 2022 14:54:42 +0000 Subject: [PATCH 18/41] Format arguments in userspace This change moves away argument formatting from eBPF to the userspace. eBPF part of aya-log writes unformatted log message and all arguments to the perf buffer and the userspace part of aya-log is formatting the message after receiving all arguments. Aya-based project to test this change: https://github.com/vadorovsky/aya-log-example Fixes: #4 Signed-off-by: Michal Rostecki Signed-off-by: Tuetuopay Co-authored-by: Tuetuopay --- aya-log/aya-log-common/src/lib.rs | 33 ++- aya-log/aya-log-ebpf/src/lib.rs | 130 ----------- aya-log/aya-log-ebpf/src/macros.rs | 205 ------------------ aya-log/aya-log/Cargo.toml | 1 + aya-log/aya-log/src/lib.rs | 107 ++++++++- .../{aya-log-ebpf => ebpf}/.cargo/config.toml | 0 aya-log/ebpf/Cargo.toml | 2 + aya-log/ebpf/aya-log-ebpf-macros/Cargo.toml | 12 + .../ebpf/aya-log-ebpf-macros/src/expand.rs | 185 ++++++++++++++++ aya-log/ebpf/aya-log-ebpf-macros/src/lib.rs | 52 +++++ aya-log/{ => ebpf}/aya-log-ebpf/Cargo.toml | 9 +- aya-log/ebpf/aya-log-ebpf/src/lib.rs | 133 ++++++++++++ aya-log/ebpf/rust-toolchain.toml | 2 + 13 files changed, 524 insertions(+), 347 deletions(-) delete mode 100644 aya-log/aya-log-ebpf/src/lib.rs delete mode 100644 aya-log/aya-log-ebpf/src/macros.rs rename aya-log/{aya-log-ebpf => ebpf}/.cargo/config.toml (100%) create mode 100644 aya-log/ebpf/Cargo.toml create mode 100644 aya-log/ebpf/aya-log-ebpf-macros/Cargo.toml create mode 100644 aya-log/ebpf/aya-log-ebpf-macros/src/expand.rs create mode 100644 aya-log/ebpf/aya-log-ebpf-macros/src/lib.rs rename aya-log/{ => ebpf}/aya-log-ebpf/Cargo.toml (66%) create mode 100644 aya-log/ebpf/aya-log-ebpf/src/lib.rs create mode 100644 aya-log/ebpf/rust-toolchain.toml diff --git a/aya-log/aya-log-common/src/lib.rs b/aya-log/aya-log-common/src/lib.rs index 837bdbbf..b30fafc7 100644 --- a/aya-log/aya-log-common/src/lib.rs +++ b/aya-log/aya-log-common/src/lib.rs @@ -2,7 +2,7 @@ pub const LOG_BUF_CAPACITY: usize = 1024; -pub const LOG_FIELDS: usize = 6; +pub const LOG_FIELDS: usize = 7; #[repr(usize)] #[derive(Copy, Clone, Eq, PartialEq, Debug, Hash)] @@ -37,8 +37,37 @@ pub enum RecordField { Module, File, Line, + NumArgs, Log, } +#[repr(usize)] +#[derive(Copy, Clone, Debug)] +pub enum ArgType { + I8, + I16, + I32, + I64, + I128, + Isize, + + U8, + U16, + U32, + U64, + U128, + Usize, + + F32, + F64, + + Str, +} + #[cfg(feature = "userspace")] -unsafe impl aya::Pod for RecordField {} +mod userspace { + use super::*; + + unsafe impl aya::Pod for RecordField {} + unsafe impl aya::Pod for ArgType {} +} diff --git a/aya-log/aya-log-ebpf/src/lib.rs b/aya-log/aya-log-ebpf/src/lib.rs deleted file mode 100644 index 5d2dd3b3..00000000 --- a/aya-log/aya-log-ebpf/src/lib.rs +++ /dev/null @@ -1,130 +0,0 @@ -#![no_std] - -pub extern crate ufmt; - -mod macros; - -use core::{cmp, mem, ptr}; - -use aya_bpf::{ - macros::map, - maps::{PerCpuArray, PerfEventByteArray}, -}; -pub use aya_log_common::Level; -use aya_log_common::RecordField; -pub use aya_log_common::LOG_BUF_CAPACITY; - -#[doc(hidden)] -#[repr(C)] -pub struct LogBuf { - pub buf: [u8; LOG_BUF_CAPACITY], -} - -#[doc(hidden)] -#[map] -pub static mut AYA_LOG_BUF: PerCpuArray = PerCpuArray::with_max_entries(1, 0); - -#[doc(hidden)] -#[map] -pub static mut AYA_LOGS: PerfEventByteArray = PerfEventByteArray::new(0); - -#[doc(hidden)] -pub struct LogBufWriter<'a> { - pos: usize, - data: &'a mut [u8], -} - -impl<'a> LogBufWriter<'a> { - pub fn new(data: &mut [u8]) -> LogBufWriter<'_> { - LogBufWriter { - pos: mem::size_of::() + mem::size_of::(), - data, - } - } - - pub fn finish(self) -> usize { - let mut buf = self.data; - unsafe { ptr::write_unaligned(buf.as_mut_ptr() as *mut _, RecordField::Log) }; - buf = &mut buf[mem::size_of::()..]; - - let len = self.pos - mem::size_of::() - mem::size_of::(); - unsafe { ptr::write_unaligned(buf.as_mut_ptr() as *mut _, len) }; - - self.pos - } -} - -impl<'a> ufmt::uWrite for LogBufWriter<'a> { - type Error = (); - - fn write_str(&mut self, s: &str) -> Result<(), Self::Error> { - let bytes = s.as_bytes(); - let len = bytes.len(); - - // this is to make sure the verifier knows about the upper bound - if len > LOG_BUF_CAPACITY { - return Err(()); - } - - let available = self.data.len() - self.pos; - if available < len { - return Err(()); - } - - self.data[self.pos..self.pos + len].copy_from_slice(&bytes[..len]); - self.pos += len; - Ok(()) - } -} - -struct TagLenValue<'a> { - tag: RecordField, - value: &'a [u8], -} - -impl<'a> TagLenValue<'a> { - #[inline(always)] - pub(crate) fn new(tag: RecordField, value: &'a [u8]) -> TagLenValue<'a> { - TagLenValue { tag, value } - } - - pub(crate) fn try_write(&self, mut buf: &mut [u8]) -> Result { - let size = mem::size_of::() + mem::size_of::() + self.value.len(); - if buf.len() < size { - return Err(()); - } - - unsafe { ptr::write_unaligned(buf.as_mut_ptr() as *mut _, self.tag) }; - buf = &mut buf[mem::size_of::()..]; - - unsafe { ptr::write_unaligned(buf.as_mut_ptr() as *mut _, self.value.len()) }; - buf = &mut buf[mem::size_of::()..]; - - let len = cmp::min(buf.len(), self.value.len()); - buf[..len].copy_from_slice(&self.value[..len]); - Ok(size) - } -} - -#[doc(hidden)] -pub fn write_record_header( - buf: &mut [u8], - target: &str, - level: Level, - module: &str, - file: &str, - line: u32, -) -> Result { - let mut size = 0; - for attr in [ - TagLenValue::new(RecordField::Target, target.as_bytes()), - TagLenValue::new(RecordField::Level, &(level as usize).to_ne_bytes()), - TagLenValue::new(RecordField::Module, module.as_bytes()), - TagLenValue::new(RecordField::File, file.as_bytes()), - TagLenValue::new(RecordField::Line, &line.to_ne_bytes()), - ] { - size += attr.try_write(&mut buf[size..])?; - } - - Ok(size) -} diff --git a/aya-log/aya-log-ebpf/src/macros.rs b/aya-log/aya-log-ebpf/src/macros.rs deleted file mode 100644 index abc8a586..00000000 --- a/aya-log/aya-log-ebpf/src/macros.rs +++ /dev/null @@ -1,205 +0,0 @@ -/// Logs a message at the error level. -/// -/// # Examples -/// -/// ```no_run -/// # let ctx = (); -/// # let err_code = -1; -/// use aya_log_ebpf::error; -/// -/// error!(&ctx, "Error redirecting packet: {}", err_code); -/// error!(&ctx, target: "ingress", "Error redirecting packet: {}", err_code); -/// ``` -#[macro_export] -macro_rules! error { - ($ctx:expr, target: $target:expr, $($arg:tt)+) => ( - $crate::log!($ctx, target: $target, $crate::Level::Error, $($arg)+) - ); - ($ctx:expr, $($arg:tt)+) => ( - $crate::log!($ctx, $crate::Level::Error, $($arg)+) - ) -} - -/// Logs a message at the warn level. -/// -/// # Examples -/// -/// ``` -/// use aya_log_ebpf::warn; -/// -/// # fn main() { -/// let warn_description = "Invalid Input"; -/// -/// warn!("Warning! {}!", warn_description); -/// warn!(target: "input_events", "App received warning: {}", warn_description); -/// # } -/// ``` -#[macro_export] -macro_rules! warn { - ($ctx:expr, target: $target:expr, $($arg:tt)+) => ( - $crate::log!($ctx, target: $target, $crate::Level::Warn, $($arg)+) - ); - ($ctx:expr, $($arg:tt)+) => ( - $crate::log!($ctx, $crate::Level::Warn, $($arg)+) - ) -} - -/// Logs a message at the info level. -/// -/// # Examples -/// -/// ```edition2018 -/// use log::info; -/// -/// # fn main() { -/// # struct Connection { port: u32, speed: u32 } -/// let conn_info = Connection { port: 40, speed: 3 }; -/// -/// info!("Connected to port {} at {} Mb/s", conn_info.port, conn_info.speed); -/// info!(target: "connection_events", "Successfull connection, port: {}, speed: {}", -/// conn_info.port, conn_info.speed); -/// # } -/// ``` -#[macro_export] -macro_rules! info { - ($ctx:expr, target: $target:expr, $($arg:tt)+) => ( - $crate::log!($ctx, target: $target, $crate::Level::Info, $($arg)+) - ); - ($ctx:expr, $($arg:tt)+) => ( - $crate::log!($ctx, $crate::Level::Info, $($arg)+) - ) -} - -/// Logs a message at the debug level. -/// -/// # Examples -/// -/// ```edition2018 -/// use log::debug; -/// -/// # fn main() { -/// # struct Position { x: i64, y: i64 } -/// let pos = Position { x: 3.234, y: -1223 }; -/// -/// debug!("New position: x: {}, y: {}", pos.x, pos.y); -/// debug!(target: "app_events", "New position: x: {}, y: {}", pos.x, pos.y); -/// # } -/// ``` -#[macro_export] -macro_rules! debug { - ($ctx:expr, target: $target:expr, $($arg:tt)+) => ( - $crate::log!($ctx, target: $target, $crate::Level::Debug, $($arg)+) - ); - ($ctx:expr, $($arg:tt)+) => ( - $crate::log!($ctx, $crate::Level::Debug, $($arg)+) - ) -} - -/// Logs a message at the trace level. -/// -/// # Examples -/// -/// ```edition2018 -/// use log::trace; -/// -/// # fn main() { -/// # struct Position { x: i64, y: i64 } -/// let pos = Position { x: 3234, y: -1223 }; -/// -/// trace!("Position is: x: {}, y: {}", pos.x, pos.y); -/// trace!(target: "app_events", "x is {} and y is {}", -/// if pos.x >= 0 { "positive" } else { "negative" }, -/// if pos.y >= 0 { "positive" } else { "negative" }); -/// # } -/// ``` -#[macro_export] -macro_rules! trace { - ($ctx:expr, target: $target:expr, $($arg:tt)+) => ( - $crate::log!($ctx, target: $target, $crate::Level::Trace, $($arg)+) - ); - ($ctx:expr, $($arg:tt)+) => ( - $crate::log!($ctx, $crate::Level::Trace, $($arg)+) - ) -} - -// /// Determines if a message logged at the specified level in that module will -// /// be logged. -// /// -// /// This can be used to avoid expensive computation of log message arguments if -// /// the message would be ignored anyway. -// /// -// /// # Examples -// /// -// /// ```edition2018 -// /// use log::Level::Debug; -// /// use log::{debug, log_enabled}; -// /// -// /// # fn foo() { -// /// if log_enabled!(Debug) { -// /// let data = expensive_call(); -// /// debug!("expensive debug data: {} {}", data.x, data.y); -// /// } -// /// if log_enabled!(target: "Global", Debug) { -// /// let data = expensive_call(); -// /// debug!(target: "Global", "expensive debug data: {} {}", data.x, data.y); -// /// } -// /// # } -// /// # struct Data { x: u32, y: u32 } -// /// # fn expensive_call() -> Data { Data { x: 0, y: 0 } } -// /// # fn main() {} -// /// ``` -// macro_rules! log_enabled { -// (target: $target:expr, $lvl:expr) => {{ -// let lvl = $lvl; -// lvl <= $crate::STATIC_MAX_LEVEL -// }}; -// ($lvl:expr) => { -// log_enabled!(target: __log_module_path!(), $lvl) -// }; -// } - -/// Log a message at the given level. -/// -/// This macro will generically log with the specified `Level` and `format!` -/// based argument list. -/// -/// # Examples -/// -/// ```edition2018 -/// use log::{log, Level}; -/// -/// # fn main() { -/// let data = (42, "Forty-two"); -/// let private_data = "private"; -/// -/// log!(Level::Error, "Received errors: {}, {}", data.0, data.1); -/// log!(target: "app_events", Level::Warn, "App warning: {}, {}, {}", -/// data.0, data.1, private_data); -/// # } -/// ``` -#[macro_export] -macro_rules! log { - ($ctx:expr, target: $target:expr, $lvl:expr, $($arg:tt)+) => ({ - if let Some(buf) = unsafe { $crate::AYA_LOG_BUF.get_mut(0) } { - if let Ok(header_len) = $crate::write_record_header(&mut buf.buf, module_path!(), $lvl, module_path!(), file!(), line!()) { - if let Ok(message_len) = $crate::write_record_message!(&mut buf.buf[header_len..], $($arg)+) { - let record_len = header_len + message_len; - if record_len <= $crate::LOG_BUF_CAPACITY { - let _ = unsafe { $crate::AYA_LOGS.output($ctx, &buf.buf[..record_len], 0) }; - } - }; - } - } - }); - ($ctx:expr, $lvl:expr, $($arg:tt)+) => ($crate::log!($ctx, target: __log_module_path!(), $lvl, $($arg)+)) -} - -#[doc(hidden)] -#[macro_export] -macro_rules! write_record_message { - ($buf:expr, $($arg:tt)+) => {{ - use aya_log_ebpf::ufmt; - let mut writer = $crate::LogBufWriter::new($buf); - aya_log_ebpf::ufmt::uwrite!(writer, $($arg)+).map(|_| writer.finish()) - }} -} diff --git a/aya-log/aya-log/Cargo.toml b/aya-log/aya-log/Cargo.toml index 47056232..8bdad41b 100644 --- a/aya-log/aya-log/Cargo.toml +++ b/aya-log/aya-log/Cargo.toml @@ -13,6 +13,7 @@ edition = "2018" [dependencies] aya = { version = "0.10.5", features=["async_tokio"] } aya-log-common = { version = "0.1", path = "../aya-log-common", features=["userspace"] } +dyn-fmt = "0.3.0" thiserror = "1" log = "0.4" bytes = "1.1" diff --git a/aya-log/aya-log/src/lib.rs b/aya-log/aya-log/src/lib.rs index 559a2692..dfee4db8 100644 --- a/aya-log/aya-log/src/lib.rs +++ b/aya-log/aya-log/src/lib.rs @@ -59,11 +59,12 @@ //! [Log]: https://docs.rs/log/0.4.14/log/trait.Log.html //! [log]: https://docs.rs/log //! -use std::{convert::TryInto, io, mem, ptr, sync::Arc}; +use std::{convert::TryInto, io, mem, ptr, str, sync::Arc}; -use aya_log_common::{RecordField, LOG_BUF_CAPACITY, LOG_FIELDS}; +use aya_log_common::{ArgType, RecordField, LOG_BUF_CAPACITY, LOG_FIELDS}; use bytes::BytesMut; -use log::{Level, Log, Record}; +use dyn_fmt::AsStrFormatExt; +use log::{error, Level, Log, Record}; use thiserror::Error; use aya::{ @@ -157,6 +158,7 @@ fn log_buf(mut buf: &[u8], logger: &dyn Log) -> Result<(), ()> { let mut file = None; let mut line = None; let mut log = None; + let mut num_args = None; for _ in 0..LOG_FIELDS { let (attr, rest) = unsafe { TagLenValue::<'_, RecordField>::try_read(buf)? }; @@ -177,6 +179,9 @@ fn log_buf(mut buf: &[u8], logger: &dyn Log) -> Result<(), ()> { RecordField::Line => { line = Some(u32::from_ne_bytes(attr.value.try_into().map_err(|_| ())?)); } + RecordField::NumArgs => { + num_args = Some(usize::from_ne_bytes(attr.value.try_into().map_err(|_| ())?)); + } RecordField::Log => { log = Some(std::str::from_utf8(attr.value).map_err(|_| ())?); } @@ -185,9 +190,103 @@ fn log_buf(mut buf: &[u8], logger: &dyn Log) -> Result<(), ()> { buf = rest; } + let log_msg = log.ok_or(())?; + let full_log_msg = match num_args { + Some(n) => { + let mut args: Vec = Vec::new(); + for _ in 0..n { + let (attr, rest) = unsafe { TagLenValue::<'_, ArgType>::try_read(buf)? }; + + match attr.tag { + ArgType::I8 => { + args.push( + i8::from_ne_bytes(attr.value.try_into().map_err(|_| ())?).to_string(), + ); + } + ArgType::I16 => { + args.push( + i16::from_ne_bytes(attr.value.try_into().map_err(|_| ())?).to_string(), + ); + } + ArgType::I32 => { + args.push( + i32::from_ne_bytes(attr.value.try_into().map_err(|_| ())?).to_string(), + ); + } + ArgType::I64 => { + args.push( + i64::from_ne_bytes(attr.value.try_into().map_err(|_| ())?).to_string(), + ); + } + ArgType::I128 => { + args.push( + i128::from_ne_bytes(attr.value.try_into().map_err(|_| ())?).to_string(), + ); + } + ArgType::Isize => { + args.push( + isize::from_ne_bytes(attr.value.try_into().map_err(|_| ())?) + .to_string(), + ); + } + ArgType::U8 => { + args.push( + u8::from_ne_bytes(attr.value.try_into().map_err(|_| ())?).to_string(), + ); + } + ArgType::U16 => { + args.push( + u16::from_ne_bytes(attr.value.try_into().map_err(|_| ())?).to_string(), + ); + } + ArgType::U32 => { + args.push( + u32::from_ne_bytes(attr.value.try_into().map_err(|_| ())?).to_string(), + ); + } + ArgType::U64 => { + args.push( + u64::from_ne_bytes(attr.value.try_into().map_err(|_| ())?).to_string(), + ); + } + ArgType::U128 => { + args.push( + u128::from_ne_bytes(attr.value.try_into().map_err(|_| ())?).to_string(), + ); + } + ArgType::Usize => { + args.push( + usize::from_ne_bytes(attr.value.try_into().map_err(|_| ())?) + .to_string(), + ); + } + ArgType::F32 => { + args.push( + f32::from_ne_bytes(attr.value.try_into().map_err(|_| ())?).to_string(), + ); + } + ArgType::F64 => { + args.push( + f64::from_ne_bytes(attr.value.try_into().map_err(|_| ())?).to_string(), + ); + } + ArgType::Str => match str::from_utf8(attr.value) { + Ok(v) => args.push(v.to_string()), + Err(e) => error!("received invalid utf8 string: {}", e), + }, + } + + buf = rest; + } + + log_msg.format(&args) + } + None => log_msg.to_string(), + }; + logger.log( &Record::builder() - .args(format_args!("{}", log.ok_or(())?)) + .args(format_args!("{}", full_log_msg)) .target(target.ok_or(())?) .level(level) .module_path(module) diff --git a/aya-log/aya-log-ebpf/.cargo/config.toml b/aya-log/ebpf/.cargo/config.toml similarity index 100% rename from aya-log/aya-log-ebpf/.cargo/config.toml rename to aya-log/ebpf/.cargo/config.toml diff --git a/aya-log/ebpf/Cargo.toml b/aya-log/ebpf/Cargo.toml new file mode 100644 index 00000000..4a122eef --- /dev/null +++ b/aya-log/ebpf/Cargo.toml @@ -0,0 +1,2 @@ +[workspace] +members = ["aya-log-ebpf", "aya-log-ebpf-macros"] diff --git a/aya-log/ebpf/aya-log-ebpf-macros/Cargo.toml b/aya-log/ebpf/aya-log-ebpf-macros/Cargo.toml new file mode 100644 index 00000000..8e0262b0 --- /dev/null +++ b/aya-log/ebpf/aya-log-ebpf-macros/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "aya-log-ebpf-macros" +version = "0.1.0" +edition = "2018" + +[dependencies] +proc-macro2 = "1.0" +quote = "1.0" +syn = "1.0" + +[lib] +proc-macro = true \ No newline at end of file diff --git a/aya-log/ebpf/aya-log-ebpf-macros/src/expand.rs b/aya-log/ebpf/aya-log-ebpf-macros/src/expand.rs new file mode 100644 index 00000000..5ff93aac --- /dev/null +++ b/aya-log/ebpf/aya-log-ebpf-macros/src/expand.rs @@ -0,0 +1,185 @@ +use proc_macro2::TokenStream; +use quote::quote; +use syn::{ + parse::{Parse, ParseStream}, + punctuated::Punctuated, + Error, Expr, LitStr, Result, Token, +}; + +pub(crate) struct LogArgs { + pub(crate) ctx: Expr, + pub(crate) target: Option, + pub(crate) level: Option, + pub(crate) format_string: LitStr, + pub(crate) formatting_args: Option>, +} + +mod kw { + syn::custom_keyword!(target); +} + +impl Parse for LogArgs { + fn parse(input: ParseStream) -> Result { + let ctx: Expr = input.parse()?; + input.parse::()?; + + // Parse `target: &str`, which is an optional argument. + let target: Option = if input.peek(kw::target) { + input.parse::()?; + input.parse::()?; + let t: Expr = input.parse()?; + input.parse::()?; + Some(t) + } else { + None + }; + + // Check whether the next token is `format_string: &str` (which i + // always provided) or `level` (which is an optional expression). + // If `level` is provided, it comes before `format_string`. + let (level, format_string): (Option, LitStr) = if input.peek(LitStr) { + // Only `format_string` is provided. + (None, input.parse()?) + } else { + // Both `level` and `format_string` are provided. + let level: Expr = input.parse()?; + input.parse::()?; + let format_string: LitStr = input.parse()?; + (Some(level), format_string) + }; + + // Parse variadic arguments. + let formatting_args: Option> = if input.is_empty() { + None + } else { + input.parse::()?; + Some(Punctuated::parse_terminated(input)?) + }; + + Ok(Self { + ctx, + target, + level, + format_string, + formatting_args, + }) + } +} + +pub(crate) fn log(args: LogArgs, level: Option) -> Result { + let ctx = args.ctx; + let target = match args.target { + Some(t) => quote! { #t }, + None => quote! { module_path!() }, + }; + let lvl: TokenStream = if let Some(l) = level { + l + } else if let Some(l) = args.level { + quote! { #l } + } else { + return Err(Error::new( + args.format_string.span(), + "missing `level` argument: try passing an `aya_log_ebpf::Level` value", + )); + }; + let format_string = args.format_string; + + let (num_args, write_args) = match args.formatting_args { + Some(formatting_args) => { + let formatting_exprs = formatting_args.iter(); + let num_args = formatting_exprs.len(); + + let write_args = quote! {{ + use ::aya_log_ebpf::WriteToBuf; + Ok::<_, ()>(record_len) #( .and_then(|record_len| { + { #formatting_exprs }.write(&mut buf.buf[record_len..]).map(|len| record_len + len) + }) )* + }}; + + (num_args, write_args) + } + None => (0, quote! {}), + }; + + // The way of writing to the perf buffer is different depending on whether + // we have variadic arguments or not. + let write_to_perf_buffer = if num_args > 0 { + // Writing with variadic arguments. + quote! { + if let Ok(record_len) = #write_args { + unsafe { ::aya_log_ebpf::AYA_LOGS.output( + #ctx, + &buf.buf[..record_len], 0 + )} + } + } + } else { + // Writing with no variadic arguments. + quote! { + unsafe { ::aya_log_ebpf::AYA_LOGS.output( + #ctx, + &buf.buf[..record_len], 0 + )} + } + }; + + Ok(quote! { + { + if let Some(buf) = unsafe { ::aya_log_ebpf::AYA_LOG_BUF.get_mut(0) } { + if let Ok(header_len) = ::aya_log_ebpf::write_record_header( + &mut buf.buf, + #target, + #lvl, + module_path!(), + file!(), + line!(), + #num_args, + ) { + if let Ok(message_len) = ::aya_log_ebpf::write_record_message( + &mut buf.buf[header_len..], + #format_string, + ) { + let record_len = header_len + message_len; + + #write_to_perf_buffer + } + } + } + } + }) +} + +pub(crate) fn error(args: LogArgs) -> Result { + log( + args, + Some(quote! { ::aya_log_ebpf::macro_support::Level::Error }), + ) +} + +pub(crate) fn warn(args: LogArgs) -> Result { + log( + args, + Some(quote! { ::aya_log_ebpf::macro_support::Level::Warn }), + ) +} + +pub(crate) fn info(args: LogArgs) -> Result { + log( + args, + Some(quote! { ::aya_log_ebpf::macro_support::Level::Info }), + ) +} + +pub(crate) fn debug(args: LogArgs) -> Result { + log( + args, + Some(quote! { ::aya_log_ebpf::macro_support::Level::Debug }), + ) +} + +pub(crate) fn trace(args: LogArgs) -> Result { + log( + args, + Some(quote! { ::aya_log_ebpf::macro_support::Level::Trace }), + ) +} diff --git a/aya-log/ebpf/aya-log-ebpf-macros/src/lib.rs b/aya-log/ebpf/aya-log-ebpf-macros/src/lib.rs new file mode 100644 index 00000000..264d93fb --- /dev/null +++ b/aya-log/ebpf/aya-log-ebpf-macros/src/lib.rs @@ -0,0 +1,52 @@ +use proc_macro::TokenStream; +use syn::parse_macro_input; + +mod expand; + +#[proc_macro] +pub fn log(args: TokenStream) -> TokenStream { + let args = parse_macro_input!(args as expand::LogArgs); + expand::log(args, None) + .unwrap_or_else(|err| err.to_compile_error()) + .into() +} + +#[proc_macro] +pub fn error(args: TokenStream) -> TokenStream { + let args = parse_macro_input!(args as expand::LogArgs); + expand::error(args) + .unwrap_or_else(|err| err.to_compile_error()) + .into() +} + +#[proc_macro] +pub fn warn(args: TokenStream) -> TokenStream { + let args = parse_macro_input!(args as expand::LogArgs); + expand::warn(args) + .unwrap_or_else(|err| err.to_compile_error()) + .into() +} + +#[proc_macro] +pub fn info(args: TokenStream) -> TokenStream { + let args = parse_macro_input!(args as expand::LogArgs); + expand::info(args) + .unwrap_or_else(|err| err.to_compile_error()) + .into() +} + +#[proc_macro] +pub fn debug(args: TokenStream) -> TokenStream { + let args = parse_macro_input!(args as expand::LogArgs); + expand::debug(args) + .unwrap_or_else(|err| err.to_compile_error()) + .into() +} + +#[proc_macro] +pub fn trace(args: TokenStream) -> TokenStream { + let args = parse_macro_input!(args as expand::LogArgs); + expand::trace(args) + .unwrap_or_else(|err| err.to_compile_error()) + .into() +} diff --git a/aya-log/aya-log-ebpf/Cargo.toml b/aya-log/ebpf/aya-log-ebpf/Cargo.toml similarity index 66% rename from aya-log/aya-log-ebpf/Cargo.toml rename to aya-log/ebpf/aya-log-ebpf/Cargo.toml index 5e00a175..9ee4e548 100644 --- a/aya-log/aya-log-ebpf/Cargo.toml +++ b/aya-log/ebpf/aya-log-ebpf/Cargo.toml @@ -5,8 +5,8 @@ edition = "2018" [dependencies] aya-bpf = { git = "https://github.com/aya-rs/aya", branch = "main" } -aya-log-common = { path = "../aya-log-common" } -ufmt = { version = "0.1", package = "aya-ufmt" } +aya-log-common = { path = "../../aya-log-common" } +aya-log-ebpf-macros = { path = "../aya-log-ebpf-macros" } [lib] path = "src/lib.rs" @@ -18,7 +18,4 @@ opt-level = 2 overflow-checks = false [profile.release] -panic = "abort" - -[workspace] -members = [] \ No newline at end of file +panic = "abort" \ No newline at end of file diff --git a/aya-log/ebpf/aya-log-ebpf/src/lib.rs b/aya-log/ebpf/aya-log-ebpf/src/lib.rs new file mode 100644 index 00000000..633fa52a --- /dev/null +++ b/aya-log/ebpf/aya-log-ebpf/src/lib.rs @@ -0,0 +1,133 @@ +#![no_std] + +use core::{cmp, mem, ptr}; + +use aya_bpf::{ + macros::map, + maps::{PerCpuArray, PerfEventByteArray}, +}; +use aya_log_common::{ArgType, RecordField}; +pub use aya_log_common::{Level, LOG_BUF_CAPACITY}; +pub use aya_log_ebpf_macros::{debug, error, info, log, trace, warn}; + +#[doc(hidden)] +#[repr(C)] +pub struct LogBuf { + pub buf: [u8; LOG_BUF_CAPACITY], +} + +#[doc(hidden)] +#[map] +pub static mut AYA_LOG_BUF: PerCpuArray = PerCpuArray::with_max_entries(1, 0); + +#[doc(hidden)] +#[map] +pub static mut AYA_LOGS: PerfEventByteArray = PerfEventByteArray::new(0); + +struct TagLenValue<'a, T> { + tag: T, + value: &'a [u8], +} + +impl<'a, T> TagLenValue<'a, T> +where + T: Copy, +{ + #[inline(always)] + pub(crate) fn new(tag: T, value: &'a [u8]) -> TagLenValue<'a, T> { + TagLenValue { tag, value } + } + + pub(crate) fn write(&self, mut buf: &mut [u8]) -> Result { + let size = mem::size_of::() + mem::size_of::() + self.value.len(); + if buf.len() < size { + return Err(()); + } + + unsafe { ptr::write_unaligned(buf.as_mut_ptr() as *mut _, self.tag) }; + buf = &mut buf[mem::size_of::()..]; + + unsafe { ptr::write_unaligned(buf.as_mut_ptr() as *mut _, self.value.len()) }; + buf = &mut buf[mem::size_of::()..]; + + let len = cmp::min(buf.len(), self.value.len()); + buf[..len].copy_from_slice(&self.value[..len]); + Ok(size) + } +} + +pub trait WriteToBuf { + #[allow(clippy::result_unit_err)] + fn write(&self, buf: &mut [u8]) -> Result; +} + +macro_rules! impl_write_to_buf { + ($type:ident, $arg_type:expr) => { + impl WriteToBuf for $type { + fn write(&self, buf: &mut [u8]) -> Result { + TagLenValue::::new($arg_type, &self.to_ne_bytes()).write(buf) + } + } + }; +} + +impl_write_to_buf!(i8, ArgType::I8); +impl_write_to_buf!(i16, ArgType::I16); +impl_write_to_buf!(i32, ArgType::I32); +impl_write_to_buf!(i64, ArgType::I64); +impl_write_to_buf!(i128, ArgType::I128); +impl_write_to_buf!(isize, ArgType::Isize); + +impl_write_to_buf!(u8, ArgType::U8); +impl_write_to_buf!(u16, ArgType::U16); +impl_write_to_buf!(u32, ArgType::U32); +impl_write_to_buf!(u64, ArgType::U64); +impl_write_to_buf!(u128, ArgType::U128); +impl_write_to_buf!(usize, ArgType::Usize); + +impl_write_to_buf!(f32, ArgType::F32); +impl_write_to_buf!(f64, ArgType::F64); + +impl WriteToBuf for str { + fn write(&self, buf: &mut [u8]) -> Result { + TagLenValue::::new(ArgType::Str, self.as_bytes()).write(buf) + } +} + +#[allow(clippy::result_unit_err)] +#[doc(hidden)] +pub fn write_record_header( + buf: &mut [u8], + target: &str, + level: Level, + module: &str, + file: &str, + line: u32, + num_args: usize, +) -> Result { + let mut size = 0; + for attr in [ + TagLenValue::::new(RecordField::Target, target.as_bytes()), + TagLenValue::::new(RecordField::Level, &(level as usize).to_ne_bytes()), + TagLenValue::::new(RecordField::Module, module.as_bytes()), + TagLenValue::::new(RecordField::File, file.as_bytes()), + TagLenValue::::new(RecordField::Line, &line.to_ne_bytes()), + TagLenValue::::new(RecordField::NumArgs, &num_args.to_ne_bytes()), + ] { + size += attr.write(&mut buf[size..])?; + } + + Ok(size) +} + +#[allow(clippy::result_unit_err)] +#[doc(hidden)] +pub fn write_record_message(buf: &mut [u8], msg: &str) -> Result { + TagLenValue::::new(RecordField::Log, msg.as_bytes()).write(buf) +} + +#[doc(hidden)] +pub mod macro_support { + pub use aya_log_common::{Level, LOG_BUF_CAPACITY}; + pub use aya_log_ebpf_macros::log; +} diff --git a/aya-log/ebpf/rust-toolchain.toml b/aya-log/ebpf/rust-toolchain.toml new file mode 100644 index 00000000..5d56faf9 --- /dev/null +++ b/aya-log/ebpf/rust-toolchain.toml @@ -0,0 +1,2 @@ +[toolchain] +channel = "nightly" From 81befa06610b9e771523bceee4871a704851b1f0 Mon Sep 17 00:00:00 2001 From: Michal Rostecki Date: Fri, 25 Mar 2022 13:44:35 +0100 Subject: [PATCH 19/41] Update aya to 0.10.7 Signed-off-by: Michal Rostecki --- aya-log/aya-log-common/Cargo.toml | 2 +- aya-log/aya-log/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/aya-log/aya-log-common/Cargo.toml b/aya-log/aya-log-common/Cargo.toml index 9d0da97a..86c4c318 100644 --- a/aya-log/aya-log-common/Cargo.toml +++ b/aya-log/aya-log-common/Cargo.toml @@ -14,7 +14,7 @@ default = [] userspace = [ "aya" ] [dependencies] -aya = { version = "0.10.5", optional=true } +aya = { version = "0.10.7", optional=true } [lib] path = "src/lib.rs" diff --git a/aya-log/aya-log/Cargo.toml b/aya-log/aya-log/Cargo.toml index 8bdad41b..98e210fc 100644 --- a/aya-log/aya-log/Cargo.toml +++ b/aya-log/aya-log/Cargo.toml @@ -11,7 +11,7 @@ documentation = "https://docs.rs/aya-log" edition = "2018" [dependencies] -aya = { version = "0.10.5", features=["async_tokio"] } +aya = { version = "0.10.7", features=["async_tokio"] } aya-log-common = { version = "0.1", path = "../aya-log-common", features=["userspace"] } dyn-fmt = "0.3.0" thiserror = "1" From bdb2750e66f922ebfbcba7250add38e2c932c293 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alessandro=C2=A0Decina?= Date: Sat, 16 Apr 2022 10:10:56 +0000 Subject: [PATCH 20/41] ebpf: inline write_record_header This seems to help the verifier keep track of where we're writing into LOG_BUF --- aya-log/ebpf/aya-log-ebpf/src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/aya-log/ebpf/aya-log-ebpf/src/lib.rs b/aya-log/ebpf/aya-log-ebpf/src/lib.rs index 633fa52a..33210970 100644 --- a/aya-log/ebpf/aya-log-ebpf/src/lib.rs +++ b/aya-log/ebpf/aya-log-ebpf/src/lib.rs @@ -96,6 +96,7 @@ impl WriteToBuf for str { #[allow(clippy::result_unit_err)] #[doc(hidden)] +#[inline(always)] pub fn write_record_header( buf: &mut [u8], target: &str, From c4d89fa13cb4e96a62ccd5cae7cf1834c3c582f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alessandro=C2=A0Decina?= Date: Sat, 23 Apr 2022 09:02:35 +0000 Subject: [PATCH 21/41] aya-log: use stricter version for the aya-log-common dep --- aya-log/aya-log/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aya-log/aya-log/Cargo.toml b/aya-log/aya-log/Cargo.toml index 98e210fc..4087d64a 100644 --- a/aya-log/aya-log/Cargo.toml +++ b/aya-log/aya-log/Cargo.toml @@ -12,7 +12,7 @@ edition = "2018" [dependencies] aya = { version = "0.10.7", features=["async_tokio"] } -aya-log-common = { version = "0.1", path = "../aya-log-common", features=["userspace"] } +aya-log-common = { version = "0.1.1", path = "../aya-log-common", features=["userspace"] } dyn-fmt = "0.3.0" thiserror = "1" log = "0.4" From d1a0ce51ee4e67cf9b03b695940f356ee950f8c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alessandro=C2=A0Decina?= Date: Sat, 23 Apr 2022 09:07:27 +0000 Subject: [PATCH 22/41] xtask: do not release --- aya-log/xtask/Cargo.toml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/aya-log/xtask/Cargo.toml b/aya-log/xtask/Cargo.toml index 3b50084b..32cde2ab 100644 --- a/aya-log/xtask/Cargo.toml +++ b/aya-log/xtask/Cargo.toml @@ -1,10 +1,12 @@ [package] name = "xtask" +publish = false 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" \ No newline at end of file +anyhow = "1" + +[package.metadata.release] +release = false From a8d133f6b0919bb7d8e821f1309ee264d8b03a71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alessandro=C2=A0Decina?= Date: Sat, 23 Apr 2022 09:09:46 +0000 Subject: [PATCH 23/41] Add cargo-release config --- aya-log/release.toml | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 aya-log/release.toml diff --git a/aya-log/release.toml b/aya-log/release.toml new file mode 100644 index 00000000..b2239c1f --- /dev/null +++ b/aya-log/release.toml @@ -0,0 +1,7 @@ +pre-release-commit-message = "aya-log, aya-log-common: release version {{version}}" +post-release-commit-message = "aya-log, aya-log-common: start next development iteration {{next_version}}" +consolidate-pushes = true +consolidate-commits = true +shared-version = true +dev-version = true +dev-version-ext = "dev.0" From 8bc1bbb3abe588e89161e67ad013c34f1ec3ab6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alessandro=C2=A0Decina?= Date: Sat, 23 Apr 2022 09:17:42 +0000 Subject: [PATCH 24/41] aya-log, aya-log-common: release version 0.1.9 --- aya-log/aya-log-common/Cargo.toml | 2 +- aya-log/aya-log/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/aya-log/aya-log-common/Cargo.toml b/aya-log/aya-log-common/Cargo.toml index 86c4c318..705c1859 100644 --- a/aya-log/aya-log-common/Cargo.toml +++ b/aya-log/aya-log-common/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "aya-log-common" -version = "0.1.1" +version = "0.1.9" description = "A logging library for eBPF programs." keywords = ["ebpf", "bpf", "log", "logging"] license = "MIT OR Apache-2.0" diff --git a/aya-log/aya-log/Cargo.toml b/aya-log/aya-log/Cargo.toml index 4087d64a..c1fd5a80 100644 --- a/aya-log/aya-log/Cargo.toml +++ b/aya-log/aya-log/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "aya-log" -version = "0.1.1" +version = "0.1.9" description = "A logging library for eBPF programs." keywords = ["ebpf", "bpf", "log", "logging"] license = "MIT OR Apache-2.0" From bd9a5c8fdff9c20952137908388b1d833ab60fcc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alessandro=C2=A0Decina?= Date: Sat, 23 Apr 2022 09:18:04 +0000 Subject: [PATCH 25/41] aya-log, aya-log-common: start next development iteration 0.1.10-dev.0 --- aya-log/aya-log-common/Cargo.toml | 2 +- aya-log/aya-log/Cargo.toml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/aya-log/aya-log-common/Cargo.toml b/aya-log/aya-log-common/Cargo.toml index 705c1859..7430032b 100644 --- a/aya-log/aya-log-common/Cargo.toml +++ b/aya-log/aya-log-common/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "aya-log-common" -version = "0.1.9" +version = "0.1.10-dev.0" description = "A logging library for eBPF programs." keywords = ["ebpf", "bpf", "log", "logging"] license = "MIT OR Apache-2.0" diff --git a/aya-log/aya-log/Cargo.toml b/aya-log/aya-log/Cargo.toml index c1fd5a80..f5165f56 100644 --- a/aya-log/aya-log/Cargo.toml +++ b/aya-log/aya-log/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "aya-log" -version = "0.1.9" +version = "0.1.10-dev.0" description = "A logging library for eBPF programs." keywords = ["ebpf", "bpf", "log", "logging"] license = "MIT OR Apache-2.0" @@ -12,7 +12,7 @@ edition = "2018" [dependencies] aya = { version = "0.10.7", features=["async_tokio"] } -aya-log-common = { version = "0.1.1", path = "../aya-log-common", features=["userspace"] } +aya-log-common = { version = "0.1.10-dev.0", path = "../aya-log-common", features=["userspace"] } dyn-fmt = "0.3.0" thiserror = "1" log = "0.4" From 70b4e681301eb23ca776cd703e11f19cc879ac69 Mon Sep 17 00:00:00 2001 From: Michal Rostecki Date: Thu, 26 May 2022 17:15:41 +0200 Subject: [PATCH 26/41] common: Bump the buffer size 1024 is too small for many kernel string limits (i.e. PATH_MAX, which is 4096). Signed-off-by: Michal Rostecki --- aya-log/aya-log-common/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aya-log/aya-log-common/src/lib.rs b/aya-log/aya-log-common/src/lib.rs index b30fafc7..5a0bd419 100644 --- a/aya-log/aya-log-common/src/lib.rs +++ b/aya-log/aya-log-common/src/lib.rs @@ -1,6 +1,6 @@ #![no_std] -pub const LOG_BUF_CAPACITY: usize = 1024; +pub const LOG_BUF_CAPACITY: usize = 8192; pub const LOG_FIELDS: usize = 7; From 628b473e0937eef94b0b337608a5d6c51ad2fd2a Mon Sep 17 00:00:00 2001 From: Michal Rostecki Date: Thu, 26 May 2022 17:53:46 +0200 Subject: [PATCH 27/41] ebpf: Ensure the bounds of log buffer eBPF verifier rejects programs which are not checking the bounds of the log buffer before writing any arguments. This change ensures that written log arguments. In practice, it means that doing this kind of checks is not going to be needed in eBPF program code anymore: https://github.com/alessandrod/aya-echo-tracepoint/blob/33a1aee2eaa7503615a444ffa574dfba2be943f9/echo-ebpf/src/main.rs#L47 Tested on: https://github.com/vadorovsky/aya-echo-tracepoint/tree/876f8b45511d0818b683de9a2196e8103b92e1a7 Signed-off-by: Michal Rostecki --- aya-log/ebpf/aya-log-ebpf-macros/src/expand.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/aya-log/ebpf/aya-log-ebpf-macros/src/expand.rs b/aya-log/ebpf/aya-log-ebpf-macros/src/expand.rs index 5ff93aac..f0cfc62e 100644 --- a/aya-log/ebpf/aya-log-ebpf-macros/src/expand.rs +++ b/aya-log/ebpf/aya-log-ebpf-macros/src/expand.rs @@ -92,6 +92,9 @@ pub(crate) fn log(args: LogArgs, level: Option) -> Result(record_len) #( .and_then(|record_len| { + if record_len >= buf.buf.len() { + return Err(()); + } { #formatting_exprs }.write(&mut buf.buf[record_len..]).map(|len| record_len + len) }) )* }}; From 5d82d9a73e77d386c8be3dc3764b3dd361fcac71 Mon Sep 17 00:00:00 2001 From: Dave Tucker Date: Mon, 6 Jun 2022 18:33:52 +0100 Subject: [PATCH 28/41] Add Tests This moves a large chunk of code from ebpf to shared so we can re-use write_record_header and write_record_message and friends so that we can write test cases to ensure that logs are properly formatted given certain input. Signed-off-by: Dave Tucker --- aya-log/aya-log-common/src/lib.rs | 105 +++++++++++++++++++++++++ aya-log/aya-log/Cargo.toml | 4 + aya-log/aya-log/src/lib.rs | 53 ++++++++++++- aya-log/ebpf/aya-log-ebpf/src/lib.rs | 111 +-------------------------- 4 files changed, 163 insertions(+), 110 deletions(-) diff --git a/aya-log/aya-log-common/src/lib.rs b/aya-log/aya-log-common/src/lib.rs index 5a0bd419..b91d07db 100644 --- a/aya-log/aya-log-common/src/lib.rs +++ b/aya-log/aya-log-common/src/lib.rs @@ -1,5 +1,7 @@ #![no_std] +use core::{cmp, mem, ptr}; + pub const LOG_BUF_CAPACITY: usize = 8192; pub const LOG_FIELDS: usize = 7; @@ -71,3 +73,106 @@ mod userspace { unsafe impl aya::Pod for RecordField {} unsafe impl aya::Pod for ArgType {} } + +struct TagLenValue<'a, T> { + tag: T, + value: &'a [u8], +} + +impl<'a, T> TagLenValue<'a, T> +where + T: Copy, +{ + #[inline(always)] + pub(crate) fn new(tag: T, value: &'a [u8]) -> TagLenValue<'a, T> { + TagLenValue { tag, value } + } + + pub(crate) fn write(&self, mut buf: &mut [u8]) -> Result { + let size = mem::size_of::() + mem::size_of::() + self.value.len(); + if buf.len() < size { + return Err(()); + } + + unsafe { ptr::write_unaligned(buf.as_mut_ptr() as *mut _, self.tag) }; + buf = &mut buf[mem::size_of::()..]; + + unsafe { ptr::write_unaligned(buf.as_mut_ptr() as *mut _, self.value.len()) }; + buf = &mut buf[mem::size_of::()..]; + + let len = cmp::min(buf.len(), self.value.len()); + buf[..len].copy_from_slice(&self.value[..len]); + Ok(size) + } +} + +pub trait WriteToBuf { + #[allow(clippy::result_unit_err)] + fn write(&self, buf: &mut [u8]) -> Result; +} + +macro_rules! impl_write_to_buf { + ($type:ident, $arg_type:expr) => { + impl WriteToBuf for $type { + fn write(&self, buf: &mut [u8]) -> Result { + TagLenValue::::new($arg_type, &self.to_ne_bytes()).write(buf) + } + } + }; +} + +impl_write_to_buf!(i8, ArgType::I8); +impl_write_to_buf!(i16, ArgType::I16); +impl_write_to_buf!(i32, ArgType::I32); +impl_write_to_buf!(i64, ArgType::I64); +impl_write_to_buf!(i128, ArgType::I128); +impl_write_to_buf!(isize, ArgType::Isize); + +impl_write_to_buf!(u8, ArgType::U8); +impl_write_to_buf!(u16, ArgType::U16); +impl_write_to_buf!(u32, ArgType::U32); +impl_write_to_buf!(u64, ArgType::U64); +impl_write_to_buf!(u128, ArgType::U128); +impl_write_to_buf!(usize, ArgType::Usize); + +impl_write_to_buf!(f32, ArgType::F32); +impl_write_to_buf!(f64, ArgType::F64); + +impl WriteToBuf for str { + fn write(&self, buf: &mut [u8]) -> Result { + TagLenValue::::new(ArgType::Str, self.as_bytes()).write(buf) + } +} + +#[allow(clippy::result_unit_err)] +#[doc(hidden)] +#[inline(always)] +pub fn write_record_header( + buf: &mut [u8], + target: &str, + level: Level, + module: &str, + file: &str, + line: u32, + num_args: usize, +) -> Result { + let mut size = 0; + for attr in [ + TagLenValue::::new(RecordField::Target, target.as_bytes()), + TagLenValue::::new(RecordField::Level, &(level as usize).to_ne_bytes()), + TagLenValue::::new(RecordField::Module, module.as_bytes()), + TagLenValue::::new(RecordField::File, file.as_bytes()), + TagLenValue::::new(RecordField::Line, &line.to_ne_bytes()), + TagLenValue::::new(RecordField::NumArgs, &num_args.to_ne_bytes()), + ] { + size += attr.write(&mut buf[size..])?; + } + + Ok(size) +} + +#[allow(clippy::result_unit_err)] +#[doc(hidden)] +pub fn write_record_message(buf: &mut [u8], msg: &str) -> Result { + TagLenValue::::new(RecordField::Log, msg.as_bytes()).write(buf) +} diff --git a/aya-log/aya-log/Cargo.toml b/aya-log/aya-log/Cargo.toml index f5165f56..082de210 100644 --- a/aya-log/aya-log/Cargo.toml +++ b/aya-log/aya-log/Cargo.toml @@ -19,5 +19,9 @@ log = "0.4" bytes = "1.1" tokio = { version = "1.2.0" } +[dev-dependencies] +simplelog = "0.12" +testing_logger = "0.1.1" + [lib] path = "src/lib.rs" diff --git a/aya-log/aya-log/src/lib.rs b/aya-log/aya-log/src/lib.rs index dfee4db8..fb82e448 100644 --- a/aya-log/aya-log/src/lib.rs +++ b/aya-log/aya-log/src/lib.rs @@ -34,7 +34,7 @@ //! //! With the following eBPF code: //! -//! ```no_run +//! ```ignore //! # let ctx = (); //! use aya_log_ebpf::{debug, error, info, trace, warn}; //! @@ -328,3 +328,54 @@ impl<'a, T: Pod> TagLenValue<'a, T> { )) } } + +#[cfg(test)] +mod test { + use super::*; + use aya_log_common::{write_record_header, write_record_message, WriteToBuf}; + use log::logger; + use testing_logger; + + fn new_log(msg: &str, args: usize) -> Result<(usize, Vec), ()> { + let mut buf = vec![0; 8192]; + let mut len = write_record_header( + &mut buf, + "test", + aya_log_common::Level::Info, + "test", + "test.rs", + 123, + args, + )?; + len += write_record_message(&mut buf[len..], msg)?; + Ok((len, buf)) + } + + #[test] + fn test_str() { + testing_logger::setup(); + let (_, input) = new_log("test", 0).unwrap(); + let logger = logger(); + let _ = log_buf(&input, logger); + testing_logger::validate(|captured_logs| { + assert_eq!(captured_logs.len(), 1); + assert_eq!(captured_logs[0].body, "test"); + assert_eq!(captured_logs[0].level, Level::Info); + }); + } + + #[test] + fn test_str_with_args() { + testing_logger::setup(); + let (len, mut input) = new_log("hello {}", 1).unwrap(); + let name = "test"; + (*name).write(&mut input[len..]).unwrap(); + let logger = logger(); + let _ = log_buf(&input, logger); + testing_logger::validate(|captured_logs| { + assert_eq!(captured_logs.len(), 1); + assert_eq!(captured_logs[0].body, "hello test"); + assert_eq!(captured_logs[0].level, Level::Info); + }); + } +} diff --git a/aya-log/ebpf/aya-log-ebpf/src/lib.rs b/aya-log/ebpf/aya-log-ebpf/src/lib.rs index 33210970..b0da7f7f 100644 --- a/aya-log/ebpf/aya-log-ebpf/src/lib.rs +++ b/aya-log/ebpf/aya-log-ebpf/src/lib.rs @@ -1,13 +1,9 @@ #![no_std] - -use core::{cmp, mem, ptr}; - use aya_bpf::{ macros::map, - maps::{PerCpuArray, PerfEventByteArray}, + maps::{PerCpuArray, PerfEventByteArray} }; -use aya_log_common::{ArgType, RecordField}; -pub use aya_log_common::{Level, LOG_BUF_CAPACITY}; +pub use aya_log_common::{Level, LOG_BUF_CAPACITY, write_record_header, write_record_message, WriteToBuf}; pub use aya_log_ebpf_macros::{debug, error, info, log, trace, warn}; #[doc(hidden)] @@ -24,109 +20,6 @@ pub static mut AYA_LOG_BUF: PerCpuArray = PerCpuArray::with_max_entries( #[map] pub static mut AYA_LOGS: PerfEventByteArray = PerfEventByteArray::new(0); -struct TagLenValue<'a, T> { - tag: T, - value: &'a [u8], -} - -impl<'a, T> TagLenValue<'a, T> -where - T: Copy, -{ - #[inline(always)] - pub(crate) fn new(tag: T, value: &'a [u8]) -> TagLenValue<'a, T> { - TagLenValue { tag, value } - } - - pub(crate) fn write(&self, mut buf: &mut [u8]) -> Result { - let size = mem::size_of::() + mem::size_of::() + self.value.len(); - if buf.len() < size { - return Err(()); - } - - unsafe { ptr::write_unaligned(buf.as_mut_ptr() as *mut _, self.tag) }; - buf = &mut buf[mem::size_of::()..]; - - unsafe { ptr::write_unaligned(buf.as_mut_ptr() as *mut _, self.value.len()) }; - buf = &mut buf[mem::size_of::()..]; - - let len = cmp::min(buf.len(), self.value.len()); - buf[..len].copy_from_slice(&self.value[..len]); - Ok(size) - } -} - -pub trait WriteToBuf { - #[allow(clippy::result_unit_err)] - fn write(&self, buf: &mut [u8]) -> Result; -} - -macro_rules! impl_write_to_buf { - ($type:ident, $arg_type:expr) => { - impl WriteToBuf for $type { - fn write(&self, buf: &mut [u8]) -> Result { - TagLenValue::::new($arg_type, &self.to_ne_bytes()).write(buf) - } - } - }; -} - -impl_write_to_buf!(i8, ArgType::I8); -impl_write_to_buf!(i16, ArgType::I16); -impl_write_to_buf!(i32, ArgType::I32); -impl_write_to_buf!(i64, ArgType::I64); -impl_write_to_buf!(i128, ArgType::I128); -impl_write_to_buf!(isize, ArgType::Isize); - -impl_write_to_buf!(u8, ArgType::U8); -impl_write_to_buf!(u16, ArgType::U16); -impl_write_to_buf!(u32, ArgType::U32); -impl_write_to_buf!(u64, ArgType::U64); -impl_write_to_buf!(u128, ArgType::U128); -impl_write_to_buf!(usize, ArgType::Usize); - -impl_write_to_buf!(f32, ArgType::F32); -impl_write_to_buf!(f64, ArgType::F64); - -impl WriteToBuf for str { - fn write(&self, buf: &mut [u8]) -> Result { - TagLenValue::::new(ArgType::Str, self.as_bytes()).write(buf) - } -} - -#[allow(clippy::result_unit_err)] -#[doc(hidden)] -#[inline(always)] -pub fn write_record_header( - buf: &mut [u8], - target: &str, - level: Level, - module: &str, - file: &str, - line: u32, - num_args: usize, -) -> Result { - let mut size = 0; - for attr in [ - TagLenValue::::new(RecordField::Target, target.as_bytes()), - TagLenValue::::new(RecordField::Level, &(level as usize).to_ne_bytes()), - TagLenValue::::new(RecordField::Module, module.as_bytes()), - TagLenValue::::new(RecordField::File, file.as_bytes()), - TagLenValue::::new(RecordField::Line, &line.to_ne_bytes()), - TagLenValue::::new(RecordField::NumArgs, &num_args.to_ne_bytes()), - ] { - size += attr.write(&mut buf[size..])?; - } - - Ok(size) -} - -#[allow(clippy::result_unit_err)] -#[doc(hidden)] -pub fn write_record_message(buf: &mut [u8], msg: &str) -> Result { - TagLenValue::::new(RecordField::Log, msg.as_bytes()).write(buf) -} - #[doc(hidden)] pub mod macro_support { pub use aya_log_common::{Level, LOG_BUF_CAPACITY}; From 5789585994776d18afa58f3bb816cfcb1367298e Mon Sep 17 00:00:00 2001 From: Dave Tucker Date: Mon, 6 Jun 2022 18:48:05 +0100 Subject: [PATCH 29/41] ebpf: Add example This ensures that macro expansion works properly and that expanded code compiles Signed-off-by: Dave Tucker --- aya-log/ebpf/Cargo.toml | 12 ++++++++++- aya-log/ebpf/aya-log-ebpf-macros/Cargo.toml | 2 +- aya-log/ebpf/aya-log-ebpf/Cargo.toml | 9 --------- aya-log/ebpf/aya-log-ebpf/src/lib.rs | 6 ++++-- aya-log/ebpf/example/Cargo.toml | 13 ++++++++++++ aya-log/ebpf/example/src/main.rs | 22 +++++++++++++++++++++ 6 files changed, 51 insertions(+), 13 deletions(-) create mode 100644 aya-log/ebpf/example/Cargo.toml create mode 100644 aya-log/ebpf/example/src/main.rs diff --git a/aya-log/ebpf/Cargo.toml b/aya-log/ebpf/Cargo.toml index 4a122eef..d29c15e0 100644 --- a/aya-log/ebpf/Cargo.toml +++ b/aya-log/ebpf/Cargo.toml @@ -1,2 +1,12 @@ [workspace] -members = ["aya-log-ebpf", "aya-log-ebpf-macros"] +members = ["aya-log-ebpf", "aya-log-ebpf-macros", "example"] + + +[profile.dev] +panic = "abort" +debug = 1 +opt-level = 2 +overflow-checks = false + +[profile.release] +panic = "abort" diff --git a/aya-log/ebpf/aya-log-ebpf-macros/Cargo.toml b/aya-log/ebpf/aya-log-ebpf-macros/Cargo.toml index 8e0262b0..9247eb82 100644 --- a/aya-log/ebpf/aya-log-ebpf-macros/Cargo.toml +++ b/aya-log/ebpf/aya-log-ebpf-macros/Cargo.toml @@ -9,4 +9,4 @@ quote = "1.0" syn = "1.0" [lib] -proc-macro = true \ No newline at end of file +proc-macro = true diff --git a/aya-log/ebpf/aya-log-ebpf/Cargo.toml b/aya-log/ebpf/aya-log-ebpf/Cargo.toml index 9ee4e548..5b716747 100644 --- a/aya-log/ebpf/aya-log-ebpf/Cargo.toml +++ b/aya-log/ebpf/aya-log-ebpf/Cargo.toml @@ -10,12 +10,3 @@ aya-log-ebpf-macros = { path = "../aya-log-ebpf-macros" } [lib] path = "src/lib.rs" - -[profile.dev] -panic = "abort" -debug = 1 -opt-level = 2 -overflow-checks = false - -[profile.release] -panic = "abort" \ No newline at end of file diff --git a/aya-log/ebpf/aya-log-ebpf/src/lib.rs b/aya-log/ebpf/aya-log-ebpf/src/lib.rs index b0da7f7f..6faa949d 100644 --- a/aya-log/ebpf/aya-log-ebpf/src/lib.rs +++ b/aya-log/ebpf/aya-log-ebpf/src/lib.rs @@ -1,9 +1,11 @@ #![no_std] use aya_bpf::{ macros::map, - maps::{PerCpuArray, PerfEventByteArray} + maps::{PerCpuArray, PerfEventByteArray}, +}; +pub use aya_log_common::{ + write_record_header, write_record_message, Level, WriteToBuf, LOG_BUF_CAPACITY, }; -pub use aya_log_common::{Level, LOG_BUF_CAPACITY, write_record_header, write_record_message, WriteToBuf}; pub use aya_log_ebpf_macros::{debug, error, info, log, trace, warn}; #[doc(hidden)] diff --git a/aya-log/ebpf/example/Cargo.toml b/aya-log/ebpf/example/Cargo.toml new file mode 100644 index 00000000..e78d7049 --- /dev/null +++ b/aya-log/ebpf/example/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "example" +version = "0.1.0" +edition = "2018" +publish = false + +[dependencies] +aya-bpf = { git = "https://github.com/aya-rs/aya", branch = "main" } +aya-log-ebpf = { path = "../aya-log-ebpf" } + +[[bin]] +name = "example" +path = "src/main.rs" diff --git a/aya-log/ebpf/example/src/main.rs b/aya-log/ebpf/example/src/main.rs new file mode 100644 index 00000000..b43b5364 --- /dev/null +++ b/aya-log/ebpf/example/src/main.rs @@ -0,0 +1,22 @@ +#![no_std] +#![no_main] + +use aya_bpf::{macros::tracepoint, programs::TracePointContext, BpfContext}; +use aya_log_ebpf::{debug, error, info, trace, warn}; + +#[tracepoint] +pub fn example(ctx: TracePointContext) -> u32 { + error!(&ctx, "this is an error message 🚨"); + warn!(&ctx, "this is a warning message ⚠ī¸"); + info!(&ctx, "this is an info message ℹī¸"); + debug!(&ctx, "this is a debug message ī¸đŸ"); + trace!(&ctx, "this is a trace message 🔍"); + let pid = ctx.pid(); + info!(&ctx, "a message with args PID: {}", pid); + 0 +} + +#[panic_handler] +fn panic(_info: &core::panic::PanicInfo) -> ! { + unsafe { core::hint::unreachable_unchecked() } +} From 3f0085195f178fdba6c214b4129f8321e612d4e7 Mon Sep 17 00:00:00 2001 From: Dave Tucker Date: Mon, 6 Jun 2022 18:50:35 +0100 Subject: [PATCH 30/41] Add rustfmt.toml Signed-off-by: Dave Tucker --- aya-log/ebpf/rustfmt.toml | 4 ++++ aya-log/rustfmt.toml | 4 ++++ aya-log/xtask/src/build_ebpf.rs | 3 +-- 3 files changed, 9 insertions(+), 2 deletions(-) create mode 100644 aya-log/ebpf/rustfmt.toml create mode 100644 aya-log/rustfmt.toml diff --git a/aya-log/ebpf/rustfmt.toml b/aya-log/ebpf/rustfmt.toml new file mode 100644 index 00000000..0c3bc0ee --- /dev/null +++ b/aya-log/ebpf/rustfmt.toml @@ -0,0 +1,4 @@ +unstable_features = true +reorder_imports = true +imports_granularity = "Crate" + diff --git a/aya-log/rustfmt.toml b/aya-log/rustfmt.toml new file mode 100644 index 00000000..0c3bc0ee --- /dev/null +++ b/aya-log/rustfmt.toml @@ -0,0 +1,4 @@ +unstable_features = true +reorder_imports = true +imports_granularity = "Crate" + diff --git a/aya-log/xtask/src/build_ebpf.rs b/aya-log/xtask/src/build_ebpf.rs index 756801f3..517a8a1d 100644 --- a/aya-log/xtask/src/build_ebpf.rs +++ b/aya-log/xtask/src/build_ebpf.rs @@ -1,5 +1,4 @@ -use std::path::PathBuf; -use std::process::Command; +use std::{path::PathBuf, process::Command}; use structopt::StructOpt; From c1bb790c0d8d467ac41603b15b56823c7ba0f663 Mon Sep 17 00:00:00 2001 From: Dave Tucker Date: Mon, 6 Jun 2022 18:53:50 +0100 Subject: [PATCH 31/41] Add vim/vscode rust-analyzer settings Signed-off-by: Dave Tucker --- aya-log/.vim/coc-settings.json | 3 +++ aya-log/.vscode/settings.json | 3 +++ 2 files changed, 6 insertions(+) create mode 100644 aya-log/.vim/coc-settings.json create mode 100644 aya-log/.vscode/settings.json diff --git a/aya-log/.vim/coc-settings.json b/aya-log/.vim/coc-settings.json new file mode 100644 index 00000000..f8d723bb --- /dev/null +++ b/aya-log/.vim/coc-settings.json @@ -0,0 +1,3 @@ +{ + "rust-analyzer.linkedProjects": ["Cargo.toml", "ebpf/Cargo.toml"] +} diff --git a/aya-log/.vscode/settings.json b/aya-log/.vscode/settings.json new file mode 100644 index 00000000..f8d723bb --- /dev/null +++ b/aya-log/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "rust-analyzer.linkedProjects": ["Cargo.toml", "ebpf/Cargo.toml"] +} From 0038b43627e6564b03d9837f535ec64ada6d70f2 Mon Sep 17 00:00:00 2001 From: Dave Tucker Date: Mon, 6 Jun 2022 19:01:06 +0100 Subject: [PATCH 32/41] Add CI Signed-off-by: Dave Tucker --- aya-log/.github/dependabot.yml | 9 ++++++ aya-log/.github/workflows/build-bpf.yml | 37 ++++++++++++++++++++++ aya-log/.github/workflows/build.yml | 26 ++++++++++++++++ aya-log/.github/workflows/lint.yml | 41 +++++++++++++++++++++++++ 4 files changed, 113 insertions(+) create mode 100644 aya-log/.github/dependabot.yml create mode 100644 aya-log/.github/workflows/build-bpf.yml create mode 100644 aya-log/.github/workflows/build.yml create mode 100644 aya-log/.github/workflows/lint.yml diff --git a/aya-log/.github/dependabot.yml b/aya-log/.github/dependabot.yml new file mode 100644 index 00000000..08c94a23 --- /dev/null +++ b/aya-log/.github/dependabot.yml @@ -0,0 +1,9 @@ +# Please see the documentation for all configuration options: +# https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates + +version: 2 +updates: + - package-ecosystem: "cargo" + directory: "/" + schedule: + interval: "weekly" diff --git a/aya-log/.github/workflows/build-bpf.yml b/aya-log/.github/workflows/build-bpf.yml new file mode 100644 index 00000000..002e965d --- /dev/null +++ b/aya-log/.github/workflows/build-bpf.yml @@ -0,0 +1,37 @@ +name: build-bpf + +on: + push: + branches: + - main + + pull_request: + branches: + - main + +env: + CARGO_TERM_COLOR: always + +jobs: + build: + runs-on: ubuntu-20.04 + + steps: + - uses: actions/checkout@v2 + + - uses: actions-rs/toolchain@v1 + with: + toolchain: nightly + components: rust-src + override: true + + - uses: Swatinem/rust-cache@v1 + + - name: Pre-requisites + run: cargo install bpf-linker + + - name: Build + run: | + pushd ebpf + cargo build --verbose + popd diff --git a/aya-log/.github/workflows/build.yml b/aya-log/.github/workflows/build.yml new file mode 100644 index 00000000..dd031044 --- /dev/null +++ b/aya-log/.github/workflows/build.yml @@ -0,0 +1,26 @@ +name: build + +on: + push: + branches: + - main + + pull_request: + branches: + - main + +env: + CARGO_TERM_COLOR: always + +jobs: + build: + runs-on: ubuntu-20.04 + steps: + - uses: actions/checkout@v2 + - uses: Swatinem/rust-cache@v1 + + - name: Build + run: cargo build --verbose + + - name: Run tests + run: RUST_BACKTRACE=full cargo test --verbose diff --git a/aya-log/.github/workflows/lint.yml b/aya-log/.github/workflows/lint.yml new file mode 100644 index 00000000..169b536e --- /dev/null +++ b/aya-log/.github/workflows/lint.yml @@ -0,0 +1,41 @@ +name: lint + +on: + push: + branches: + - main + + pull_request: + branches: + - main + +env: + CARGO_TERM_COLOR: always + +jobs: + lint: + runs-on: ubuntu-20.04 + + steps: + - uses: actions/checkout@v2 + + - uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: nightly + components: rustfmt, clippy, rust-src + override: true + + - name: Check formatting + run: | + cargo fmt --all -- --check + pushd ebpf + cargo fmt --all -- --check + popd + + - name: Run clippy + run: | + cargo clippy -- --deny warnings + pushd ebpf + cargo clippy -- --deny warnings + popd From 060ba451535b1a90c2faaf2dcd634fa36e784efb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 7 Jun 2022 07:48:41 +0000 Subject: [PATCH 33/41] Update aya requirement from 0.10.7 to 0.11.0 Updates the requirements on [aya](https://github.com/aya-rs/aya) to permit the latest version. - [Release notes](https://github.com/aya-rs/aya/releases) - [Commits](https://github.com/aya-rs/aya/compare/aya-v0.10.7...aya-v0.11.0) --- updated-dependencies: - dependency-name: aya dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- aya-log/aya-log-common/Cargo.toml | 2 +- aya-log/aya-log/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/aya-log/aya-log-common/Cargo.toml b/aya-log/aya-log-common/Cargo.toml index 7430032b..1445f52f 100644 --- a/aya-log/aya-log-common/Cargo.toml +++ b/aya-log/aya-log-common/Cargo.toml @@ -14,7 +14,7 @@ default = [] userspace = [ "aya" ] [dependencies] -aya = { version = "0.10.7", optional=true } +aya = { version = "0.11.0", optional=true } [lib] path = "src/lib.rs" diff --git a/aya-log/aya-log/Cargo.toml b/aya-log/aya-log/Cargo.toml index 082de210..24f503e2 100644 --- a/aya-log/aya-log/Cargo.toml +++ b/aya-log/aya-log/Cargo.toml @@ -11,7 +11,7 @@ documentation = "https://docs.rs/aya-log" edition = "2018" [dependencies] -aya = { version = "0.10.7", features=["async_tokio"] } +aya = { version = "0.11.0", features=["async_tokio"] } aya-log-common = { version = "0.1.10-dev.0", path = "../aya-log-common", features=["userspace"] } dyn-fmt = "0.3.0" thiserror = "1" From 3abd97307ef32bfbd384f38f7a0de40cc7afa0b1 Mon Sep 17 00:00:00 2001 From: Dave Tucker Date: Wed, 8 Jun 2022 12:41:29 +0100 Subject: [PATCH 34/41] aya-log, aya-log-common: release version 0.1.10 --- aya-log/aya-log-common/Cargo.toml | 2 +- aya-log/aya-log/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/aya-log/aya-log-common/Cargo.toml b/aya-log/aya-log-common/Cargo.toml index 1445f52f..ac29898f 100644 --- a/aya-log/aya-log-common/Cargo.toml +++ b/aya-log/aya-log-common/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "aya-log-common" -version = "0.1.10-dev.0" +version = "0.1.10" description = "A logging library for eBPF programs." keywords = ["ebpf", "bpf", "log", "logging"] license = "MIT OR Apache-2.0" diff --git a/aya-log/aya-log/Cargo.toml b/aya-log/aya-log/Cargo.toml index 24f503e2..00aad833 100644 --- a/aya-log/aya-log/Cargo.toml +++ b/aya-log/aya-log/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "aya-log" -version = "0.1.10-dev.0" +version = "0.1.10" description = "A logging library for eBPF programs." keywords = ["ebpf", "bpf", "log", "logging"] license = "MIT OR Apache-2.0" From 526493b444ed91f1c315ace494b41b8f4178fe65 Mon Sep 17 00:00:00 2001 From: Dave Tucker Date: Wed, 8 Jun 2022 12:43:13 +0100 Subject: [PATCH 35/41] aya-log, aya-log-common: start next development iteration 0.1.11-dev.0 --- aya-log/aya-log-common/Cargo.toml | 2 +- aya-log/aya-log/Cargo.toml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/aya-log/aya-log-common/Cargo.toml b/aya-log/aya-log-common/Cargo.toml index ac29898f..a3a54d8c 100644 --- a/aya-log/aya-log-common/Cargo.toml +++ b/aya-log/aya-log-common/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "aya-log-common" -version = "0.1.10" +version = "0.1.11-dev.0" description = "A logging library for eBPF programs." keywords = ["ebpf", "bpf", "log", "logging"] license = "MIT OR Apache-2.0" diff --git a/aya-log/aya-log/Cargo.toml b/aya-log/aya-log/Cargo.toml index 00aad833..605386d4 100644 --- a/aya-log/aya-log/Cargo.toml +++ b/aya-log/aya-log/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "aya-log" -version = "0.1.10" +version = "0.1.11-dev.0" description = "A logging library for eBPF programs." keywords = ["ebpf", "bpf", "log", "logging"] license = "MIT OR Apache-2.0" @@ -12,7 +12,7 @@ edition = "2018" [dependencies] aya = { version = "0.11.0", features=["async_tokio"] } -aya-log-common = { version = "0.1.10-dev.0", path = "../aya-log-common", features=["userspace"] } +aya-log-common = { version = "0.1.11-dev.0", path = "../aya-log-common", features=["userspace"] } dyn-fmt = "0.3.0" thiserror = "1" log = "0.4" From 6aea88089087194c831b259a61eef5ccebcb45bc Mon Sep 17 00:00:00 2001 From: Alessandro Decina Date: Thu, 9 Jun 2022 23:15:25 +1000 Subject: [PATCH 36/41] ebpf: use new PerCpuArray::get_ptr_mut API --- aya-log/ebpf/aya-log-ebpf-macros/src/expand.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/aya-log/ebpf/aya-log-ebpf-macros/src/expand.rs b/aya-log/ebpf/aya-log-ebpf-macros/src/expand.rs index f0cfc62e..b1279b12 100644 --- a/aya-log/ebpf/aya-log-ebpf-macros/src/expand.rs +++ b/aya-log/ebpf/aya-log-ebpf-macros/src/expand.rs @@ -128,7 +128,8 @@ pub(crate) fn log(args: LogArgs, level: Option) -> Result Date: Wed, 20 Jul 2022 16:39:42 +0200 Subject: [PATCH 37/41] Ensure log buffer bounds This change adds checks in `TagLenValue.write()` to ensure that the size of written data doesn't exceed the buffer size. Verifier in recent kernel versions requires the bound to be a constant value, so using `buf.len()` does not work. Signed-off-by: Michal Rostecki --- aya-log/aya-log-common/src/lib.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/aya-log/aya-log-common/src/lib.rs b/aya-log/aya-log-common/src/lib.rs index b91d07db..c1a5b666 100644 --- a/aya-log/aya-log-common/src/lib.rs +++ b/aya-log/aya-log-common/src/lib.rs @@ -90,7 +90,9 @@ where pub(crate) fn write(&self, mut buf: &mut [u8]) -> Result { let size = mem::size_of::() + mem::size_of::() + self.value.len(); - if buf.len() < size { + // The verifier rejects the program if it can't see that `size` doesn't + // exceed the buffer size. + if size > LOG_BUF_CAPACITY { return Err(()); } @@ -101,6 +103,11 @@ where buf = &mut buf[mem::size_of::()..]; let len = cmp::min(buf.len(), self.value.len()); + // The verifier rejects the program if it can't see that `size` doesn't + // exceed the buffer size. + if len > LOG_BUF_CAPACITY { + return Err(()); + } buf[..len].copy_from_slice(&self.value[..len]); Ok(size) } From 28abaece2af732cf2b2b2f8b12aeb02439e76d4c Mon Sep 17 00:00:00 2001 From: Michal Rostecki Date: Thu, 21 Jul 2022 11:57:42 +0200 Subject: [PATCH 38/41] Fix the log buffer bounds Change 821ba0b243fd removed the `size > buf.len()` check, which was a mistake, because we might write to a subslice of the whole buffer, so then `buf` can be lower than `LOG_BUF_CAPACITY`. This change compares `size` with `min::(buf.len(), LOG_BUF_CAPACITY)` instead. Fixes: 821ba0b243fd ("Ensure log buffer bounds") Signed-off-by: Michal Rostecki --- aya-log/aya-log-common/src/lib.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/aya-log/aya-log-common/src/lib.rs b/aya-log/aya-log-common/src/lib.rs index c1a5b666..0aa63846 100644 --- a/aya-log/aya-log-common/src/lib.rs +++ b/aya-log/aya-log-common/src/lib.rs @@ -90,9 +90,9 @@ where pub(crate) fn write(&self, mut buf: &mut [u8]) -> Result { let size = mem::size_of::() + mem::size_of::() + self.value.len(); - // The verifier rejects the program if it can't see that `size` doesn't - // exceed the buffer size. - if size > LOG_BUF_CAPACITY { + let remaining = cmp::min(buf.len(), LOG_BUF_CAPACITY); + // Check if the size doesn't exceed the buffer bounds. + if size > remaining { return Err(()); } @@ -103,8 +103,8 @@ where buf = &mut buf[mem::size_of::()..]; let len = cmp::min(buf.len(), self.value.len()); - // The verifier rejects the program if it can't see that `size` doesn't - // exceed the buffer size. + // The verifier isn't happy with `len` being unbounded, so compare it + // with `LOG_BUF_CAPACITY`. if len > LOG_BUF_CAPACITY { return Err(()); } From dc31e11691bbb8ae916da9da873fdc37ff261c27 Mon Sep 17 00:00:00 2001 From: Dave Tucker Date: Mon, 25 Jul 2022 17:37:42 +0000 Subject: [PATCH 39/41] Re-organize into a single workspace This commit moves the aya-log projects from the subtree and adds them to the main cargo workspace. It also brings the BPF crates into the workspace and moves the macro crates up a level since they aren't BPF code. Miri was disabled for aya-bpf as the previous config wasn't actually checking anything. CI, clippy, fmt and release configurations have all been adjusted appropriately. CI was not properly running for other supported arches which was also ixed here. Signed-off-by: Dave Tucker --- .cargo/config | 2 + .github/workflows/build-aya-bpf.yml | 52 ++--------- .github/workflows/build-aya.yml | 27 ++++-- .github/workflows/lint.yml | 15 +--- .vim/coc-settings.json | 4 +- .vscode/settings.json | 4 +- Cargo.toml | 23 ++++- .../Cargo.toml | 2 +- .../src/expand.rs | 0 .../src/lib.rs | 0 .../Cargo.toml | 2 +- aya-log-common/release.toml | 1 + .../src/lib.rs | 0 .../Cargo.toml | 0 .../src/expand.rs | 0 .../src/lib.rs | 0 aya-log/.cargo/config.toml | 2 - aya-log/.github/dependabot.yml | 9 -- aya-log/.github/workflows/build-bpf.yml | 37 -------- aya-log/.github/workflows/build.yml | 26 ------ aya-log/.github/workflows/lint.yml | 41 --------- aya-log/.gitignore | 13 --- aya-log/.vim/coc-settings.json | 3 - aya-log/.vscode/settings.json | 3 - aya-log/Cargo.toml | 29 +++++- aya-log/aya-log/Cargo.toml | 27 ------ aya-log/aya-log/README.md | 73 --------------- aya-log/ebpf/Cargo.toml | 12 --- aya-log/ebpf/example/Cargo.toml | 13 --- aya-log/ebpf/example/src/main.rs | 22 ----- aya-log/ebpf/rustfmt.toml | 4 - aya-log/release.toml | 6 -- aya-log/rustfmt.toml | 4 - aya-log/{aya-log => }/src/lib.rs | 0 aya-log/xtask/Cargo.toml | 12 --- aya-log/xtask/src/build_ebpf.rs | 61 ------------- aya-log/xtask/src/main.rs | 29 ------ {aya-log/ebpf => bpf}/.cargo/config.toml | 0 bpf/Cargo.toml | 2 - bpf/aya-bpf/Cargo.toml | 2 +- {aya-log/ebpf => bpf}/aya-log-ebpf/Cargo.toml | 4 +- {aya-log/ebpf => bpf}/aya-log-ebpf/src/lib.rs | 0 {aya-log/ebpf => bpf}/rust-toolchain.toml | 0 bpf/rustfmt.toml | 1 - netlify.toml | 2 +- release.toml | 6 ++ test/integration-ebpf/Cargo.toml | 14 +-- xtask/src/docs/mod.rs | 90 +++++++------------ 48 files changed, 131 insertions(+), 548 deletions(-) rename {bpf/aya-bpf-macros => aya-bpf-macros}/Cargo.toml (87%) rename {bpf/aya-bpf-macros => aya-bpf-macros}/src/expand.rs (100%) rename {bpf/aya-bpf-macros => aya-bpf-macros}/src/lib.rs (100%) rename {aya-log/aya-log-common => aya-log-common}/Cargo.toml (87%) create mode 100644 aya-log-common/release.toml rename {aya-log/aya-log-common => aya-log-common}/src/lib.rs (100%) rename {aya-log/ebpf/aya-log-ebpf-macros => aya-log-ebpf-macros}/Cargo.toml (100%) rename {aya-log/ebpf/aya-log-ebpf-macros => aya-log-ebpf-macros}/src/expand.rs (100%) rename {aya-log/ebpf/aya-log-ebpf-macros => aya-log-ebpf-macros}/src/lib.rs (100%) delete mode 100644 aya-log/.cargo/config.toml delete mode 100644 aya-log/.github/dependabot.yml delete mode 100644 aya-log/.github/workflows/build-bpf.yml delete mode 100644 aya-log/.github/workflows/build.yml delete mode 100644 aya-log/.github/workflows/lint.yml delete mode 100644 aya-log/.gitignore delete mode 100644 aya-log/.vim/coc-settings.json delete mode 100644 aya-log/.vscode/settings.json delete mode 100644 aya-log/aya-log/Cargo.toml delete mode 100644 aya-log/aya-log/README.md delete mode 100644 aya-log/ebpf/Cargo.toml delete mode 100644 aya-log/ebpf/example/Cargo.toml delete mode 100644 aya-log/ebpf/example/src/main.rs delete mode 100644 aya-log/ebpf/rustfmt.toml delete mode 100644 aya-log/rustfmt.toml rename aya-log/{aya-log => }/src/lib.rs (100%) delete mode 100644 aya-log/xtask/Cargo.toml delete mode 100644 aya-log/xtask/src/build_ebpf.rs delete mode 100644 aya-log/xtask/src/main.rs rename {aya-log/ebpf => bpf}/.cargo/config.toml (100%) delete mode 100644 bpf/Cargo.toml rename {aya-log/ebpf => bpf}/aya-log-ebpf/Cargo.toml (55%) rename {aya-log/ebpf => bpf}/aya-log-ebpf/src/lib.rs (100%) rename {aya-log/ebpf => bpf}/rust-toolchain.toml (100%) delete mode 120000 bpf/rustfmt.toml create mode 100644 release.toml diff --git a/.cargo/config b/.cargo/config index 757fd081..dbd1a2b4 100644 --- a/.cargo/config +++ b/.cargo/config @@ -1,5 +1,7 @@ [alias] xtask = "run --package xtask --" +build-bpfel = "build -Zbuild-std=core --target=bpfel-unknown-none" +build-bpfeb = "build -Zbuild-std=core --target=bpfeb-unknown-none" [target.armv7-unknown-linux-gnueabi] linker = "arm-linux-gnueabi-gcc" diff --git a/.github/workflows/build-aya-bpf.yml b/.github/workflows/build-aya-bpf.yml index e520833d..a29d7c09 100644 --- a/.github/workflows/build-aya-bpf.yml +++ b/.github/workflows/build-aya-bpf.yml @@ -31,59 +31,19 @@ jobs: - uses: actions-rs/toolchain@v1 with: toolchain: nightly + components: rust-src override: true - uses: Swatinem/rust-cache@v1 - name: Prereqs - run: cargo install cross --git https://github.com/cross-rs/cross + run: cargo install bpf-linker - name: Build env: CARGO_CFG_BPF_TARGET_ARCH: ${{ matrix.arch }} run: | - pushd bpf - cargo build --workspace --exclude aya-bpf-macros --verbose - popd - - - name: Run tests - env: - CARGO_CFG_BPF_TARGET_ARCH: ${{ matrix.arch }} - run: | - pushd bpf - cargo test --workspace --exclude aya-bpf-macros --verbose - popd - - build-macros: - strategy: - matrix: - arch: - - x86_64-unknown-linux-gnu - - aarch64-unknown-linux-gnu - - armv7-unknown-linux-gnueabi - - riscv64gc-unknown-none-elf - runs-on: ubuntu-20.04 - steps: - - uses: actions/checkout@v2 - - - uses: actions-rs/toolchain@v1 - with: - toolchain: nightly - override: true - - - uses: Swatinem/rust-cache@v1 - - - name: Prereqs - run: cargo install cross --git https://github.com/cross-rs/cross - - - name: Build bpf macros - run: | - pushd bpf - cross build -p aya-bpf-macros --verbose - popd - - - name: Test bpf macros - run: | - pushd bpf - RUST_BACKTRACE=full cross test -p aya-bpf-macros --verbose - popd + cargo build-bpfel -p aya-bpf --verbose + cargo build-bpfeb -p aya-bpf --verbose + cargo build-bpfel -p aya-log-ebpf --verbose + cargo build-bpfeb -p aya-log-ebpf --verbose diff --git a/.github/workflows/build-aya.yml b/.github/workflows/build-aya.yml index 9d523875..c16373a7 100644 --- a/.github/workflows/build-aya.yml +++ b/.github/workflows/build-aya.yml @@ -21,19 +21,38 @@ jobs: - x86_64-unknown-linux-gnu - aarch64-unknown-linux-gnu - armv7-unknown-linux-gnueabi - - riscv64gc-unknown-none-elf + - riscv64gc-unknown-linux-gnu runs-on: ubuntu-20.04 steps: - uses: actions/checkout@v2 + + - uses: actions-rs/toolchain@v1 + with: + toolchain: stable + override: true + + - uses: actions-rs/toolchain@v1 + with: + toolchain: nightly + override: false + - uses: Swatinem/rust-cache@v1 - name: Prereqs run: cargo install cross --git https://github.com/cross-rs/cross - name: Build - run: cross build --verbose + run: cross build --verbose --target ${{matrix.arch}} - name: Run test - run: RUST_BACKTRACE=full cross test --verbose + env: + RUST_BACKTRACE: full + run: | + cross test --verbose -p aya --target ${{matrix.arch}} + cross test --verbose -p aya-gen --target ${{matrix.arch}} + cross test --verbose -p aya-log --target ${{matrix.arch}} + cross test --verbose -p aya-log-ebpf-macros --target ${{matrix.arch}} + # aya-bpf-macros can only be tested on nightly since its tests depend on aya-bpf, which requires the never type + cross +nightly test --verbose -p aya-bpf-macros --target ${{matrix.arch}} test: runs-on: ubuntu-20.04 @@ -60,12 +79,10 @@ jobs: sudo apt-get -qy install linux-tools-common qemu-system-x86 cloud-image-utils openssh-client libelf-dev gcc-multilib cargo install bpf-linker - - name: Lint integration tests run: | cargo xtask build-integration-test-ebpf --libbpf-dir ./libbpf cargo clippy -p integration-test -- --deny warnings - cargo clippy -p integration-test-macros -- --deny warnings - name: Run integration tests run: | diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index ffb10c76..afe47cf0 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -30,22 +30,11 @@ jobs: - name: Check formatting run: | cargo fmt --all -- --check - (cd bpf && cargo fmt --all -- --check) - (cd test/integration-ebpf && cargo fmt --all -- --check) - name: Run clippy run: | - cargo clippy -p aya -- --deny warnings - cargo clippy -p aya-gen -- --deny warnings - cargo clippy -p xtask -- --deny warnings - (cd bpf && cargo clippy -p aya-bpf -- --deny warnings) - (cd test/integration-ebpf && cargo clippy -- --deny warnings) + cargo clippy --workspace --exclude integration-test -- --deny warnings - name: Run miri - env: - MIRIFLAGS: -Zmiri-disable-stacked-borrows run: | - cargo miri test --all-targets - pushd bpf - cargo miri test - popd + cargo miri test --all-targets \ No newline at end of file diff --git a/.vim/coc-settings.json b/.vim/coc-settings.json index 4b796ccb..07c919f9 100644 --- a/.vim/coc-settings.json +++ b/.vim/coc-settings.json @@ -1,4 +1,4 @@ { - "rust-analyzer.linkedProjects": ["Cargo.toml", "bpf/Cargo.toml", "test/integration-ebpf/Cargo.toml"], - "rust-analyzer.checkOnSave.allTargets": false + "rust-analyzer.checkOnSave.allTargets": false, + "rust-analyzer.checkOnSave.command": "clippy" } diff --git a/.vscode/settings.json b/.vscode/settings.json index 4b796ccb..07c919f9 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,4 +1,4 @@ { - "rust-analyzer.linkedProjects": ["Cargo.toml", "bpf/Cargo.toml", "test/integration-ebpf/Cargo.toml"], - "rust-analyzer.checkOnSave.allTargets": false + "rust-analyzer.checkOnSave.allTargets": false, + "rust-analyzer.checkOnSave.command": "clippy" } diff --git a/Cargo.toml b/Cargo.toml index c5a9a2e0..61c3b7df 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,3 +1,22 @@ [workspace] -members = ["aya", "aya-gen", "test/integration-test", "test/integration-test-macros", "xtask"] -default-members = ["aya", "aya-gen"] +members = [ + "aya", "aya-gen", "aya-log", "aya-log-common", "test/integration-test", "test/integration-test-macros", "xtask", + # macros + "aya-bpf-macros", "aya-log-ebpf-macros", + # ebpf crates + "bpf/aya-bpf", "bpf/aya-bpf-bindings", "bpf/aya-log-ebpf", "test/integration-ebpf" +] +default-members = ["aya", "aya-gen", "aya-log", "aya-bpf-macros", "aya-log-ebpf-macros"] + +[profile.dev] +panic = "abort" + +[profile.release] +panic = "abort" + +[profile.dev.package.integration-ebpf] +opt-level = 2 +overflow-checks = false + +[profile.release.package.integration-ebpf] +debug = 2 \ No newline at end of file diff --git a/bpf/aya-bpf-macros/Cargo.toml b/aya-bpf-macros/Cargo.toml similarity index 87% rename from bpf/aya-bpf-macros/Cargo.toml rename to aya-bpf-macros/Cargo.toml index 9c43f7bd..929098ee 100644 --- a/bpf/aya-bpf-macros/Cargo.toml +++ b/aya-bpf-macros/Cargo.toml @@ -13,4 +13,4 @@ quote = "1.0" syn = {version = "1.0", features = ["full"]} [dev-dependencies] -aya-bpf = { path = "../aya-bpf" } +aya-bpf = { path = "../bpf/aya-bpf" } diff --git a/bpf/aya-bpf-macros/src/expand.rs b/aya-bpf-macros/src/expand.rs similarity index 100% rename from bpf/aya-bpf-macros/src/expand.rs rename to aya-bpf-macros/src/expand.rs diff --git a/bpf/aya-bpf-macros/src/lib.rs b/aya-bpf-macros/src/lib.rs similarity index 100% rename from bpf/aya-bpf-macros/src/lib.rs rename to aya-bpf-macros/src/lib.rs diff --git a/aya-log/aya-log-common/Cargo.toml b/aya-log-common/Cargo.toml similarity index 87% rename from aya-log/aya-log-common/Cargo.toml rename to aya-log-common/Cargo.toml index a3a54d8c..4b50e2a1 100644 --- a/aya-log/aya-log-common/Cargo.toml +++ b/aya-log-common/Cargo.toml @@ -14,7 +14,7 @@ default = [] userspace = [ "aya" ] [dependencies] -aya = { version = "0.11.0", optional=true } +aya = { path = "../aya", version = "0.11.0", optional=true } [lib] path = "src/lib.rs" diff --git a/aya-log-common/release.toml b/aya-log-common/release.toml new file mode 100644 index 00000000..bf6eaefc --- /dev/null +++ b/aya-log-common/release.toml @@ -0,0 +1 @@ +shared-version = true diff --git a/aya-log/aya-log-common/src/lib.rs b/aya-log-common/src/lib.rs similarity index 100% rename from aya-log/aya-log-common/src/lib.rs rename to aya-log-common/src/lib.rs diff --git a/aya-log/ebpf/aya-log-ebpf-macros/Cargo.toml b/aya-log-ebpf-macros/Cargo.toml similarity index 100% rename from aya-log/ebpf/aya-log-ebpf-macros/Cargo.toml rename to aya-log-ebpf-macros/Cargo.toml diff --git a/aya-log/ebpf/aya-log-ebpf-macros/src/expand.rs b/aya-log-ebpf-macros/src/expand.rs similarity index 100% rename from aya-log/ebpf/aya-log-ebpf-macros/src/expand.rs rename to aya-log-ebpf-macros/src/expand.rs diff --git a/aya-log/ebpf/aya-log-ebpf-macros/src/lib.rs b/aya-log-ebpf-macros/src/lib.rs similarity index 100% rename from aya-log/ebpf/aya-log-ebpf-macros/src/lib.rs rename to aya-log-ebpf-macros/src/lib.rs diff --git a/aya-log/.cargo/config.toml b/aya-log/.cargo/config.toml deleted file mode 100644 index f0ccbc9a..00000000 --- a/aya-log/.cargo/config.toml +++ /dev/null @@ -1,2 +0,0 @@ -[alias] -xtask = "run --package xtask --" \ No newline at end of file diff --git a/aya-log/.github/dependabot.yml b/aya-log/.github/dependabot.yml deleted file mode 100644 index 08c94a23..00000000 --- a/aya-log/.github/dependabot.yml +++ /dev/null @@ -1,9 +0,0 @@ -# Please see the documentation for all configuration options: -# https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates - -version: 2 -updates: - - package-ecosystem: "cargo" - directory: "/" - schedule: - interval: "weekly" diff --git a/aya-log/.github/workflows/build-bpf.yml b/aya-log/.github/workflows/build-bpf.yml deleted file mode 100644 index 002e965d..00000000 --- a/aya-log/.github/workflows/build-bpf.yml +++ /dev/null @@ -1,37 +0,0 @@ -name: build-bpf - -on: - push: - branches: - - main - - pull_request: - branches: - - main - -env: - CARGO_TERM_COLOR: always - -jobs: - build: - runs-on: ubuntu-20.04 - - steps: - - uses: actions/checkout@v2 - - - uses: actions-rs/toolchain@v1 - with: - toolchain: nightly - components: rust-src - override: true - - - uses: Swatinem/rust-cache@v1 - - - name: Pre-requisites - run: cargo install bpf-linker - - - name: Build - run: | - pushd ebpf - cargo build --verbose - popd diff --git a/aya-log/.github/workflows/build.yml b/aya-log/.github/workflows/build.yml deleted file mode 100644 index dd031044..00000000 --- a/aya-log/.github/workflows/build.yml +++ /dev/null @@ -1,26 +0,0 @@ -name: build - -on: - push: - branches: - - main - - pull_request: - branches: - - main - -env: - CARGO_TERM_COLOR: always - -jobs: - build: - runs-on: ubuntu-20.04 - steps: - - uses: actions/checkout@v2 - - uses: Swatinem/rust-cache@v1 - - - name: Build - run: cargo build --verbose - - - name: Run tests - run: RUST_BACKTRACE=full cargo test --verbose diff --git a/aya-log/.github/workflows/lint.yml b/aya-log/.github/workflows/lint.yml deleted file mode 100644 index 169b536e..00000000 --- a/aya-log/.github/workflows/lint.yml +++ /dev/null @@ -1,41 +0,0 @@ -name: lint - -on: - push: - branches: - - main - - pull_request: - branches: - - main - -env: - CARGO_TERM_COLOR: always - -jobs: - lint: - runs-on: ubuntu-20.04 - - steps: - - uses: actions/checkout@v2 - - - uses: actions-rs/toolchain@v1 - with: - profile: minimal - toolchain: nightly - components: rustfmt, clippy, rust-src - override: true - - - name: Check formatting - run: | - cargo fmt --all -- --check - pushd ebpf - cargo fmt --all -- --check - popd - - - name: Run clippy - run: | - cargo clippy -- --deny warnings - pushd ebpf - cargo clippy -- --deny warnings - popd diff --git a/aya-log/.gitignore b/aya-log/.gitignore deleted file mode 100644 index 54f741e1..00000000 --- a/aya-log/.gitignore +++ /dev/null @@ -1,13 +0,0 @@ -### https://raw.github.com/github/gitignore/master/Rust.gitignore - -# Generated by Cargo -# will have compiled files and executables -debug/ -target/ - -# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries -# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html -Cargo.lock - -# These are backup files generated by rustfmt -**/*.rs.bk diff --git a/aya-log/.vim/coc-settings.json b/aya-log/.vim/coc-settings.json deleted file mode 100644 index f8d723bb..00000000 --- a/aya-log/.vim/coc-settings.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "rust-analyzer.linkedProjects": ["Cargo.toml", "ebpf/Cargo.toml"] -} diff --git a/aya-log/.vscode/settings.json b/aya-log/.vscode/settings.json deleted file mode 100644 index f8d723bb..00000000 --- a/aya-log/.vscode/settings.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "rust-analyzer.linkedProjects": ["Cargo.toml", "ebpf/Cargo.toml"] -} diff --git a/aya-log/Cargo.toml b/aya-log/Cargo.toml index a8e5e117..701f3adb 100644 --- a/aya-log/Cargo.toml +++ b/aya-log/Cargo.toml @@ -1,2 +1,27 @@ -[workspace] -members = ["aya-log", "aya-log-common", "xtask"] +[package] +name = "aya-log" +version = "0.1.11-dev.0" +description = "A logging library for eBPF programs." +keywords = ["ebpf", "bpf", "log", "logging"] +license = "MIT OR Apache-2.0" +authors = ["The Aya Contributors"] +repository = "https://github.com/aya-rs/aya-log" +readme = "README.md" +documentation = "https://docs.rs/aya-log" +edition = "2018" + +[dependencies] +aya = { path = "../aya", version = "0.11.0", features=["async_tokio"] } +aya-log-common = { path = "../aya-log-common", version = "0.1.11-dev.0", features=["userspace"] } +dyn-fmt = "0.3.0" +thiserror = "1" +log = "0.4" +bytes = "1.1" +tokio = { version = "1.2.0" } + +[dev-dependencies] +simplelog = "0.12" +testing_logger = "0.1.1" + +[lib] +path = "src/lib.rs" diff --git a/aya-log/aya-log/Cargo.toml b/aya-log/aya-log/Cargo.toml deleted file mode 100644 index 605386d4..00000000 --- a/aya-log/aya-log/Cargo.toml +++ /dev/null @@ -1,27 +0,0 @@ -[package] -name = "aya-log" -version = "0.1.11-dev.0" -description = "A logging library for eBPF programs." -keywords = ["ebpf", "bpf", "log", "logging"] -license = "MIT OR Apache-2.0" -authors = ["The Aya Contributors"] -repository = "https://github.com/aya-rs/aya-log" -readme = "README.md" -documentation = "https://docs.rs/aya-log" -edition = "2018" - -[dependencies] -aya = { version = "0.11.0", features=["async_tokio"] } -aya-log-common = { version = "0.1.11-dev.0", path = "../aya-log-common", features=["userspace"] } -dyn-fmt = "0.3.0" -thiserror = "1" -log = "0.4" -bytes = "1.1" -tokio = { version = "1.2.0" } - -[dev-dependencies] -simplelog = "0.12" -testing_logger = "0.1.1" - -[lib] -path = "src/lib.rs" diff --git a/aya-log/aya-log/README.md b/aya-log/aya-log/README.md deleted file mode 100644 index 5e1245ca..00000000 --- a/aya-log/aya-log/README.md +++ /dev/null @@ -1,73 +0,0 @@ -# aya-log - a logging library for eBPF programs - -## Overview - -`aya-log` is a logging library for eBPF programs written using [aya]. Think of -it as the [log] crate for eBPF. - -## Installation - -### User space - -Add `aya-log` to `Cargo.toml`: - -```toml -[dependencies] -aya-log = { git = "https://github.com/aya-rs/aya-log", branch = "main" } -``` - -### eBPF side - -Add `aya-log-ebpf` to `Cargo.toml`: - -```toml -[dependencies] -aya-log-ebpf = { git = "https://github.com/aya-rs/aya-log", branch = "main" } -``` - -## Example - -Here's an example that uses `aya-log` in conjunction with the [simplelog] crate -to log eBPF messages to the terminal. - -### User space code - -```rust -use simplelog::{ColorChoice, ConfigBuilder, LevelFilter, TermLogger, TerminalMode}; -use aya_log::BpfLogger; - -TermLogger::init( - LevelFilter::Debug, - ConfigBuilder::new() - .set_target_level(LevelFilter::Error) - .set_location_level(LevelFilter::Error) - .build(), - TerminalMode::Mixed, - ColorChoice::Auto, -) -.unwrap(); - -// Will log using the default logger, which is TermLogger in this case -BpfLogger::init(&mut bpf).unwrap(); -``` - -### eBPF code - -```rust -use aya_log_ebpf::info; - -fn try_xdp_firewall(ctx: XdpContext) -> Result { - if let Some(port) = tcp_dest_port(&ctx)? { - if block_port(port) { - info!(&ctx, "❌ blocked incoming connection on port: {}", port); - return Ok(XDP_DROP); - } - } - - Ok(XDP_PASS) -} -``` - -[aya]: https://github.com/aya-rs/aya -[log]: https://docs.rs/log -[simplelog]: https://docs.rs/simplelog diff --git a/aya-log/ebpf/Cargo.toml b/aya-log/ebpf/Cargo.toml deleted file mode 100644 index d29c15e0..00000000 --- a/aya-log/ebpf/Cargo.toml +++ /dev/null @@ -1,12 +0,0 @@ -[workspace] -members = ["aya-log-ebpf", "aya-log-ebpf-macros", "example"] - - -[profile.dev] -panic = "abort" -debug = 1 -opt-level = 2 -overflow-checks = false - -[profile.release] -panic = "abort" diff --git a/aya-log/ebpf/example/Cargo.toml b/aya-log/ebpf/example/Cargo.toml deleted file mode 100644 index e78d7049..00000000 --- a/aya-log/ebpf/example/Cargo.toml +++ /dev/null @@ -1,13 +0,0 @@ -[package] -name = "example" -version = "0.1.0" -edition = "2018" -publish = false - -[dependencies] -aya-bpf = { git = "https://github.com/aya-rs/aya", branch = "main" } -aya-log-ebpf = { path = "../aya-log-ebpf" } - -[[bin]] -name = "example" -path = "src/main.rs" diff --git a/aya-log/ebpf/example/src/main.rs b/aya-log/ebpf/example/src/main.rs deleted file mode 100644 index b43b5364..00000000 --- a/aya-log/ebpf/example/src/main.rs +++ /dev/null @@ -1,22 +0,0 @@ -#![no_std] -#![no_main] - -use aya_bpf::{macros::tracepoint, programs::TracePointContext, BpfContext}; -use aya_log_ebpf::{debug, error, info, trace, warn}; - -#[tracepoint] -pub fn example(ctx: TracePointContext) -> u32 { - error!(&ctx, "this is an error message 🚨"); - warn!(&ctx, "this is a warning message ⚠ī¸"); - info!(&ctx, "this is an info message ℹī¸"); - debug!(&ctx, "this is a debug message ī¸đŸ"); - trace!(&ctx, "this is a trace message 🔍"); - let pid = ctx.pid(); - info!(&ctx, "a message with args PID: {}", pid); - 0 -} - -#[panic_handler] -fn panic(_info: &core::panic::PanicInfo) -> ! { - unsafe { core::hint::unreachable_unchecked() } -} diff --git a/aya-log/ebpf/rustfmt.toml b/aya-log/ebpf/rustfmt.toml deleted file mode 100644 index 0c3bc0ee..00000000 --- a/aya-log/ebpf/rustfmt.toml +++ /dev/null @@ -1,4 +0,0 @@ -unstable_features = true -reorder_imports = true -imports_granularity = "Crate" - diff --git a/aya-log/release.toml b/aya-log/release.toml index b2239c1f..bf6eaefc 100644 --- a/aya-log/release.toml +++ b/aya-log/release.toml @@ -1,7 +1 @@ -pre-release-commit-message = "aya-log, aya-log-common: release version {{version}}" -post-release-commit-message = "aya-log, aya-log-common: start next development iteration {{next_version}}" -consolidate-pushes = true -consolidate-commits = true shared-version = true -dev-version = true -dev-version-ext = "dev.0" diff --git a/aya-log/rustfmt.toml b/aya-log/rustfmt.toml deleted file mode 100644 index 0c3bc0ee..00000000 --- a/aya-log/rustfmt.toml +++ /dev/null @@ -1,4 +0,0 @@ -unstable_features = true -reorder_imports = true -imports_granularity = "Crate" - diff --git a/aya-log/aya-log/src/lib.rs b/aya-log/src/lib.rs similarity index 100% rename from aya-log/aya-log/src/lib.rs rename to aya-log/src/lib.rs diff --git a/aya-log/xtask/Cargo.toml b/aya-log/xtask/Cargo.toml deleted file mode 100644 index 32cde2ab..00000000 --- a/aya-log/xtask/Cargo.toml +++ /dev/null @@ -1,12 +0,0 @@ -[package] -name = "xtask" -publish = false -version = "0.1.0" -edition = "2018" - -[dependencies] -structopt = {version = "0.3", default-features = false } -anyhow = "1" - -[package.metadata.release] -release = false diff --git a/aya-log/xtask/src/build_ebpf.rs b/aya-log/xtask/src/build_ebpf.rs deleted file mode 100644 index 517a8a1d..00000000 --- a/aya-log/xtask/src/build_ebpf.rs +++ /dev/null @@ -1,61 +0,0 @@ -use std::{path::PathBuf, 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 { - 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("aya-log-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(()) -} diff --git a/aya-log/xtask/src/main.rs b/aya-log/xtask/src/main.rs deleted file mode 100644 index 96308fd3..00000000 --- a/aya-log/xtask/src/main.rs +++ /dev/null @@ -1,29 +0,0 @@ -mod build_ebpf; - -use std::process::exit; - -use structopt::StructOpt; -#[derive(StructOpt)] -pub struct Options { - #[structopt(subcommand)] - command: Command, -} - -#[derive(StructOpt)] -enum Command { - BuildEbpf(build_ebpf::Options), -} - -fn main() { - let opts = Options::from_args(); - - use Command::*; - let ret = match opts.command { - BuildEbpf(opts) => build_ebpf::build(opts), - }; - - if let Err(e) = ret { - eprintln!("{:#}", e); - exit(1); - } -} diff --git a/aya-log/ebpf/.cargo/config.toml b/bpf/.cargo/config.toml similarity index 100% rename from aya-log/ebpf/.cargo/config.toml rename to bpf/.cargo/config.toml diff --git a/bpf/Cargo.toml b/bpf/Cargo.toml deleted file mode 100644 index b7cf3e2c..00000000 --- a/bpf/Cargo.toml +++ /dev/null @@ -1,2 +0,0 @@ -[workspace] -members = ["aya-bpf", "aya-bpf-macros", "aya-bpf-bindings"] diff --git a/bpf/aya-bpf/Cargo.toml b/bpf/aya-bpf/Cargo.toml index c28551c2..e8822cb4 100644 --- a/bpf/aya-bpf/Cargo.toml +++ b/bpf/aya-bpf/Cargo.toml @@ -6,5 +6,5 @@ edition = "2018" [dependencies] aya-bpf-cty = { path = "../aya-bpf-cty" } -aya-bpf-macros = { path = "../aya-bpf-macros" } +aya-bpf-macros = { path = "../../aya-bpf-macros" } aya-bpf-bindings = { path = "../aya-bpf-bindings" } diff --git a/aya-log/ebpf/aya-log-ebpf/Cargo.toml b/bpf/aya-log-ebpf/Cargo.toml similarity index 55% rename from aya-log/ebpf/aya-log-ebpf/Cargo.toml rename to bpf/aya-log-ebpf/Cargo.toml index 5b716747..ffbd9dca 100644 --- a/aya-log/ebpf/aya-log-ebpf/Cargo.toml +++ b/bpf/aya-log-ebpf/Cargo.toml @@ -4,9 +4,9 @@ version = "0.1.0" edition = "2018" [dependencies] -aya-bpf = { git = "https://github.com/aya-rs/aya", branch = "main" } +aya-bpf = { path = "../aya-bpf" } aya-log-common = { path = "../../aya-log-common" } -aya-log-ebpf-macros = { path = "../aya-log-ebpf-macros" } +aya-log-ebpf-macros = { path = "../../aya-log-ebpf-macros" } [lib] path = "src/lib.rs" diff --git a/aya-log/ebpf/aya-log-ebpf/src/lib.rs b/bpf/aya-log-ebpf/src/lib.rs similarity index 100% rename from aya-log/ebpf/aya-log-ebpf/src/lib.rs rename to bpf/aya-log-ebpf/src/lib.rs diff --git a/aya-log/ebpf/rust-toolchain.toml b/bpf/rust-toolchain.toml similarity index 100% rename from aya-log/ebpf/rust-toolchain.toml rename to bpf/rust-toolchain.toml diff --git a/bpf/rustfmt.toml b/bpf/rustfmt.toml deleted file mode 120000 index 39f97b04..00000000 --- a/bpf/rustfmt.toml +++ /dev/null @@ -1 +0,0 @@ -../rustfmt.toml \ No newline at end of file diff --git a/netlify.toml b/netlify.toml index 0b8e1991..3ad62912 100644 --- a/netlify.toml +++ b/netlify.toml @@ -1,3 +1,3 @@ [build] publish = "site" - command = "rustup toolchain install nightly && cargo xtask docs" + command = "rustup toolchain install nightly -c rust-src && cargo xtask docs" diff --git a/release.toml b/release.toml new file mode 100644 index 00000000..e5351ffa --- /dev/null +++ b/release.toml @@ -0,0 +1,6 @@ +pre-release-commit-message = "{crate_name}: release version {{version}}" +post-release-commit-message = "{crate_name}: start next development iteration {{next_version}}" +consolidate-pushes = true +consolidate-commits = true +dev-version = true +dev-version-ext = "dev.0" diff --git a/test/integration-ebpf/Cargo.toml b/test/integration-ebpf/Cargo.toml index b2a526fa..8688d790 100644 --- a/test/integration-ebpf/Cargo.toml +++ b/test/integration-ebpf/Cargo.toml @@ -21,16 +21,4 @@ path = "src/pass.rs" [[bin]] name = "test" -path = "src/test.rs" - -[profile.dev] -panic = "abort" -opt-level = 2 -overflow-checks = false - -[profile.release] -panic = "abort" -debug = 2 - -[workspace] -members = [] +path = "src/test.rs" \ No newline at end of file diff --git a/xtask/src/docs/mod.rs b/xtask/src/docs/mod.rs index 0496ef63..8705cae7 100644 --- a/xtask/src/docs/mod.rs +++ b/xtask/src/docs/mod.rs @@ -8,67 +8,19 @@ use std::{fs, io, io::Write}; use indoc::indoc; pub fn docs() -> Result<(), anyhow::Error> { - let mut working_dir = PathBuf::from("."); - - let replace = Command::new("sed") - .current_dir(&working_dir) - .args(vec![ - "-i.bak", - "s/crabby.svg/crabby_dev.svg/", - "aya/src/lib.rs", - ]) - .status() - .expect("failed to replace logo"); - assert!(replace.success()); - - let mut header_path = PathBuf::from("."); - header_path.push("header.html"); + let current_dir = PathBuf::from("."); + let header_path = current_dir.join("header.html"); let mut header = fs::File::create(&header_path).expect("can't create header.html"); header .write_all(r#""#.as_bytes()) .expect("can't write header.html contents"); header.flush().expect("couldn't flush contents"); - let abs_header_path = fs::canonicalize(&header_path).unwrap(); - let args = vec!["+nightly", "doc", "--no-deps", "--all-features"]; - - let status = Command::new("cargo") - .current_dir(&working_dir) - .env( - "RUSTDOCFLAGS", - format!("--html-in-header {}", abs_header_path.to_str().unwrap()), - ) - .args(&args) - .status() - .expect("failed to build aya docs"); - assert!(status.success()); - - working_dir.push("bpf"); - - let replace = Command::new("sed") - .current_dir(&working_dir) - .args(vec![ - "-i.bak", - "s/crabby.svg/crabby_dev.svg/", - "aya-bpf/src/lib.rs", - ]) - .status() - .expect("failed to replace logo"); - assert!(replace.success()); - - let status = Command::new("cargo") - .current_dir(&working_dir) - .env( - "RUSTDOCFLAGS", - format!("--html-in-header {}", abs_header_path.to_str().unwrap()), - ) - .args(&args) - .status() - .expect("failed to build aya-bpf docs"); - assert!(status.success()); - copy_dir_all("./target/doc", "site/user")?; - copy_dir_all("./bpf/target/doc", "site/bpf")?; + build_docs(¤t_dir.join("aya"), &abs_header_path)?; + build_docs(¤t_dir.join("bpf/aya-bpf"), &abs_header_path)?; + copy_dir_all("./target/doc", "./site/user")?; + copy_dir_all("./target/bpfel-unknown-none/doc", "./site/bpf")?; let mut robots = fs::File::create("site/robots.txt").expect("can't create robots.txt"); robots @@ -98,14 +50,38 @@ pub fn docs() -> Result<(), anyhow::Error> { .as_bytes(), ) .expect("can't write index.html"); + Ok(()) +} + +fn build_docs(working_dir: &PathBuf, abs_header_path: &Path) -> Result<(), anyhow::Error> { + let replace = Command::new("sed") + .current_dir(&working_dir) + .args(vec!["-i.bak", "s/crabby.svg/crabby_dev.svg/", "src/lib.rs"]) + .status() + .expect("failed to replace logo"); + assert!(replace.success()); - fs::rename("aya/src/lib.rs.bak", "aya/src/lib.rs").unwrap(); - fs::rename("bpf/aya-bpf/src/lib.rs.bak", "bpf/aya-bpf/src/lib.rs").unwrap(); + let args = vec!["+nightly", "doc", "--no-deps", "--all-features"]; + let status = Command::new("cargo") + .current_dir(&working_dir) + .env( + "RUSTDOCFLAGS", + format!("--html-in-header {}", abs_header_path.to_str().unwrap()), + ) + .args(&args) + .status() + .expect("failed to build aya docs"); + assert!(status.success()); + fs::rename( + working_dir.join("src/lib.rs.bak"), + working_dir.join("src/lib.rs"), + ) + .unwrap(); Ok(()) } -fn copy_dir_all>(src: P, dst: P) -> io::Result<()> { +fn copy_dir_all, P2: AsRef>(src: P1, dst: P2) -> io::Result<()> { fs::create_dir_all(&dst)?; for entry in fs::read_dir(src)? { let entry = entry?; From c9e70a8758ef10cfe1970e5f7a1e830e0ba5ec8e Mon Sep 17 00:00:00 2001 From: Dave Tucker Date: Wed, 27 Jul 2022 14:36:32 +0000 Subject: [PATCH 40/41] aya: Fix rlimit warning on for 32bit systems Signed-off-by: Dave Tucker --- aya/src/maps/mod.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/aya/src/maps/mod.rs b/aya/src/maps/mod.rs index 8e390681..e0e8d066 100644 --- a/aya/src/maps/mod.rs +++ b/aya/src/maps/mod.rs @@ -33,7 +33,7 @@ //! versa. Because of that, all map values must be plain old data and therefore //! implement the [Pod] trait. use std::{ - convert::TryFrom, + convert::{TryFrom, TryInto}, ffi::CString, fmt, io, marker::PhantomData, @@ -226,7 +226,7 @@ impl AsRawFd for MapFd { } #[derive(PartialEq, Eq, PartialOrd, Ord)] -struct RlimitSize(u64); +struct RlimitSize(usize); impl fmt::Display for RlimitSize { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { if self.0 < 1024 { @@ -246,8 +246,9 @@ fn maybe_warn_rlimit() { let ret = unsafe { getrlimit(RLIMIT_MEMLOCK, limit.as_mut_ptr()) }; if ret == 0 { let limit = unsafe { limit.assume_init() }; - let limit: RlimitSize = RlimitSize(limit.rlim_cur); - if limit.0 == RLIM_INFINITY { + + let limit: RlimitSize = RlimitSize(limit.rlim_cur.try_into().unwrap()); + if limit.0 == RLIM_INFINITY.try_into().unwrap() { return; } warn!( From 6ab7148731444e157fcc8f2a09bfcd457fe771c0 Mon Sep 17 00:00:00 2001 From: Dave Tucker Date: Wed, 27 Jul 2022 21:55:01 +0000 Subject: [PATCH 41/41] bpf: Only use never type with rust nightly Signed-off-by: Dave Tucker --- .github/workflows/build-aya.yml | 12 +----------- bpf/aya-bpf/Cargo.toml | 3 +++ bpf/aya-bpf/build.rs | 9 +++++++++ bpf/aya-bpf/src/lib.rs | 2 +- bpf/aya-bpf/src/maps/program_array.rs | 24 ++++++++++++++++++++++++ 5 files changed, 38 insertions(+), 12 deletions(-) diff --git a/.github/workflows/build-aya.yml b/.github/workflows/build-aya.yml index c16373a7..8f57b1de 100644 --- a/.github/workflows/build-aya.yml +++ b/.github/workflows/build-aya.yml @@ -31,11 +31,6 @@ jobs: toolchain: stable override: true - - uses: actions-rs/toolchain@v1 - with: - toolchain: nightly - override: false - - uses: Swatinem/rust-cache@v1 - name: Prereqs run: cargo install cross --git https://github.com/cross-rs/cross @@ -47,12 +42,7 @@ jobs: env: RUST_BACKTRACE: full run: | - cross test --verbose -p aya --target ${{matrix.arch}} - cross test --verbose -p aya-gen --target ${{matrix.arch}} - cross test --verbose -p aya-log --target ${{matrix.arch}} - cross test --verbose -p aya-log-ebpf-macros --target ${{matrix.arch}} - # aya-bpf-macros can only be tested on nightly since its tests depend on aya-bpf, which requires the never type - cross +nightly test --verbose -p aya-bpf-macros --target ${{matrix.arch}} + cross test --verbose --target ${{matrix.arch}} test: runs-on: ubuntu-20.04 diff --git a/bpf/aya-bpf/Cargo.toml b/bpf/aya-bpf/Cargo.toml index e8822cb4..03fedb59 100644 --- a/bpf/aya-bpf/Cargo.toml +++ b/bpf/aya-bpf/Cargo.toml @@ -8,3 +8,6 @@ edition = "2018" aya-bpf-cty = { path = "../aya-bpf-cty" } aya-bpf-macros = { path = "../../aya-bpf-macros" } aya-bpf-bindings = { path = "../aya-bpf-bindings" } + +[build-dependencies] +rustversion = "1.0" \ No newline at end of file diff --git a/bpf/aya-bpf/build.rs b/bpf/aya-bpf/build.rs index a8a2c261..8ae418f2 100644 --- a/bpf/aya-bpf/build.rs +++ b/bpf/aya-bpf/build.rs @@ -1,6 +1,7 @@ use std::env; fn main() { + check_rust_version(); println!("cargo:rerun-if-env-changed=CARGO_CFG_BPF_TARGET_ARCH"); if let Ok(arch) = env::var("CARGO_CFG_BPF_TARGET_ARCH") { println!("cargo:rustc-cfg=bpf_target_arch=\"{}\"", arch); @@ -10,3 +11,11 @@ fn main() { println!("cargo:rustc-cfg=bpf_target_arch=\"{}\"", arch); } } + +#[rustversion::nightly] +fn check_rust_version() { + println!("cargo:rustc-cfg=unstable"); +} + +#[rustversion::not(nightly)] +fn check_rust_version() {} diff --git a/bpf/aya-bpf/src/lib.rs b/bpf/aya-bpf/src/lib.rs index 8de7fc3b..d6855584 100644 --- a/bpf/aya-bpf/src/lib.rs +++ b/bpf/aya-bpf/src/lib.rs @@ -9,7 +9,7 @@ html_logo_url = "https://aya-rs.dev/assets/images/crabby.svg", html_favicon_url = "https://aya-rs.dev/assets/images/crabby.svg" )] -#![feature(never_type)] +#![cfg_attr(unstable, feature(never_type))] #![allow(clippy::missing_safety_doc)] #![no_std] diff --git a/bpf/aya-bpf/src/maps/program_array.rs b/bpf/aya-bpf/src/maps/program_array.rs index 62fa07d4..b7e54a6c 100644 --- a/bpf/aya-bpf/src/maps/program_array.rs +++ b/bpf/aya-bpf/src/maps/program_array.rs @@ -80,6 +80,30 @@ impl ProgramArray { /// /// On success, this function **does not return** into the original program. /// On failure, a negative error is returned, wrapped in `Err()`. + #[cfg(not(unstable))] + pub unsafe fn tail_call(&self, ctx: &C, index: u32) -> Result<(), c_long> { + let res = bpf_tail_call(ctx.as_ptr(), self.def.get() as *mut _, index); + if res != 0 { + Err(res) + } else { + unreachable_unchecked() + } + } + + /// Perform a tail call into a program indexed by this map. + /// + /// # Safety + /// + /// This function is inherently unsafe, since it causes control flow to jump into + /// another eBPF program. This can have side effects, such as drop methods not being + /// called. Note that tail calling into an eBPF program is not the same thing as + /// a function call -- control flow never returns to the caller. + /// + /// # Return Value + /// + /// On success, this function **does not return** into the original program. + /// On failure, a negative error is returned, wrapped in `Err()`. + #[cfg(unstable)] pub unsafe fn tail_call(&self, ctx: &C, index: u32) -> Result { let res = bpf_tail_call(ctx.as_ptr(), self.def.get() as *mut _, index); if res != 0 {