ååã®ç¶ãã§ãã
ä»åã¯libbpf-toolsã«ããexecsnoopã®ã³ãã³ãã©ã¤ã³é¨åãRustã«ç§»æ¤ãããã¾ããRustã®workspaceã使ã£ã¦ä»¥ä¸ã®ããã« execsnoop
ã¯ã¬ã¼ããä½æããã bpf/
ãã£ã¬ã¯ããªã«BPFã®ããã°ã©ã ãç½®ãã
. âââ Cargo.toml âââ LICENSE âââ README.md âââ execsnoop/   âââ Cargo.toml   âââ src   âââ bpf/   âââ main.rs
bpf/
ã«é
ç½®ããã execsnoop.bpf.c
ã¯ä»¥ä¸ããã¼ã¹ã«ã³ãã¼ãã¦ããã°ãããã execsnoop.h
ã§å®ç¾©ãããæ§é ä½ãå®æ°ã¯åããã¡ã¤ã«ã«å±éãã¦ããã
åããã¦ãã³ã³ãã¤ã«ã§ããããã«åããã£ã¬ã¯ããªã« vmlinux.h
ãç½®ãã¦ãããæ¬æ¥ã¯ã«ã¼ãã«ãã«ãæã«ã§ãããã®ãç½®ãã¦ããæããªã®ã ãããï¼ ãã£ãã以ä¸ã®ãã¡ã¤ã«ã¨åããã®ãã³ãã¼ããã
ãã®æ®µéã§ã¾ãBPFããã°ã©ã ã ãã³ã³ãã¤ã«ãããªãã¸ã§ã¯ããä½ããã¾ããskelãä½ãã
$ cd execsnoop $ cargo libbpf build $ ls -l ../target/bpf/ total 992 -rw-r--r-- 1 vagrant vagrant 1015216 Jan 7 10:52 execsnoop.bpf.o $ cargo libbpf gen Warning: unrecognized map: .maps Warning: unrecognized map: license $ ls -l src/bpf/ total 2692 -rw-r--r-- 1 vagrant vagrant 3887 Jan 5 13:21 execsnoop.bpf.c -rw-r--r-- 1 vagrant vagrant 6622 Jan 7 10:52 execsnoop.skel.rs -rw-r--r-- 1 vagrant vagrant 189 Jan 7 10:52 mod.rs -rw-r--r-- 1 vagrant vagrant 2734479 Jan 5 11:02 vmlinux.h
Rustããã°ã©ã å´ã§ã¯ãã®skelãç¨ãã¦ãã³ãã³ãããBPFããã°ã©ã ãã¢ã¿ãããPerf Bufferããåºã¦ãããã¼ã¿ã表示ããã°OKã ... ã¨ããã§execsnoopã®å ã®å®è£ ã¯ããã©ã¼ãããç³»ã®ãªãã·ã§ã³ããããããããä¸æ¦å ¨é¨åºãããã©ã¡ã¼ã¿ç³»ã ããµãã¼ããããã¨ããæãã«ããã
å®è£ ã¨èª¬æã®ã³ã¡ã³ã
æçµçã«ããããæãã®å®è£ ãã¾ãã¯å ¨ä½ãã
// Rust port of execsnoop.c // See also: https://github.com/iovisor/bcc/blob/master/libbpf-tools/execsnoop.c use core::mem; use core::time::Duration; use std::str; use std::convert::TryFrom; use chrono::Local; use anyhow::Result; use libbpf_rs::PerfBufferBuilder; use plain::Plain; use structopt::StructOpt; #[macro_use] extern crate lazy_static; mod bpf; use bpf::*; // ãªãã·ã§ã³ç¨ã®æ§é ä½ãStructOptã§å®ç¾© #[derive(Debug, StructOpt)] struct Command { /// Trace this UID only #[structopt(short = "u", default_value = "-1", value_name = "UID")] uid: i32, /// Include failed exec()s #[structopt(short = "x")] fails: bool, /// Maximum number of arguments parsed and displayed #[structopt(long = "max-args", default_value = "20", value_name = "MAX_ARGS")] max_args: i32, } // Perf Bufferããéãããã¤ãã³ããåãåãããã®æ§é ä½ // Cã®ã¬ã¤ã¢ã¦ãã«ããã詳細ã¯å¾è¿° #[repr(C)] #[derive(Default)] struct Event { pub comm: [u8; 16], pub pid: i32, pub tgid: i32, pub ppid: i32, pub uid: i32, pub retval: i32, pub args_count: i32, pub args_size: u32, } unsafe impl Plain for Event {} // çµéæéãè¨æ¸¬ããããã®ã¿ã¤ãã¼ããlazy_staticã§staticã«çæããã mod timer { lazy_static! { pub static ref TIMER: std::time::Instant = { std::time::Instant::now() }; } } // perfã®ã¤ãã³ããã³ãã© fn handle_event(_cpu: i32, data: &[u8]) { let now = Local::now(); // ä¸è¨ã® timer::TIMER ã¯ããããã£ã¦å¼ã³åºããã³ã«ãèµ·åæããã®çµéDurationãè¿ãã¦ãããã // BPF toolsã§ã¯é »åºã®ãã¿ã¼ã³ãªã®ã§ä¾¿å©ã let elap = timer::TIMER.elapsed().as_nanos() as f32 / (1000*1000*1000) as f32; let mut event: Event = Event::default(); let event_size = mem::size_of_val(&event); // ãã®è¾ºã® Event ã®ã¢ã³ããã¯ã args ã®æ±ãã¯å¾è¿°... plain::copy_from_bytes(&mut event, data).expect("Data buffer was too short"); let comm = str::from_utf8(&event.comm).unwrap().trim_end_matches('\0'); let args: Vec<&str> = str::from_utf8(&data[event_size..]).unwrap().trim_end_matches('\0').split('\0').collect(); // 表示é¨å println!( "{:8} {:<8.3} {:<6} {:16} {:<6} {:<6} {:3} {:?}", now.format("%H:%M:%S"), elap, event.uid, comm, event.pid, event.ppid, event.retval, args ); } // ãã¡ãã¯Perf Bufferã®ã¤ãã³ããã¹ãæã®ãã㯠fn handle_lost_events(cpu: i32, count: u64) { eprintln!("Lost {} events on CPU {}", count, cpu); } // main // ãªããä¸ã® println! ã¯ãã®è¦åã«å¯¾å¿ããã¨éã«æå³ãåããã¥ããã®ã§ãç¡è¦æå®ã #[allow(clippy::print_literal)] fn main() -> Result<()> { let opts: Command = Command::from_args(); // Builder ã®çæã¨ãªã¼ãã³ let mut skel_builder: ExecsnoopSkelBuilder = ExecsnoopSkelBuilder::default(); let mut open_skel: OpenExecsnoopSkel = skel_builder.open()?; // ãã©ã¡ã¼ã¿ãåã込㿠if opts.uid >= 0 { let uid: u32 = TryFrom::try_from(opts.uid)?; open_skel.rodata().targ_uid = uid; } else { open_skel.rodata().targ_uid = u32::MAX; } if opts.fails { open_skel.rodata().ignore_failed = 0 } else { open_skel.rodata().ignore_failed = 1 } open_skel.rodata().max_args = opts.max_args; // ãã¼ãã¨ã¢ã¿ãã let mut skel = open_skel.load()?; skel.attach()?; // ãããã表示ããã®è¾ºã¯ãBCCãªããã¨åãããªã println!( "{:8} {:8} {:6} {:16} {:6} {:6} {:3} {:}", "TIME", "TIME(s)", "UID", "PCOMM", "PID", "PPID", "RET", "ARGS" ); // lazy_staticãªã¿ã¤ãã¼ãããã§åæåãããããããã¼ã§ elapsed() ãå¼ã¶ã timer::TIMER.elapsed(); // To initialize static timer // Perf Bufferã®ãªã¼ãã³ let perf = PerfBufferBuilder::new(skel.maps().events()) .sample_cb(handle_event) .lost_cb(handle_lost_events) .build()?; // ãã¨ã¯loopã§ãã¼ãªã³ã° loop { perf.poll(Duration::from_millis(100))?; } }
ç´°ããç®æã®èª¬æ
Skel ã®å©ç¨ã¨ãã©ã¡ã¼ã¿ã®å¤æ´
bpfã®ããã°ã©ã ããçæãããSkelã¯ã大ä½ä»¥ä¸ã®ãããªæãã§ä½¿ããã¨ã«ãªãã
let mut builder = HogeSkelBuilder::default(); let mut open_skel = builder.open()?; open_skel.rodata().foo_arg = opts.foo_arg; //... let mut skel = open_skel.load()?; skel.attach()?;
ãã®éããã¼ãæã®ãªãã·ã§ã³ã渡ãç®æã¯ãBPFããã°ã©ã å´ã® const
ãªå¤æ°ã«å¯¾å¿ãã¦çæããããexecsnoopãªã以ä¸ã
const volatile bool ignore_failed = true; const volatile uid_t targ_uid = INVALID_UID; const volatile int max_args = DEFAULT_MAXARGS; // å®ã¯ãªããstatic const struct event empty_event = {} ã // å¯å¤ãªãã©ã¡ã¼ã¿æ±ãã«ãªã£ã¦ãã¾ããã触ããªããã¨ã
ãã®å®ç¾©ãæ¤ç¥ãã¦skelã§ä»¥ä¸ã®ãããªæ§é ä½ãçæããã
#[derive(Debug, Copy, Clone)] #[repr(C)] pub struct rodata { pub ignore_failed: u8, pub targ_uid: u32, pub max_args: i32, pub empty_event: event, }
openããskelã®ã¡ã½ãã rodata()
ãããã®æ§é ä½ã«ã¢ã¯ã»ã¹ã§ããããããã§å¤ãåãã¦ããã°OKã
Perf Bufferããæ»ã£ã¦ãããã¼ã¿ã®æ±ã
ä»åã®execsnoopã§ã¯ã以ä¸ã®Cæ§é ä½ã«å¯¾å¿ããRustã®æ§é ä½ã«å¯¾ãã plain ã¯ã¬ã¼ãçµç±ã§ãã¼ã¿ãã¢ã³ããã¯ãã¦ãããã°å©ç¨ã§ããã
#define TASK_COMM_LEN 16 #define ARGSIZE 128 #define TOTAL_MAX_ARGS 60 #define FULL_MAX_ARGS_ARR (TOTAL_MAX_ARGS * ARGSIZE) struct event { char comm[TASK_COMM_LEN]; pid_t pid; pid_t tgid; pid_t ppid; uid_t uid; int retval; int args_count; unsigned int args_size; char args[FULL_MAX_ARGS_ARR]; };
ãã ããã®ã¬ã¤ã¢ã¦ãã«ãã®ã¾ã¾å¯¾å¿ããRustã®æ§é ä½ãå®ç¾©ãããã¨ãã¦ã以ä¸ã®ããã«è¨è¿°ããã¨é£åããã
#[repr(C)] #[derive(Default)] struct Event { pub comm: [u8; 16], pub pid: i32, pub tgid: i32, pub ppid: i32, pub uid: i32, pub retval: i32, pub args_count: i32, pub args_size: u32, pub args: [u8; 7680], }
ã¾ã pub args: [u8; 7680]
ã¨ããå®ç¾©ãé·ãããããã§ã Default
ã注éã§ããªãããã®ã¾ã¾ã§ã¯ã¤ã³ã¹ã¿ã³ã¹ã®åæåã«é£åããç¾½ç®ã«ãªãã
ããã«ããã¨ãå®ã¯ãä»åã¯argsã¯å¯å¤é·ã®ãããªæ±ãã«ãªãããã§ã handle_event(_cpu: i32, data: &[u8])
ã«æ¸¡ã£ã¦ãã data
ã®é·ããã¾ã¡ã¾ã¡ã«ãªã£ã¦ãã¾ãããããã£ã¦ãplainã®ä»æ§ã«ããããã Event::default()
ã§ç¢ºä¿ãããµã¤ãºããçããµã¤ãºã® data ãæ¥ã¦ãã¾ã£ãå ´åã plain::copy_from_bytes
ã¯å¤±æããã
ä»åã¯å¾ãã® args
ã¡ã³ãã¯ã¢ã³ããã¯ããç¯å²ã«å«ããã args_size
ã¾ã§ãã¢ã³ããã¯ãã¦å©ç¨ãããã¨ã«ãããéã« plain::copy_from_bytes
ã®ã³ãã¼å
ã Event::default()
æ§é ä½ã®ãµã¤ãºããããã¼ã¿ã®ã»ããé·ãå ´åã¯ããã¼ã¿ã®å¾ããç¡è¦ããã ããªã®ã§åé¡ããªãã
#[repr(C)] #[derive(Default)] struct Event { pub comm: [u8; 16], pub pid: i32, pub tgid: i32, pub ppid: i32, pub uid: i32, pub retval: i32, pub args_count: i32, pub args_size: u32, } unsafe impl Plain for Event {}
æ®ãã® args
ã¯ä»¥ä¸ã®ããã«åãåºãã¦çã® [u8]
ã®ã¹ã©ã¤ã¹ã¨ãã¦ã¢ã¯ã»ã¹ã§ããã
let mut event: Event = Event::default(); let event_size = mem::size_of_val(&event); let raw_args = &data[event_size..];
å½ç¶ã¨ãããããã㯠\0
åºåãã®ãã¤ãåã§ããã®ã§ããã¨ã¯ä»¥ä¸ã®ããã«æ±ãã°ããããã
let args: Vec<&str> = str::from_utf8(raw_args) .unwrap() .trim_end_matches('\0') .split('\0') .collect();
å®éã®åä½
ãã«ãããã¨ä»¥ä¸ã®ããã«ãæ®éã« execsnoop ã¨ãã¦åä½ããã -u
ã -x
ã --max-args
ãåä½ãã¾ãã
$ sudo ../target/debug/execsnoop --help execsnoop 0.1.0 USAGE: execsnoop [FLAGS] [OPTIONS] FLAGS: -x Include failed exec()s -h, --help Prints help information -V, --version Prints version information OPTIONS: --max-args <MAX_ARGS> Maximum number of arguments parsed and displayed [default: 20] -u <UID> Trace this UID only [default: -1]
ã¾ã¨ã
æ¬è¨äºã§ãRustã§ã®BPF (CO-RE) toolã®å®è£ ä¾ã¨ãã¦ãexecsnoopã®å®è£ ããã¼ããããã¾ããRustã¨BPFã§ã©ã®ããã«ãã©ã¡ã¼ã¿ããã¼ã¿ãããåããããã®çæç¹ãæ¸ããã
BPFããã°ã©ã ã¯æ¢åã®ãã®ãå©ç¨ãããããä»å¾ã¯ããã¤ãç°¡åã§ããªãªã¸ãã«ã®ãã¼ã«ãå®è£ ãã¦ããããã
ä»åã®å®è£ ã¯ããã«ã¢ãããã¦ã¾ã: