mirror of
https://github.com/eunomia-bpf/bpf-developer-tutorial.git
synced 2026-10-31 20:54:56 +08:00
Add advanced eBPF tutorials 51–54 and tutorial agent skills (#220)
* Add targeted TCP connection quarantine tool * Document TCP quarantine workflow * Refresh package indexes before CI install * Document TCP quarantine namespace scope * Require BPF events for TCP iterator * Add production fsession latency tracer * Document fsession latency workflow * Reject empty fsession CLI values * Preserve successful fsession exit status * Document fsession deadline regression test * Drain fsession events before summary * Document fsession PID and shutdown semantics * Simplify fsession polling control flow * Add bounded BPF egress pacer tutorial * Test queued packet cleanup on signal * Document the BPF egress pacer * Polish and review the BPF egress pacer * Refactor egress pacer option parsing * Add executable image inspector lesson * Build the exec image inspector in CI * Document executable image inspection * Handle re-exec chains in the image inspector * Polish exec image inspector guidance * Reduce exec inspector wait complexity * Rewrite the egress pacer tutorial * Rewrite the exec image inspector tutorial * Polish the exec image inspector tutorial * Correct the egress pacer tutorial * Make the exec tutorial Chinese natural * Fix egress tutorial prose checks * Finish the exec tutorial style pass * Clarify exec tutorial runtime evidence * Polish exec tutorial scope wording * Rewrite the egress pacer tutorial for readers * Regenerate tutorial indexes * Teach the egress pacer component by component * Use standard Markdown in the egress tutorial * Strengthen the egress pacer tutorial narrative * jiji * Use positive framing in the egress tutorial * Rewrite the exec image inspector tutorial * Rewrite the TCP quarantine tutorial * Add the egress pacer lifecycle diagram * Refresh TCP tutorial indexes * Add the exec image inspection flow diagram * Use one source directory link * Rewrite the fsession latency tutorial * Add fsession feature background * Add BPF qdisc feature background * Add TCP iterator feature background * Add exec inspection feature background * Require feature background in tutorial openings * Polish fsession background prose * Simplify repository tutorial skills * Update exec inspector tooling revisions * Restructure tutorials 51-54 following advanced guideline - Update SKILL.md with strict section ordering and "why" requirements - Rewrite README.zh.md for tutorials 51-54: - Add background sections explaining why traditional approaches fail - Move compilation/execution sections after code analysis - Add high-level mechanism explanation before code - Use paragraph style instead of bullet lists * Consolidate skill guidelines and remove duplicates - Update write-bpf-production-tutorial skill to reference 47, 48, 49 - Add structure requirements section with "Why" section emphasis - Remove duplicate advanced-tutorial-guidelines.md (use scripts/guideline_advance.md instead) * Rewrite English READMEs 51-54 to match Chinese structure Restructure all four English tutorials to match their Chinese counterparts: - Add "Why this approach is needed" sections explaining traditional limitations - Add high-level mechanism sections before code - Move compilation/execution sections after code analysis - Add environment requirements tables - Use consistent section ordering across all tutorials * Regenerate TOC after tutorial title changes Run generate_toc.py to sync SUMMARY and README files with updated tutorial titles from the restructuring. * Regenerate compatibility tables after title changes --------- Co-authored-by: LinuxDev9002 <i@littlefisher.me>
This commit is contained in:
198
src/54-exec-image-inspector/exec_image_inspector.bpf.c
Normal file
198
src/54-exec-image-inspector/exec_image_inspector.bpf.c
Normal file
@@ -0,0 +1,198 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
#include "vmlinux.h"
|
||||
#include <bpf/bpf_helpers.h>
|
||||
#include <bpf/bpf_tracing.h>
|
||||
#include "bpf_experimental.h"
|
||||
#include "exec_image_inspector.h"
|
||||
|
||||
char LICENSE[] SEC("license") = "GPL";
|
||||
|
||||
#define ENOENT 2
|
||||
|
||||
#define EI_CLASS 4
|
||||
#define EI_DATA 5
|
||||
#define ELFCLASS32 1
|
||||
#define ELFCLASS64 2
|
||||
#define ELFDATA2LSB 1
|
||||
#define ELFDATA2MSB 2
|
||||
|
||||
const volatile __u32 target_tgid;
|
||||
const volatile __u32 probe_offset;
|
||||
|
||||
struct inspector_stats stats;
|
||||
|
||||
struct {
|
||||
__uint(type, BPF_MAP_TYPE_RINGBUF);
|
||||
__uint(max_entries, 256 * 1024);
|
||||
} events SEC(".maps");
|
||||
|
||||
struct exec_work {
|
||||
__u64 scheduled_ns;
|
||||
int direct_probe_error;
|
||||
struct bpf_task_work work;
|
||||
};
|
||||
|
||||
struct {
|
||||
__uint(type, BPF_MAP_TYPE_ARRAY);
|
||||
__uint(max_entries, 1);
|
||||
__type(key, __u32);
|
||||
__type(value, struct exec_work);
|
||||
} pending SEC(".maps");
|
||||
|
||||
static __u16 read_elf_u16(const unsigned char *header, int offset, __u8 data)
|
||||
{
|
||||
if (data == ELFDATA2MSB)
|
||||
return ((__u16)header[offset] << 8) | header[offset + 1];
|
||||
return header[offset] | ((__u16)header[offset + 1] << 8);
|
||||
}
|
||||
|
||||
static int probe_file_without_sleep(struct file *file)
|
||||
{
|
||||
unsigned char sample[EXEC_PROBE_LEN];
|
||||
struct bpf_dynptr dynptr;
|
||||
int err;
|
||||
|
||||
if (!probe_offset)
|
||||
return 0;
|
||||
|
||||
__sync_fetch_and_add(&stats.direct_probes, 1);
|
||||
if (!file) {
|
||||
err = -ENOENT;
|
||||
goto record;
|
||||
}
|
||||
|
||||
err = bpf_dynptr_from_file(file, 0, &dynptr);
|
||||
if (err) {
|
||||
bpf_dynptr_file_discard(&dynptr);
|
||||
goto record;
|
||||
}
|
||||
|
||||
err = bpf_dynptr_read(sample, sizeof(sample), &dynptr, probe_offset, 0);
|
||||
bpf_dynptr_file_discard(&dynptr);
|
||||
|
||||
record:
|
||||
if (err)
|
||||
__sync_fetch_and_add(&stats.direct_probe_errors, 1);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int inspect_executable(struct bpf_map *map, void *key, void *value)
|
||||
{
|
||||
unsigned char header[64] = {};
|
||||
struct exec_work *work = value;
|
||||
struct exec_event event = {};
|
||||
struct task_struct *task;
|
||||
struct bpf_dynptr dynptr;
|
||||
struct file *file;
|
||||
__u64 pid_tgid;
|
||||
int err;
|
||||
|
||||
(void)map;
|
||||
(void)key;
|
||||
__sync_fetch_and_add(&stats.callbacks, 1);
|
||||
|
||||
pid_tgid = bpf_get_current_pid_tgid();
|
||||
event.pid = (__u32)pid_tgid;
|
||||
event.tgid = pid_tgid >> 32;
|
||||
event.latency_ns = bpf_ktime_get_ns() - work->scheduled_ns;
|
||||
event.direct_probe_error = work->direct_probe_error;
|
||||
event.probe_offset = probe_offset;
|
||||
bpf_get_current_comm(event.comm, sizeof(event.comm));
|
||||
|
||||
task = bpf_get_current_task_btf();
|
||||
file = bpf_get_task_exe_file(task);
|
||||
if (!file) {
|
||||
event.header_error = -ENOENT;
|
||||
__sync_fetch_and_add(&stats.header_errors, 1);
|
||||
if (probe_offset) {
|
||||
event.deferred_probe_error = -ENOENT;
|
||||
__sync_fetch_and_add(&stats.deferred_probes, 1);
|
||||
__sync_fetch_and_add(&stats.deferred_probe_errors, 1);
|
||||
}
|
||||
goto emit;
|
||||
}
|
||||
|
||||
err = bpf_path_d_path(&file->f_path, event.path, sizeof(event.path));
|
||||
if (err < 0) {
|
||||
event.path_error = err;
|
||||
__sync_fetch_and_add(&stats.path_errors, 1);
|
||||
}
|
||||
|
||||
err = bpf_dynptr_from_file(file, 0, &dynptr);
|
||||
if (err) {
|
||||
bpf_dynptr_file_discard(&dynptr);
|
||||
event.header_error = err;
|
||||
__sync_fetch_and_add(&stats.header_errors, 1);
|
||||
if (probe_offset) {
|
||||
event.deferred_probe_error = err;
|
||||
__sync_fetch_and_add(&stats.deferred_probes, 1);
|
||||
__sync_fetch_and_add(&stats.deferred_probe_errors, 1);
|
||||
}
|
||||
goto put_file;
|
||||
}
|
||||
|
||||
err = bpf_dynptr_read(header, sizeof(header), &dynptr, 0, 0);
|
||||
if (err) {
|
||||
event.header_error = err;
|
||||
__sync_fetch_and_add(&stats.header_errors, 1);
|
||||
}
|
||||
|
||||
if (probe_offset) {
|
||||
__sync_fetch_and_add(&stats.deferred_probes, 1);
|
||||
err = bpf_dynptr_read(event.probe_bytes, sizeof(event.probe_bytes),
|
||||
&dynptr, probe_offset, 0);
|
||||
event.deferred_probe_error = err;
|
||||
if (err)
|
||||
__sync_fetch_and_add(&stats.deferred_probe_errors, 1);
|
||||
}
|
||||
bpf_dynptr_file_discard(&dynptr);
|
||||
|
||||
if (!event.header_error && header[0] == 0x7f && header[1] == 'E' &&
|
||||
header[2] == 'L' && header[3] == 'F') {
|
||||
event.is_elf = 1;
|
||||
event.elf_class = header[EI_CLASS];
|
||||
event.elf_data = header[EI_DATA];
|
||||
event.elf_type = read_elf_u16(header, 16, event.elf_data);
|
||||
event.elf_machine = read_elf_u16(header, 18, event.elf_data);
|
||||
}
|
||||
|
||||
put_file:
|
||||
bpf_put_file(file);
|
||||
emit:
|
||||
if (bpf_ringbuf_output(&events, &event, sizeof(event), 0))
|
||||
__sync_fetch_and_add(&stats.dropped, 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
SEC("lsm/bprm_committed_creds")
|
||||
void BPF_PROG(schedule_exec_inspection, struct linux_binprm *bprm)
|
||||
{
|
||||
struct task_struct *task;
|
||||
struct exec_work *work;
|
||||
__u64 pid_tgid;
|
||||
__u32 key = 0, tgid;
|
||||
int err;
|
||||
|
||||
pid_tgid = bpf_get_current_pid_tgid();
|
||||
tgid = pid_tgid >> 32;
|
||||
if (target_tgid && tgid != target_tgid)
|
||||
return;
|
||||
|
||||
__sync_fetch_and_add(&stats.matched, 1);
|
||||
work = bpf_map_lookup_elem(&pending, &key);
|
||||
if (!work) {
|
||||
__sync_fetch_and_add(&stats.schedule_errors, 1);
|
||||
return;
|
||||
}
|
||||
|
||||
work->scheduled_ns = bpf_ktime_get_ns();
|
||||
work->direct_probe_error = probe_file_without_sleep(bprm->file);
|
||||
task = bpf_get_current_task_btf();
|
||||
err = bpf_task_work_schedule_signal(task, &work->work, &pending,
|
||||
inspect_executable);
|
||||
if (err) {
|
||||
__sync_fetch_and_add(&stats.schedule_errors, 1);
|
||||
return;
|
||||
}
|
||||
__sync_fetch_and_add(&stats.scheduled, 1);
|
||||
}
|
||||
Reference in New Issue
Block a user