mirror of
https://github.com/outbackdingo/firezone.git
synced 2026-01-27 10:18:54 +00:00
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:
@@ -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
|
||||
}
|
||||
|
||||
@@ -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(())
|
||||
|
||||
@@ -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",
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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"] }
|
||||
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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}");
|
||||
|
||||
Reference in New Issue
Block a user