### Generate XDP Program Project Source: https://github.com/aya-rs/book/blob/main/src/book/programs/xdp.md Use the aya-rs template to generate a new XDP program project. This command sets up the basic project structure for an XDP program. ```bash cargo generate --name simple-xdp-program -d program_type=xdp \ https://github.com/aya-rs/aya-template ``` -------------------------------- ### Import Dependencies for XDP Program Source: https://github.com/aya-rs/book/blob/main/src/book/programs/xdp.md Imports necessary crates and modules for eBPF development with Aya-rs, including logging, argument parsing, and network address handling. ```rust use anyhow::Context; use aya:: maps::HashMap, programs::{Xdp, XdpMode}, }; use aya_log::EbpfLogger; use clap::Parser; use log::{info, warn}; use std::net::Ipv4Addr; use tokio::signal; ``` -------------------------------- ### Full User-Space XDP Firewall Program Source: https://github.com/aya-rs/book/blob/main/src/book/programs/xdp.md This Rust program loads an XDP program, initializes eBPF logging, attaches the program to a network interface, configures a blocklist map, and waits for a Ctrl+C signal to exit. ```rust use anyhow::Context; use aya::{ maps::HashMap, programs::{Xdp, XdpMode}, }; use aya_log::EbpfLogger; use clap::Parser; use log::{info, warn}; use std::net::Ipv4Addr; use tokio::signal; #[derive(Debug, Parser)] struct Opt { #[clap(short, long, default_value = "eth0")] iface: String, } #[tokio::main] async fn main() -> Result<(), anyhow::Error> { let opt = Opt::parse(); env_logger::init(); let mut bpf = aya::Ebpf::load(aya::include_bytes_aligned!(concat!( env!("OUT_DIR"), "/simple-xdp-program" )))?; match EbpfLogger::init(&mut bpf) { Err(e) => { // This can happen if you remove all log statements from your eBPF program. warn!("failed to initialize eBPF logger: {e}"); } Ok(logger) => { let mut logger = tokio::io::unix::AsyncFd::with_interest( logger, tokio::io::Interest::READABLE, )?; tokio::task::spawn(async move { loop { let mut guard = logger.readable_mut().await.unwrap(); guard.get_inner_mut().flush(); guard.clear_ready(); } }); } } let program: &mut Xdp = bpf.program_mut("xdp_firewall").unwrap().try_into()?; program.load()?; program.attach(&opt.iface, XdpMode::default()) .context("failed to attach the XDP program with default mode - " "try changing XdpMode::default() to " "XdpMode::Skb")?; let mut blocklist: HashMap<_, u32, u32> = HashMap::try_from(bpf.map_mut("BLOCKLIST").unwrap())?; let block_addr: u32 = Ipv4Addr::new(1, 1, 1, 1).into(); blocklist.insert(block_addr, 0, 0)?; let ctrl_c = signal::ctrl_c(); info!("Waiting for Ctrl-C..."); ctrl_c.await?; info!("Exiting..."); Ok(()) } ``` -------------------------------- ### Main Function for XDP Firewall Source: https://github.com/aya-rs/book/blob/main/src/book/programs/xdp.md The main entry point for the XDP firewall application. It handles argument parsing, eBPF program loading, logger initialization, XDP program attachment, IP blocklist configuration, and signal handling for graceful exit. ```rust #[tokio::main] async fn main() -> Result<(), anyhow::Error> { let opt = Opt::parse(); env_logger::init(); let mut bpf = aya::Ebpf::load(aya::include_bytes_aligned!(concat!( env!("OUT_DIR"), "/simple-xdp-program" )))?; match EbpfLogger::init(&mut bpf) { Err(e) => { // This can happen if you remove all log statements from your eBPF program. warn!("failed to initialize eBPF logger: {e}"); } Ok(logger) => { let mut logger = tokio::io::unix::AsyncFd::with_interest( logger, tokio::io::Interest::READABLE, )?; tokio::task::spawn(async move { loop { let mut guard = logger.readable_mut().await.unwrap(); guard.get_inner_mut().flush(); guard.clear_ready(); } }); } } let program: &mut Xdp = bpf.program_mut("xdp_firewall").unwrap().try_into()?; program.load()?; program.attach(&opt.iface, XdpMode::default()) .context("failed to attach the XDP program with default mode - " "try changing XdpMode::default() to XdpMode::Skb")?; let mut blocklist: HashMap<_, u32, u32> = HashMap::try_from(bpf.map_mut("BLOCKLIST").unwrap())?; let block_addr: u32 = Ipv4Addr::new(1, 1, 1, 1).into(); blocklist.insert(block_addr, 0, 0)?; let ctrl_c = signal::ctrl_c(); info!("Waiting for Ctrl-C..."); ctrl_c.await?; info!("Exiting..."); Ok(()) } ``` -------------------------------- ### XDP Firewall Entry Point Source: https://github.com/aya-rs/book/blob/main/src/book/programs/xdp.md The main XDP program function that delegates packet processing to try_xdp_firewall and handles potential errors by returning XDP_ABORTED. ```rust #[xdp] pub fn xdp_firewall(ctx: XdpContext) -> u32 { match try_xdp_firewall(ctx) { Ok(ret) => ret, Err(_) => xdp_action::XDP_ABORTED, } } ``` -------------------------------- ### Import necessary dependencies for Aya eBPF Source: https://github.com/aya-rs/book/blob/main/src/book/programs/xdp.md Imports required modules from aya_ebpf, aya_log_ebpf, core::mem, and network_types for eBPF program development. ```rust #![no_std] #![no_main] use aya_ebpf::{ bindings::xdp_action, macros::{map, xdp}, maps::HashMap, programs::XdpContext, }; use aya_log_ebpf::info; use core::mem; use network_types::{ eth::{EthHdr, EtherType}, ip::Ipv4Hdr, }; ``` -------------------------------- ### Define Command-Line Arguments for Network Interface Source: https://github.com/aya-rs/book/blob/main/src/book/programs/xdp.md Defines a struct for command-line argument parsing using clap, allowing users to specify the network interface name. ```rust #[derive(Debug, Parser)] struct Opt { #[clap(short, long, default_value = "eth0")] iface: String, } ``` -------------------------------- ### Full Aya eBPF XDP Firewall Code Source: https://github.com/aya-rs/book/blob/main/src/book/programs/xdp.md The complete source code for the eBPF XDP firewall, including all necessary imports, panic handler, blocklist definition, and packet processing logic. ```rust #![no_std] #![no_main] #![allow(nonstandard_style, dead_code)] use aya_ebpf::{ bindings::xdp_action, macros::{map, xdp}, maps::HashMap, programs::XdpContext, }; use aya_log_ebpf::info; use core::mem; use network_types::{ eth::{EthHdr, EtherType}, ip::Ipv4Hdr, }; #[cfg(not(test))] #[panic_handler] ``` -------------------------------- ### Define eBPF Blocklist HashMap Source: https://github.com/aya-rs/book/blob/main/src/book/programs/xdp.md Defines a static HashMap named BLOCKLIST to store blocked IP addresses (u32) with a maximum capacity of 1024 entries. ```rust #[map] static BLOCKLIST: HashMap = HashMap::with_max_entries(1024, 0); ``` -------------------------------- ### XDP Firewall eBPF Program Source: https://github.com/aya-rs/book/blob/main/src/book/programs/xdp.md This eBPF program implements an XDP firewall. It checks incoming IPv4 packets against a blocklist and drops packets from blocked IPs. Requires the `xdp_action` enum and `EthHdr`, `Ipv4Hdr` types. ```rust fn panic(_info: &core::panic::PanicInfo) -> ! { loop {} } #[map] static IP_BLOCKLIST: HashMap = HashMap::with_max_entries(1024, 0); #[xdp] pub fn xdp_firewall(ctx: XdpContext) -> u32 { match try_xdp_firewall(ctx) { Ok(ret) => ret, Err(_) => xdp_action::XDP_ABORTED, } } #[inline(always)] unsafe fn ptr_at( ctx: &XdpContext, offset: usize, ) -> Result<*const T, ()> { let start = ctx.data(); let end = ctx.data_end(); let len = mem::size_of::(); if start + offset + len > end { return Err(()); } let ptr = (start + offset) as *const T; Ok(&*ptr) } fn block_ip(address: u32) -> bool { unsafe { IP_BLOCKLIST.get(&address).is_some() } } fn try_xdp_firewall(ctx: XdpContext) -> Result { let ethhdr: *const EthHdr = unsafe { ptr_at(&ctx, 0)? }; match unsafe { (*ethhdr).ether_type() } { Ok(EtherType::Ipv4) => {} _ => return Ok(xdp_action::XDP_PASS), } let ipv4hdr: *const Ipv4Hdr = unsafe { ptr_at(&ctx, EthHdr::LEN)? }; let source = u32::from_be_bytes(unsafe { (*ipv4hdr).src_addr }); let action = if block_ip(source) { xdp_action::XDP_DROP } else { xdp_action::XDP_PASS }; info!(&ctx, "SRC: {:i}, ACTION: {}", source, action); Ok(action) } ``` -------------------------------- ### eBPF Panic Handler Source: https://github.com/aya-rs/book/blob/main/src/book/programs/xdp.md Provides a minimal panic handler required for eBPF programs, as they cannot use the default behavior. ```rust #[cfg(not(test))] #[panic_handler] fn panic(_info: &core::panic::PanicInfo) -> ! { loop {} } ``` -------------------------------- ### XDP Firewall Packet Processing Logic Source: https://github.com/aya-rs/book/blob/main/src/book/programs/xdp.md Processes incoming packets, checks if the source IP is in the blocklist, and returns XDP_DROP or XDP_PASS accordingly. Logs the source IP and action taken. ```rust fn block_ip(address: u32) -> bool { unsafe { BLOCKLIST.get(&address).is_some() } } fn try_xdp_firewall(ctx: XdpContext) -> Result { let ethhdr: *const EthHdr = unsafe { ptr_at(&ctx, 0)? }; match unsafe { (*ethhdr).ether_type() } { Ok(EtherType::Ipv4) => {} // Process IPv4 packets _ => return Ok(xdp_action::XDP_PASS), // Pass non-IPv4 packets } let ipv4hdr: *const Ipv4Hdr = unsafe { ptr_at(&ctx, EthHdr::LEN)? }; let source = u32::from_be_bytes(unsafe { (*ipv4hdr).src_addr }); let action = if block_ip(source) { xdp_action::XDP_DROP } else { xdp_action::XDP_PASS }; info!(&ctx, "SRC: {:i}, ACTION: {}", source, action); Ok(action) } ``` -------------------------------- ### Safe Pointer Access for eBPF Context Source: https://github.com/aya-rs/book/blob/main/src/book/programs/xdp.md A helper function to safely access data within an XdpContext at a given offset, performing bounds checking to prevent out-of-bounds reads. ```rust #[inline(always)] unsafe fn ptr_at( ctx: &XdpContext, offset: usize, ) -> Result<*const T, ()> { let start = ctx.data(); let end = ctx.data_end(); let len = mem::size_of::(); if start + offset + len > end { return Err(()); } let ptr = (start + offset) as *const T; Ok(&*ptr) } ```