Init commit

pull/165/head
Joseph Ligier 1 month ago
parent db916612e8
commit 675464a396

@ -0,0 +1,7 @@
module gaya.com/m
go 1.24.4
require github.com/cilium/ebpf v0.20.0
require golang.org/x/sys v0.37.0 // indirect

@ -0,0 +1,26 @@
github.com/cilium/ebpf v0.20.0 h1:atwWj9d3NffHyPZzVlx3hmw1on5CLe9eljR8VuHTwhM=
github.com/cilium/ebpf v0.20.0/go.mod h1:pzLjFymM+uZPLk/IXZUL63xdx5VXEo+enTzxkZXdycw=
github.com/go-quicktest/qt v1.101.1-0.20240301121107-c6c8733fa1e6 h1:teYtXy9B7y5lHTp8V9KPxpYRAVA7dozigQcMiBust1s=
github.com/go-quicktest/qt v1.101.1-0.20240301121107-c6c8733fa1e6/go.mod h1:p4lGIVX+8Wa6ZPNDvqcxq36XpUDLh42FLetFU7odllI=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/josharian/native v1.1.0 h1:uuaP0hAbW7Y4l0ZRQ6C9zfb7Mg1mbFKry/xzDAfmtLA=
github.com/josharian/native v1.1.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
github.com/jsimonetti/rtnetlink/v2 v2.0.1 h1:xda7qaHDSVOsADNouv7ukSuicKZO7GgVUCXxpaIEIlM=
github.com/jsimonetti/rtnetlink/v2 v2.0.1/go.mod h1:7MoNYNbb3UaDHtF8udiJo/RH6VsTKP1pqKLUTVCvToE=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/mdlayher/netlink v1.7.2 h1:/UtM3ofJap7Vl4QWCPDGXY8d3GIY2UGSDbK+QWmY8/g=
github.com/mdlayher/netlink v1.7.2/go.mod h1:xraEF7uJbxLhc5fpHL4cPe221LI2bdttWlU+ZGLfQSw=
github.com/mdlayher/socket v0.4.1 h1:eM9y2/jlbs1M615oshPQOHZzj6R6wMT7bX5NPiQvn2U=
github.com/mdlayher/socket v0.4.1/go.mod h1:cAqeGjoufqdxWkD7DkpyS+wcefOtmu5OQ8KuoJGIReA=
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
golang.org/x/net v0.46.0 h1:giFlY12I07fugqwPuWJi68oOnpfqFnJIJzaIIm2JVV4=
golang.org/x/net v0.46.0/go.mod h1:Q9BGdFy1y4nkUwiLvT5qtyhAnEHgnQ/zd8PfU6nc210=
golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug=
golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ=
golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=

@ -0,0 +1,89 @@
package main
import (
"context"
"fmt"
"log"
"net"
"os"
"os/signal"
"syscall"
"github.com/cilium/ebpf"
"github.com/cilium/ebpf/link"
"github.com/cilium/ebpf/ringbuf"
)
const progName = "{{crate_name}}"
func main() {
if len(os.Args) < 2 {
fmt.Printf("Usage: %s <interface>\n", os.Args[0])
os.Exit(1)
}
ifaceName := os.Args[1]
spec, err := ebpf.LoadCollectionSpec("/tmp/{{project-name}}")
if err != nil {
log.Fatalf("LoadCollectionSpec failed: %v", err)
}
coll, err := ebpf.NewCollection(spec)
if err != nil {
log.Fatalf("NewCollection failed: %v", err)
}
defer coll.Close()
prog := coll.Programs[progName]
if prog == nil {
log.Fatalf("Program %s not found", progName)
}
iface, err := net.InterfaceByName(ifaceName)
if err != nil {
log.Fatalf("Interface not found: %v", err)
}
l, err := link.AttachXDP(link.XDPOptions{
Program: prog,
Interface: iface.Index,
})
if err != nil {
log.Fatalf("AttachXDP failed: %v", err)
}
defer l.Close()
fmt.Printf("✅ Program '%s' attached to %s\n", progName, ifaceName)
logMap, ok := coll.Maps["AYA_LOGS"]
if !ok {
log.Fatal("AYA_LOGS map not found")
}
reader, err := ringbuf.NewReader(logMap)
if err != nil {
log.Fatalf("failed to create ringbuf reader: %v", err)
}
defer reader.Close()
ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM)
defer stop()
go func() {
fmt.Println("Listening to Aya logs...")
for {
select {
case <-ctx.Done():
return
default:
record, err := reader.Read()
if err != nil {
continue
}
fmt.Printf("Aya log: %s\n", string(record.RawSample))
}
}
}()
<-ctx.Done()
fmt.Println("Shutting down...")
}

@ -1,82 +1,8 @@
{%- case program_type -%}
{%- when "kprobe", "kretprobe" -%}
use aya::programs::KProbe;
{%- when "fentry" -%}
use anyhow::Context as _;
use aya::{Btf, programs::FEntry};
{%- when "fexit" -%}
use anyhow::Context as _;
use aya::{Btf, programs::FExit};
{%- when "uprobe", "uretprobe" -%}
use aya::programs::UProbe;
{%- when "sock_ops" -%}
use anyhow::Context as _;
use aya::programs::{SockOps, links::CgroupAttachMode};
{%- when "sk_msg" -%}
use aya::{maps::SockHash, programs::SkMsg};
use {{crate_name}}_common::SockKey;
{%- when "xdp" -%}
use anyhow::Context as _;
use aya::programs::{Xdp, XdpFlags};
{%- when "classifier" -%}
use aya::programs::{SchedClassifier, TcAttachType, tc};
{%- when "cgroup_skb" -%}
use anyhow::Context as _;
use aya::programs::{CgroupSkb, CgroupSkbAttachType, links::CgroupAttachMode};
{%- when "cgroup_sysctl" -%}
use anyhow::Context as _;
use aya::programs::{CgroupSysctl, links::CgroupAttachMode};
{%- when "cgroup_sockopt" -%}
use anyhow::Context as _;
use aya::programs::{CgroupSockopt, links::CgroupAttachMode};
{%- when "tracepoint" -%}
use aya::programs::TracePoint;
{%- when "lsm" -%}
use aya::{Btf, programs::Lsm};
{%- when "perf_event" -%}
use aya::{
programs::{PerfEvent, perf_event},
util::online_cpus,
};
{%- when "tp_btf" -%}
use aya::{Btf, programs::BtfTracePoint};
{%- when "socket_filter" -%}
use aya::programs::SocketFilter;
{%- when "raw_tracepoint" -%}
use aya::programs::RawTracePoint;
{%- endcase %}
{% if program_types_with_opts contains program_type -%}
use clap::Parser;
{% endif -%}
#[rustfmt::skip]
use log::{debug, warn};
use tokio::signal;
{% if program_types_with_opts contains program_type -%}
#[derive(Debug, Parser)]
struct Opt {
{%- case program_type -%}
{%- when "xdp", "classifier" %}
#[clap(short, long, default_value = "{{default_iface}}")]
iface: String,
{%- when "sock_ops", "cgroup_skb", "cgroup_sysctl", "cgroup_sockopt" %}
#[clap(short, long, default_value = "/sys/fs/cgroup")]
cgroup_path: std::path::PathBuf,
{%- when "uprobe", "uretprobe" %}
#[clap(short, long)]
pid: Option<i32>,
{%- endcase %}
}
{% endif -%}
#[tokio::main]
async fn main() -> anyhow::Result<()> {
{%- if program_types_with_opts contains program_type %}
let opt = Opt::parse();
{% endif %}
env_logger::init();
use log::debug;
use std::fs;
fn main() -> anyhow::Result<()> {
// Bump the memlock rlimit. This is needed for older kernels that don't use the
// new memcg based accounting, see https://lwn.net/Articles/837122/
let rlim = libc::rlimit {
@ -88,139 +14,9 @@ async fn main() -> anyhow::Result<()> {
debug!("remove limit on locked memory failed, ret is: {ret}");
}
// This will include your eBPF object file as raw bytes at compile-time and load it at
// runtime. This approach is recommended for most real-world use cases. If you would
// like to specify the eBPF program at runtime rather than at compile-time, you can
// reach for `Bpf::load_file` instead.
let mut ebpf = aya::Ebpf::load(aya::include_bytes_aligned!(concat!(
env!("OUT_DIR"),
"/{{project-name}}"
)))?;
match aya_log::EbpfLogger::init(&mut ebpf) {
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();
}
});
}
}
{%- case program_type -%}
{%- when "kprobe", "kretprobe" %}
let program: &mut KProbe = ebpf.program_mut("{{crate_name}}").unwrap().try_into()?;
program.load()?;
program.attach("{{kprobe}}", 0)?;
{%- when "fentry" %}
let btf = Btf::from_sys_fs().context("BTF from sysfs")?;
let program: &mut FEntry = ebpf.program_mut("{{crate_name}}").unwrap().try_into()?;
program.load("{{fn_name}}", &btf)?;
program.attach()?;
{%- when "fexit" %}
let btf = Btf::from_sys_fs().context("BTF from sysfs")?;
let program: &mut FExit = ebpf.program_mut("{{crate_name}}").unwrap().try_into()?;
program.load("{{fn_name}}", &btf)?;
program.attach()?;
{%- when "uprobe", "uretprobe" %}
let Opt { pid } = opt;
let program: &mut UProbe = ebpf.program_mut("{{crate_name}}").unwrap().try_into()?;
program.load()?;
program.attach("{{uprobe_fn_name}}", "{{uprobe_target}}", pid, None /* cookie */)?;
{%- when "sock_ops", "cgroup_skb", "cgroup_sysctl", "cgroup_sockopt" %}
let Opt { cgroup_path } = opt;
let cgroup =
std::fs::File::open(&cgroup_path).with_context(|| format!("{}", cgroup_path.display()))?;
{%- if program_type == "sock_ops" %}
let program: &mut SockOps = ebpf.program_mut("{{crate_name}}").unwrap().try_into()?;
program.load()?;
program.attach(cgroup, CgroupAttachMode::default())?;
{%- elsif program_type == "cgroup_skb" %}
let program: &mut CgroupSkb = ebpf.program_mut("{{crate_name}}").unwrap().try_into()?;
program.load()?;
program.attach(
cgroup,
CgroupSkbAttachType::{{direction}},
CgroupAttachMode::default(),
)?;
{%- elsif program_type == "cgroup_sysctl" %}
let program: &mut CgroupSysctl = ebpf.program_mut("{{crate_name}}").unwrap().try_into()?;
program.load()?;
program.attach(cgroup, CgroupAttachMode::default())?;
{%- elsif program_type == "cgroup_sockopt" %}
let program: &mut CgroupSockopt = ebpf.program_mut("{{crate_name}}").unwrap().try_into()?;
program.load()?;
program.attach(cgroup, CgroupAttachMode::default())?;
{%- endif -%}
{%- when "sk_msg" %}
let sock_map: SockHash<_, SockKey> = ebpf.map("{{sock_map}}").unwrap().try_into()?;
let map_fd = sock_map.fd().try_clone()?;
let prog: &mut SkMsg = ebpf.program_mut("{{crate_name}}").unwrap().try_into()?;
prog.load()?;
prog.attach(&map_fd)?;
// insert sockets to the map using sock_map.insert here, or from a sock_ops program
{%- when "xdp" %}
let Opt { iface } = opt;
let program: &mut Xdp = ebpf.program_mut("{{crate_name}}").unwrap().try_into()?;
program.load()?;
program.attach(&iface, XdpFlags::default())
.context("failed to attach the XDP program with default flags - try changing XdpFlags::default() to XdpFlags::SKB_MODE")?;
{%- when "classifier" %}
let Opt { iface } = opt;
// error adding clsact to the interface if it is already added is harmless
// the full cleanup can be done with 'sudo tc qdisc del dev eth0 clsact'.
let _ = tc::qdisc_add_clsact(&iface);
let program: &mut SchedClassifier = ebpf.program_mut("{{crate_name}}").unwrap().try_into()?;
program.load()?;
program.attach(&iface, TcAttachType::{{direction}})?;
{%- when "tracepoint" %}
let program: &mut TracePoint = ebpf.program_mut("{{crate_name}}").unwrap().try_into()?;
program.load()?;
program.attach("{{tracepoint_category}}", "{{tracepoint_name}}")?;
{%- when "lsm" %}
let btf = Btf::from_sys_fs()?;
let program: &mut Lsm = ebpf.program_mut("{{lsm_hook}}").unwrap().try_into()?;
program.load("{{lsm_hook}}", &btf)?;
program.attach()?;
{%- when "tp_btf" %}
let btf = Btf::from_sys_fs()?;
let program: &mut BtfTracePoint = ebpf.program_mut("{{tracepoint_name}}").unwrap().try_into()?;
program.load("{{tracepoint_name}}", &btf)?;
program.attach()?;
{%- when "socket_filter" %}
let listener = std::net::TcpListener::bind("localhost:0")?;
let prog: &mut SocketFilter = ebpf.program_mut("{{crate_name}}").unwrap().try_into()?;
prog.load()?;
prog.attach(&listener)?;
{%- when "perf_event" %}
// This will raise scheduled events on each CPU at 1 HZ, triggered by the kernel based
// on clock ticks.
let program: &mut PerfEvent = ebpf.program_mut("{{crate_name}}").unwrap().try_into()?;
program.load()?;
for cpu in online_cpus().map_err(|(_, error)| error)? {
program.attach(
perf_event::PerfEventConfig::Software(perf_event::SoftwareEvent::CpuClock),
perf_event::PerfEventScope::AllProcessesOneCpu { cpu },
perf_event::SamplePolicy::Frequency(1),
true,
)?;
}
{%- when "raw_tracepoint" %}
let program: &mut RawTracePoint = ebpf.program_mut("{{crate_name}}").unwrap().try_into()?;
program.load()?;
program.attach("{{tracepoint_name}}")?;
{%- endcase %}
let ctrl_c = signal::ctrl_c();
println!("Waiting for Ctrl-C...");
ctrl_c.await?;
println!("Exiting...");
let src = concat!(env!("OUT_DIR"), "/{{project-name}}");
let dst = "/tmp/{{project-name}}";
fs::copy(src, dst)?;
Ok(())
}

Loading…
Cancel
Save