build(deps): update aya to latest main (#10424)

We haven't updated `aya` in a while. Unfortunately, the update is not without problems. For one, the logging infrastructure changed, requiring us to drop the error details from `xdp_adjust_head`. See https://github.com/aya-rs/aya/issues/1348. Two, the `tokio` feature flag got removed but luckily that can be worked around quite easily.

Resolves: #10344
This commit is contained in:
Thomas Eizinger
2025-09-23 07:45:59 +00:00
committed by GitHub
parent f09232e983
commit 94a56fc6bc
8 changed files with 174 additions and 107 deletions

View File

@@ -34,13 +34,13 @@ pub fn handle_turn(ctx: aya_ebpf::programs::XdpContext) -> u32 {
| Error::UdpChecksumMissing
| Error::Ipv4PacketWithOptions),
) => {
debug!(&ctx, target: "eBPF", "^^^ pass packet to userspace: {}", e);
debug!(&ctx, target: "eBPF", "^^^ pass packet to userspace: {}", e.as_str());
xdp_action::XDP_PASS
}
// In a double symmetric NAT setup, it is easily possible for packets to arrive from IPs that don't have channel bindings.
Err(e @ Error::NoEntry(_)) => {
debug!(&ctx,target: "eBPF", "XXX drop packet: {}", e);
debug!(&ctx,target: "eBPF", "XXX drop packet: {}", e.as_str());
xdp_action::XDP_DROP
}
@@ -48,9 +48,9 @@ pub fn handle_turn(ctx: aya_ebpf::programs::XdpContext) -> u32 {
e @ (Error::ArrayIndexOutOfBounds
| Error::IpAddrUnset
| Error::BadChannelDataLength
| Error::XdpAdjustHeadFailed(_)),
| Error::XdpAdjustHeadFailed),
) => {
warn!(&ctx,target: "eBPF", "XXX drop packet: {}", e);
warn!(&ctx,target: "eBPF", "XXX drop packet: {}", e.as_str());
xdp_action::XDP_DROP
}

View File

@@ -7,7 +7,7 @@ pub fn adjust_head(ctx: &XdpContext, size: i32) -> Result<(), Error> {
// SAFETY: The attach mode and NIC driver support headroom adjustment by `size` bytes.
let ret = unsafe { bpf_xdp_adjust_head(ctx.ctx, size) };
if ret < 0 {
return Err(Error::XdpAdjustHeadFailed(ret));
return Err(Error::XdpAdjustHeadFailed);
}
Ok(())

View File

@@ -1,5 +1,3 @@
use core::num::NonZeroUsize;
#[derive(Debug, Clone, Copy)]
pub enum Error {
ArrayIndexOutOfBounds,
@@ -14,7 +12,7 @@ pub enum Error {
NotAChannelDataMessage,
BadChannelDataLength,
NoEntry(SupportedChannel),
XdpAdjustHeadFailed(i64),
XdpAdjustHeadFailed,
}
#[derive(Debug, Clone, Copy)]
@@ -25,11 +23,10 @@ pub enum SupportedChannel {
Chan6ToUdp,
}
impl aya_log_ebpf::WriteToBuf for Error {
impl Error {
#[inline(always)]
fn write(self, buf: &mut [u8]) -> Option<NonZeroUsize> {
// Use a simpler match structure to help the verifier
let msg = match self {
pub fn as_str(&self) -> &'static str {
match self {
Error::ArrayIndexOutOfBounds => "Array index is out of bounds",
Error::IpAddrUnset => "IP address has not been configured",
Error::UdpChecksumMissing => "UDP checksum is missing",
@@ -47,54 +44,9 @@ impl aya_log_ebpf::WriteToBuf for Error {
SupportedChannel::Udp6ToChan => "No entry in UDPv6 to channel IPv4 or IPv6 map",
SupportedChannel::Chan6ToUdp => "No entry in channel IPv6 to UDPv4 or UDPv6 map",
},
Error::XdpAdjustHeadFailed(ret) => {
// Handle this case separately to avoid complex control flow
let mut written = 0;
written += "Failed to adjust tail: ".write(buf)?.get();
written += errno_to_str(ret).write(buf)?.get();
return NonZeroUsize::new(written);
}
};
msg.write(buf)
Error::XdpAdjustHeadFailed => "Failed to adjust tail",
}
}
}
impl aya_log_ebpf::macro_support::DefaultFormatter for Error {}
/// Helper function to map Linux/eBPF error codes to human-readable strings
/// This avoids integer formatting which can cause pointer arithmetic verifier issues
#[inline(always)]
fn errno_to_str(errno: i64) -> &'static str {
match errno {
-1 => "EPERM (Operation not permitted)",
-2 => "ENOENT (No such file or directory)",
-3 => "ESRCH (No such process)",
-4 => "EINTR (Interrupted system call)",
-5 => "EIO (I/O error)",
-6 => "ENXIO (No such device or address)",
-7 => "E2BIG (Argument list too long)",
-8 => "ENOEXEC (Exec format error)",
-9 => "EBADF (Bad file number)",
-10 => "ECHILD (No child processes)",
-11 => "EAGAIN (Try again)",
-12 => "ENOMEM (Out of memory)",
-13 => "EACCES (Permission denied)",
-14 => "EFAULT (Bad address)",
-16 => "EBUSY (Device or resource busy)",
-17 => "EEXIST (File exists)",
-19 => "ENODEV (No such device)",
-22 => "EINVAL (Invalid argument)",
-24 => "EMFILE (Too many open files)",
-28 => "ENOSPC (No space left on device)",
-32 => "EPIPE (Broken pipe)",
-34 => "ERANGE (Math result not representable)",
-61 => "ENODATA (No data available)",
-75 => "EOVERFLOW (Value too large for defined data type)",
-84 => "EILSEQ (Illegal byte sequence)",
-90 => "EMSGSIZE (Message too long)",
-95 => "ENOTSUP (Operation not supported)",
-105 => "ENOBUFS (No buffer space available)",
_ => "Unknown error",
}
}

View File

@@ -50,7 +50,7 @@ url = { workspace = true }
uuid = { workspace = true, features = ["v4"] }
[target.'cfg(target_os = "linux")'.dependencies]
aya = { workspace = true, features = ["tokio"] }
aya = { workspace = true }
aya-log = { workspace = true }
ebpf-shared = { workspace = true, features = ["std"] }

View File

@@ -9,7 +9,7 @@ fn main() -> anyhow::Result<()> {
.context("MetadataCommand::exec")?
.packages
.into_iter()
.find(|Package { name, .. }| name == "ebpf-turn-router")
.find(|Package { name, .. }| name.as_str() == "ebpf-turn-router")
.context("`ebpf-turn-router` package not found")?;
aya_build::build_ebpf(

View File

@@ -1,9 +1,12 @@
use std::net::{Ipv4Addr, Ipv6Addr, SocketAddr};
use std::{
io,
net::{Ipv4Addr, Ipv6Addr, SocketAddr},
};
use anyhow::{Context as _, Result};
use aya::{
Pod,
maps::{AsyncPerfEventArray, HashMap, MapData, PerCpuArray, PerCpuValues},
maps::{HashMap, MapData, PerCpuArray, PerCpuValues, PerfEventArray},
programs::{Xdp, XdpFlags},
};
use aya_log::EbpfLogger;
@@ -12,6 +15,7 @@ use ebpf_shared::{
ClientAndChannelV4, ClientAndChannelV6, PortAndPeerV4, PortAndPeerV6, StatsEvent,
};
use stun_codec::rfc5766::attributes::ChannelNumber;
use tokio::io::{Interest, unix::AsyncFd};
use crate::ebpf::AttachMode;
@@ -28,7 +32,7 @@ pub struct Program {
ebpf: aya::Ebpf,
#[expect(dead_code, reason = "We are just keeping it alive.")]
stats: AsyncPerfEventArray<MapData>,
stats: PerfEventArray<MapData>,
}
impl Program {
@@ -44,7 +48,22 @@ impl Program {
env!("OUT_DIR"),
"/ebpf-turn-router-main"
)))?;
let _ = EbpfLogger::init(&mut ebpf);
let logger = EbpfLogger::init(&mut ebpf)?;
let mut logger = AsyncFd::with_interest(logger, Interest::READABLE)?;
tokio::task::spawn(async move {
loop {
let mut guard = match logger.readable_mut().await {
Ok(guard) => guard,
Err(e) => {
tracing::warn!("Failed to wait for logger to be readable: {e}");
return;
}
};
guard.get_inner_mut().flush();
guard.clear_ready();
}
});
let program: &mut Xdp = ebpf
.program_mut("handle_turn")
.context("No program")?
@@ -60,7 +79,7 @@ impl Program {
.attach(interface, xdp_flags)
.with_context(|| format!("Failed to attached to interface {interface}"))?;
let mut stats = AsyncPerfEventArray::try_from(
let mut stats = PerfEventArray::try_from(
ebpf.take_map("STATS")
.context("`STATS` perf array not found")?,
)?;
@@ -76,7 +95,8 @@ impl Program {
.context("Failed to determine number of CPUs")?
{
// open a separate perf buffer for each cpu
let mut stats_array_buf = stats.open(cpu_id, Some(PAGE_COUNT))?;
let stats_array_buf = stats.open(cpu_id, Some(PAGE_COUNT))?;
let mut fd = AsyncFd::new(stats_array_buf)?;
tracing::debug!(%cpu_id, "Subscribing to stats events from eBPF kernel");
@@ -90,7 +110,17 @@ impl Program {
.collect::<Vec<_>>();
loop {
let events = match stats_array_buf.read_events(&mut buffers).await {
let events = fd
.async_io_mut(Interest::READABLE, |fd| {
if !fd.readable() {
return Err(io::Error::from(io::ErrorKind::WouldBlock));
}
fd.read_events(&mut buffers).map_err(io::Error::other)
})
.await;
let events = match events {
Ok(events) => events,
Err(e) => {
tracing::warn!("Failed to read perf events: {e}");